Ran*_*y W 2 javascript canvas bitwise-operators computer-vision jsfeat
我正在使用JSFeat计算机视觉库,我正在尝试将图像转换为灰度.函数jsfeat.imgproc.grayscale输出到矩阵(下面的img_u8),其中每个元素都是0到255之间的整数.我不确定如何将这个矩阵应用于原始图像,所以我在https://inspirit.github中查看了他们的示例. io/jsfeat/sample_grayscale.htm.
下面是我将图像转换为灰度的代码.我采用他们的方法更新原始图像中的像素,但我不明白它是如何工作的.
/**
* I understand this stuff
*/
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let img = document.getElementById('img-in');
ctx.drawImage(img, 0, 0, img.width, img.height);
let imageData = ctx.getImageData(0, 0, img.width, img.height);
let img_u8 = new jsfeat.matrix_t(img.width, img.height, jsfeat.U8C1_t);
jsfeat.imgproc.grayscale(imageData.data, img.width, img.height, img_u8);
let data_u32 = new Uint32Array(imageData.data.buffer);
let i = img_u8.cols*img_u8.rows, pix = 0;
/**
* Their logic to update the pixel values of the original image
* I need help understanding how the following works
*/
let alpha = (0xff << 24);
while(--i >= 0) {
pix = img_u8.data[i];
data_u32[i] = alpha | (pix << 16) | (pix << 8) | pix;
}
/**
* I understand this stuff
*/
context.putImageData(imageData, 0, 0);
Run Code Online (Sandbox Code Playgroud)
提前致谢!
小智 5
这是一个广泛的主题,但我会尝试大致涵盖基础知识,以了解这里发生了什么.
我们知道,它使用的是32位整数值,这意味着您可以使用更少的CPU指令同时操作四个字节,因此在许多情况下可以提高整体性能.
32位值通常标记为十六进制,如下所示:
0x00000000
Run Code Online (Sandbox Code Playgroud)
并且表示从右边的位0开始到左边的位31的位的等效位.有点当然只能打开/关闭或关闭/取消设置.4位是半字节,2个半字节是一个字节.十六进制值显示八位字节等于半字节,所以这里有8个半字节/八位字节= 4个字节或32位.不需要标记前缀为0的值,即值0xff与0x000000ff相同.
您可以直接对这些值进行位移和执行逻辑运算,例如AND,OR,NOT,XOR(在汇编语言中,您将从指针/地址获取值并将其加载到注册表中,然后在该注册表上执行这些操作).
那么会发生什么:
这<<意味着向左移位.在这种情况下,值为:
0xff
Run Code Online (Sandbox Code Playgroud)
或二进制(位)表示(半字节0xf = 1111):
0b11111111
Run Code Online (Sandbox Code Playgroud)
这与:
0x000000ff
Run Code Online (Sandbox Code Playgroud)
或二进制(遗憾的是,我们无法在JavaScript中本地表示位表示实际上,0bES6中有-prefix):
0b00000000 00000000 00000000 11111111
Run Code Online (Sandbox Code Playgroud)
然后位移到左侧24位位置,产生新值:
0b00000000 00000000 00000000 11111111
<< 24 bit positions =
0b11111111 00000000 00000000 00000000
Run Code Online (Sandbox Code Playgroud)
要么
0xff000000
Run Code Online (Sandbox Code Playgroud)
那为什么这里有必要呢?嗯,这是一个很好的问题!
与canvas相关的32位值表示RGBA,每个组件可以具有0到255之间的值,或者以十六进制表示0x00和0xff之间的值.但是,由于目前大多数消费类CPU都使用little-endian字节顺序,因此颜色的每个组件在内存级别存储为ABGR而不是RGBA用于32位值.
我们通常用JavaScript等高级语言来抽象,但是由于我们现在通过类型化数组直接处理内存字节,我们也必须考虑这个方面,并且与注册表宽度有关(这里是32-位).
所以这里我们尝试将alpha通道设置为255(完全不透明),然后将其移位24位,使其变为正确的位置:
0xff000000
0xAABBGGRR
Run Code Online (Sandbox Code Playgroud)
(虽然,这是一个不必要的步骤,因为他们可以直接将其设置为0xff000000,这将更快,但anyhoo).
接下来我们使用OR(|)运算符结合位移.我们先移位以获得正确位位置的值,然后将其移到现有值上.
如果设置了现有位或新位,OR将设置一个位,否则它将保持为0. F.ex以现有值开始,现在保持alpha通道值:
0xff000000
Run Code Online (Sandbox Code Playgroud)
然后,我们想要将值为0xcc(十进制为204)的蓝色分量组合在一起,当前以32位表示为:
0x000000cc
Run Code Online (Sandbox Code Playgroud)
所以我们需要在这种情况下首先将它向左移16位:
0x000000cc
<< 16 bits
0x00cc0000
Run Code Online (Sandbox Code Playgroud)
当我们现在将该值与现有的alpha值相加时,我们得到:
0xff000000
OR 0x00cc0000
= 0xffcc0000
Run Code Online (Sandbox Code Playgroud)
由于目的地全是0位,所以只设置源(0xcc)的值,这就是我们想要的(我们可以使用AND来删除不需要的位,但这是另一天).
对于绿色和红色成分等等(它们在OR中的顺序并不重要).
那么这条线就行了,让我们说pix = 0xcc:
data_u32[i] = alpha | (pix << 16) | (pix << 8) | pix;
Run Code Online (Sandbox Code Playgroud)
这意味着:
alpha = 0xff000000 Alpha
pix = 0x000000cc Red
pix << 8 = 0x0000cc00 Green
pix << 16 = 0x00cc0000 Blue
Run Code Online (Sandbox Code Playgroud)
和OR'ed一起将成为:
value = 0xffcccccc
Run Code Online (Sandbox Code Playgroud)
我们有一个灰色值,因为所有组件都具有相同的值.我们有正确的字节顺序,可以使用单个操作将其写回Uint32缓冲区(无论如何都是JS).
你可以通过使用硬编码的alpha而不是参考来优化这一行,因为我们知道它的作用(如果alpha通道有所不同,那么当然你需要以与其他值相同的方式读取alpha分量值):
data_u32[i] = 0xff000000 | (pix << 16) | (pix << 8) | pix;
Run Code Online (Sandbox Code Playgroud)
使用整数,位和位运算符是一个广泛的主题,这只是表面上的划分,但希望足以使这个特定情况下发生的事情变得更清楚.