Delphi中实现调整图像的色阶的算法


本文整理自网络,侵删。

 

 Photoshop的色阶调整主要有3个调整点,即通常所说的黑场、白场及灰场调整,本文代码只涉及到黑场和白场调整,至于灰场调整算法以及与黑白场之间的关系等问题,因为本人还没完全琢磨透,只好以后再完善了(其实算法并不复杂,麻烦的是后者)。
    色阶调整的基本算法并不复杂,用伪代码表示:
    Diff = levelHigh - levelLow
    newRGB = (oldRGB - levelLow) * 255 / Diff
    其中levelLow为色阶低端数据(黑场),levelHigh为色阶高端数据(白场),Diff为二者的离差,oldRGB为调整前的像素颜色,newRGB为调整后的颜色。
    色阶调整涉及四个通道,即R、G、B各分量通道及整体颜色通道,如果每个通道单独调整,将是比较麻烦和耗时的,本文过程采用MMX代码,可一次性处理R、G、B3个分量通道,加上整体颜色通道调整,最多需进行2遍调整处理,所以运行速度还是很快的,在我的P42.8G机器上处理一遍千万像素图片,不包括装载图片时间,只需47ms。
    下面直接给出图像色阶调整的源代码:
数据及过程定义:  
type 
  // 色阶调整数据  
  PColorLevelData = ^TColorLevelData;  
  TColorLevelData = packed record 
    BlueLow: LongWord;      // 蓝色通道低阶值  
    BlueHigh: LongWord;     // 蓝色通道高阶值  
    GreenLow: LongWord;     // 绿色通道低阶值  
    GreenHigh: LongWord;    // 绿色通道高阶值  
    RedLow: LongWord;       // 红色通道低阶值  
    RedHigh: LongWord;      // 红色通道高阶值  
  end;  
 
  // 色阶调整。参数:  
  //   Dest输出图,Source原图,Data自身操作图像  
  //   LevelData R、G、B各通道的色阶调整数据  
  //   Callback回调函数,返回True终止操作,CallbackData回调函数参数地址  
  function ImageColorLevels(var Dest: TImageData; const Source: TImageData;  
    const LevelData: TColorLevelData;  
    Callback: TImageAbort = nil; CallbackData: Pointer = nil): Boolean; overload;  
  procedure ImageColorLevels(var Data: TImageData; const LevelData: TColorLevelData); overload;  
 
实现代码:  
 
procedure ColorLevels(mmxBuf: Pointer); pascal;  
asm 
    push        ebx  
    mov         ebx, mmxBuf     // mm5 = 512 Coef Coef Coef  
    movq        mm5, qword ptr [ebx]  
    movq        mm6, qword ptr [ebx+8]  
    pxor        mm7, mm7        // mm6 = 00 00 LevelLow LevelLow LevelLow  
    pop         ebx  
@@yLoop:  
    push        ecx  
@@xLoop:  
    movd        mm0, [esi]  
    punpcklbw   mm0, mm7  
    psubw       mm0, mm6  
    psllw       mm0, 7 
    pmulhw      mm0, mm5  
    packuswb    mm0, mm7        // newRgb = (rgb - LevelLow) * 128 * Coef / 65536  
    movd        [edi], mm0      // newAlpha = (alpha - 0) * 128 * 512 / 65536  
    add         esi, 4 
    add         edi, 4 
    loop        @@xLoop  
    add         esi, eax  
    add         edi, ebx  
    pop         ecx  
    dec         edx  
    jnz         @@yLoop  
    emms  
end;  
 
function ImageColorLevels(var Dest: TImageData; const Source: TImageData;  
  const LevelData: TColorLevelData;  
  Callback: TImageAbort; CallbackData: Pointer): Boolean;  
type 
  TIntArray = array[0..5] of Integer;  
var 
  mmxBuf: array[0..7] of Word;  
  i, j, Diff: Integer;  
begin 
  Result := not ImageEmpty(Dest) and not ImageEmpty(Source);  
  if not Result then Exit;  
  for i := 0 to 3 do 
  begin 
    mmxBuf[i] := 512;  
    mmxBuf[i + 4] := 0;  
  end;  
  j := 0;  
  for i := 0 to 2 do 
  begin 
    Diff := TIntArray(LevelData)[j + 1];  
    if Diff > 255 then Diff := 255;  
    Dec(Diff, TIntArray(LevelData)[j]);  // Diff = LevelHigh - LevelLow  
    if (Diff >= 4) and (Diff < 255) then 
    begin 
      mmxBuf[i] := (255 * 512) div Diff; // Coef = 255 * 512 / Diff  
      mmxBuf[i + 4] := TIntArray(LevelData)[j];  
    end;  
    Inc(j, 2);  
  end;  
  if Assigned(Callback) then 
    Result := ExecuteAbort(Dest, Source, @ColorLevels, [@mmxBuf], Callback, CallbackData)  
  else 
    Result := ExecuteProc(Dest, Source, @ColorLevels, [@mmxBuf]);  
end;  
 
procedure ImageColorLevels(var Data: TImageData; const LevelData: TColorLevelData);  
begin 
  ImageColorLevels(Data, Data, LevelData);  
end; 
上面的调整过程即可一次性调整R、G、B3个分量通道的色阶,也可进行整体颜色通道的色阶调整。当整体颜色通道和分量通道都需要进行色阶调整时,应先调整分量通道,再调整整体颜色通道。
    需要说明的是,Photoshop色阶调整时,白场与黑场之间的离差最小值是2,而本文过程要求的白场与黑场之间离差最小值是4,否则,MMX代码就会溢出了。事实上,无论最小允许离差是2还是4,在实际运用中都没有多大意义。

更多关注请访问作者博客:http://blog.csdn.net/maozefa/archive/2010/06/02/5643459.aspx

相关阅读 >>

Delphi xe6 string转memorystream

Delphi 10.3.2 社区版的安装

Delphi中编写无输出函数名的dll文件

Delphi 颜色转换函数: 从 Delphi 到 html

了解Delphi过程类型/过程类型学习

Delphi获取mac地址

Delphi base64编码/解码及zlib压缩/解压

Delphi 虚拟键码对照表

Delphi 安卓app自动升级

Delphi取得当前目录的上一级目录

更多相关阅读请进入《Delphi》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...