软件报
首页 |  新闻 |  软件 |  网络 |  编程 |  硬件 |  业界 | 
  首页>编程>正文
 
打造自己的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下经调试可可靠运行。



文章检索    


编程>热门文章

·VB中串口通讯的实现
·Visual C++技巧问答
·用ASP.NET打造个人留言本
·心铃回音(2003-2)
·心铃回音(2003-1)
·GDI+与GDI屏幕抓图比较
·埃及祖玛游戏编程模拟
·ASP.NET上传文件到数据库
·财经网站股票成交数据批量下载的VB实现
·一步一步开发CHM在线帮助文件系统-1
·在Visual C++中实现一个DLL木马
·在VB中设计丰富的图片转场效果
·ASP.NET 开发实例之博客系统(2)--用户管理
·让FLASH能读会写
·ASP.NET 开发实例之博客系统(一)
·用GTK+2编制Linux平台上的定时提醒备忘录
·用VB.NET制作Office XP风格的工具栏
·一步一步开发CHM在线帮助文件系统-3
·用Java存取SQL2000中的图片
·VC编程处理数据文件的一些体会
·基于互联网的网络校时软件开发
·用VB编写从目标文件中提取图标的程序
·用Delphi实现通用的定时自动关机程序
·VB使用WinSock设计网络五子棋
·Ado.net与T-SQL快速搭建ERP筐架

日期:2008-4-8 16:01:13
 【关闭窗口】 
我要评论
                
评论标题:     (共有0条评论)   

请注意:
· 遵守国家有关法律、法规,尊重网上道德。
· 本网拥有管理用户和评论的一切权利,并有权在网站内转载或引用评论。
软件报


 

加入收藏 | 帮助中心 | 投稿指南 | 广告服务 | 报社简介 | 关于我们

Copyright 2004 By SoftWeek. All Rights Reserved     WebMaster:softsky@sweek.com
版权所有  软件报