• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Delphi图像处理 -- 图像颜色矩阵调整

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文在《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); // 注意:为使矩阵与ARGB排列顺序一致,以下运算中调整了行列的顺序 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 // 处理矩阵中大与255的值(取模),并判断主对角线外是否存在数据 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实现过程:

过程定义: // 设置图像颜色矩阵。参数: // Dest输出图,Source原图,Data自身操作图像 // Matrix颜色矩阵 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; 实现代码: procedure ImageSetColorMatrix(var Dest: TImageData; const Source: TImageData; Matrix: TColorMatrix); asm push esi push edi push ebx call IsValid32 jc @@Exit mov ebx, eax mov eax, edx call IsValid32 jc @@Exit mov edi, ecx // edi = matrix mov esi, 4 // for (i = 4; i >= 0; i --) fldz // { @@iLoop: mov ecx, 4 // for (j = 4; j >= 0; j --) @@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) // if (i != j && matrix[i, j] != 0) jmp @@TransformAll // goto 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] // matrixI[0, 0] = matrix[2, 2] * 256 fld st(0) fmul dword ptr [edi+(1*5+1)*4] fistp dword ptr [esp+2] // matrixI[0, 1] = matrix[1, 1] * 256 fld st(0) fmul dword ptr [edi+(0*5+0)*4] fistp dword ptr [esp+4] // matrixI[0, 2] = matrix[0, 0] * 256 fmul dword ptr [edi+(3*5+3)*4] fistp dword ptr [esp+6] // matrixI[0, 3] = matrix[3, 3] * 256 mov eax, ebx call SetCopyRegs32 pxor mm7, mm7 movq mm1, [esp] // mm1 = m44 m11 m22 m33 @@yLoop: push ecx @@xLoop: movd mm0, [esi] punpcklbw mm0, mm7 // mm0 = 00 A 00 R 00 G 00 B pmullw mm0, mm1 // mm0 = A*m44 R*m11 G*m22 B*m33 psrlw mm0, 8 // mm0 = A*m44/256 R*m11/256 G*m22/256 B*m33/256 packuswb mm0, mm0 // mm0 = 00 00 00 00 An Rn Gn Bn 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 // 浮点颜色矩阵行列交换转换为128倍整数 mov dword ptr [esp], 128 fild dword ptr [esp] mov esi, esp // esi = matrixI mov eax, edi mov ecx, 4 // for (i = 0; i < 4; i ++) @@cvtLoop: // { fld st(0) fmul dword ptr [edi] fistp dword ptr [esi] // matrixI[i, 0] = matrix[0, i] * 128 fld st(0) fmul dword ptr [edi+1*5*4] fistp dword ptr [esi+2] // matrixI[i, 1] = matrix[1, i] * 128 fld st(0) fmul dword ptr [edi+2*5*4] fistp dword ptr [esi+4] // matrixI[i, 2] = matrix[2, i] * 128 fld st(0) fmul dword ptr [edi+3*5*4] fistp dword ptr [esi+6] // matrixI[i, 3] = matrix[3, i] * 128 add esi, 8 add edi, 4 loop @@cvtLoop // } fstp st(0) add eax, 4*5*4 // 浮点数平移量转换为255倍整数 mov dword ptr [esi], 255 fild dword ptr [esi] mov ecx, 4 // for (j = 0; j < 4; j ++) @@tLoop: fld st(0) fmul dword ptr [eax] fistp dword ptr [esi] // matrixI[4, j] = matrix[4, j] * 255 add esi, 2 add eax, 4 loop @@tLoop fstp st(0) mov esi, esp // 红蓝(0、2列)交换 mov ecx, 5 // for (i = 0; i < 5; i ++) @@swapLoop: // matrixI[i, 0] <--> matrixI[i, 2] mov ax, [esi].TARGBQuadW.wBlue xchg ax, [esi].TARGBQuadW.wRed mov [esi].TARGBQuadW.wBlue, ax add esi, 8 loop @@swapLoop mov eax, ebx call SetCopyRegs32 pxor mm7, mm7 pcmpeqb mm4, mm4 // mm4 = FF FF FF FF FF FF FF FF psrlw mm4, 15 // mm4 = 00 01 00 01 00 01 00 01 @@yLoopA: push ecx @@xLoopA: movd mm0, [esi] punpcklbw mm0, mm7 // mm0 = 00 A 00 R 00 G 00 B movq mm1, mm0 movq mm2, mm0 movq mm3, mm0 // esp+4: ecx push stack pmaddwd mm0, [esp+16+4] // mm0 = A*m43+R*m13 G*m23+B*m33 蓝色行 pmaddwd mm1, [esp+8+4] // mm1 = A*m42+R*m12 G*m22+B*m32 绿色行 pmaddwd mm2, [esp+4] // mm2 = A*m41+R*m11 G*m21+B*m31 红色行 pmaddwd mm3, [esp+24+4] // mm3 = A*m44+R*m14 G*m24+B*m34 Alpha行 psrad mm0, 7 // mm0 = A*m43+R*m13/128 G*m23+B*m33/128 psrad mm1, 7 // mm1 = A*m42+R*m12/128 G*m22+B*m32/128 psrad mm2, 7 // mm2 = A*m41+R*m11/128 G*m21+B*m31/128 psrad mm3, 7 // mm3 = A*m44+R*m14/128 G*m24+B*m34/128 packssdw mm0, mm1 // mm0 = Ag+Rg Gg+Bg Ab+Rb Gb+Bb packssdw mm2, mm3 // mm2 = Aa+Ra Ga+Ba Ar+Rr Gr+Br pmaddwd mm0, mm4 // mm0 = Ag+Rg+Gg+Bg=Gn Ab+Rb+Gb+Bb=Bn pmaddwd mm2, mm4 // mm2 = Aa+Ra+Ga+Ba=An Ar+Rr+Gr+Br=Rn packssdw mm0, mm2 // mm0 = 00 An 00 Rn 00 Gn 00 Bn paddd mm0, [esp+32+4] // mm0 = An+At Rn+Rt Gn+Gt Bn+Bt 平移行 packuswb mm0, mm0 // mm0 = 00 00 00 00 An Rn Gn Bn 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+,用TJPEGImage对象装入图像转换为TImageData类型后,有上面的ImageSetColorMatrix过程进行处理。该测试代码界面与《GDI+ for VCL基础 -- 颜色调整矩阵ColorMatrix详解》是一样的。有兴趣的朋友可以同里面的测试代码作一下比较。

unit Main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Grids, ImageUtils; type TMainForm = 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 BitBtn1Click(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 FormDestroy(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure SpeedButton2Click(Sender: TObject); procedure SpeedButton1Click(Sender: TObject); procedure SpeedButton4Click(Sender: TObject); procedure SpeedButton3Click(Sender: TObject); procedure BitBtn3Click(Sender: TObject); private { Private declarations } Matrix: TColorMatrix; Source: TImageData; procedure SetMatrix; procedure GetMatrix; public { Public declarations } end; var MainForm: TMainForm; implementation uses JPEG; {$R *.dfm} const ColorMatrix: TColorMatrix = ( (1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 0.0, 0.0, 1.0) ); procedure TMainForm.GetMatrix; var i, j: Integer; begin for i := 0 to 4 do for j := 0 to 4 do Matrix[i, j] := StrToFloat(StringGrid1.Cells[j, i]); end; procedure TMainForm.SetMatrix; var i, j: Integer; begin for i := 0 to 4 do for j := 0 to 4 do StringGrid1.Cells[j, i] := Format('%.2f', [Matrix[i, j]]); end; procedure TMainForm.FormCreate(Sender: TObject); var jpg: TJPEGImage; begin jpg := TJPEGImage.Create; jpg.LoadFromFile('../../Media/100_0349.jpg'); Source := GetImageData(jpg); jpg.Free; BitBtn2.Click; end; procedure TMainForm.FormDestroy(Sender: TObject); begin FreeImageData(Source); end; // 灰度化 procedure TMainForm.SpeedButton1Click(Sender: TObject); var I: Integer; begin Move(ColorMatrix, Matrix, Sizeof(Matrix)); for I := 0 to 2 do begin Matrix[0, I] := 0.3; Matrix[1, I] := 0.59; Matrix[2, I] := 0.11; end; SetMatrix; PaintBox1.Invalidate; end; // 调整亮度0.1(25.5) procedure TMainForm.SpeedButton2Click(Sender: TObject); var I: Integer; begin Move(ColorMatrix, Matrix, Sizeof(Matrix)); for I := 0 to 2 do Matrix[4, I] := 0.1; SetMatrix; PaintBox1.Invalidate; end; // 反色 procedure TMainForm.SpeedButton3Click(Sender: TObject); begin Move(ColorMatrix, Matrix, Sizeof(Matrix)); Matrix[0, 0] := -1; Matrix[1, 1] := -1; Matrix[2, 2] := -1; SetMatrix; PaintBox1.Invalidate; end; // 半透明显示 procedure TMainForm.SpeedButton4Click(Sender: TObject); begin Move(ColorMatrix, Matrix, Sizeof(Matrix)); Matrix[3, 3] := 0.5; SetMatrix; PaintBox1.Invalidate; end; procedure TMainForm.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var r: TRect; Text: string; x: Integer; begin with StringGrid1 do begin Text := Cells[ACol, ARow]; if Text = '' then Text := '0.00' else begin x := Pos('.', Text); if x < 1 then Text := Text + '.00' else if x = 1 then Text := '0' + Text; end; r := TRect(Rect); Canvas.FillRect(r); Canvas.Pen.Color := clBtnShadow; Canvas.Rectangle(r); InflateRect(r, -2, -2); DrawText(Canvas.Handle, PChar(Text), Length(Text), r, DT_RIGHT); end; end; procedure TMainForm.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer; var Value: String); var v: Double; begin if Value = '' then v := 0.0 else v := StrToFloat(Value); Value := Format('%.2f', [v]); end; procedure TMainForm.PaintBox1Paint(Sender: TObject); var Data: TImageData; begin Data := NewImageData(Source.Width, Source.Height, 0); try ImageSetColorMatrix(Data, Source, Matrix); DrawImage(PaintBox1.Canvas, 10, 10, Source); DrawImage(PaintBox1.Canvas, Data.Width + 20, 10, Data); finally FreeImageData(Data); end; end; procedure TMainForm.BitBtn1Click(Sender: TObject); begin GetMatrix; SetMatrix; PaintBox1.Invalidate; end; procedure TMainForm.BitBtn2Click(Sender: TObject); begin Move(ColorMatrix, Matrix, Sizeof(Matrix)); SetMatrix; PaintBox1.Invalidate; end; procedure TMainForm.BitBtn3Click(Sender: TObject); begin Close(); end; end.

例子代码中的图像显示过程DrawImage见《Delphi图像处理 -- 图像显示》。
下面是运行效果图:
该效果图是个处理-1矩阵实现图像取反的画面,仔细观察右边的取反图,不难发现其中有不少黑色的点,尤其是画面右上角“圣亚”2个字下面,更是出现一些难看的色斑,这些问题在《GDI+ ColorMatrix的完全揭秘》中已经作了详细的解说。其实这个问题完全可以进行改进,但本文的目的是揭秘和完整实现,改进后,一些非主要效果肯定会与“正版”不一样。^_^

文章中所用数据类型及一些过程见《Delphi图像处理 -- 数据类型及内部过程》和《Delphi图像处理 -- 图像像素结构与图像数据转换》。

尽管我十分努力,但水平有限,错误在所难免,欢迎指正和指导。邮箱地址:

[email protected]

说明:本文代码于2010.5.20重新修订过,增加了拷贝形式的处理过程。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
【MATLAB与C的混合编程】之【MATLAB调用C程序】发布时间:2022-07-18
下一篇:
MATLAB奇异值分解压缩图像发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap