装配中的灰度滤镜不适用于较小的图像

问题描述 投票:0回答:1

我在汇编中编写的灰度滤镜有问题 - 较大图像上的结果很好,但是当我尝试在较小图像(例如 5x1 位图)上测试它时,显示的不是预期结果,而是显示颜色和平均值计算不正确。

位图为RGB格式,并按照BGR的顺序填充values数组

要过滤的位图(2px 黑色 3px 白色):
base bitmap

预期结果:
expected result

我的结果:
my result

我的大图结果:
my result on bigger image

代码:

.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);
}

目前我主要试图弄清楚颜色如何显示,据我发现,黄色和蓝色是由于部分取自黑色像素的值造成的,因此平均变化一点点,它不只是白色,而是显示黄色,然后通常是白色(周围没有黑色像素),然后是浅蓝色。

即使经过大量调试,我也无法理解为什么较大的图像可以完美地工作,而较小的图像则不能。

assembly image-processing x86 simd sse
1个回答
0
投票

您的代码中存在算法问题,梯度示例加剧了该问题,但在具有非常微妙梯度的图像中不太明显。

正如您所描述的,您的像素格式是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,因为这比浮点运算更快)。最后交错存储。

确保不要过度读取输入或写入超过输出末尾!

© www.soinside.com 2019 - 2024. All rights reserved.