カラー画像をモノクロ画像に変換


24bitのフルカラー画像を256階調のモノクロ画像に変換(グレースケール変換)する方法をまとめる. グレースケール変換は,二値化やフィルタ処理の前処理として重要である.

処理の大まかな流れは以下のようになる.

  1. TBitmapオブジェクトを作成し(SourceBitmapとする), カラー画像を読み込んでPixelFormatプロパティをpf24bitにする.
  2. 別にTBitmapオブジェクトを作成し(GrayScaleBitmapとする),PixelFormatプロパティを pf8bitにする.
  3. 256階調のモノクロパレットを作成し,GrayScaleBitmapに割り当てる.
  4. SourceBitmapの各ピクセルのRGB値からGraySceleBitmapの各ピクセルの階調値を計算する.
  5. GrayScaleBitmapを表示する.
カラーのRGB値からグレースケールの階調値を計算する方法として,ビデオドライバに任せる方法,NTSC系 加重平均法,単純平均法などがある. ここに 各方法の詳細が解説されている.

なお, ここに示したコードはDelphi メーリングリストにて中村拓男さんが 発案されたコードを一部書き直したものである.

パレットの作成
ビデオドライバに任せる方法
NTSC系加重平均法
単純平均法
各方法の比較


パレットの作成

以下のCreateGrayScalePalette関数は,指定された階調のモノクロパレットを作成し, そのハンドルを返す.

function CreateGrayScalePalette( Tone: Byte ): HPALETTE;
var
  Palette: ^TLogPalette;
  i: Integer;
begin
   GetMem( Palette, SizeOf( TLogPalette ) + SizeOf( TPaletteEntry ) * Tone );
   Palette^.palNumEntries := Tone + 1;
   Palette^.palVersion := $0300;
   for i := 0 to Tone - 1 do begin
      Palette^.palPalEntry[ i ].peRed   := Tone - i;
      Palette^.palPalEntry[ i ].peGreen := Tone - i;
      Palette^.palPalEntry[ i ].peBlue  := Tone - i;
      Palette^.palPalEntry[ i ].peFlags := 0;
   end;
   Result := CreatePalette( Palette^ );
   FreeMem( Palette );
end;

引数ToneはByte型であるから0から255までの値を渡すことができる. 255を渡した場合,256階調のモノクロパレットが作成される. パレットを作成する際に必要なTLogPalette構造体は可変長の構造体だが, Pascalでは可変長の構造体を扱うことができないため,palPalEntryはwindows.pasの中で

  tagLOGPALETTE = packed record
    palVersion: Word;
    palNumEntries: Word;
    palPalEntry: array[0..0] of TPaletteEntry;
  end;
  TLogPalette = tagLOGPALETTE;

のように要素1個の配列として宣言されている. 実際には,palPalEntryは色数分の要素が必要なため,GetMemを使って色数分の要素が収まる 領域を確保しておかなければならない.

TLogPalette構造体の代わりに,Delphiが独自に定義しているTMaxLogPalette構造体を 使えば,配列要素の動的確保をしなくてもよい. TMaxLogPalette構造体は,以下のようにpalPalEntryがByte型(256個)の要素を持つように宣言されている.

  TMaxLogPalette = packed record
    palVersion: Word;
    palNumEntries: Word;
    palPalEntry: array [Byte] of TPaletteEntry;
  end;

TMaxLogPalette構造体を使った場合,先のCreateGrayScalePaletteは以下のようになる.

function CreateGrayScalePalette( Tone: Byte ): HPALETTE;
var
  Palette: TMaxLogPalette;
  i: Integer;
begin
   Palette.palNumEntries := Tone + 1;
   Palette.palVersion := $0300;
   for i := 0 to Tone - 1 do begin
      Palette.palPalEntry[ i ].peRed   := Tone - i;
      Palette.palPalEntry[ i ].peGreen := Tone - i;
      Palette.palPalEntry[ i ].peBlue  := Tone - i;
      Palette.palPalEntry[ i ].peFlags := 0;
   end;
   Result := CreatePalette( PLogPalette( @Palette )^ );
end;

CreatePaletteにTMaxLogPalette型変数を渡す場合は, 変数のアドレスをPLogPaletteにキャストして逆参照する. TLogPaletteとTMaxLogPaletteには互換性がないためである.


ビデオドライバに任せる方法

おそらくこの方法が最も速いと思われる. Drawメソッドで一気に描く. 上で説明したCreateGrayScalePalette関数を使っている.

procedure ColorToGrayScale1( ColorBitmap, GrayScaleBitmap: TBitmap );
var
  SourceBitmap: TBitmap;
begin
   SourceBitmap := TBitmap.Create;
   try
     SourceBitmap.Assign( ColorBitmap );
     SourceBitmap.PixelFormat := pf24bit;
     with GrayScaleBitmap do
     begin
        PixelFormat := pf8Bit;
        Width := SourceBitmap.Width;
        Height := SourceBitmap.Height;
        Palette := CreateGrayScalePalette( 255 );
        Canvas.Draw( 0, 0, SourceBitmap );
     end;
   finally
     SourceBitmap.Free;
   end;
end;


NTSC系加重平均法

この方法は階調値Yを以下の式により計算する方法である.

Y = 0.298912*R + 0.586611*G + 0.114478*B

この式の係数は,日本やアメリカで使われているテレビ放送の規格で使われている係数であり, 各色に対する人間の視感的特性から実験的に求められたものである.

この方法でグレースケール変換を行う場合は, まず,モノクロ画像となるTBitmapオブジェクトに256階調のモノクロパレットを 割り当てておき, カラー画像の各ピクセルをスキャンしながら, 上記の計算式に従ってモノクロ画像の各ピクセルの階調値を計算する. コードを以下に示す.

type
   TTriple = packed record R, G, B: Byte; end;
   PTriple = ^TTriple;

procedure ColorToGrayScale2( ColorBitmap, GrayScaleBitmap: TBitmap );
var
   SourceBitmap: TBitmap;
   x, y: Integer;
   pSource: PTriple;
   pDest: PByte;
begin
   SourceBitmap := TBitmap.Create;
   try
     SourceBitmap.Assign( ColorBitmap );
     SourceBitmap.PixelFormat := pf24bit;
     with GrayScaleBitmap do
     begin
        PixelFormat := pf8Bit;
        Width := SourceBitmap.Width;
        Height := SourceBitmap.Height;
        Palette := CreateGrayScalePalette( 255 );
     end;
     for y := 0 to SourceBitmap.Height - 1 do
     begin
        pSource := SourceBitmap.ScanLine[ y ];
        pDest := GrayScaleBitmap.ScanLine[ y ];
        for x := 0 to SourceBitmap.Width - 1 do
        begin
           pDest^ := Round( 255 - pSource.R * 0.298912 -
                                  pSource.G * 0.586611 -
                                  pSource.B * 0.114478 );
           Inc( pSource );
           Inc( pDest );
        end;
     end;
   finally
     SourceBitmap.Free;
   end;
end;


単純平均法

この方法は階調値YをR,G,Bの平均値とする方法である. すなわち,

Y = ( R + G + B ) / 3

コードは以下のようになる.

procedure ColorToGrayScale3( ColorBitmap, GrayScaleBitmap: TBitmap);
var
  SourceBitmap: TBitmap;
  x, y: Integer;
  pSource: PTriple;
  pDest: PByte;
begin
   SourceBitmap := TBitmap.Create;
   try
     SourceBitmap.Assign( ColorBitmap );
     SourceBitmap.PixelFormat := pf24bit;
     with GrayScaleBitmap do
     begin
        PixelFormat := pf8Bit;
        Width := SourceBitmap.Width;
        Height := SourceBitmap.Height;
        Palette := CreateGrayScalePalette( 255 );
     end;
     for y := 0 to SourceBitmap.Height - 1 do
     begin
        pSource := SourceBitmap.ScanLine[ y ];
        pDest := GrayScaleBitmap.ScanLine[ y ];
        for x := 0 to SourceBitmap.Width - 1 do
        begin
           pDest^ := 255 - ( pSource.R + pSource.G + pSource.B ) div 3;
           Inc( pSource );
           Inc( pDest );
        end;
     end;
   finally
     SourceBitmap.Free;
   end;
end;


各方法の比較

苦労した割には,どれもあんまり変わらんな〜
原画像
ビデオドライバに任せる方法
NTSC系加重平均法
単純平均法


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送