在C#和ASM中应用色盲模拟后图像出现黑洞问题

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

我正在开展一个大学项目,尝试实现一种在图像上模拟不同类型色盲(绿色盲、红色盲、蓝色盲)的算法。目标是转换 RGB 彩色图像 (24 bpp) 以模拟它们在不同类型色盲的个体中的显示效果。

不幸的是,在应用红色盲和绿色盲的变换后,生成的图像显示黑洞。图像看起来几乎是黑色的,有一些奇怪的扭曲,类似于“洞”。我怀疑 RGB 通道转换或缩放可能有问题,但我无法查明确切的问题。

问题描述:

我使用 C# 来处理图像处理和文件 I/O。 我编写了汇编代码 (ASM) 来模拟色盲转换。 运行模拟后,处理后的图像几乎全黑,并带有奇怪的“孔状”区域。 我尝试过的:

仔细检查每种色盲类型(绿色盲、红色盲)的转换。 确保 RGB 值正确计算并限制在 0-255 范围内。 验证图像在 C# 中正确加载和保存。

c#代码:

    private void ProcessColorBlindnessAsm(int blindnessType)
{
    if (_originalImage == null || _processedImage == null)
    {
        MessageBox.Show("Błąd: Image not loaded.");
        return;
    }

    Rectangle rect = new Rectangle(0, 0, _originalImage.Width, _originalImage.Height);
    BitmapData originalData = _originalImage.LockBits(rect, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    BitmapData processedData = _processedImage.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

    int pixelCount = _originalImage.Width * _originalImage.Height;
    int stride = originalData.Stride;

    IntPtr originalPtr = originalData.Scan0;
    IntPtr processedPtr = processedData.Scan0;

    try
    {
        Parallel.For(0, Environment.ProcessorCount, partition =>
        {
            int rowsPerPartition = _originalImage.Height / Environment.ProcessorCount;
            int startRow = partition * rowsPerPartition;
            int endRow = (partition == Environment.ProcessorCount - 1) ? _originalImage.Height : startRow + rowsPerPartition;

            int partitionPixelCount = (endRow - startRow) * _originalImage.Width;
            IntPtr originalPartitionPtr = IntPtr.Add(originalPtr, startRow * stride);
            IntPtr processedPartitionPtr = IntPtr.Add(processedPtr, startRow * stride);

            // Pass blindnessType to the ASM function
            DeuteranopiaAsm(originalPartitionPtr, processedPartitionPtr, partitionPixelCount, stride, blindnessType);
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Error while proccesing asm: {ex.Message}");
    }
    finally
    {
        _originalImage.UnlockBits(originalData);
        _processedImage.UnlockBits(processedData);
    }
}

asm代码:

    .code
DeuteranopiaAsm proc

SimulateColorBlindnessASM:
    ; RCX = originalImage (pointer)
    ; RDX = processedImage (pointer)
    ; R8  = pixelCount (number of pixels)
    ; R9  = stride (bytes per row)
    ; R10 = blindnessType (0 = Deuteranopia, 1 = Protanopia, 2 = Tritanopia)

    mov r11, rcx              ; Save pointer to original image
    mov r12, rdx              ; Save pointer to processed image

    xor rbx, rbx              ; Use RBX as pixel counter

PixelLoop:
    cmp rbx, r8               ; Check if all pixels are processed
    jge EndLoop               ; If rbx >= pixelCount, exit

    mov rax, rbx              ; Copy pixel counter to rax
    shl rax, 1                ; rax = rbx * 2 (shift left by 1)
    add rax, rbx              ; rax = rax + rbx = rbx * 3

    ; Load color data (B, G, R)
    mov al, byte ptr [r11 + rax]     ; Load blue channel
    mov cl, byte ptr [r11 + rax + 1] ; Load green channel
    mov dl, byte ptr [r11 + rax + 2] ; Load red channel

    ; Select transformation based on blindnessType
    cmp r10, 0               ; Check if Deuteranopia
    je SimulateDeuteranopia
    cmp r10, 1               ; Check if Protanopia
    je SimulateProtanopia
    cmp r10, 2               ; Check if Tritanopia
    je SimulateTritanopia
    jmp EndPixel             ; Skip if invalid type

SimulateDeuteranopia:
    ; Transform for Deuteranopia
    movzx r8d, dl              ; Convert red channel to 32-bit
    imul r8d, 625              ; R = R * 0.625
    movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 375              ; G = G * 0.375
    add r8d, r9d               ; New Red = (R * 0.625 + G * 0.375)
    shr r8d, 8                 ; Divide by 256 to fit in byte range
    
     ; Green transformation (G = G * 0.7)
    movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 700              ; G = G * 0.7
    shr r9d, 10                ; Divide by 1024 to fit in byte range

    ; Blue transformation (B = B * 0.8)
    movzx r10d, al             ; Convert blue channel to 32-bit
    imul r10d, 800             ; B = B * 0.8
    shr r10d, 10               ; Divide by 1024 to fit in byte range

    ; Store processed pixel back into the image
    mov byte ptr [r12 + rax], al     ; Store blue channel
    mov byte ptr [r12 + rax + 1], cl ; Store green channel
    mov byte ptr [r12 + rax + 2], dl ; Store red channel

    jmp EndPixel

SimulateProtanopia:
    ; Transform for Protanopia
    movzx r8d, dl              ; Convert red channel to 32-bit
    imul r8d, 567              ; R = R * 0.567
    movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 433              ; G = G * 0.433
    add r8d, r9d               ; New Red = (R * 0.567 + G * 0.433)
    shr r8d, 8                 ; Divide by 256
    
     ; Green transformation (G = G * 0.558)
    movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 558              ; G = G * 0.558
    shr r9d, 10                ; Divide by 1024 to fit in byte range

    ; Blue transformation (B = B * 0.0)
    xor r10d, r10d             ; Set blue channel to 0

    ; Store processed pixel back into the image
    mov byte ptr [r12 + rax], al     ; Store blue channel
    mov byte ptr [r12 + rax + 1], cl ; Store green channel
    mov byte ptr [r12 + rax + 2], dl ; Store red channel

    jmp EndPixel

SimulateTritanopia:
    ; Transform for Tritanopia
    movzx r8d, dl              ; Convert red channel to 32-bit
    imul r8d, 950              ; R = R * 0.95
    movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 433              ; G = G * 0.433
    add r8d, r9d               ; New Red = (R * 0.95 + G * 0.433)
    shr r8d, 8                 ; Divide by 256
   
   movzx r9d, cl              ; Convert green channel to 32-bit
    imul r9d, 433              ; G = G * 0.433
    shr r9d, 10                ; Divide by 1024 to fit in byte range

    ; Blue transformation (B = B * 0.567)
    movzx r10d, al             ; Convert blue channel to 32-bit
    imul r10d, 567             ; B = B * 0.567
    shr r10d, 10               ; Divide by 1024 to fit in byte range

    ; Store processed pixel back into the image
    mov byte ptr [r12 + rax], al     ; Store blue channel
    mov byte ptr [r12 + rax + 1], cl ; Store green channel
    mov byte ptr [r12 + rax + 2], dl ; Store red channel

    jmp EndPixel

EndPixel:
    ; Store processed pixel back into the image
    mov byte ptr [r12 + rax], al     ; Store blue channel
    mov byte ptr [r12 + rax + 1], cl ; Store green channel
    mov byte ptr [r12 + rax + 2], dl ; Store red channel

    inc rbx                   ; Increment pixel counter
    jmp PixelLoop             ; Repeat for next pixel

EndLoop:
    ret
DeuteranopiaAsm endp
end

你能帮我吗?

原图:

original image

它应该是什么样子:

how it should look like

我的汇编输出:

my asm output

c# assembly x86-64 nasm
1个回答
0
投票

我想知道您是否阅读了我对您之前问题的回答有关此计划的信息!

你又在重复同样的错误了。
仅举三个:

; R10 = blindnessType (0 = Deuteranopia, 1 = Protanopia, 2 = Tritanopia)

第 5 个参数在堆栈上传递(在 32 字节影子空间之上)。

mov rax, rbx              ; Copy pixel counter to rax
shl rax, 1                ; rax = rbx * 2 (shift left by 1)
add rax, rbx              ; rax = rax + rbx = rbx * 3

我的代码版本向您展示了一种更好的方法来寻址 RGB 字节,而不需要这种丑陋的

* 3
计算。

mov al, byte ptr [r11 + rax]     ; Load blue channel

您不能使用 AL 来保存蓝色通道 同时使用 RAX 作为像素数据中的偏移量。 AL 是 RAX 寄存器的最低字节。 AL 不是一个单独的寄存器

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