我正在开展一个大学项目,尝试实现一种在图像上模拟不同类型色盲(绿色盲、红色盲、蓝色盲)的算法。目标是转换 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
你能帮我吗?
原图:
它应该是什么样子:
我的汇编输出:
我想知道您是否阅读了我对您之前问题的回答有关此计划的信息!
你又在重复同样的错误了。
仅举三个:
; 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 不是一个单独的寄存器。