javascript - 使用按位运算符(JSFeat)修改图像像素

标签 javascript canvas computer-vision bitwise-operators jsfeat

我正在使用JSFeat Computer Vision Library,并尝试将图像转换为灰度。函数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);

提前致谢!

最佳答案

这是一个广泛的话题,但是我将尝试粗略地介绍基础知识,以了解此处发生的情况。
众所周知,它使用的是32位整数值,这意味着您可以使用更少的CPU指令同时对四个字节进行操作,因此在许多情况下可以提高整体性能。
速成类(class)
通常将32位值表示为十六进制,如下所示:

0x00000000
代表从右边的最低有效位0到左边的最高有效位31开始的等价位。当然,位只能是on / set / 1或off / unset / 0。 4位是半字节,2个半字节是一个字节。十六进制值的每个半字节都为一位,因此这里有8个半字节= 4字节或32位。与十进制表示法一样,前导0对值没有影响,即0xff0x000000ff相同(0x前缀也对值无效;这只是十六进制数字的传统C表示法,后来被大多数人接管了其他通用语言)。
操作数
您可以对这些值进行位移位并直接执行诸如AND,OR,NOT,XOR之类的逻辑运算(用汇编语言,您将从指针/地址中获取值并将其加载到注册表中,然后在该注册表上执行这些操作)。
那么会发生什么:<<表示向左移位。在这种情况下,值为:
0xff
或以二进制(位)表示(半字节0xf = 1111):
0b11111111
这与以下内容相同:
0x000000ff
或二进制格式(不幸的是,实际上我们无法在JavaScript中原生表示位表示,ES6中存在0b -prefix):
0b00000000 00000000 00000000 11111111
然后向左移24位,得到新值:
0b00000000 00000000 00000000 11111111
                         << 24 bit positions =
0b11111111 00000000 00000000 00000000
要么
0xff000000
那么,为什么在这里需要这样做? 好,这是一个很好的问题!
与 Canvas 相关的32位值表示RGBA,每个组件的值可以在0到255之间,或者以十六进制在0x00到0xff之间。但是,由于当今大多数消费类CPU使用低位字节序,因此每种颜色的分量都以ABGR而不是RGBA的32位值存储在内存级别。
通常,我们通常使用JavaScript之类的高级语言对此进行抽象,但是由于我们现在通过类型化数组直接处理内存字节,因此我们也必须考虑这一方面,并​​考虑注册表宽度(此处为32-位)。
因此,在这里我们尝试将Alpha通道设置为255(完全不透明),然后将其移位24位,使其处于正确的位置:
0xff000000
0xAABBGGRR
(不过,这在这里是不必要的步骤,因为他们也可以直接将其设置为0xff000000,这样会更快,但是可以)。
接下来,我们将OR(|)运算符与移位结合使用。我们首先进行移位以使该值位于正确的位位置,然后将其与现有值进行“或”运算。
如果设置了现有位或新位,则OR会设置一个位,否则它将保持为0。F.ex从现有值开始,现在保留alpha通道值:
0xff000000
然后,我们希望将蓝色值(例如0xcc(十进制为204))组合起来,当前以32位表示为:
0x000000cc
因此,在这种情况下,我们需要先将其左移16位:
0x000000cc
     << 16 bits
0x00cc0000
现在,当我们将该值与现有的alpha值进行或运算时,将得到:
   0xff000000
OR 0x00cc0000
 = 0xffcc0000
由于目标全是0位,因此只设置了源(0xcc)的值,这就是我们想要的(我们可以使用AND删除不需要的位,但这又是一天了)。
以此类推,对于绿色和红色成分(对它们进行“或”运算的顺序并不重要)。
所以这行就做,让我们说pix = 0xcc:
data_u32[i] = alpha | (pix << 16) | (pix << 8) | pix;
转换为:
alpha     = 0xff000000  Alpha
pix       = 0x000000cc  Red
pix <<  8 = 0x0000cc00  Green
pix << 16 = 0x00cc0000  Blue
和OR'ed在一起将变成:
value     = 0xffcccccc
由于所有组件的值相同,因此我们具有灰度值。我们具有正确的字节顺序,并且可以使用单个操作(无论如何在JS中)将其写回到Uint32缓冲区。
您可以通过使用硬编码值(而不是引用)来优化此行,因为我们知道它的作用(如果alpha通道发生变化,那么您当然需要以与其他值相同的方式读取alpha分量值):
data_u32[i] = 0xff000000 | (pix << 16) | (pix << 8) | pix;
如前所述,使用整数,位和位运算符是一个广泛的话题,但这只是表面问题,但希望足以使它更加清楚这种情况下的情况。

关于javascript - 使用按位运算符(JSFeat)修改图像像素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37583915/

相关文章:

python - 使用 opencv_traincascade 创建 haar 分类器

computer-vision - 在 Colab 中,使用 "imgaug"进行图像数据增强未按预期工作

javascript - DateRangePicker - 从第一个输入选定值开始的第二个输入的最小日期

javascript - 简单的 Javascript 不执行任何操作,控制台中没有错误

java - 三角形路径的弯曲边?

Android绘制动画线

javascript - 将连续帧绘制到 HTML5 Canvas 仅显示最后一帧

javascript - 如何将 lightGallery.js 用于多行?

javascript - JS单元测试: run tests on file changes (like nodemon)

python - 二值化图像数据