ガンマ補正
カンマ補正は、一般的な補正なのです。
ブラウン管方式の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;