ガンマ補正


 カンマ補正は、一般的な補正なのです。

 ブラウン管方式のCRTモニター出力は、RGB色の色信号出力に対しての、輝度がリニアに出力されず、色信号の出力が低い時、輝度が低くなる傾向があります。
それを防ぐため、ガンマ補正をかけています。
しかし、現在のモニターは、殆どが液晶となり、補正値が変わっていますが、液晶モニターの回路で、ブラウン管方式のCRTモニターのガンマ特性に合わせています。
ガンマの意味は、その特性値のカーブが、ガンマの文字に似ているのでガンマ補正となっています。(ガンマ線も同じです。)
 現在のデジタルカメラは、撮影時に自動的に、補正され撮影時の失敗が殆ど無いようになっています。
この様な補正を必要とするのは、一般では殆ど無いでしょう。
しかし、写真をよりきれいにする為、暗い部分を明るくしたい時にする補正として非常に有効な手段です。

γグラフ
 実際にがんま補正をする場合は、ゼロから255迄の配列テーブルを用意して、計算をせずに変換をします。

ガンマ補正画像
 右側がガンマ補正した画像です。
暗い部分が明るく補正され、見やすい画像になってします。

 画像の読み込みは、ファイルから行いますが、多種のファイルに対応するため、ここのプログラムでは、TWICImageを使用しています。
Helpでは、”TWICImage が利用できるのは、Windows XP SP3 以上の OS で、DirectX ランタイムを使用している場合だけです。”
と、なっていますが、現在、Windows XP SP3 以前のOSを使用しているのは稀でしょう。又、Windows XP SP3以上でDirectXが組み込まれていないものはありません。

var
 BitmapD : Tbitmap; // Windows ビットマップイメージ
 Gbitmap : Tbitmap; // Windows ビットマップイメージ

procedure TForm1.FormDestroy(Sender: TObject);
begin
  BitmapD.Free;
  Gbitmap.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  BitmapD := Tbitmap.Create;        // ビットマップイメージの生成
  Gbitmap := Tbitmap.Create;        // ビットマップイメージの生成
end;


procedure TForm1.FileOpenClick(Sender: TObject);
var
  WIC : TWICImage;                  // Delphi 2010 以降
begin
  FileOpen.Enabled := False;
  if OpenDialog1.Execute then       // ファイルが指定されたら
    begin
      WIC := TWICImage.Create;
      try
        WIC.LoadFromFile(OpenDialog1.FileName);
        BitmapD.Assign(nil);
        BitmapD.PixelFormat := pf24bit;             // 24ビットフォーマット
        BitmapD.SetSize(WIC.Width, WIC.Height);
        BitmapD.Canvas.Draw(0, 0, WIC);             // フォーマット変換 24ビットフォーマットにします
      finally
        WIC.Free;
      end;
    end
    else
    begin
      FileOpen.Enabled := True;
      exit;                                         // ファイルが選択されなかったら終了
    end;
  .......
  .......
  .......

end;

 TWICImageを使用して、ファイルからデーターを読み込むと、32ビットのWindows標準フォーマットになるので、TBitmapの24ビットフォーマットに描いて24ビットフォーマットに変換してプログラムで利用しています。
保存を行う場合、TWICImageを使用すると、XE3、XE4の場合メモリーリークが発生します。メモリーリークを防ぐ場合、Vcl.Graphics の TWICImage.SaveToStream の修正が必要です。
ファイルの保存形式に応じて、 Jpeg、GIFImage、PNGImage を使用すればメモリーリークは発生しません。

procedure TForm1.SaveBtnClick(Sender: TObject);
var
  WIC : TWICImage;
begin
  WIC := TWICImage.Create;
  try
    WIC.Assign(Gbitma);
    WIC.ImageFormat := wifTiff;
    WIC.SaveToFile('test.tif');
  finally
    FreeAndNil(WIC);
  end;
end;

TWICImageの修正 (Vcl.Graphics の TWICImage.SaveToStream)
Delphiのインストールホルダーの source\vcl から Vcl.Graphics.pas の 、下記の赤部分を修正します。
SaveToFile と、SaveToStream を override して、SaveToStreamの修正したものを使用します。
EMBARCADERO DEVELOPER NETWORK QualityCentral を参照して下さい。
ここでダウンロード出来るファイルには、XE3のものを修正したのが入っています。

 修正部分

procedure TWICImage.SaveToStream(Stream: TStream);
var
  Encoder: IWICBitmapEncoder;
  Frame: IWICBitmapFrameEncode;
  Props: IPropertyBag2;
  LStreamAdapter: IStream;                // TStreamAdapter -> IStream
//  LStreamAdapter: TStreamAdapter;
  PixelFormat: TGUID;
  LStream: IWICStream;
  Palette: IWICPalette;
begin
  if FFormatChanged then
    begin
      FData.Clear;
      LStreamAdapter := TStreamAdapter.Create(FData);
//    IUnknown(LStreamAdapter)._AddRef;   // Comment Out

      FImagingFactory.CreateStream(LStream);
      LStream.InitializeFromIStream(LStreamAdapter);
      ....................
      ....................
      FFormatChanged := False;
    end;

  FData.Position := 0;
  Stream.CopyFrom(FData, FData.Size);
end;

新規ユニット
 
Vcl.Graphics.TWICImageを継承し、SaveToStreamのバグを修正しています 。
 SaveToFileは、分かりやすくするため、こちらに移していますが特に変更はありません。
 XE3,XE4でなければ、必要ないと思いますが、確認していません。

unit WicUnit;

interface

uses Vcl.Graphics, System.Classes, Winapi.wincodec, Winapi.ActiveX;

Type TWICImage = Class (Vcl.Graphics.TWICImage)
public
  procedure SaveToFile(const Filename: string); override;
  procedure SaveToStream(Stream: TStream); override;
end;

implementation

procedure TWICImage.SaveToFile(const Filename: string);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(Filename, fmCreate);
  try
    SaveToStream(Stream);
  finally
    Stream.Free;
  end;
end;

procedure TWICImage.SaveToStream(Stream: TStream);
var
  FGData: TMemoryStream;
  Encoder: IWICBitmapEncoder;
  Frame: IWICBitmapFrameEncode;
  Props: IPropertyBag2;
  LStreamAdapter: IStream;
  PixelFormat: TGUID;
  LStream: IWICStream;
  Palette: IWICPalette;
begin
  FGData := TMemoryStream.Create;
  try
    LStreamAdapter := TStreamAdapter.Create(FGData);

    ImagingFactory.CreateStream(LStream);
    LStream.InitializeFromIStream(LStreamAdapter);
    ImagingFactory.CreateEncoder(EncoderContainerFormat, guid_null, Encoder);

    Encoder.Initialize(LStream, WICBitmapEncoderNoCache);
    Encoder.CreateNewFrame(Frame, Props);

    Frame.Initialize(Props);
    Handle.GetPixelFormat(PixelFormat);
    Frame.SetPixelFormat(PixelFormat);

    Frame.SetSize(Width, Height);

    ImagingFactory.CreatePalette(Palette);
    Handle.CopyPalette(Palette);
    Frame.SetPalette(Palette);
    Frame.WriteSource(Handle, nil);
    Frame.Commit;
    Encoder.Commit;

    FGData.Position := 0;
    Stream.CopyFrom(FGData, FGData.Size);
  finally
    FGData.Free;
  end;
end;

end.


32ビットフォーマットのままでも良いのですが、24ビットフォーマットの方が扱いやすいので、変換をしています。

//*************************************************
// ガンマ値修正
//*************************************************
procedure TForm1.GummaBtnClick(Sender: TObject);
var
  II : integer;
  CH : integer;
  PD, PG : PByteArray;                    // バイトの静的配列へのポインタ 配列のサイズは、32,768
  XX, YY : integer;
  MM : integer;
begin
  Val(GummaEdit.Text,Gumma, CH);
  if CH <> 0 then begin
    application.MessageBox('入力に誤りがあります。','注意',0);
    exit;
  end;
  if Gumma <= 0 then begin
    application.MessageBox('ガンマの値にゼロ及びマイナスの値は指定できません。','注意',0);
    exit;
  end;
  GummaBtn.Enabled := False;

  Series1.Clear;                         // グラフクリア
  // 補正用 テンプレート作成
  for II := 0 to 255 do begin
    Gummadata[II] := Round(255 * Power(II / 255, 1 / Gumma));   // γ値テンプレートデーター作成
    Series1.Add(Gummadata[II],'');       // グラフに追加表示
  end;
  Chart1.Update;
  // 補正画像用ビットマップ
  Gbitmap.Assign(nil);                   // ビットマップ初期化
  Gbitmap.PixelFormat := pf24bit;
  Gbitmap.Width := BWidth;
  Gbitmap.Height := BHeight;
  // ガンマ補正
  for YY := 0 to BHeight -1 do begin
    PG := GBitmap.Scanline[YY];         // ビットマップラインポインター取得
    PD := BitmapD.ScanLine[YY];
    for XX := 0 to BWidth - 1 do begin
      MM := XX * 3;
      PG[MM] := Gummadata[PD[MM]];          // B テンプレート補正
      PG[MM + 1] := Gummadata[PD[MM + 1]];  // G テンプレート補正
      PG[MM + 2] := Gummadata[PD[MM + 2]];  // R テンプレート補正
    end;
  end;
  Image1.Canvas.StretchDraw(Vcrect,Gbitmap); // ストレッチ表示
  GummaBtn.Enabled := True;
end;


   download gamma_correction.zip


画像処理一覧へ戻る

    最初に戻る