转载自阿发伯:http://blog.csdn.net/maozefa/article/details/8316430
阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。
本文在《GDI+ ColorMatrix的完全揭秘》的ColorMatrix原理揭秘的基础上,用Delphi代码来完整实现GDI+的ColorMatrix功能。
GDI+中设置ColorMatrix时有2个枚举选项,在实际运用中极少使用,所以代码中以GDI+设置ColorMatrix的缺省方式实现。
先给一个简易的浮点版本,因为该过程没有考虑子图处理,所以称之为简易版本,主要方便阅读者理解ColorMatrix实现原理:
- procedure SetColorMatrixF(Data: TImageData; Matrix: TColorMatrix);
- var
- I, J, Count: Integer;
- P: PRGBQuad;
- MainValue: Boolean;
- v: Integer;
-
- procedure SetPixel;
- var
- Pixel: array[0..3] of Byte;
- I, J: Integer;
- ps: PByteArray;
- begin
- ps := Pointer(P);
-
- for I := 0 to 3 do
- begin
- if I < 3 then
- J := 2 - I
- else
- J := I;
-
- if MainValue then
- Pixel[J] := Round(Matrix[I, I] * ps[J])
-
- else
- Pixel[J] := Max(0, Min(255, Round(Matrix[0, I] * ps[2] +
- Matrix[1, I] * ps[1] +
- Matrix[2, I] * ps[0] +
- Matrix[3, I] * ps[3] +
- Matrix[4, I] * 255)));
- end;
- for I := 0 to 3 do
- ps[I] := Pixel[I];
- end;
-
- begin
-
- MainValue := True;
- for I := 0 to 4 do
- for J := 0 to 4 do
- begin
- v := Round(Matrix[I, J]) div 256;
- if v > 0 then
- Matrix[I, J] := Matrix[I, J] - 256.0 * v;
- if (I <> J) and (Matrix[I, J] <> 0) then
- MainValue := False;
- end;
- Count := Data.Width * Data.Height;
- P := Data.Scan0;
- for I := 1 to Count do
- begin
- SetPixel;
- Inc(P);
- end;
- end;
因代码已经有了注释,而实现原理、公式已经在《GDI+ ColorMatrix的完全揭秘》中进行了详尽的介绍,所以本文不再累述。
该过程代码的特点是简单易读,缺点是效率较低,在我的P4 2.8G计算机上,处理一张千万像素的照片,耗时为1000ms左右(不包括GDI+图像格式转换耗时。千万像素的24位格式图像转换为32位格式,耗时就达650ms)。
下面是一个MMX BASM代码的整数ColorMatrix实现过程:
- 过程定义:
-
-
-
-
- procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix); overload;
- {$IF RTLVersion >= 17.00}inline;{$IFEND}
- procedure ImageSetColorMatrix(var Dest: TImageData;
- const Source: TImageData; Matrix: TColorMatrix); overload;
-
- 实现代码:
-
- type
- PARGBQuadW = ^TARGBQuadW;
- TARGBQuadW = packed record
- wBlue: Word;
- wGreen: Word;
- wRed: Word;
- wAlpha: Word;
- end;
-
- procedure ImageSetColorMatrix(var Dest: TImageData;
- const Source: TImageData; Matrix: TColorMatrix);
- asm
- push esi
- push edi
- push ebx
- mov ebx, eax
- mov edi, ecx
- mov esi, 4
- fldz
- @@iLoop:
- mov ecx, 4
- @@jLoop:
- cmp ecx, esi
- je @@1
- mov eax, esi
- imul eax, 5
- add eax, ecx
- fcom dword ptr [edi+eax*4]
- fstsw ax
- sahf
- je @@1
- fstp st(0)
- jmp @@TransformAll
- @@1:
- dec ecx
- jns @@jLoop
- dec esi
- jns @@iLoop
- fstp st(0)
- fwait
-
-
-
- sub esp, 8+2
- mov dword ptr [esp], 256
- fild dword ptr [esp]
- fld st(0)
- fmul dword ptr [edi+(2*5+2)*4]
- fistp dword ptr [esp]
- fld st(0)
- fmul dword ptr [edi+(1*5+1)*4]
- fistp dword ptr [esp+2]
- fld st(0)
- fmul dword ptr [edi+(0*5+0)*4]
- fistp dword ptr [esp+4]
- fmul dword ptr [edi+(3*5+3)*4]
- fistp dword ptr [esp+6]
- mov eax, ebx
- call _SetCopyRegs
- pxor mm7, mm7
- movq mm1, [esp]
- @@yLoop:
- push ecx
- @@xLoop:
- movd mm0, [esi]
- punpcklbw mm0, mm7
- pmullw mm0, mm1
- psrlw mm0, 8
- packuswb mm0, mm0
- movd [edi], mm0
- add esi, 4
- add edi, 4
- loop @@xLoop
- add esi, eax
- add edi, ebx
- pop ecx
- dec edx
- jnz @@yLoop
- add esp, 8+2
- jmp @@end
-
-
-
- @@TransformAll:
- sub esp, 5*8+2
- mov dword ptr [esp], 128
- fild dword ptr [esp]
- mov esi, esp
- mov eax, edi
- mov ecx, 4
- @@cvtLoop:
- fld st(0)
- fmul dword ptr [edi]
- fistp dword ptr [esi]
- fld st(0)
- fmul dword ptr [edi+1*5*4]
- fistp dword ptr [esi+2]
- fld st(0)
- fmul dword ptr [edi+2*5*4]
- fistp dword ptr [esi+4]
- fld st(0)
- fmul dword ptr [edi+3*5*4]
- fistp dword ptr [esi+6]
- add esi, 8
- add edi, 4
- loop @@cvtLoop
- fstp st(0)
- add eax, 4*5*4
- mov dword ptr [esi], 255
- fild dword ptr [esi]
- mov ecx, 4
- @@tLoop:
- fld st(0)
- fmul dword ptr [eax]
- fistp dword ptr [esi]
- add esi, 2
- add eax, 4
- loop @@tLoop
- fstp st(0)
- mov esi, esp
- mov ecx, 5
- @@swapLoop:
- mov ax, [esi].TARGBQuadW.wBlue
- xchg ax, [esi].TARGBQuadW.wRed
- mov [esi].TARGBQuadW.wBlue, ax
- add esi, 8
- loop @@swapLoop
- mov eax, ebx
- call _SetCopyRegs
- pxor mm7, mm7
- pcmpeqb mm4, mm4
- psrlw mm4, 15
- @@yLoopA:
- push ecx
- @@xLoopA:
- movd mm0, [esi]
- punpcklbw mm0, mm7
- movq mm1, mm0
- movq mm2, mm0
- movq mm3, mm0
-
- pmaddwd mm0, [esp+16+4]
- pmaddwd mm1, [esp+8+4]
- pmaddwd mm2, [esp+4]
- pmaddwd mm3, [esp+24+4]
- psrad mm0, 7
- psrad mm1, 7
- psrad mm2, 7
- psrad mm3, 7
- packssdw mm0, mm1
- packssdw mm2, mm3
- pmaddwd mm0, mm4
- pmaddwd mm2, mm4
- packssdw mm0, mm2
- paddw mm0, [esp+32+4]
- packuswb mm0, mm0
- movd [edi], mm0
- add esi, 4
- add edi, 4
- loop @@xLoopA
- add esi, eax
- add edi, ebx
- pop ecx
- dec edx
- jnz @@yLoopA
- add esp, 5*8+2
- @@end:
- emms
- @@Exit:
- pop ebx
- pop edi
- pop esi
- end;
-
- procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix);
- begin
- ImageSetColorMatrix(Data, Data, Matrix);
- end;
该过程中作了更详细的注释,其特点是处理速度较快。在我的机器上,不包括图像格式转换耗时,处理千万像素图片主对角线数据耗时不到50ms,而处理全部变换耗时350-400ms。
下面是一个测试程序代码。该测试代码界面与《GDI+ for VCL基础 -- 颜色调整矩阵ColorMatrix详解》是一样的。有兴趣的朋友可以同里面的测试代码作一下比较。
- unit main2;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, Buttons, Grids, ExtCtrls, Gdiplus, ImageData;
-
- type
- TForm1 = class(TForm)
- Label1: TLabel;
- PaintBox1: TPaintBox;
- SpeedButton1: TSpeedButton;
- SpeedButton2: TSpeedButton;
- SpeedButton3: TSpeedButton;
- SpeedButton4: TSpeedButton;
- StringGrid1: TStringGrid;
- BitBtn1: TBitBtn;
- BitBtn3: TBitBtn;
- BitBtn2: TBitBtn;
- procedure FormCreate(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
- Rect: TRect; State: TGridDrawState);
- procedure StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
- var Value: string);
- procedure PaintBox1Paint(Sender: TObject);
- procedure BitBtn1Click(Sender: TObject);
- procedure BitBtn2Click(Sender: TObject);
- procedure BitBtn3Click(Sender: TObject);
- procedure SpeedButton2Click(Sender: TObject);
- procedure SpeedButton3Click(Sender: TObject);
- procedure SpeedButton1Click(Sender: TObject);
- procedure SpeedButton4Click(Sender: TObject);
- procedure StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
- const Value: string);
- private
-
- Source: TGpBitmap;
- Dest: TGpBitmap;
- SrcData: TImageData;
- DstData: TImageData;
- Matrix: TColorMatrix;
-
- function CheckFloatStr(Str: string): Double;
- procedure InitColorMatrix;
- public
-
- end;
-
- var
- Form1: TForm1;
-
- implementation
-
- {$R *.dfm}
-
- procedure TForm1.BitBtn1Click(Sender: TObject);
- begin
- ImageSetColorMatrix(DstData, SrcData, Matrix);
- PaintBox1.Invalidate;
- with StringGrid1 do
- begin
- Cells[Col, Row] := FloatToStr(Matrix[Row, Col]);
- Invalidate;
- SetFocus;
- end;
- end;
-
- procedure TForm1.BitBtn2Click(Sender: TObject);
- begin
- InitColorMatrix;
- BitBtn1.Click;
- end;
-
- procedure TForm1.BitBtn3Click(Sender: TObject);
- begin
- Close;
- end;
-
- function TForm1.CheckFloatStr(Str: string): Double;
- var
- i, len: Integer;
- dec, neg: Boolean;
- s: string;
- begin
- Result := 0;
- len := Length(Str);
- if len = 0 then Exit;
- dec := False;
- neg := False;
- i := 1;
- s := '';
- if (Str[i] = '-') or (Str[i] = '+') then
- begin
- if Str[i] = '-' then neg := True;
- Inc(i);
- end;
- while (i <= len) do
- begin
- if Str[i] = '.' then
- begin
- if dec then Break;
- dec := True;
- end
- else if (Str[i] < '0') or (Str[i] > '9') then Break;
- s := s + Str[i];
- Inc(i);
- end;
- if Length(s) > 0 then
- begin
- if neg then s := '-' + s;
- Result := StrToFloat(s);
- end;
- end;
-
- procedure TForm1.FormCreate(Sender: TObject);
- var
- Bmp: TGpBitmap;
- Data: TBitmapData;
- R: TGpRect;
- begin
-
- Bmp := TGpBitmap.Create('..\..\media\100_0349.jpg');
- R := GpRect(0, 0, Bmp.Width, Bmp.Height);
-
- SrcData := NewImageData(R.Width, R.Height);
- DstData := NewImageData(R.Width, R.Height);
-
- Data := TBitmapData(SrcData);
- Data := Bmp.LockBits(R, [imRead, imWrite, imUserInputBuf], pf32bppARGB);
- Bmp.UnlockBits(Data);
- Data.Scan0 := DstData.Scan0;
- Data := Bmp.LockBits(R, [imRead, imWrite, imUserInputBuf], pf32bppARGB);
- Bmp.UnlockBits(Data);
- Bmp.Free;
-
-
-
- Source := TGpBitmap.Create(SrcData.Width, SrcData.Height, SrcData.Stride,
- pf32bppARGB, SrcData.Scan0);
- Dest := TGpBitmap.Create(DstData.Width, DstData.Height, DstData.Stride,
- pf32bppARGB, DstData.Scan0);
-
- InitColorMatrix;
- end;
-
- procedure TForm1.FormDestroy(Sender: TObject);
- begin
- Dest.Free;
- Source.Free;
- FreeImageData(DstData);
- FreeImageData(SrcData);
- end;
-
- procedure TForm1.InitColorMatrix;
- var
- i, j: Integer;
- begin
- for i := 0 to 4 do
- begin
- for j := 0 to 4 do
- if i = j then Matrix[i, j] := 1 else Matrix[i, j] := 0;
- end;
- end;
-
- procedure TForm1.PaintBox1Paint(Sender: TObject);
- var
- g: TGpGraphics;
- begin
- g := TGpGraphics.Create(PaintBox1.Canvas.Handle);
- try
- g.DrawImage(Source, 10, 10);
- g.DrawImage(Dest, SrcData.Width + 20, 10);
- finally
- g.Free;
- end;
- end;
-
- procedure TForm1.SpeedButton1Click(Sender: TObject);
- var
- i: Integer;
- begin
- InitColorMatrix;
- for i := 0 to 2 do
- begin
- Matrix[0, i] := 0.30;
- Matrix[1, i] := 0.59;
- Matrix[2, i] := 0.11;
- end;
- BitBtn1.Click;
- end;
-
- procedure TForm1.SpeedButton2Click(Sender: TObject);
- var
- i: Integer;
- begin
- InitColorMatrix;
- for i := 0 to 2 do
- Matrix[4, i] := 0.10;
- BitBtn1.Click;
- end;
-
- procedure TForm1.SpeedButton3Click(Sender: TObject);
- begin
- InitColorMatrix;
- Matrix[0, 0] := -1;
- Matrix[1, 1] := -1;
- Matrix[2, 2] := -1;
- BitBtn1.Click;
- end;
-
- procedure TForm1.SpeedButton4Click(Sender: TObject);
- begin
- InitColorMatrix;
- Matrix[3, 3] := 0.5;
- BitBtn1.Click;
- end;
-
- procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
- Rect: TRect; State: TGridDrawState);
- var
- Text: string;
- begin
- Text := Format('%.2f', [Matrix[ARow, ACol]]);
- StringGrid1.Canvas.FillRect(Rect);
- StringGrid1.Canvas.Pen.Color := clBtnShadow;
- StringGrid1.Canvas.Rectangle(Rect);
- InflateRect(Rect, -2, -2);
- DrawText(StringGrid1.Canvas.Handle, PChar(text), Length(text), &Rect, DT_RIGHT);
- end;
-
- procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
- var Value: string);
- begin
- Value := Format('%.2f', [Matrix[ARow, ACol]]);
- end;
-
- procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
- const Value: string);
- begin
- Matrix[ARow, ACol] := CheckFloatStr(Value);
- end;
-
- end.
下面是运行效果图:
该效果图是个处理-1矩阵实现图像取反的画面,仔细观察右边的取反图,不难发现其中有不少黑色的点,尤其是画面右上角“圣亚”2个字下面,更是出现一些难看的色斑,这些问题在《 GDI+ ColorMatrix的完全揭秘》中已经作了详细的解说。其实这个问题完全可以进行改进,但本文的目的是揭秘和完整实现,改进后,一些非主要效果肯定会与“正版”不一样。^_^
后记(2012/12/20):
昨天整理BLOG,更新《C++图像处理 -- 颜色矩阵变换》文章时,却发现Win7环境下的GDI+ 颜色矩阵变换效果同XP环境下的效果有很大不同(比如前面例子运行的反色效果图在Win7中为黑色图),而本文前面的颜色矩阵变换代码是仿XP效果写的,因此,重新写了一段仿Win7 GDI+颜色矩阵变换效果的代码贴在下面:
- procedure ImageSetColorMatrix(var Dest: TImageData;
- const Source: TImageData; Matrix: TColorMatrix); overload;
- asm
- push ebp
- push esi
- push edi
- push ebx
-
- sub esp, 32
- mov ebp, esp
- add ebp, 16
- and ebp, -16
-
-
- mov edi, ecx
- mov esi, 4
- @@iLoop:
- mov ecx, 3
- @@jLoop:
- cmp ecx, esi
- je @@1
- lea ebx, [esi+esi*4]
- add ebx, ecx
- cmp dword ptr[edi+ebx*4], 0
- jne @@Transform
- @@1:
- dec ecx
- jns @@jLoop
- dec esi
- jns @@iLoop
-
-
- mov ebx, [edi+(2*5+2)*4]
- mov ecx, [edi+(1*5+1)*4]
- mov [ebp], ebx
- mov [ebp+4], ecx
- mov ebx, [edi+(0*5+0)*4]
- mov ecx, [edi+(3*5+3)*4]
- mov [ebp+8], ebx
- mov [ebp+12], ecx
- movaps xmm1, [ebp]
- pxor xmm7, xmm7
- call _SetCopyRegs
- @@yLoop_Scale:
- push ecx
- @@xLoop_Scale:
- movd xmm0, [esi]
- punpcklbw xmm0, xmm7
- punpcklwd xmm0, xmm7
- cvtdq2ps xmm0, xmm0
- mulps xmm0, xmm1
- cvtps2dq xmm0, xmm0
- packssdw xmm0, xmm7
- packuswb xmm0, xmm7
- movd [edi], xmm0
- add esi, 4
- add edi, 4
- loop @@xLoop_Scale
- pop ecx
- add esi, eax
- add edi, ebx
- dec edx
- jnz @@yLoop_Scale
- jmp @@Exit
-
-
- @@Transform:
-
- movups xmm1, [edi+0*5*4]
- movups xmm2, [edi+1*5*4]
- movups xmm3, [edi+2*5*4]
- movups xmm4, [edi+3*5*4]
- movups xmm5, [edi+4*5*4]
-
- mov ebx, 255
- cvtsi2ss xmm6, ebx
- pshufd xmm6, xmm6, 0
- mulps xmm5, xmm6
-
- pshufd xmm1, xmm1, 11000110b
- pshufd xmm2, xmm2, 11000110b
- pshufd xmm3, xmm3, 11000110b
- pshufd xmm4, xmm4, 11000110b
- pshufd xmm5, xmm5, 11000110b
-
- movaps [ebp], xmm5
- pxor xmm7, xmm7
- call _SetCopyRegs
- @@yLoop:
- push ecx
- @@xLoop:
- movd xmm0, [esi]
- punpcklbw xmm0, xmm7
- punpcklwd xmm0, xmm7
- cvtdq2ps xmm0, xmm0
- pshufd xmm5, xmm0, 0
- pshufd xmm6, xmm0, 01010101b
- mulps xmm5, xmm3
- mulps xmm6, xmm2
- addps xmm5, [ebp]
- addps xmm5, xmm6
- pshufd xmm6, xmm0, 10101010b
- pshufd xmm0, xmm0, 11111111b
- mulps xmm6, xmm1
- mulps xmm0, xmm4
- addps xmm0, xmm6
- addps xmm0, xmm5
- cvtps2dq xmm0, xmm0
- packssdw xmm0, xmm7
- packuswb xmm0, xmm7
- movd [edi], xmm0
- add esi, 4
- add edi, 4
- loop @@xLoop
- pop ecx
- add esi, eax
- add edi, ebx
- dec edx
- jnz @@yLoop
- @@Exit:
- add esp, 32
- pop ebx
- pop edi
- pop esi
- pop ebp
- end;
-
- procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix); overload;
- begin
- ImageSetColorMatrix(Data, Data, Matrix);
- end;
|
请发表评论