以前看了一些有关图像处理的书,对我起到了很大的帮助。所以,今天我就将我学过的知识整理出来,一方面可以给人学习,另一方面也可以请各位高手指点指点。
我要说的图像处理是针对程序方面的。所以,先做一个程序来放置图形。在这里,我使用了Delphi作为工具。因为,在我使用过的众多编译器当中,Delphi对图形的支持最好。还有,这里我并不是讲语法。所以,有些代码我就不详细说明。不便之处,敬请原谅。
注意:本文章的示例程序所用的东西不超过GDI的范围。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
在图像处理中,速度是很重要的。因此,我们得重新处理一下TBitmap,得到TVczhBitmap。这只是因为GetPixels和SetPixels的速度太慢,换一个方法而已。
unit untBitmapProc;
interface
uses Graphics, SysUtils;
type
TVczhBitmap=class(TBitmap)
private
Data:PByteArray;
Line:Integer;
procedure SetFormat;
function GetBytePointer(X,Y:Integer):PByte;
procedure SetBytes(X,Y:Integer;Value:Byte);
function GetBytes(X,Y:Integer):Byte;
protected
published
constructor Create;
public
property Bytes[X,Y:Integer]:Byte read GetBytes write SetBytes;
procedure LoadFromFile(FileName:String);
procedure ToGray;
end;
implementation
procedure TVczhBitmap.SetFormat;
begin
HandleType:=bmDIB;
PixelFormat:=pf24bit;
end;
function TVczhBitmap.GetBytePointer(X,Y:Integer):PByte;
begin
if Line<>Y then
begin
Line:=Y;
Data:=ScanLine[Y];
end;
Longint(result):=Longint(Data)+X;
end;
procedure TVczhBitmap.SetBytes(X,Y:Integer;Value:Byte);
begin
GetBytePointer(X,Y)^:=Value;
end;
function TVczhBitmap.GetBytes(X,Y:Integer):Byte;
begin
result:=GetBytePointer(X,Y)^;
end;
constructor TVczhBitmap.Create;
begin
inherited Create;
SetFormat;
Line:=-1;
end;
procedure TVczhBitmap.LoadFromFile(FileName:String);
begin
inherited LoadFromFile(FileName);
SetFormat;
Line:=-1;
end;
procedure TVczhBitmap.ToGray;
var X,Y,R:Integer;
B:Byte;
begin
for Y:=0 to Height-1 do
for X:=0 to Width-1 do
begin
R:=0;
for B:=0 to 2 do
R:=R+GetBytes(X*3+B,Y);
for B:=0 to 2 do
SetBytes(X*3+B,Y,R div 3);
end;
end;
end.
此后,我们需要建立几个窗体。第一个用来显示图片,第二个用来处理图片,其他的窗体都继承自第二个窗体,包含实际的处理方法。
先看第二个窗口:
unit untProc;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, untBitmapProc, StdCtrls, ComCtrls;
type
TfrmProcessor = class(TForm)
pbBar: TPaintBox;
gpProc: TGroupBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure pbBarPaint(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
BarData:array[0..255]of Byte;
Bar:TVczhBitmap;
procedure DrawBar;
end;
var
frmProcessor: TfrmProcessor;
implementation
{$R *.dfm}
uses untViewer;
procedure TfrmProcessor.DrawBar;
var I:Integer;
begin
Bar.Canvas.FillRect(Bar.Canvas.ClipRect);
Bar.Canvas.MoveTo(0,255-BarData[0]);
for I:=1 to 255 do
Bar.Canvas.LineTo(I,255-BarData[I]);
end;
procedure TfrmProcessor.FormCreate(Sender: TObject);
begin
Bar:=TVczhBitmap.Create;
Bar.Width:=256;
Bar.Height:=256;
Bar.Canvas.Brush.Color:=clWhite;
Bar.Canvas.Brush.Style:=bsSolid;
end;
procedure TfrmProcessor.FormDestroy(Sender: TObject);
begin
Bar.Free;
end;
procedure TfrmProcessor.FormShow(Sender: TObject);
var I:Integer;
begin
for I:=0 to 255 do
BarData[I]:=I;
DrawBar;
end;
procedure TfrmProcessor.pbBarPaint(Sender: TObject);
begin
pbBar.Canvas.Draw(0,0,Bar);
end;
procedure TfrmProcessor.Button1Click(Sender: TObject);
var X,Y:Integer;
begin
for Y:=0 to Buffer.Height-1 do
for X:=0 to Buffer.Width*3-1 do
Played.Bytes[X,Y]:=BarData[Buffer.Bytes[X,Y]];
frmViewer.FormPaint(frmViewer);
end;
end.
之后,做一个窗口继承自它,则调整BarData[]后,按Apply即可看到结果。
现在开始将图像处理。具体效果见示例程序。
一、颜色反转。
灰度图像的颜色都是从0~255,所以,为了使颜色反转,我们可以用255减去该颜色值以得到反转后的颜色。
var I:Integer;
begin
inherited;
for I:=0 to 255 do
BarData[I]:=255-I;//用255减去该颜色值
DrawBar;
pbBarPaint(pbBar);
end;
二、缩小颜色范围以增强或减弱亮度
颜色本来是从0~255的。如果调节它的范围,例如从0~16,则会是图像明显变暗。我们可以把起始值设为a,把终止值设为b,则新的颜色值New=a+(b-1)*Old/255。这样做的话可以改变亮度,并且不会破坏原先颜色的顺序。代码如下
var I:Integer;
begin
for I:=0 to 255 do
BarData[I]:=(255-sbMin.Position)+Round((sbMin.Position-sbMax.Position)/255*I);
DrawBar;
pbBarPaint(pbBar);
Button1Click(Button1);
end;
这里的sbMin.Position和sbMaxPosition都是反转过的。所以使用时要用255去减
三、增加某个范围内的颜色范围
如果图像本身的颜色范围很小的画,你可以通过这种方法来加大图像的对比度,有利于对图像的分析。具体做法:
选取一个值a做为起始值,选取一个值b做为终止值,然后按以下公式变形:
| 0 (X<=a)
f(X)= | 255/(b-a)*(X-a)
| 255(X>=b)
var I:Integer;
begin
for I:=0 to 255 do
begin
if I<=sbMin.Position then
BarData[I]:=0
else if I>=sbMax.Position then
BarData[I]:=255
else
BarData[I]:=Round(255/(sbMax.Position-sbMin.Position)*(I-sbMin.Position));
end;
DrawBar;
pbBarPaint(pbBar);
Button1Click(Button1);
end;
四、变为黑白图片
在使用第三个功能的时候,你会发现当b<=a时,图像上的颜色除了黑色就是白色。这样操作的好处是不能直接显示出来的。这只要到了比较高级的图像处理如边缘检测等,才有作用。本例可以拿第三种方法的公式再变形,因此不作详细阐述。
五、指数级亮度调整
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><group id="_x0000_s1026" style="MARGIN-TOP: 7.8pt; Z-INDEX: 1; LEFT: 0px; MARGIN-LEFT: 18pt; WIDTH: 180pt; POSITION: absolute; HEIGHT: 156pt; TEXT-ALIGN: left" wrapcoords="-180 208 -450 935 -180 21704 20520 22119 20970 22119 21510 21808 21870 21185 540 20146 990 18485 1350 16823 1800 15162 2970 11838 4500 8515 5490 6854 7020 5192 9360 3531 14220 1869 20790 1142 20520 935 180 208 -180 208" coordsize="3600,3120" coordorigin="2340,5652"><line id="_x0000_s1027" style="POSITION: absolute; flip: y; mso-wrap-edited: f" wrapcoords="0 0 0 19938 0 20458 0 21600 0 21600 0 21600 0 21185 0 20562 0 19938 0 0 0 0" to="2340,8772" from="2340,5652"><stroke endarrow="block"></stroke></line><line id="_x0000_s1028" style="POSITION: absolute; mso-wrap-edited: f" wrapcoords="20520 0 -90 0 -90 0 20520 0 20970 0 21690 0 21690 0 21060 0 20520 0" to="5940,8772" from="2340,8772"><stroke endarrow="block"></stroke></line><shape id="_x0000_s1029" style="LEFT: 2340px; WIDTH: 3420px; POSITION: absolute; TOP: 5808px; HEIGHT: 2964px; mso-wrap-edited: f" wrapcoords="3300 0 2805 30 1770 180 1770 240 1605 299 1245 464 960 719 885 808 765 958 600 1198 375 1677 60 2635 0 2874 -15 2964 1725 2964 1725 2874 30 2874 30 2635 1725 2635 195 2395 345 1916 675 1198 1035 719 1335 479 1845 240 3450 15 3450 0 3300 0" coordsize="3420,2964" filled="f" path="m,2964c255,2041,510,1118,1080,624,1650,130,2535,65,3420,e"><path arrowok="t"></path></shape><?xml:namespace prefix = w ns = "urn:schemas-microsoft-com:office:word" /><wrap type="tight"></wrap></group>
我们假设这个图的定义域是[0,1],值域也是[0,1]。那么,定义函数f(x)=x^c,则f(x)的图像有一段如上图。我们再用鼠标操作时,可以在上面取一点P(a,b),然后使f(x)通过点P,则c=ln(b)/ln(a)。有了c之后,我们就可以对颜色进行操作了:
New=(Old/255)^c*255=exp(ln(old/255)*c)*255
var ea,eb,ec:Extended;
I:Integer;
begin
ea:=A/255;
eb:=B/255;
ec:=Ln(eb)/Ln(ea);
for I:=1 to 255 do
BarData[I]:=Round(Exp(Ln((I/255))*ec)*255);
DrawBar;
pbBarPaint(pbBar);
Button1Click(Button1);
end;
这样做可以调节图像的亮度。
先到这里,在下一篇文章中,我会向大家整理出彩色图像亮度调整的方法。
示例程序:http://vczh.cstc.net.cn/vczh/Article/A_Files/Gray256.rar 打开后按右键弹菜单