程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 由圖像的灰度化看基本圖像處理(1)

由圖像的灰度化看基本圖像處理(1)

編輯:Delphi

  [基礎篇]

  首先看一段實現24位色圖像灰度化轉換的代碼

  procedure Grayscale(const Bitmap:TBitmap);
  var
    X: Integer;
    Y: Integer;
    R,G,B,Gray: Byte;
    Color: TColor;
  begin
    for Y := 0 to (Bitmap.Height - 1) do
    begin
      for X := 0 to (Bitmap.Width - 1) do
      begin
        Color := Bitmap.Canvas.Pixels[X,Y];
        R := Color and $FF;
        G := (Color and $FF00) shr 8;
        B := (Color and $FF0000) shr 16;
        Gray := Trunc(0.3 * R + 0.59 * G + 0.11 * B);
        Bitmap.Canvas.Pixels[X,Y] := Gray shl 16 or Gray shl 8 or Gray;
      end
    end
  end;

  {這段代碼效率是非常低的,但可以方便我們理解同時一些問題}

  Delphi的幫助中對TColor已經有了詳細的描述,這可以方便我們理解上面的代碼!

  首先看:

  R := Color and $FF;
  G := (Color and $FF00) shr 8;
  B := (Color and $FF0000) shr 16;

  這是段常見的從TColor中提取三原色的代碼,但它是什麼意思呢?
  首先應該知道and是與(.)運算,0.1=0,0.0=0,1.1=1,以取綠色為例:$FF00實際上就是$00FF00,它與一個TColor類型數按位進行與運算後,表示紅色和綠色的位都變為了$00,而表示綠色的部分不變(0,1和1進行與運算值都不變),再右移8位,自然就獲得了綠色值的8位表示!

  再獲得三原色的值後,就是計算灰度值,0.3 * Red + 0.59 * Green + 0.11 * Blue 這是求加權平均值的公式。(因為人眼對顏色的敏感度不同,所以權值不同,就像在pf16bit中用了6位表示綠色,其它兩種顏色只用了5位,這問題以後另寫文章說明)

  然後就是像素顏色信息的寫回,剛才是右移,現在自然就是左移,而或(+)運算就是(0+1=1,0+0=0,1+1=1),舉個簡單例子就是:($FF shl 16 = $FF0000) or ($FF shl 8 = $FF00) or $FF = $FFFFFF ,其實這裡的或運算當然也可以用 + 代替。

  雖然上面的代碼實現了24位色圖像的灰度化,但當圖像比較大時,速度非常慢,為什麼?查看相關VCL代碼可知調用Bitmap.Canvas.Pixels獲取,寫入像素的顏色信息實際上是利用了API GetPixel、SetPixel,這種方法是非常低效的!(唯一的好處是在進行一些和顏色無關的操作,如圖像的旋轉,翻轉時不需要因為PixelFormat的不同而修改代碼)所以應該換一種更高效的訪問像素點數據的方法,如用API GetDIBits、SetDIBits,但這種方法比較復雜,好在Delphi3以後版本的TBitmap中提供了Scanline。利用Scanline可以快速對像素進行訪問!

  還是以24位色(PixelFormats=pf24bit)為例,可改寫為:

  procedure Grayscale(const Bitmap:TBitmap);
  const
    PixelCountMax = 32768;
  type
    pRGBTripleArray = ^TRGBTripleArray;
    TRGBTripleArray = ARRAY[0..PixelCountMax-1] OF TRGBTriple;
  var
    Row: pRGBTripleArray;
    X: Integer;
    Y: Integer;
    Gray: Byte;
  begin
    for Y := 0 to (Bitmap.Height - 1) do
    begin
      Row := Bitmap.ScanLine[Y];
      for X := 0 to (Bitmap.Width - 1) do
      begin
        Gray := Trunc(0.3 * Row^[X].rgbtRed + 0.59 * Row^[X].rgbtGreen + 0.11 * Row^[X].rgbtBlue);
        Row^[X].rgbtRed:=Gray;
        Row^[X].rgbtGreen:=Gray;
        Row^[X].rgbtBlue:=Gray;
      end;
    end;
  end;

  上面的例子用了一個TRGBTriple數組

    PRGBTriple = ^TRGBTriple;
    tagRGBTRIPLE = packed record
      rgbtBlue: Byte;
      rgbtGreen: Byte;
      rgbtRed: Byte;
    end;
    TRGBTriple = tagRGBTRIPLE;

  這種方法會限制位圖的大小,但一般不用理會,直接用TBitmap可處理不了那麼大的位圖

  當然也可用指針的移動實現,實測結果這樣更快~~~

  procedure Grayscale(const Bitmap:TBitmap);
  var
    X: Integer;
    Y: Integer;
    PRGB: pRGBTriple;
    Gray: Byte;
  begin
    for Y := 0 to (Bitmap.Height - 1) do
    begin
      PRGB := Bitmap.ScanLine[Y];
      for X := 0 to (Bitmap.Width - 1) do
      begin
        Gray := Trunc(0.3 * PRGB^.rgbtRed + 0.59 * PRGB^.rgbtGreen + 0.11 * PRGB^.rgbtBlue);
        PRGB^.rgbtRed:=Gray;
        PRGB^.rgbtGreen:=Gray;
        PRGB^.rgbtBlue:=Gray;
        Inc(PRGB);
      end;
    end;
  end;

  下篇中將進行進一步的探討!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved