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
迅雷专用高速下载