| 打造自己的ACDSEE(续) |
| 赵亮 |
|
《软件报》2008年15期 邮发代号:61-74
在本刊2007年38期至46期连载发表了《打造ACDSEE工具》一文。在该文中没有介绍如何对图像的品质如:亮度、对比度等进行调整的知识,而这些知识对图像处理软件应该是必备的,因此编写此文对此进行补充。这里将介绍图像的亮度、对比度、色饱和度、RGB分量的调整和如何形成灰度图像。
1 图像调整的基本原理
一般来说,当前的数码图像大部分都是JPG格式的图像,要对这种格式的图像进行调整必须转换为BMP格式才能进行。BMP格式的图像要求必须是24Bit真彩色的才能按下述方法处理。前文中已经对JPG格式与BMP格式的相互转换方法作过介绍,这里不再重复。我们知道图像是由像素构成的,每个像素是由红(R)、绿(G)、蓝(B)三原色构成的,每个原色由一个字节(8位二进制数)表示,也就是说每个原色分量用0~255表示。要对图像的品质进行调整,其实就是通过改变这三个原色分量的值来实现的。
2先介绍几个自定义函数和过程
在“ACDSEE工具软件”中图像调整是通过几个自定义过程或函数实现的。下边分别介绍这些自定义过程和函数。
2.1 自定义函数scz
按照前述图像调整的基本原理,在图像调整时,各颜色分量的值不能超越0~255这个范围。调整后的值如果大于255时,就取值为255,如果小于0时,就取值为0,其它情况才能取值为调整后的值。因此编写这部份内容代码是很繁琐的。但如果使用这个scz自定义函数,可以大大的简化程序。有兴趣的读者可以试一下,不使用scz自定义函数,用常规的方式编写一下相同的程序,会发现这个自定义函数会使代码量减少一半多。该函数原理很简单,读者很容易理解。
function scz(intSr: Integer): Integer; //输出值
begin
if intSr < 0 then
Result := 0
else if intSr > 255 then
Result := 255
else
Result := intSr;
end;
2.2 图像亮度的调整
图像亮度是指图像的明亮程度。图像的亮度调整往往是很有必要的,特别是数码照片如果拍摄时照度不夠,而造成图像亮度较低时就更有必要进行调整。在编程中调整图像亮度其实就是增大或减少图像中各像素的RGB三原色的值。增大RGB三原色的值,就提高图像的亮度,减小RGB三原色的值,就降低图像的亮度。下边是调整图像亮度的代码。在代码中,intSl是该过程中的自变量,其值的范围是±255。z1行设置数组对象变量。z2行设置TBitmap对象变量。z3行建立TBitmap对象。z4行将要处理的图像赋予该对象。z5行设置图像为24位模式,因为不是24位模式不能应用本过程进行处理。z6行从0开始按行扫描全幅图像。z7行读取当前行到objHa对象。z8行每行图像从0开始按位扫描。z9行及下2行逐个像素调整三原色的值,注意这里采用了自定义函数scz,由式中可知,调整后的值如果小于0,则设为0,如果大于255则设为255,其它条件下为调整后的值。z10行将调整后的objBm对象赋予当前显示对象。
procedure ldtz(intSl: Integer);//图像亮度调整
var
objHa: PByteArray; //z1
x, y: Integer;
objBm: TBitmap; //z2
begin
objBm := TBitmap.Create; //z3
objBm.Assign(frmCk.imgTp.Picture.Bitmap); //z4
objBm.PixelFormat := pf24Bit; //z5
for y := 0 to objBm.Height - 1 do //z6
begin
objHa := objBm.ScanLine[y]; //z7
for x := 0 to (objBm.Width - 1) do //z8
begin
objHa[x * 3] := scz(objHa[x * 3] + intSl);//z9
objHa[x * 3 + 1] := scz(objHa[x * 3 + 1] + intSl);
objHa[x * 3 + 2] := scz(objHa[x * 3 + 2] + intSl);
end;
end;
frmCk.imgTp.Picture.Bitmap.Assign(objBm); //z10
objBm.Free;
end;
2.3 图像对比度的调整
图像的对比度是指图像的较亮部份与较暗部份的比率。比率越大对比度越高,比率越小对比度越低。调整对比度往往可以改善图像的清晰度。调整对比度时,为了避免影响图像的亮度,一般是以127为界限,像素的颜色分量值大于127时,如果提高对比度则增加该值,减少对比度时则减少该值。像素的颜色分量值小于127时,如果提高对比度则减少该值,减少对比度时则增加该值。像素的颜色分量值等于127时,像素的颜色分量值不变。增加和减少的值要相等。当然改变后的值也要保证在0~255的范围。下边是调整图像/对比度的代码。在代码中,z1行:据某些资料,调整对比度的数量是以亮的部份和暗的部份的差值表示,这时按总调整量折半分配到提高和降低部份,也就说如果调整对比度的量为10,则大于127时增加5,小于127时,减少5。这其实没什么必要,因为实际调整时是以图像质量满足要示为原则的。z2行及以下行逐个像素调整三原色的值,注意这里同样采用了自定义函数scz。
procedure dbtz(intDb: Integer);//对比度调整
var
objHa: PByteArray;
x, y: Integer;
objBm: TBitmap;
begin
//intDb := intDb Div 2;//z1
objBm := TBitmap.Create;
objBm.Assign(frmCk.imgTp.Picture.Bitmap);
objBm.PixelFormat := pf24Bit;
for y := 0 to objBm.Height - 1 do
begin
objHa := objBm.ScanLine[y];
for x := 0 to (objBm.Width - 1) do
begin
if objHa[x * 3] > 127 then//z2
objHa[x * 3] := scz(objHa[x * 3] + intDb);
if objHa[x * 3] < 127 then
objHa[x * 3] := scz(objHa[x * 3] - intDb);
if objHa[x * 3 + 1] > 127 then
objHa[x * 3 + 1] := scz(objHa[x * 3 + 1] + intDb);
if objHa[x * 3 + 1] < 127 then
objHa[x * 3 + 1] := scz(objHa[x * 3 + 1] - intDb);
if objHa[x * 3 + 2] > 127 then
objHa[x * 3 + 2] := scz(objHa[x * 3 + 2] + intDb);
if objHa[x * 3 + 2] < 127 then
objHa[x * 3 + 2] := scz(objHa[x * 3 + 2] - intDb);
end;
end;
frmCk.imgTp.Picture.Bitmap.Assign(objBm);
objBm.Free;
end;
2.4 图像色饱和度的调整
色饱和度是表示图像色彩的鲜艳程度,其实质是色彩的纯度,纯度越高,表现越鲜明,纯度较低,表现则较黯淡,色饱和度表示光线的彩色深浅度或鲜艳度,取决于彩色中的白色光含量,白光含量越高,即彩色光含量就越低,色彩饱和度即越低,反之亦然。
调整饱和度一般是针对各像素不同的亮度确定调整量的。下边介绍的方法是网络上比较流行的方法。在代码中,z1行、z2行建立数组变量,以保存不同亮度值时饱和度的调整量。z3行将调整量转换为绝对量。z4行及以下13行,数组变量保存不同亮度值时饱和度的调整量。z5行按该像素的亮度值选择相应的调整值。z6行对各像素的饱和度进行调整。
procedure bhtz(intBh: Integer);//饱和度调整
var
intSz: Array[0..767] Of Integer;//z1
intS2: Array[0..255] Of Word;//z2
x, y: Integer;
intZ1: Integer;
objHa: PByteArray;
i: Byte;
objBm: TBitmap;
begin
intBh := intBh + 255; //z3
objBm := TBitmap.Create;
objBm.Assign(frmTx.imgTx.Picture.Bitmap);
objBm.PixelFormat := pf24Bit;
for i := 0 to 255 do //z4
intS2[i] := (i * intBh) Shr 8;
x := 0;
for i := 0 to 255 do
begin
intZ1 := i - intS2[i];
intSz[x] := intZ1;
Inc(x);
intSz[x] := intZ1;
Inc(x);
intSz[x] := intZ1;
Inc(x);
end;
for y := 0 to objBm.Height - 1 do
begin
objHa := objBm.ScanLine[y];
for x := 0 to objBm.Width - 1 do
begin
intZ1 := intSz[objHa[x * 3] + objHa[x * 3 + 1] + objHa[x * 3 + 2]];//z5
objHa[x * 3] := scz(intZ1 + intS2[objHa[x * 3]]);//z6
objHa[x * 3 + 1] := scz(intZ1 + intS2[objHa[x * 3 + 1]]);
objHa[x * 3 + 2] := scz(intZ1 + intS2[objHa[x * 3 + 2]]);
end;
end;
frmTx.imgTx.Picture.Bitmap.Assign(objBm);
objBm.Free;
end;
2.5 图像RGB分量的调整
我们知道图像是由三原色组成的,显然,如果分别调整三原色各分量的值,会调整图像的品质,使图像的颜色偏向于某一种颜色。很明显,如果三原色的调整量相同,则相当于同样调整量的亮度调整。代码中,intHo、 intLu、 intLa分别代表红、绿、蓝三原色的调整量。其它参见上述。
procedure rgbt(intHo, intLu, intLa: Integer);//RGB调整
var
objHa: PByteArray;
x, y: Integer;
objBm: TBitmap;
begin
objBm := TBitmap.Create;
objBm.Assign(frmCk.imgTp.Picture.Bitmap);
objBm.PixelFormat := pf24Bit;
for y := 0 to objBm.Height - 1 do
begin
objHa := objBm.ScanLine[y];
for x := 0 to (objBm.Width - 1) do
begin
objHa[x * 3] := scz(objHa[x * 3] + intLa);
objHa[x * 3 + 1] := scz(objHa[x * 3 + 1] + intLu);
objHa[x * 3 + 2] := scz(objHa[x * 3 + 2] + intHo);
end;
end;
frmCk.imgTp.Picture.Bitmap.Assign(objBm);
objBm.Free;
end;
2.6 图像灰度的调整
彩色图像可以转换为黑白图像,图像的层次由图像的亮度层次来表示。其实质就是将各像素的三原色的值都等于三原色的平均值。这里的灰度调整实际上只是提高各像素的平均亮度。在代码中,z1行计算各像素三原色的平均值(即灰度),再加上intHd灰度调整量。
procedure hdtz(intHd: Integer); //灰度调整
var
objHa: PByteArray;
objBm: TBitmap;
x, y: Integer;
intSz: Integer;
begin
objBm := TBitmap.Create;
objBm.Assign(frmCk.imgTp.Picture.Bitmap);
objBm.PixelFormat := pf24Bit;
for y := 0 to objBm.Height - 1 do
begin
objHa := objBm.ScanLine[y];
for x := 0 to objBm.Width - 1 do
begin
intSz := (objHa[x * 3] + objHa[x * 3 + 1] + objHa[x * 3 + 2]) Div 3 + intHd;//z1
objHa[x * 3] := intSz;
objHa[x * 3 + 1] := intSz;
objHa[x * 3 + 2] := intSz;
end;
end;
frmCk.imgTp.Picture.Bitmap.Assign(objBm);
objBm.Free;
end;
3 软件界面布置
3.1 主窗体:在主窗体tlrBj工具条上添加一个按钮(如原已有可直接利用)。设定Name为tlbXs,Hint为“图像修饰”,并配置合适的图标,设置Visible和Enabled为True。
3.2 在frmFz窗体中添加一个Pancel控件。设定Name为panXs,Left为0,Top为0,Width为353,Height为240。再在panXs控件上添加Label控件。Name为lblBt,Left为20,Top为5,Width为316,Height为13,Alignment为taCenter。再添加TabControl控件。Name为tabXz,Left为6,Top为24,Width为345,Height为213。在tabXz控件上添加三个Label控件。Name分别为lblHo、lblLu、lblLa,其Caption分别为:“调整量:”、“G(绿色):”、“B(蓝色):”,Top依次设置为:45、85、125。均设置Left为27,Width为54,Height为12,其Font.Color依次为clRed、clGreen、clBlue,Alignment为taRightJustify。再添加三个TrackBar控件。Name分别为tckSz、tckLu、tckLa,Top依次设置为:41、81、121。这三个TrackBar控件均设置Left为81,Width为200,Height为33,Frequency为20,LineSize为1,PageSize为5。添加三个Edit控件。Name分别为edtSz、edtLu、edtLa,Top依次设置为:45、85、125, Left为280,Width为30,Height为20。
软件界面布置可参见附图。下边的图为设置“RGB分量”时的状态。注意图中的两个按钮是原来窗口中的。
4 相关的程序和说明
4.1 调用图像调整功能:在选择好要处理的图片后,按下tlbXs(图像修饰)按钮,将打开“图像修饰调整”对话框,见附图。默认状态是上边的一幅。即显示的是调整亮度的状态。可点击“页框”选择要进行的图像调整内容。当选择“RGB调整”时显示下图的状态。下边是tlbXsClick的代码。
procedure TfrmCk.tlbXsClick(Sender: TObject);
begin
with frmFz do
begin
mstrFz := ’xs’;
frmFz.Position := poScreenCenter;
frmFz.Show;
end;
end;
4.2 在TfrmFz.FormActivate事件中添加如下内容,以保证在打开“图像修饰调整”对话框时,设置好窗口的界面。
procedure TfrmFz.FormActivate(Sender: TObject);
begin
……
panXs.Visible := mstrFz = ’xs’;
if mstrFz = ’xs’ then
begin
Caption := ’图像修饰调整’;
spbQd.Parent := tabXz;
spbQd.Left := 252;
spbQx.Parent := tabXz;
spbQx.Left := 175;
tabXz.TabIndex := 0;
edtSz.Text := ’0’;
chkHd.Checked := False;
tckSz.Position := 0;
tckSz.SetTick(0);
tabXzChange(tabXz);
panXs.Visible := True;
Width := 360;
end;
……
end;
4.3 下边的TfrmFz.tabXzChange事件,通过点击tabXz来切换选择图像修饰的内容。这里要说明的是z1及下一行,可确定调整量的范围,这里是选择的范围是:灰度为0~100其它项目为±255。主要是为了读者实验时可观察调整量与图像显示效果的关系。实际上笔者认为取值为±100较好。因为较大的调整量失真非常严重,比如亮度调整量为255,将使所有像素都为纯白,这还有什么意义。在代码中z2行是考虑到灰度调整在调整量为0时,就应该使“确定”按钮有效。
procedure TfrmFz.tabXzChange(Sender: TObject);
begin
Case tabXz.TabIndex Of
0:
lblBt.Caption := ’图像亮度调整设置’;
1:
lblBt.Caption := ’图像对比度调整设置’;
2:
lblBt.Caption := ’图像色饱和度调整设置’;
3:
lblBt.Caption := ’图像灰度调整设置’;
else
lblBt.Caption := ’图像RGB分量调整设置’;
edtLu.Text := ’0’;
tckLu.Position := 0;
tckLu.SetTick(0);
edtLa.Text := ’0’;
tckLa.Position := 0;
tckLa.SetTick(0);
end;
edtSz.Text := ’0’;
tckSz.Max := Iifi(tabXz.TabIndex = 3, 100, 255);//z1
tckSz.Min := Iifi(tabXz.TabIndex = 3, 0, -255);
tckSz.Position := 0;
if tabXz.TabIndex < 4 then
lblHo.Caption := ’调整量:’
else
lblHo.Caption := ’R(红色):’;
lblLu.Visible := tabXz.TabIndex = 4;
lblLa.Visible := tabXz.TabIndex = 4;
tckLu.Visible := tabXz.TabIndex = 4;
tckLa.Visible := tabXz.TabIndex = 4;
edtLu.Visible := tabXz.TabIndex = 4;
edtLa.Visible := tabXz.TabIndex = 4;
tabXz.Height := Iifi(tabXz.TabIndex < 4, 153, 213);
panXs.Height := Iifi(tabXz.TabIndex < 4, 180, 240);
spbQd.Enabled := tabXz.TabIndex = 3;//z2
spbQx.Top := tabXz.Height - 48;
spbQd.Top := spbQx.Top;
Height := Iifi(tabXz.TabIndex < 4, 210, 270);
end;
4.4 本软件中在确定调整量时,即可使用滑动块也可使用文本框输入,并且互相关联。这主要是通过下边的程序实现的。对“RGB调整”的tckLu,edtLu ,tckLa,edtLa也应仿此处理。代码中:z1行的作用是点击该文本框时,文本框中处于全选中状态,便于输入。z2行的作用是,当在文本框中输入完成后,直接按“回车”键即可相当于按“确定”按钮。z3行,这里将语句封闭在“异常处理”语句中,是为了输入负号时不报错。
procedure TfrmFz.tckSzChange(Sender: TObject);
begin
edtSz.Text := IntToStr(tckSz.Position);
end;
procedure TfrmFz.edtSzMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
edtSz.SelectAll;//z1
end;
procedure TfrmFz.edtSzKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 13 then
spbQdClick(spbQd);//z2
end;
procedure TfrmFz.edtSzChange(Sender: TObject);
begin
try//z3
if edtSz.Text = ’’ then
tckSz.Position := 0
else
tckSz.Position := StrToInt(edtSz.Text);
except
end;
spbQd.Enabled := edtSz.Text <> ’0’;
end;
4.5 TfrmFz.spbQdClick事件:在对话框中输入调整量后,按下“确定”按钮,通过本事件来调用相应程序,完成图像品质的调整任务。在代码中: z1行图像亮度调整,z2行图像对比度调整,z3行/图像色饱和度调整,z4行图像灰度调整,z5行图像RGB分量调整。注意这里调整最终是按滑动块的值进行的,除了其值不必变换外,这里还有一个好处:由于滑动块的Max值和Min值可保证输入值不超限。
procedure TfrmFz.spbQdClick(Sender: TObject);
var
……
begin
……
if mstrFz = ’xs’ then
begin
Case tabXz.TabIndex Of
0:
ldtz(tckSz.Position);//z1
1:
dbtz(tckSz.Position);//z2
2:
bhtz(tckSz.Position);/z3/
3:
hdtz(tckSz.Position);//z4
else //z5
rgbt(tckSz.Position, tckLu.Position, tckLa.Position);
end;
frmCk.tlbBc.Enabled := True;
frmCk.tlbCx.Enabled := True;
spbQxClick(Sender);
end;
……
end;
图像的处理是很复杂的,需要一定的知识。作者限于水平,可能文中会出现表述不得当或错误的地方。敬请读者谅解并提宝贵意见。
本文所载程序在WinXp,Delphi7下经调试可可靠运行。
|
|
文章检索
编程>热门文章
|