精华内容
参与话题
问答
  • C++图像处理 -- 图像色阶调整

    万次阅读 2015-02-27 19:16:17
    阅读提示: 《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。 《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。 尽可能保持二者内容一致,可相互对照。 本文代码必须...

    阅读提示

        《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

        《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

        尽可能保持二者内容一致,可相互对照。

        本文代码必须包括《C++图像处理 -- 数据类型及公用函数文章中的BmpData.h头文件。


        在Photoshop中,图像色阶调整应用很广泛,本文介绍的图像色阶调整过程与Photoshop处理效果基本一致。

        Photoshop的色阶调整分输入色阶调整和输出色阶调整,其中输入色阶调整有3个调整点,即通常所说的黑场、白场及灰场调整。

        输入色阶调整的基本算法并不复杂,首先计算出白场与黑场的离差Diff,然后计算出像素各份量值与黑场的离差rgbDiff,如果rgbDiff<=0,像素各份量值等于0,否则,计算以rgbDiff与Diff的比值为底的灰场倒数的幂。用公式表示:

        Diff = Highlight -Shadow

        rgbDiff = RGB - Shadow

        clRGB = Power(rgbDiff / Diff,  1 / Midtones)

        其中Shadow为输入色阶低端数据(黑场),Highlight为输入色阶高端数据(白场), Midtones为输入色阶中间数据(灰场),Diff为二者的离差(必须大于1),RGB为调整前的像素分量值,clRGB为调整输入色阶后的像素分量值。

        输出色阶调整更简单,首先计算输出色阶白场与黑场的离差与255的比值系数,然后用输入色阶调整后的像素分量值乘上这个系数,再加上输出黑场值即可。用公式表示:

        outClRGB = clRGB * (outHighlight - outShadow) / 255 + outShadow

        其中,outShadow为输出黑场,outHighlight为输出白场,outClRGB为全部色阶调整后的像素分量值。

        前面已经提到输入色阶黑白场的离差必须大于1,而输入色阶并没有这个限制,输出黑白场的离差可以为负数,当输出黑场与白场完全颠倒时,输出色阶调整后的图片为原图片的负片。

        色阶调整涉及四个通道,即R、G、B各分量通道及整体颜色通道,如果每个通道单独调整,将是比较麻烦和耗时的,本文采用色阶表替换法,可一次性完成所有四个通道的色阶调整。

        下面是图像色阶调整的代码:

    // 色阶项结构
    typedef struct
    {
    	UINT Shadow;
    	FLOAT Midtones;
    	UINT Highlight;
    	UINT OutShadow;
    	UINT OutHighlight;
    }ColorLevelItem, *PColorLevelItem;
    
    typedef struct
    {
    	ColorLevelItem Blue;
    	ColorLevelItem Green;
    	ColorLevelItem Red;
    	ColorLevelItem RGB;
    }ColorLevelData, *PColorLevelData;
    
    VOID InitColorLevelData(PColorLevelData clData)
    {
    	PColorLevelItem item = &clData->Blue;
    	for (INT i = 0; i < 4; i ++, item ++)
    	{
    		item->Shadow = item->OutShadow = 0;
    		item->Highlight = item->OutHighlight = 255;
    		item->Midtones = 1.0;
    	}
    }
    
    BOOL GetColorLevelTable(PColorLevelItem item, LPBYTE clTable)
    {
    	INT diff = (INT)(item->Highlight - item->Shadow);
    	INT outDiff = (INT)(item->OutHighlight - item->OutShadow);
    
    	if (!((item->Highlight <= 255 && diff < 255 && diff >= 2) ||
    		(item->OutShadow <= 255 && item->OutHighlight <= 255 && outDiff < 255) ||
    		(!(item->Midtones > 9.99 && item->Midtones > 0.1) && item->Midtones != 1.0)))
    		return FALSE;
    
    	DOUBLE coef = 255.0 / diff;
    	DOUBLE outCoef = outDiff / 255.0;
    	DOUBLE exponent = 1.0 / item->Midtones;
    
    	for (INT i = 0; i < 256; i ++)
    	{
    		INT v;
    		// 计算输入色阶黑白场
    		if (clTable[i] <= (BYTE)item->Shadow)
    			v = 0;
    		else
    		{
    			v = (INT)((clTable[i] - item->Shadow) * coef + 0.5);
    			if (v > 255)
    				v = 255;
    		}
    		// 计算输入色阶灰场
    		v = (INT)(pow(v / 255.0, exponent) * 255.0 + 0.5);
    		// 计算输出色阶
    		clTable[i] = (BYTE)(v * outCoef + item->OutShadow + 0.5);
    	}
    	return TRUE;
    }
    
    BOOL CheckColorLevelData(PColorLevelData clData, BYTE clTables[][256])
    {
    	BOOL result = FALSE;
    	INT i, j;
    	for (i = 0; i < 3; i ++)
    	{
    		for (j = 0; j < 256; j ++)
    			clTables[i][j] = (BYTE)j;
    	}
    	PColorLevelItem item = &clData->Blue;
    	for (i = 0; i < 3; i ++, item ++)
    	{
    		if (GetColorLevelTable(item, clTables[i]))
    			result = TRUE;
    	}
    	for (i = 0; i < 3; i ++)
    	{
    		if (!GetColorLevelTable(item, clTables[i]))
    			break;
    		result = TRUE;
    	}
    	return result;
    }
    
    // 图像数据色阶调整
    VOID ImageColorLevel(BitmapData *dest, BitmapData *source, PColorLevelData clData)
    {
    	PARGBQuad pd, ps;
    	UINT width, height;
    	INT dstOffset, srcOffset;
    	GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);
    
    	BYTE clTables[3][256];
    	if (CheckColorLevelData(clData, clTables))
    	{
    		for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)
    		{
    			for (UINT x = 0; x < width; x ++, ps ++, pd ++)
    			{
    				pd->Blue = clTables[0][ps->Blue];
    				pd->Green = clTables[1][ps->Green];
    				pd->Red = clTables[2][ps->Red];
    				pd->Alpha = ps->Alpha;
    			}
    		}
    	}
    	else if (dest != source)
    	{
    		for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)
    		{
    			for (UINT x = 0; x < width; x ++, ps ++, pd ++)
    			{
    				pd->Color = ps->Color;
    			}
    		}
        }
    }
    

        下面给一个简单的图像色阶调整函数调用例子:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    	BitmapData dest, source;
    
    	Bitmap *sBmp = new Bitmap(L"..\\..\\media\\source1.jpg");
    	LockBitmap(sBmp, &source);
    
    	Bitmap *dBmp = new Bitmap(source.Width, source.Height, PixelFormat32bppARGB);
    	LockBitmap(dBmp, &dest);
    
    	ColorLevelData clData;
    	InitColorLevelData(&clData);
    
    	clData.RGB.Shadow = 10;
    	clData.RGB.Midtones = 1.2;
    	clData.RGB.Highlight = 240;
    	clData.RGB.OutShadow = 50;
    	clData.RGB.OutHighlight = 200;
    
    /*
    	clData.RGB.OutShadow = 255;
    	clData.RGB.OutHighlight = 0;
    */
    	ImageColorLevel(&dest, &source, &clData);
    
    	UnlockBitmap(dBmp, &dest);
    	UnlockBitmap(sBmp, &source);
    
    	Gdiplus::Graphics g(Canvas->Handle);
    	g.DrawImage(sBmp, 0, 0);
    	g.DrawImage(dBmp, source.Width, 0);
    
    	delete dBmp;
    	delete sBmp;
    }

        下面是文章Delphi图像处理 -- 图像色阶调整例子运行界面效果图,第一张效果图绿色通道色阶调整,第二张效果图是RGB输出色阶调整到完全颠倒时的负片图,详细的图像色阶调整界面例子请参考Delphi图像处理 -- 图像色阶调整》。
         


        本文代码系用BCB XE7编辑和编译。


        因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

        这里可访问《C++图像处理 -- 文章索引


    
    展开全文
  • Delphi图像处理 -- 图像色阶调整

    千次阅读 2010-06-02 22:57:00
    在Photoshop中,图像色阶调整应用很广泛,本文介绍的图像色阶调整过程与Photoshop处理效果基本一致。

    阅读提示:

        《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

        《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

        尽可能保持二者内容一致,可相互对照。

       本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元


        在Photoshop中,图像色阶调整应用很广泛,本文介绍的图像色阶调整过程与Photoshop处理效果基本一致。

        Photoshop的色阶调整分输入色阶调整和输出色阶调整,其中输入色阶调整有3个调整点,即通常所说的黑场、白场及灰场调整。

        输入色阶调整的基本算法并不复杂,首先计算出白场与黑场的离差Diff,然后计算出像素各份量值与黑场的离差rgbDiff,如果rgbDiff<=0,像素各份量值等于0,否则,计算以rgbDiff与Diff的比值为底的灰场倒数的幂。用公式表示:

        Diff = Highlight -Shadow

        rgbDiff = RGB - Shadow

        clRGB = Power(rgbDiff / Diff,  1 / Midtones)

        其中Shadow为输入色阶低端数据(黑场),Highlight为输入色阶高端数据(白场), Midtones为输入色阶中间数据(灰场),Diff为二者的离差(必须大于1),RGB为调整前的像素分量值,clRGB为调整输入色阶后的像素分量值。

        输出色阶调整更简单,首先计算输出色阶白场与黑场的离差与255的比值系数,然后用输入色阶调整后的像素分量值乘上这个系数,再加上输出黑场值即可。用公式表示:

        outClRGB = clRGB * (outHighlight - outShadow) / 255 + outShadow

        其中,outShadow为输出黑场,outHighlight为输出白场,outClRGB为全部色阶调整后的像素分量值。

        前面已经提到输入色阶黑白场的离差必须大于1,而输入色阶并没有这个限制,输出黑白场的离差可以为负数,当输出黑场与白场完全颠倒时,输出色阶调整后的图片为原图片的负片。

        色阶调整涉及四个通道,即R、G、B各分量通道及整体颜色通道,调整如果每个通道单独调整,将是比较麻烦和耗时的,本文采用色阶表替换法,可一次性完成所有四个通道的色阶调整。

        下面直接给出一个完整的图像色阶调整例子源代码,其中包含了色阶调整和灰度计算函数:

    unit main;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls, ComCtrls, ImageData, Gdiplus;
    
    type
      // 色阶项结构
      PColorLevelItem = ^TColorLevelItem;
      TColorLevelItem = packed record
        Shadow: LongWord;
        Midtones: Single;
        Highlight: LongWord;
        OutShadow: LongWord;
        OutHighlight: LongWord;
      end;
    
      // 色阶通道结构
      PColorLevelData = ^TColorLevelData;
      TColorLevelData = record
        Blue: TColorLevelItem;
        Green: TColorLevelItem;
        Red: TColorLevelItem;
        RGB: TColorLevelItem;
      end;
    
      // 256色灰度统计数组,每个元素表示该下标对应的颜色个数
      PGrayArray = ^TGrayArray;
      TGrayArray = array[0..255] of LongWord;
      // 灰度信息结构
      PImageGrayInfo = ^TImageGrayInfo;
      TImageGrayInfo = packed record
        Grays: TGrayArray;          // 灰度数组
        Total: int64;               // 总的灰度值
        Count: LongWord;            // 总的像素点数
        MaxValue: LongWord;         // 像素点最多的灰度值
        MinValue: LongWord;         // 像素点最少的灰度值
        Average: LongWord;          // 平均灰度值(Total / Count)
      end;
    
      TMainForm = class(TForm)
        LBar: TTrackBar;
        HBar: TTrackBar;
        Button1: TButton;
        Label3: TLabel;
        GrayMap: TPaintBox;
        ComboBox1: TComboBox;
        Label1: TLabel;
        LLabel: TLabel;
        HLabel: TLabel;
        MBar: TTrackBar;
        MLabel: TLabel;
        PaintBox1: TPaintBox;
        OLBar: TTrackBar;
        OHBar: TTrackBar;
        OLLabel: TLabel;
        OHLabel: TLabel;
        Label5: TLabel;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure GrayMapPaint(Sender: TObject);
        procedure LBarChange(Sender: TObject);
        procedure HBarChange(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure ComboBox1Change(Sender: TObject);
        procedure MBarChange(Sender: TObject);
        procedure PaintBox1Paint(Sender: TObject);
        procedure OLBarChange(Sender: TObject);
        procedure OHBarChange(Sender: TObject);
      private
        { Private declarations }
        FBitmap: TGpBitmap;
        FSource: TImageData;
        FDest: TImageData;
        FLevelData: TColorLevelData;
        FLevelItems: array[0..3] of PColorLevelItem;
        FGrayInfos: array[0..3] of TImageGrayInfo;
        FLock: Boolean;
        FIsRun: Boolean;
        FAbort: Boolean;
      public
        { Public declarations }
        procedure AdjustmentImage;
        procedure GetGrayInfos;
      end;
    
    var
      MainForm: TMainForm;
    
    implementation
    
    uses Math;
    
    {$R *.dfm}
    
    const
      GRAY_MMX: TMMType = (3735, 19235, 9798, 0); // [0.114,0.587,0.229] * 32768
    
    // 获取图像灰度信息
    function GetGrayInfo(const Data: TImageData; var GrayInfo: TImageGrayInfo;
      isGrayImage: Boolean = False): Integer;
    asm
        push      ebp
        push      esi
        push      edi
        push      ebx
        push      ecx
        push      eax
        mov       edi, edx
        mov       esi, edx
        xor       eax, eax
        mov       ecx, 256
        rep       stosd               // init Grays
        pop       eax
        call      _SetDataRegs
        mov       eax, ecx
        imul      eax, edx
        xchg      eax, [esp]          // pixel count
        test      al, al
        jnz       @@yGrayLoop
    
        // 建立灰度数组
        pxor      mm7, mm7
        movq      mm2, GRAY_MMX
    @@yLoop:
        push      ecx
    @@xLoop:
        movd      mm0, [edi]
        punpcklbw mm0, mm7
        pmaddwd   mm0, mm2
        movq      mm1, mm0
        psrlq     mm1, 32
        paddd     mm0, mm1
        movd      eax, mm0
        add       eax, 16384
        shr       eax, 15
        inc       [esi].TImageGrayInfo.Grays[eax*4].Integer
        add       edi, 4              // grayData.Grays[gray] ++
        loop      @@xLoop
        pop       ecx
        add       edi, ebx
        dec       edx
        jnz       @@yLoop
        emms
        jmp       @@SumStart
        // 建立灰度图的灰度数组
    @@yGrayLoop:
        push      ecx
    @@xGrayLoop:
        movzx     eax, [edi].TARGBQuad.Blue       // gray = *edi
        inc       [esi].TImageGrayInfo.Grays[eax*4].Integer// grayData.Grays[gray] ++
        add       edi, 4
        loop      @@xGrayLoop
        pop       ecx
        add       edi, ebx
        dec       edx
        jnz       @@yGrayLoop
        // 计算总的灰度值、最大灰度值及最小灰度值
    @@SumStart:
        push      esi
        mov       edi, esi            // esi = ebx = &GrayData.Grays[0]
        mov       ebx, esi
        xor       eax, eax            // edx:eax = 0 (GrayData.Total)
        xor       edx, edx
        xor       ecx, ecx            // for (index = 0; index < 256; index ++)
    @@SumLoop:                      // {
        mov       ebp, [edi]
        cmp       [esi], ebp
        cmovb     esi, edi            //   if (*esi < *edi) esi = edi
        cmp       [ebx], ebp
        cmova     ebx, edi            //   if (*ebx > *edi) ebx = edi
        imul      ebp, ecx            //   ebp = *edi * index
        add       eax, ebp            //   edx:eax += ebp
        adc       edx, 0
        add       edi, 4              //   edi += 4
        inc       ecx
        cmp       ecx, 255
        jle       @@SumLoop           // }
        pop       edi
        sub       ebx, edi
        shr       ebx, 2              // min = (ebx - &GrayData.Grays[0]) / 4
        mov       [edi].TImageGrayInfo.MinValue, ebx
        sub       esi, edi
        shr       esi, 2              // max = (esi - &GrayData.Grays[0]) / 4
        mov       [edi].TImageGrayInfo.MaxValue, esi
        pop       ebx                 // count = data.Width * data.Height
        mov       [edi].TImageGrayInfo.Count, ebx
        mov       dword ptr[edi].TImageGrayInfo.Total, eax // total = edx:eax
        mov       dword ptr[edi].TImageGrayInfo.Total+4, edx
        mov       ecx, ebx
        shr       ecx, 1
        add       eax, ecx
        adc       edx, 0
        div       ebx                 // average = (total + count / 2) / count
        mov       [edi].TImageGrayInfo.Average, eax
    @@Exit:                           // return GrayData.Average
        pop       ebx
        pop       edi
        pop       esi
        pop       ebp
    end;
    
    // 用色阶表替换Source像素值到Dest
    procedure _DoColorLevel(var Dest: TImageData; const Source: TImageData;
      const Table: PGrayTable);
    var
      height, dstOffset, srcOffset: Integer;
    asm
        push    ecx
        call    _SetCopyRegs
        mov     height, edx
        mov     dstOffset, ebx
        mov     srcOffset, eax
        pop     ebx
    @@yLoop:
        push    ecx
    @@xLoop:
        movzx   eax, [esi].TARGBQuad.Blue
        movzx   edx, [esi].TARGBQuad.Green
        mov     al, [ebx+eax]
        mov     dl, [ebx+edx+256]
        mov     [edi].TARGBQuad.Blue, al
        mov     [edi].TARGBQuad.Green, dl
        movzx   eax, [esi].TARGBQuad.Red
        mov     al, [ebx+eax+512]
        mov     ah, [esi].TARGBQuad.Alpha
        mov     [edi].TARGBQuad.Red.Word, ax
        add     esi, 4
        add     edi, 4
        loop    @@xLoop
        pop     ecx
        add     esi, srcOffset
        add     edi, dstOffset
        dec     height
        jnz     @@yLoop
    end;
    
    // 拷贝Source到Dest
    procedure _DoCopyImageData(var Dest: TImageData; const Source: TImageData);
    asm
        call    _SetCopyRegs
    @@yLoop:
        push    ecx
        rep     movsd
        pop     ecx
        add     esi, eax
        add     edi, ebx
        dec     edx
        jnz     @@yLoop
    end;
    
    // 如果色阶项Item参数非初始值,计算色阶表Table并返回真
    function GetColorLevelTable(Item: TColorLevelItem; var Table: TGrayTable): Boolean;
    var
      i, v: Integer;
      outDiff, diff: Integer;
      outCoef, coef: double;
      exponent: double;
      isMidtones: Boolean;
    begin
      outDiff := Integer(Item.OutHighlight - Item.OutShadow);
      diff := Integer(Item.Highlight - Item.Shadow);
      isMidtones := (Item.Midtones <> 1.0) and not ((Item.Midtones > 9.99) or (Item.Midtones < 0.1));
    
      Result := ((Item.Highlight <= 255) and (diff < 255) and (diff >= 2)) or
                ((Item.OutHighlight <= 255) and (Item.OutShadow <= 255) and (outDiff < 255)) or
                isMidtones;
      if not Result then Exit;
    
      Coef := 255 / diff;
      outCoef := outDiff / 255;
      exponent := 1 / Item.Midtones;
      for i := 0 to 255 do
      begin
        // 计算输入色阶黑白场
        if Table[i] <= Item.Shadow then v := 0
        else
        begin
          v := Round((Table[i] - Item.Shadow) * coef);
          if v > 255 then v := 255;
        end;
        // 计算输入色阶灰场
        v := Round(Power(v / 255, exponent) * 255);
        // 计算输出色阶
        Table[i] := Round(v * outCoef + Item.OutShadow);
      end;
    end;
    
    // 如果色阶通道数据clData参数非初始值,计算所有通道色阶表并返回真
    function _CheckColorLevelData(const clData: TColorLevelData;
      var Tables: array of TGrayTable): Boolean;
    type
      PColorLevels = ^TColorLevels;
      TColorLevels = array[0..2] of TColorLevelItem;
    var
      i, j: Integer;
    begin
      Result := False;
      for i := 0 to 2 do      // 初始化R、G、B通道色阶表
      begin
        for j := 0 to 255 do
          Tables[i, j] := j;
      end;
      for i := 0 to 2 do      // 计算R、G、B通道色阶表
      begin
        if GetColorLevelTable(PColorLevels(@clData)^[i], Tables[i]) then
          Result := True;
      end;
      for i := 0 to 2 do      // 计算整个RGB图像色阶表
      begin
        if not GetColorLevelTable(clData.RGB, Tables[i]) then
          Break;
        Result := True;
      end;
    end;
    
    // 初始化色阶通道数据
    procedure InitColorLevelData(var clData: TColorLevelData);
    
      procedure InitTColorLevelItem(var Item: TColorLevelItem);
      begin
        Item.Shadow := 0;
        Item.Midtones := 1.0;
        Item.Highlight := 255;
        Item.OutShadow := 0;
        Item.OutHighlight := 255;
      end;
    
    begin
      InitTColorLevelItem(clData.Blue);
      InitTColorLevelItem(clData.Green);
      InitTColorLevelItem(clData.Red);
      InitTColorLevelItem(clData.RGB);
    end;
    
    // 按clData拷贝Source的色阶调整数据到Dest
    procedure ImageColorLevel(var Dest: TImageData; const Source: TImageData;
      const clData: TColorLevelData);
    var
      Tables: array[0..2] of TGrayTable;
    begin
      if _CheckColorLevelData(clData, Tables) then
        _DoColorLevel(Dest, Source, @Tables)
      else
        _DoCopyImageData(Dest, Source);
    end;
    
    procedure TMainForm.AdjustmentImage;
    begin
      if not FIsRun then
      begin
        FIsRun := True;
        FAbort := False;
        ImageColorLevel(FDest, FSource, FLevelData);
        PaintBox1Paint(nil);
        FIsRun := False;
      end
      else
        FAbort := True;
    end;
    
    procedure TMainForm.Button1Click(Sender: TObject);
    begin
      Close;
    end;
    
    procedure TMainForm.ComboBox1Change(Sender: TObject);
    var
      x: Integer;
    begin
      FLock := True;
      x := ComboBox1.ItemIndex;
      LBar.Position := FLevelItems[x].Shadow;
      MBar.Position := 46 - Round(Ln(FLevelItems[x].Midtones * 10) * 10);
      HBar.Position := FLevelItems[x].Highlight;
      FLock := False;
      GrayMap.Invalidate;
    end;
    
    procedure TMainForm.FormCreate(Sender: TObject);
    begin
      FBitmap := TGpBitmap.Create('..\..\media\source1.jpg');
      FSource := LockGpBitmap(FBitmap);
      FDest := NewImageData(FSource.Width, FSource.Height);
      InitColorLevelData(FLevelData);
      FLevelItems[0] := @FLevelData.RGB;
      FLevelItems[1] := @FLevelData.Red;
      FLevelItems[2] := @FLevelData.Green;
      FLevelItems[3] := @FLevelData.Blue;
      GetGrayInfos;
      ComboBox1.ItemIndex := 0;
      AdjustmentImage;
    end;
    
    procedure TMainForm.FormDestroy(Sender: TObject);
    begin
      FreeImageData(FDest);
      UnlockGpBitmap(FBitmap, FSource);
      FBitmap.Free;
    end;
    
    procedure TMainForm.GetGrayInfos;
    var
      i, j: Integer;
      maxIdx, minIdx: Integer;
    begin
      for i := 1 to 3 do
      begin
        GetGrayInfo(FSource, FGrayInfos[i], True);
        for j := 0 to 255 do
          Inc(FGrayInfos[0].Grays[j], FGrayInfos[i].Grays[j]);
        Inc(Integer(FSource.Scan0), 1);
      end;
      Dec(Integer(FSource.Scan0), 3);
      maxIdx := 0;
      minIdx := 0;
      for i := 0 to 255 do
      begin
        FGrayInfos[0].Grays[i] := Round(FGrayInfos[0].Grays[i] / 3);
        Inc(FGrayInfos[0].Total, FGrayInfos[0].Grays[i] * i);
        if (FGrayInfos[0].Grays[minIdx] > FGrayInfos[0].Grays[i]) then
          minIdx := i;
        if (FGrayInfos[0].Grays[maxIdx] < FGrayInfos[0].Grays[i]) then
          maxIdx := i;
      end;
      FGrayInfos[0].MaxValue := maxIdx;
      FGrayInfos[0].MinValue := minIdx;
      FGrayInfos[0].Count := FGrayInfos[1].Count;
      FGrayInfos[0].Average := FGrayInfos[0].Total div FGrayInfos[0].Count;
    end;
    
    procedure TMainForm.GrayMapPaint(Sender: TObject);
    const
      PenColor: array[0..3] of TColor = ($000000, $0000FF, $008000, $FF0000);
    var
      I, v, x: Integer;
    begin
      x := ComboBox1.ItemIndex;
      GrayMap.Canvas.Brush.Color := clSkyBlue;
      GrayMap.Canvas.FillRect(GrayMap.ClientRect);
      GrayMap.Canvas.Pen.Color := PenColor[x];
      for I := 0 to 255 do
      begin
        v := Round(FGrayInfos[x].Grays[i] / FGrayInfos[x].Grays[FGrayInfos[x].MaxValue] * GrayMap.Height);
        GrayMap.Canvas.MoveTo(I, GrayMap.Height);
        GrayMap.Canvas.LineTo(I, GrayMap.Height - v);
      end;
    end;
    
    procedure TMainForm.HBarChange(Sender: TObject);
    begin
      HLabel.Caption := IntToStr(HBar.Position);
      if FLock then Exit;
      if HBar.Position - LBar.Position < 2 then
      begin
        FLock := True;
        HBar.Position := LBar.Position + 2;
        FLock := False;
      end;
      FLevelItems[ComboBox1.ItemIndex].Highlight := HBar.Position;
      AdjustmentImage;
    end;
    
    procedure TMainForm.LBarChange(Sender: TObject);
    begin
      LLabel.Caption := IntToStr(LBar.Position);
      if FLock then Exit;
      if HBar.Position - LBar.Position < 2 then
      begin
        FLock := True;
        LBar.Position := HBar.Position - 2;
        FLock := False;
      end;
      FLevelItems[ComboBox1.ItemIndex].Shadow := LBar.Position;
      AdjustmentImage;
    end;
    
    procedure TMainForm.MBarChange(Sender: TObject);
    var
      v: Single;
    begin
      v := Power(Exp(1), (MBar.Max - MBar.Position) / 10.0) / 10;
      MLabel.Caption := Format('%.1f', [v]);
      if FLock then Exit;
      FLevelItems[ComboBox1.ItemIndex].Midtones := StrToFloat(MLabel.Caption);
      AdjustmentImage;
    end;
    
    procedure TMainForm.OHBarChange(Sender: TObject);
    begin
      OHLabel.Caption := IntToStr(OHBar.Position);
      if FLock then Exit;
      FLevelItems[ComboBox1.ItemIndex].OutHighlight := OHBar.Position;
      AdjustmentImage;
    end;
    
    procedure TMainForm.OLBarChange(Sender: TObject);
    begin
      OLLabel.Caption := IntToStr(OLBar.Position);
      if FLock then Exit;
      FLevelItems[ComboBox1.ItemIndex].OutShadow := OLBar.Position;
      AdjustmentImage;
    end;
    
    procedure TMainForm.PaintBox1Paint(Sender: TObject);
    var
      dstBmp, srcBmp: TGpBitmap;
      g: TGpGraphics;
    begin
      g := TGpGraphics.Create(PaintBox1.Canvas.Handle);
      dstBmp := TGpBitmap.Create(FDest.Width, FDest.Height, FDest.Stride,
        pf32bppArgb, FDest.Scan0);
      srcBmp := TGpBitmap.Create(FSource.Width, FSource.Height, FSource.Stride,
        pf32bppArgb, FSource.Scan0);
      try
        g.DrawImage(dstBmp, 0, 0);
        g.DrawImage(srcBmp, 0, FDest.Height);
      finally
        srcBmp.Free;
        dstBmp.Free;
        g.Free;
      end;
    end;
    
    end.
    


        下面是2张运行效果图,第一张效果图绿色通道色阶调整,第二张效果图是RGB输出色阶调整到完全颠倒时的负片图:

     

        PhotoShop中的色阶调整只用了2个滑条分别进行输入、输出色阶调整,我没有这种滑槽组件,只好用了5个滑条组件,不太美观。当然在实用时完全可写一个与PhotoShop类似的元件,并不复杂。

        说明:本文章里的灰度计算和色阶调整源代码可以在http://download.csdn.net/detail/maozefa/8323289下载,但其中的输出色阶调整和输入色阶调整一样,是不允许黑白场颠倒的,而且黑白场离差最小允许值是4而不是2,可以按本文代码修正过来。


        《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

        因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

        这里可访问《Delphi图像处理 -- 文章索引》。

     

    
    
    展开全文
  • VC编程实现对位图图像自动色阶处理 VC编程实现位图拷贝、切除空白边介绍了VC实现位图图像拷贝,切除二值图空白边,本文继续介绍位图处理类CImageUtility的其它成员方法,着重介绍VC编程实现位图图像自动色阶的...

    VC编程实现对位图图像自动色阶处理

    VC编程实现位图拷贝、切除空白边介绍了VC实现位图图像拷贝,切除二值图空白边,本文继续介绍位图处理类CImageUtility的其它成员方法,着重介绍VC编程实现位图图像自动色阶的功能。

    根据互联网搜索的结果,位图图像自动色阶算法主要包含两种方案,一种是拉开LAB色彩空间的L(亮度)分量,使图像的亮度区域拉开,第二种是讲图像的RGB各分量值的区域拉开。第一种方案效果较好,但是计算比较复杂,需要用到前面介绍的色彩空间转换,第二种方案计算简单,但是效果不如前一种方案,下面列出具体的VC实现源码和处理效果,读者可以根据实际需要进行选用。

    1. VC源代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    // 自动色阶
    // gType 自动色阶的算法
    // darkLimen 暗调阈值
    // brightLimen 高光阈值
    void CImageUtility::ImageAutoGradationProcess(AutoGradationType gType,double darkLimen,double brightLimen){
        // 目前只处理24位以上的位图
        if(nPixBytes <3)
            return;
        //临时保存像素点的颜色
        int nRGB[3]={0,0,0};
        switch(gType){
            case g_L_VALUE:{    //通过LAB中的L分量自动色阶
                //保存每个亮度分量的像素个数
                unsigned long gradation[101];
                for(size_t i=0;i<101;i++)
                    gradation[i]=0;
                //保存亮度最多的像素个数
                unsigned long maxPixle=0;
                // 暗调阈值序号
                int darkPos=0;
                // 高光阈值序号
                int lightPos=0;        
                double LAB[3]={0.0,0.0,0.0};
                for(long nHeight=0; nHeight<bmSrcInfo.bmHeight; nHeight++)  
                {      
                    for(long nWidth=0; nWidth<bmSrcInfo.bmWidth; nWidth++)  
                    {
                        //获取对应像素点得颜色
                        getPixelColor(nRGB,nHeight,nWidth);    
                        //转换颜色到LAB色彩空间
                        CColorUtility::_cie_rgb2lab(nRGB,LAB);
                        //对应亮度的像素点个数加1
                        gradation[(int)(LAB[0]+0.5)]++;            //LAB中的L分量【0.0-100.0】
                        if(maxPixle<gradation[(int)(LAB[0]+0.5)])
                            maxPixle=gradation[(int)(LAB[0]+0.5)];
                    }             
                }
                //分析直方图
                // 暗调位置
                for(size_t i=0;i<101;i++){
                    if(((long double)(gradation[i]))/maxPixle>darkLimen){
                        darkPos=i;
                        break;
                    }
                }
                // 高光位置
                for(size_t i=100;i>=0;i--){
                    if(((long double)(gradation[i]))/maxPixle>brightLimen){
                        lightPos=i;
                        break;
                    }
                }
                //对像素进行处理
                double offsetLight=(lightPos+darkPos)/2-50.0;        //中间亮度偏移
                double quotiety=100.0/(lightPos-darkPos);            //亮度缩放系数
                for(long nHeight=0; nHeight<bmSrcInfo.bmHeight; nHeight++)  
                {      
                    for(long nWidth=0; nWidth<bmSrcInfo.bmWidth; nWidth++)  
                    {
                        //获取对应像素点得颜色
                        getPixelColor(nRGB,nHeight,nWidth);    
                        //转换颜色到LAB色彩空间
                        CColorUtility::_cie_rgb2lab(nRGB,LAB);
                        //移动当前亮度的中心到自动色阶后亮度的中心
                        LAB[0]=LAB[0]-offsetLight;    
                        //计算当前亮度与中心的距离
                        LAB[0]-=50;
                        //将距离乘于系数加上中心位置及时当前亮度最终的位置
                        LAB[0]=quotiety*LAB[0]+50;    
                        //重新给像素点赋值
                        CColorUtility::_cie_lab2rgb(LAB,nRGB);
                        //设置像素点颜色
                        setPixelColor(nRGB,nHeight,nWidth);    
                    }             
                }    
                break;                       
            }
            // 还有问题,有待研究
            case g_RGB:{        //通过RGB自动色阶
                //保存每个亮度分量的像素个数
                unsigned long gradation[3][256];
                for(size_t i=0;i<3;i++)
                    for(size_t j=0;j<256;j++)
                        gradation[i][j]=0;
                //保存亮度最多的像素个数
                unsigned long maxPixle[3]={0,0,0};
                // 暗调阈值序号
                int darkPos[3]={0,0,0};    
                // 高光阈值序号
                int lightPos[3]={0,0,0};            
                for(long nHeight=0; nHeight<bmSrcInfo.bmHeight; nHeight++)  
                {      
                    for(long nWidth=0; nWidth<bmSrcInfo.bmWidth; nWidth++)  
                    {
                        //获取对应像素点得颜色
                        getPixelColor(nRGB,nHeight,nWidth);
                        for(int i=0;i<3;i++){
                            //保存每种颜色分量的像素个数
                            gradation[i][nRGB[i]]++;
                            //保存每种颜色分量最大的像素个数
                            if(maxPixle[i]<gradation[i][nRGB[i]])
                                maxPixle[i]=gradation[i][nRGB[i]];
                        }
                    }             
                }
                //分析直方图
                // 暗调位置
                for(size_t i=0;i<3;i++){
                    for(size_t j=0;j<256;j++){
                        if(((long double)(gradation[i][j]))/maxPixle[i]>darkLimen){
                            darkPos[i]=j;
                            break;
                        }
                    }
                }
                // 高光位置
                for(size_t i=0;i<3;i++){
                    for(size_t j=255;j>=0;j--){
                        if(((long double)(gradation[i][j]))/maxPixle[i]>brightLimen){
                            lightPos[i]=j;
                            break;
                        }
                    }
                }
                //对像素进行处理
                int offsetLight[3]={0,0,0};
                for(int i=0;i<3;i++)
                    offsetLight[i]=(lightPos[i]+darkPos[i])/2-128;        //中间亮度偏移
                double quotiety[3]={0.0,0.0,0.0};
                for(int i=0;i<3;i++)
                    quotiety[i]=255.0/(lightPos[i]-darkPos[i]);            //亮度缩放系数
                for(long nHeight=0; nHeight<bmSrcInfo.bmHeight; nHeight++)  
                {      
                    for(long nWidth=0; nWidth<bmSrcInfo.bmWidth; nWidth++)  
                    {
                        //获取对应像素点得颜色
                        getPixelColor(nRGB,nHeight,nWidth);    
                        for(int i=0;i<3;i++){
                            //移动当前亮度的中心到自动色阶后亮度的中心
                            nRGB[i]=nRGB[i]-offsetLight[i];    
                            //计算当前亮度与中心的距离
                            nRGB[i]-=128;
                            //将距离乘于系数加上中心位置及时当前亮度最终的位置
                            nRGB[i]=(int)(quotiety[i]*nRGB[i])+128;    
                        }
                        //设置像素点颜色
                        setPixelColor(nRGB,nHeight,nWidth);    
                    }             
                }
                break;
                       }
        }    //switch over
        bmpSrc->SetBitmapBits(dwBmByteSize, pBmBits);  
    }

    2. 效果对比

    2.1 LAB亮度分量实现自动色阶:

    VC实现位图自动色阶算法

    2.2 RGB实现自动色阶:

    VC实现位图自动色阶功能

    对于RGB色彩空间与LAB色彩空间的转换,读者可以参考:VC编程实现色彩空间RGB与XYZ相互转换VC编程实现色彩空间XYZ与LAB相互转换

    展开全文
  • 自动色阶图像处理算法

    千次阅读 2017-11-07 17:10:07
    自动色阶算法python实现

    通过仿真自动色阶算法,发现其去雾效果十分明显,并且速度快于暗通道算法。

    python实现:

    #!python3.6
    
    import numpy as np
    import cv2
    
    def ComputeHist(img):
        h,w = img.shape
        hist, bin_edge = np.histogram(img.reshape(1,w*h), bins=list(range(257)))
        return hist
        
    def ComputeMinLevel(hist, rate, pnum):
        sum = 0
        for i in range(256):
            sum += hist[i]
            if (sum >= (pnum * rate * 0.01)):
                return i
                
    def ComputeMaxLevel(hist, rate, pnum):
        sum = 0
        for i in range(256):
            sum += hist[255-i]
            if (sum >= (pnum * rate * 0.01)):
                return 255-i
                
    def LinearMap(minlevel, maxlevel):
        if (minlevel >= maxlevel):
            return []
        else:
            newmap = np.zeros(256)
            for i in range(256):
                if (i < minlevel):
                    newmap[i] = 0
                elif (i > maxlevel):
                    newmap[i] = 255
                else:
                    newmap[i] = (i-minlevel)/(maxlevel-minlevel) * 255
            return newmap
            
    def CreateNewImg(img):
        h,w,d = img.shape
        newimg = np.zeros([h,w,d])
        for i in range(d):
            imgmin = np.min(img[:,:,i])
            imgmax = np.max(img[:,:,i])
            imghist = ComputeHist(img[:,:,i])
            minlevel = ComputeMinLevel(imghist, 8.3, h*w)
            maxlevel = ComputeMaxLevel(imghist, 2.2, h*w)
            newmap = LinearMap(minlevel,maxlevel)
            # print(minlevel, maxlevel)
            if (newmap.size ==0 ):
                continue
            for j in range(h):
                newimg[j,:,i] = newmap[img[j,:, i]]
        return newimg
        
        
        
    if __name__ == '__main__':
        img = cv2.imread('2017/2017_0_0_1000_.jpg',1)
        newimg = CreateNewImg(img)
        cv2.namedWindow('img',0)
        cv2.imshow('img', img)
        cv2.namedWindow('newimg',0)
        cv2.imshow('newimg', newimg/255)
        cv2.waitKey(0)
            
    
    
    
    
    
    原图去雾效果

    展开全文
  • Photoshop图像处理算法—色阶调整

    千次阅读 2015-08-27 20:42:48
    前言:之前在公司做项目的用到photoshop颜色空间的一些相关方法,在此总结一下。下面原理部分是从我的总结文档里截取来的。需要复制的童鞋自己手写一下~ 2、程序部分 1)Matlab实验程序。...R=doub
  • 自动色阶 第一步,分别统计各通道(红/绿/蓝)的直方图。 第二步,分别计算各通道按照给定的参数所确定的上下限值。什么意思呢,比如对于蓝色通道,我们从色阶0开始向上累加统计直方图,当累加值大于LowCut所有像素...
  • 1、原理部分 2、程序部分(matlab)  自动色调 clc;clear;close all; img=imread('IMG_0950_cut.jpg'); Image=double(img)/255; figure(1); imshow(Image); %% R=Image(:,:,1); G=Image(:,:,2);...
  • 文章目录一、色阶调整( Levels Adjustment )原理二、自动色阶图像处理算法 一、色阶调整( Levels Adjustment )原理 色阶:就是用直方图描述出的整张图片的明暗信息。如图 从左至右是从暗到亮的像素分布, 黑色...
  • OpenCV图像处理基础——基于C++实现

    千人学习 2020-04-10 00:21:08
    OpenCV图像处理基础——基于C++实现版本视频培训课程概况:教程中会讲解到OpenCV的基础知识及使用方法,并基于OpenCV实现基础的图像处理算法;除此之外课程包含如下的内容: 图像颜色空间及类型转换及应用(BGR...
  • VB DIB图像处理入门

    2013-08-18 21:27:29
    VB DIB图像处理入门实例 像素的获取和输出 图像色阶调整 图像的亮度对比度调整
  • 图像处理技巧

    2020-04-10 11:26:14
    文章目录 1.白平衡-解决偏色问题  白平衡,顾名思义,即白色的平衡,在不同色温下,都能准确判断出白色,而不会产生偏色.在黃色的光線照射下, 白色的物件看來是黃...一般用于调整简单的灰度图像,还是手动调整好...
  • VC 医学图像处理系统,可以调整图像色阶、锐度、反色、灰底均衡,还可以对图像进行反转、平移、旋转等操作,类似Photoshop某些功能一样,当然,和PS比,是差了点,只能作为一些C 处理图像的参考吧。
  • 1.1 数字图像 位图(BMP/JPG/GIF)和矢量图(PNG) 二值图像、灰度图像、RGB图像(表1.1常见颜色RGB组合值)、索引图像(节省存储空间,使用了Palette...灰度级分辨率(色阶)(即:可分辨的灰度级数目) 1.2 数...
  • 反色,色阶,色彩平衡,色相/饱和度/亮度
  • PaintBuster一个简单易用的图像处理软件,具备图像处理的各种功能,对于想提高相片质量的用户来说是个非常不错的选择。拥有自动曝光、数码补光、白平衡、亮度对比度、饱和度、色阶、曲线、色彩平衡等一系列非常丰富的...
  • 图像处理】图像自动对比度

    千次阅读 2015-02-06 15:24:04
    比如对于蓝色通道,我们从色阶0开始向上累加统计直方图,当累加值大于LowCut*所有像素数时,以此时的色阶值计为MinBlue。然后从色阶255开始向下累计直方图,如果累加值大于HighCut*所有像素时,以此时
  • C++数字图像处理篇之图像加马赛克

    千次阅读 2018-08-16 11:04:08
    马赛克是一种广为使用的图像处理手段,它是将影像特定区域的色阶细节劣化并造成色块打乱的效果。这种模糊看样子像一个个小格子,所以被称为马赛克。马赛克主要的目的就是使图像内容无法辨认,以保护特殊图像内容。...
  • 图像处理之灰度转换

    2019-05-16 19:22:40
    图像转灰 一幅完整的图像,是由红色、绿色、蓝色三个通道组成的。红色、绿色、蓝色三个通道的缩览图都是以灰度显示的。...通道是Photoshop处理图像的核心部分,所有的色彩调整工具都是围绕在这个...
  • 1. 灰度变换:就是值将彩色图像转换成0~255色阶的灰度图 Android提高十六篇之使用NDK把彩图转换灰度图:blog.csdn.net/hellogv/article/details/6094127 从RGB色转为灰度色算法:...
  • 色阶

    2015-06-28 22:06:14
     色阶是表示图像亮度强弱的指数标准,也就是我们说的色彩指数,在数字图像处理教程中,指的是灰度分辨率(又称为灰度级分辨率或者幅度分辨率)。图像的色彩丰满度和精细度是由色阶决定的。色阶指亮度,和颜色无关,...
  • python数字图像处理(14):高级滤波

    千次阅读 2016-07-29 11:47:01
    本文提供更多更强大的滤波方法,这些方法...这个词在photoshop里面翻译成自动色阶,用局部直方图来对图片进行滤波分级。 该滤波器局部地拉伸灰度像素值的直方图,以覆盖整个像素值范围。 格式:skimage.filter

空空如也

1 2 3 4 5 6
收藏数 113
精华内容 45
关键字:

图像处理 色阶