我在汇编中编写的灰度滤镜有问题 - 较大图像上的结果很好,但是当我尝试在较小图像(例如 5x1 位图)上测试它时,显示的不是预期结果,而是显示颜色和平均值计算不正确。
位图为RGB格式,并按照BGR的顺序填充values数组
要过滤的位图(2px 黑色 3px 白色):
预期结果:
我的结果:
我的大图结果:
代码:
.data
three_values dd 0.333333, 0.333333, 0.333333, 0.333333 ; creating a vector filled with values 3.0
.code
asmFilter proc ; procedure
mov rsi, rcx ; load our raw bitmap
mov rdi, [rsp+85] ; load a pointer to the beginning of the processed image into rdi
mov rcx, rdx ; load the width of the image into register rcx
imul rcx, r9 ; multiply it by the pixel size to get the stride
mov r10, rcx ; load the value of stride into register r10
imul rcx, r8 ; calculate the size of the image by multiplying the stride by the height
mov rax, 0 ; initialize the counter
movups xmm2, [three_values] ; load the pointer to the vector of threes into xmm2
processLoop:
cmp rax, rcx ; compare the counter with the size of the image
jae doneCopy ; if they are equal, end the loop
; processing the middle value [i]
mov r11, rsi ; load the pointer to the first pixel of our bitmap into r11
pmovzxbd xmm0, [r11 + rax] ; load the middle value into xmm0 (shift by the counter)
; processing the value to the left [i-1]
pmovzxbd xmm1, [r11 + rax - 1]
paddd xmm0, xmm1
; processing the value to the right [i+1]
pmovzxbd xmm1, [r11 + rax + 1]
paddd xmm0, xmm1
; converting int to float
cvtdq2ps xmm3, xmm0
; (R + G + B) * 0.3
mulps xmm3, xmm2
; converting float to int
cvtps2dq xmm0, xmm3
; writing the processed pixel to the result
movups [rdi + rax], xmm0
add rax, 1 ; increment the counter by 1
jmp processLoop ; repeat the loop
doneCopy:
ret
asmFilter endp
end
asmFilter签名:
private static extern void asmFilter(byte[] values, int width, int height, int pixelSize, byte[] converted_values);
public void executeAsmFilter(byte[] values, int width, int height, int pixelSize, byte[] converted_values)
{
asmFilter(values, width, height, pixelSize, converted_values);
}
目前我主要试图弄清楚颜色如何显示,据我发现,黄色和蓝色是由于部分取自黑色像素的值造成的,因此平均变化一点点,它不只是白色,而是显示黄色,然后通常是白色(周围没有黑色像素),然后是浅蓝色。
即使经过大量调试,我也无法理解为什么较大的图像可以完美地工作,而较小的图像则不能。
您的代码中存在算法问题,梯度示例加剧了该问题,但在具有非常微妙梯度的图像中不太明显。
正如您所描述的,您的像素格式是BGR。因此,在图像的开头,输入如下所示:
R11 + 0: B0
R11 + 1: G0
R11 + 2: R0
R11 + 3: B1
R11 + 4: G1
R11 + 5: R1
R11 + 6: B2
R11 + 7: G2
R11 + 8: R2
要正确转换为灰度,您希望最终得到
RDI + 0: B0/3 + G0/3 + R0/3
RDI + 1: B0/3 + G0/3 + R0/3
RDI + 2: B0/3 + G0/3 + R0/3
RDI + 3: B1/3 + G1/3 + R1/3
RDI + 4: B1/3 + G1/3 + R1/3
RDI + 5: B1/3 + G1/3 + R1/3
RDI + 6: B2/3 + G2/3 + R2/3
RDI + 7: B2/3 + G2/3 + R2/3
RDI + 8: B2/3 + G2/3 + R2/3
这是你的算法一开始所做的事情:
pmovzxbd xmm0, [r11 + rax]
这会加载
xmm0
[B0, G0, R0, B1]。
pmovzxbd xmm1, [r11 + rax - 1]
这会加载
xmm1
[??, B0, G0, R0],其中 ??是未定义值数组开头之前的一个字节。我将继续假设该字节现在的值为 0。
paddd xmm0, xmm1
这会将
xmm0
设置为 [B0, G0 + B0, R0 + G0, B1 + R0]。
pmovzxbd xmm1, [r11 + rax + 1]
这会将
xmm1
设置为 [G0, R0, B1, G1]。
paddd xmm0, xmm1
这会将
xmm0
设置为 [B0 + G0, R0 + G0 + B0, R0 + G0 + B1, B1 + R0 + G1]。
然后将该向量转换为浮点数并除以 3,然后转换回整数。最后你这样做:
movups [rdi + rax], xmm0
将双字向量写入输出。但是...您的输出应该是字节位图,而不是双字位图!幸运的是,您的代码也被破坏了,因为它在下一次迭代中继续以 1 字节的偏移量继续,覆盖了您刚刚编写的所有内容,除了第一个字节(它恰好是第一个像素值的最低有效字节)。经过一系列迭代后,您最终会得到如下输出:
RDI + 0: B0/3 + G0/3
RDI + 1: B0/3 + G0/3 + R0/3
RDI + 2: G0/3 + R0/3 + B1/3
RDI + 3: R0/3 + B1/3 + G1/3
RDI + 4: B1/3 + G1/3 + R1/3
RDI + 5: G1/3 + R1/3 + B2/3
RDI + 6: R1/3 + B2/3 + G2/3
RDI + 7: B2/3 + G2/3 + R2/3
RDI + 8: G2/3 + R2/3 + B3/3
看看它是如何涂抹在像素上的,而不是每个像素的通道是该像素通道的平均值?这就是导致错误输出的原因。要修复代码,请重写它,使其实际上分别对每个像素的 B/G/R 求和,而不是执行您所拥有的那种运行总和。
实现这一点有点棘手。例如,您可以首先对像素进行解交织,这样您就有一个所有 B 的向量、一个所有 R 的向量和一个所有 G 的向量。如果有,您可以上转换为 16 位,求和,然后除以 3(我建议使用
vpmulhw
乘以 65536/3 = 21845,因为这比浮点运算更快)。最后交错存储。
确保不要过度读取输入或写入超过输出末尾!