如何比较JavaScript / WebGL2中的2个纹理



我正在为图像处理算法编写片段着色器。着色器将在一个循环(乒乓球(中的两个框架缓冲器之间多次运行。在某个时候,当输入和输出纹理相同时,我需要停止循环。

我打算做的是Canny Edge检测器算法的最后一步," Hysterezis Edge Tracking"。我想实时进行Canny算法的GPU/WebGL2版本并将其上传到网站。

最后一步如下:
给定包含"强"边缘像素(1.0(和"弱"边缘像素(0.5(

的双阈值图像
  • 找到与坚固像素相连的弱像素的所有链条,并将它们标记为"强"

  • 保持所有"强"像素并丢弃所有剩余的"弱"。

这可以在循环中多次运行的片段着色器中实现。如果其8像素社区中至少有一个强的像素,那么当前的"弱"像素被标记为"强"。在每一次迭代中,我们都应该拥有更强大的像素和较少的像素。最后,仅保留弱像素的孤立链。这是碎片着色器成为通行的着色器的点,应检测到停止循环。

更新2019年9月:我在此处上传了GPU Canny边缘检测器http://www.oldrinb.info/dip/canny/。它在具有WebGL2支持的浏览器中工作,以及支持WebGL1和" WebGL_Draw_Buffers"扩展程序的浏览器。我还将不久将源代码放到github。

我不是100%确定您在问什么。您要在CPU上进行比较。您可以通过将纹理连接到框架缓冲器,然后调用gl.readPixels来读取纹理的内容。然后,您可以比较所有像素。注意:并非所有纹理格式都可以连接到框架缓冲器上,但假设您使用的是可以使用的格式。您已经将纹理附加到架子架上的架子上,所以您还想要什么?

就像我在GPU上的评论中写的那样,您可以编写一个着色器来比较2个纹理

#version 300 es
precision highp float;
uniform sampler2D tex1;
uniform sampler2D tex2;
out vec4 outColor;
void main() {
  ivec2 size = textureSize(tex1, 0);  // size of mip 0
  float len = 0.0;
  for (int y = 0; y < size.y; ++y) {
    for (int x = 0; x < size.x; ++x) {
      vec4 color1 = texelFetch(tex1, ivec2(x, y), 0);
      vec4 color2 = texelFetch(tex2, ivec2(x, y), 0);
      vec4 diff = color1 - color2;
      len = length(diff);
      if (len > 0.0) break;
    }
    if (len > 0.0) break;
  }
  outColor = mix(vec4(0), vec4(1), step(len, 0.0));
}

现在只绘制1个像素,然后用readpixels读取它。如果是0,则纹理相同。如果不是,它们是不同的。

代码假定纹理的大小相同,但是当然,如果它们的大小不一样,那么我们已经知道它们不能相同。

// make 3 canvaes as sources for textures
const canvases = ['A', 'B', 'B'].map((msg) => {
  const canvas = document.createElement('canvas');
  canvas.width = 128;
  canvas.height = 128;
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = 'blue';
  ctx.fillRect(0, 0, 128, 128);
  ctx.font = '80px monospace';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = 'yellow';
  ctx.fillText(msg, 64, 64);
  document.body.appendChild(canvas);
  return canvas;
});
const gl = document.createElement('canvas').getContext('webgl2');
if (!gl) { alert('need webgl2'); }
const vs = `#version 300 es
void main() {
  gl_PointSize = 1.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision highp float;
uniform sampler2D tex1;
uniform sampler2D tex2;
out vec4 outColor;
void main() {
  ivec2 size = textureSize(tex1, 0);  // size of mip 0
  float len = 0.0;
  for (int y = 0; y < size.y; ++y) {
    for (int x = 0; x < size.x; ++x) {
      vec4 color1 = texelFetch(tex1, ivec2(x, y), 0);
      vec4 color2 = texelFetch(tex2, ivec2(x, y), 0);
      vec4 diff = color1 - color2;
      len = length(diff);
      if (len > 0.0) break;
    }
    if (len > 0.0) break;
  }
  outColor = mix(vec4(0), vec4(1), step(len, 0.0));
}
`;
// compile shaders, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const textures = canvases.map((canvas) => {
  // gl.createTexture, gl.bindTexture, gl.texImage, etc.
  return twgl.createTexture(gl, {src: canvas});
});
compareTextures(0, 1);
compareTextures(1, 2);
function compareTextures(ndx1, ndx2) {
  gl.useProgram(programInfo.program);
  
  // gl.activeTexture, gl.bindTexture, gl.uniform
  twgl.setUniforms(programInfo, {
    tex1: textures[ndx1],
    tex2: textures[ndx2],
  });
  
  // draw the bottom right pixel
  gl.viewport(0, 0, 1, 1);
  
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  
  // read the pixel
  const result = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, result);
  
  console.log('textures', ndx1, 'and', ndx2, 'are', result[0] ? 'the same' : 'not the same'); 
}
canvas { padding: 5px; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

您也可以使用遮挡查询。优点是它们可能不会像读取像素那样阻止GPU。负是您无法在同一JavaScript事件中检查它们,因此它们可能不符合您的需求

// make 3 canvaes as sources for textures
const canvases = ['A', 'B', 'B'].map((msg) => {
  const canvas = document.createElement('canvas');
  canvas.width = 128;
  canvas.height = 128;
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = 'blue';
  ctx.fillRect(0, 0, 128, 128);
  ctx.font = '80px monospace';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = 'yellow';
  ctx.fillText(msg, 64, 64);
  document.body.appendChild(canvas);
  return canvas;
});
const gl = document.createElement('canvas').getContext('webgl2');
if (!gl) { alert('need webgl2'); }
const vs = `#version 300 es
void main() {
  gl_PointSize = 1.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision highp float;
uniform sampler2D tex1;
uniform sampler2D tex2;
out vec4 outColor;
void main() {
  ivec2 size = textureSize(tex1, 0);  // size of mip 0
  float len = 0.0;
  for (int y = 0; y < size.y; ++y) {
    for (int x = 0; x < size.x; ++x) {
      vec4 color1 = texelFetch(tex1, ivec2(x, y), 0);
      vec4 color2 = texelFetch(tex2, ivec2(x, y), 0);
      vec4 diff = color1 - color2;
      len = length(diff);
      if (len > 0.0) break;
    }
    if (len > 0.0) break;
  }
  if (len > 0.0) {
    discard;
  }
  outColor = vec4(1);
}
`;
// compile shaders, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const textures = canvases.map((canvas) => {
  // gl.createTexture, gl.bindTexture, gl.texImage, etc.
  return twgl.createTexture(gl, {src: canvas});
});
function wait(ms = 0) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
async function test() {
  await compareTextures(0, 1);
  await compareTextures(1, 2);
}
test();
async function compareTextures(ndx1, ndx2) {
  gl.clear(gl.DEPTH_BUFFER_BIT);
  gl.enable(gl.DEPTH_TEST);
  gl.useProgram(programInfo.program);
  
  // gl.activeTexture, gl.bindTexture, gl.uniform
  twgl.setUniforms(programInfo, {
    tex1: textures[ndx1],
    tex2: textures[ndx2],
  });
  
  // draw the bottom right pixel
  gl.viewport(0, 0, 1, 1);
  
  const query = gl.createQuery();
  gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  gl.endQuery(gl.ANY_SAMPLES_PASSED);
  gl.flush();
  
  let ready = false;
  while(!ready) {
    await wait();
    ready = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
  }
  
  const same = gl.getQueryParameter(query, gl.QUERY_RESULT);
  
  console.log('textures', ndx1, 'and', ndx2, 'are', same ? 'the same' : 'not the same'); 
}
canvas { padding: 5px; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

最新更新