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


联系电话:
020-00000000
联系电话:
020-00000000
联系电话:
020-12345678