セピアカラー化
昔懐かしい、セピア色の画像に変換します。
写真のプリントは、昔は銀塩が殆どでした。
現在では、普通のプリンターによりプリントアウト出来ますし、パソコンにデジタル画像として保存が出来るので、プリントする機会が少なくなりました。
写真を配る場合も、メールに添付したり、メモリースティックいれて、渡したりします。
プリント代よりも、メモリースティックの方が安くすみます。
銀塩写真の場合、経年変化により、セピア色に変色します。
経年変化の具合は、環境によって大きく左右され、保存状態によっては、殆ど変化しません。
セピア色に加工するプログラムは色々あるのですが、Delphiで作成してみました。
1. セピア化する方法として、グレースケール化した輝度値に、セピア色のパラメーターを乗じて変換
セピア色 R=107 G=74 B=43 R=1とすると G=0.691 B=0.401 となりますが、
このままでは、赤が強くなるので、G,Bの値を少し大きくします。
輝度値をYとすると、
赤 R=Y÷255 × 240 R=0.941
緑 G=Y÷255 × 200 G=0.784
青 B=Y÷255 × 145 B=0.568
の値が、インターネットで検索すると紹介されています。
此処では、少しでも画像を明るくするため
赤 R=Y÷255 × 255 R=1
緑 G=Y÷255 × 213 G=0.835
青 B=Y÷255 × 121
B=0.475 写真のセピアらしくするためBは少し小さく
の値を設定してみました。
パラメーターによる方法では、全体的に画像が暗くなってしまいます。
最大輝度値が小さくなります。
2. GDIプラスのカラーマトリックスを使用する方法(カラーマトリックスについては、グレースケール化を参照して下さい。)
//
Sepiaカラーマトリックス
ColorMatrix_SepiaScale : TColorMatrix = (
( 0.393, 0.349, 0.272, 0.0, 0.0),
( 0.769, 0.686, 0.534,
0.0, 0.0),
( 0.189, 0.168, 0.131, 0.0, 0.0),
( 0.0, 0.0, 0.0, 1.0,
0.0),
( 0.0, 0.0,
0.0, 0.0, 1.0));
グレースケール輝度変換係数と、セピア色の係数を合わせたような値となっています。
セピア色の濃度の調整が難しいです。
3. 輝度値からガンマ値により、RGBの値を作成する方法
R=(Y/255)1/rR
× 255
G=(Y/255)1/rG
× 255
B=(Y/255)1/rB
× 255
rB
= 1.55 rG
= 1.15 rB
= 0.55
ガンマ補正カーブを利用して、輝度値をRGBに変換してセピア色に表示します。
各輝度において、セピア色となります。
視覚的に、グレー画像と輝度が変わりません。
ガンマ値を使用すると、広い範囲の輝度のセピア化が出来ます。
青の値は、0.4位まで小さくしても良いかと思われます。
画像サンプル
好みによって、差はあると思うのですが、ガンマ補正が、一番忠実に変換が出来ていると思われます。
サンプルプログラム
unit SepiaMain; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.ExtDlgs, System.Types, System.UITypes, GDIPAPI, GDIPOBJ, Math; type TForm1 = class(TForm) FileOpen: TButton; Image1: TImage; Image2: TImage; OpenPictureDialog1: TOpenPictureDialog; SepiaColorBtn: TButton; SavePictureDialog1: TSavePictureDialog; FileSaveBtn: TButton; GDiMatGrayBtn: TButton; GammaBtn: TButton; procedure FormCreate(Sender: TObject); procedure FileOpenClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure SepiaColorBtnClick(Sender: TObject); procedure FileSaveBtnClick(Sender: TObject); procedure GDiMatGrayBtnClick(Sender: TObject); procedure GammaBtnClick(Sender: TObject); private { Private 宣言 } procedure TemplateMake; // Templateデーター作成 procedure SepiaTemplate; // SepiaTemplateデーター作成 procedure GammaTemplate; // SepiaGammaTemplateデーター作成 public { Public 宣言 } end; var Form1: TForm1; type TPrgbarry = array[0..0] of Trgbtriple; // 24ビットカラーレコード 32ビット用はTRGBQuadArray Prgbarray = ^TPrgbarry; // ポインター // 配列のポインターが必要なだけなので、長さは1 [0..0]で問題ありません。 implementation {$R *.dfm} const OpenFileFilter = '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.ico;*.wdp'+ '|*.png|*.png' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.bmp|*.bmp' + '|*.tif|*.tif' + '|*.ico|*.ico' + '|*.wdp|*.wdp'; SaveFileFilter = '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.wdp' + '|*.png|*.png' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.bmp|*.bmp' + '|*.tif|*.tif' + '|*.wdp|*.wdp'; ImageHW = 384; // 表示枠サイズ RK = 0.298912; // グレースケール輝度係数 赤 GK = 0.586611; // グレースケール輝度係数 緑 BK = 0.114478; // グレースケール輝度係数 青 BISU = 1024; // 整数化倍数 RSISF = 10; // ビットシフト数 SepiaR = 1.000; // セピア係数赤 SepiaG = 0.835; // セピア係数緑 SepiaB = 0.475; // セピア係数青 { SepiaR = 0.941; // セピア係数赤 SepiaG = 0.784; // セピア係数緑 SepiaB = 0.447; // セピア係数青 } RGamma = 1.55; // 赤ガンマ値 GGamma = 1.15; // 緑ガンマ値 BGamma = 0.55; // 青ガンマ値 // Sepiaマトリックス ColorMatrix_SepiaScale : TColorMatrix = ( ( 0.393, 0.349, 0.272, 0.0, 0.0), ( 0.769, 0.686, 0.534, 0.0, 0.0), ( 0.189, 0.168, 0.131, 0.0, 0.0), ( 0.0, 0.0, 0.0, 1.0, 0.0), ( 0.0, 0.0, 0.0, 0.0, 1.0)); var InBitmap : TBitmap; // ビットマップ OutBitmap24 : TBitmap; GHeight, GWidth : integer; // ソース画像サイズ Vrect : Trect; // 表示枠 IHeight, IWidth : Integer; // 表示サイズ RTemp : array[0..255] of Cardinal; // テンプレート用配列 赤 GTemp : array[0..255] of Cardinal; // テンプレート用配列 緑 BTemp : array[0..255] of Cardinal; // テンプレート用配列 青 RSTemp : array[0..255] of Byte; // セピアテンプレート用配列 赤 GSTemp : array[0..255] of Byte; // セピアテンプレート用配列 緑 BSTemp : array[0..255] of Byte; // セピアテンプレート用配列 青 RGTemp : array[0..255] of Byte; // セピアガンマテンプレート用配列 赤 GGTemp : array[0..255] of Byte; // セピアガンマテンプレート用配列 緑 BGTemp : array[0..255] of Byte; // セピアガンマテンプレート用配列 青 procedure TForm1.GammaTemplate; // SepiaGammaTemplateデーター作成 var II : Integer; begin for II := 0 to 255 do begin RGTemp[II] := Round(255 * Power(II / 255, 1 / RGamma)); // セピアテンプレート赤 GGTemp[II] := Round(255 * Power(II / 255, 1 / GGamma)); // セピアテンプレート緑 BGTemp[II] := Round(255 * Power(II / 255, 1 / BGamma)); // セピアテンプレート青 end; end; procedure TForm1.SepiaTemplate; // SepiaTemplateデーター作成 var II : Integer; begin for II := 0 to 255 do begin RSTemp[II] := Round(II * SepiaR); // セピアテンプレート赤 GSTemp[II] := Round(II * SepiaG); // セピアテンプレート緑 BSTemp[II] := Round(II * SepiaB); // セピアテンプレート青 end; end; // NTSC 係数を1024倍してテンプレートデーター作成 procedure TForm1.TemplateMake; // Templateデーター作成 var II : Integer; begin for II := 0 to 255 do begin RTemp[II] := round(RK * BISU * II); // 赤テンプレート GTemp[II] := round(GK * BISU * II); // 緑テンプレート BTemp[II] := round(BK * BISU * II); // 青テンプレート end; end; // ファイルのオープン WIC がファイルの種類が多いので使用 procedure TForm1.FileOpenClick(Sender: TObject); var WIC : TWICImage; InFilename : String; begin SepiaColorBtn.Enabled := False; GDiMatGrayBtn.Enabled := False; GammaBtn.Enabled := False; FileSaveBtn.Enabled := False; VRect := Rect(0, 0, Image1.Width, Image1.Height); Image1.Canvas.Brush.Style := bsSolid; Image1.Canvas.Brush.Color := clBtnface; Image1.Canvas.FillRect(VRect); // Canvas 画像消去 Image2.Canvas.Brush.Style := bsSolid; Image2.Canvas.Brush.Color := clBtnface; Image2.Canvas.FillRect(VRect); // Canvas 画像消去 OpenPictureDialog1.Filter := OpenFileFilter; // ファイルオープンフィルターの設定 if OpenPictureDialog1.Execute then // ファイルが指定されたら begin WIC := TWICImage.Create; // TWICImageの生成 try InFilename := OpenPictureDialog1.FileName; // ファイル名の取得 WIC.LoadFromFile(InFilename); // 画像の読み込み GHeight := WIC.Height; // 画像高さ取得 GWidth := WIC.Width; // 画像幅 IWidth := ImageHW; // 出力先イメージ1の幅 IHeight := ImageHW; // 出力先イメージ1の高さ if GHeight <= GWidth then // 縦横比により出力サイズ設定 IHeight := Round(IWidth * GHeight / GWidth) else IWidth := Round(IHeight * GWidth / GHeight); Image1.Width := IWidth; Image1.Height:= IHeight; Image1.Picture.Bitmap.SetSize(IWidth, IHeight); // 表示用ビットマップサイズの設定 Image2.Width := IWidth; Image2.Height:= IHeight; Image2.Picture.Bitmap.SetSize(IWidth, IHeight); // 表示用ビットマップサイズの設定 VRect := Rect(0, 0, IWidth, IHeight); // 出力枠設定 Image1.Canvas.StretchDraw(VRect, WIC); // 出力枠に変倍出力 InBitmap.Width := GWidth; InBitmap.Height := GHeight; InBitmap.Canvas.Draw(0, 0, WIC); // DrawでInBitmapに入力画像設定フォーマット24ビットに変換されます OutBitmap24.Width := GWidth; OutBitmap24.Height:= GHeight; finally WIC.Free; // TWICImage 解放 end; end else Exit; SepiaColorBtn.Enabled := True; GDiMatGrayBtn.Enabled := True; GammaBtn.Enabled := True; end; // NTSC係数で輝度変換し更にセピア色に変換します procedure TForm1.SepiaColorBtnClick(Sender: TObject); // セピア色変換 var II, JJ : Integer; InpPrgbarray : Prgbarray; OutParray : Prgbarray; OutInt : DWORD; OutByte : BYTE; begin for II := 0 to GHeight - 1 do begin InpPrgbarray := InBitmap.ScanLine[II]; // ラインポインタ設定 OutParray := OutBitmap24.ScanLine[II]; for JJ := 0 to GWidth - 1 do begin // 三色加算 OutInt := RTemp[InpPrgbarray[JJ].rgbtRed] // 赤テンプレート + GTemp[InpPrgbarray[JJ].rgbtGreen] // 緑テンプレート + BTemp[InpPrgbarray[JJ].rgbtBlue]; // 青テンプレート OutByte := OutInt shr RSISF; // 10ビット右シフト OutInt div 1024 OutParray[JJ].rgbtBlue := BSTemp[OutByte]; // セピアテンプレート青適用 OutParray[JJ].rgbtGreen := GSTemp[OutByte]; // セピアテンプレート緑適用 OutParray[JJ].rgbtRed := RSTemp[OutByte]; // セピアテンプレート赤適用 end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap24); // 出力枠に変倍出力 FileSaveBtn.Enabled := True; end; // マトリックスによるセピア変換 procedure TForm1.GDiMatGrayBtnClick(Sender: TObject); var Gpgraph : TGPGraphics; GPImage : TGPImage; GPImageAttr : TGPImageAttributes; Astream : TMemoryStream; begin // GPImage := TGPImage.Create(InFilename); // TGPImageにファイル名関連付け // ファイルから読み込む代わりにメモリーへ出力しメモリーから読み込みます // メモリーストリームを作成し inbitmap を出力 Astream := TMemoryStream.Create; InBitmap.SaveToStream(Astream); // メモリーストリームから,TGPBitmapのオブジェクトに読み込み指定 GPImage := TGPBitmap.Create(TStreamAdapter.Create(AStream, soReference)); // GPImage := TGPImage.Create(InFilename); // TGPImageにファイル名関連付け // GD+にTBitmapの描画ハンドル設定 Gpgraph := TGPGraphics.Create(OutBitmap24.Canvas.Handle); // TGPImageAttributesのオブジェクトを生成 GPImageAttr := TGPImageAttributes.Create; GPImageAttr.SetGamma(1.0); // ガンマ値設定 1.0 なので無くても可 // 使用するマトリックスをアトリビュートに設定 GPImageAttr.SetColorMatrix(ColorMatrix_SepiaScale); // TCanvasのStretchDrawに相当する // GpgraphのHandleがOutBitmap24.Canvasに指定されているのでOutBitmap24.Canvasに描画されます Gpgraph.DrawImage( // Gpgraph (OutBitmap24) に描画 GPImage, // 元画像 MakeRect(0, 0, GWidth, GHeight), // 出力位置 サイズ 0, 0, GPImage.GetWidth, GPImage.GetHeight, // 入力位置 サイズ UnitPixel, // ピクセル GPImageAttr); // アトリビュート Image2.Canvas.StretchDraw(Vrect, OutBitmap24); // Image1に Dbitmap変倍表示 //Createしたオブジェクトは解放処理します。 GPImageAttr.Free; GPImage.Free; Gpgraph.Free; Astream.Free; FileSaveBtn.Enabled := True; end; // ガンマ係数によるセピア変換 procedure TForm1.GammaBtnClick(Sender: TObject); var II, JJ : Integer; InpPrgbarray : Prgbarray; OutParray : Prgbarray; OutInt : DWORD; OutByte : BYTE; begin for II := 0 to GHeight - 1 do begin InpPrgbarray := InBitmap.ScanLine[II]; // ラインポインタ設定 OutParray := OutBitmap24.ScanLine[II]; for JJ := 0 to GWidth - 1 do begin // 三色加算 OutInt := RTemp[InpPrgbarray[JJ].rgbtRed] // 赤テンプレート + GTemp[InpPrgbarray[JJ].rgbtGreen] // 緑テンプレート + BTemp[InpPrgbarray[JJ].rgbtBlue]; // 青テンプレート OutByte := OutInt shr RSISF; // 10ビット右シフト OutInt div 1024 OutParray[JJ].rgbtBlue := BGTemp[OutByte]; // セピアガンマテンプレート青適用 OutParray[JJ].rgbtGreen := GGTemp[OutByte]; // セピアガンマテンプレート緑適用 OutParray[JJ].rgbtRed := RGTemp[OutByte]; // セピアガンマテンプレート赤適用 end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap24); // 出力枠に変倍出力 FileSaveBtn.Enabled := True; end; // ファイルの保存 procedure TForm1.FileSaveBtnClick(Sender: TObject); var WIC : TWicImage; WICF : TWicImageFormat; Fname : String; ExeStr : String; FnameTop: String; Findex : integer; function WFormatSet: Boolean; // 拡張子によるファイルフォーマット設定 begin Result := false; ExeStr := LowerCase(ExeStr); if ExeStr = '.jpg' then begin WICF := Wifjpeg; Result := True; end; if ExeStr = '.jpeg' then begin WICF := Wifjpeg; Result := True; end; if ExeStr = '.tif' then begin WICF := Wiftiff; Result := True; end; if ExeStr = '.tiff' then begin WICF := Wiftiff; Result := True; end; if ExeStr = '.png' then begin WICF := Wifpng; Result := True; end; if ExeStr = '.gif' then begin WICF := Wifgif; Result := True; end; if ExeStr = '.bmp' then begin WICF := Wifbmp; Result := True; end; if ExeStr = '.wdp' then begin WICF := WifWMPhoto; Result := True; end; if ExeStr = '.hdp' then begin WICF := WifWMPhoto; Result := True; end; end; begin SavePictureDialog1.Filter := SaveFileFilter; // SavePictureDialog1.DefaultExt := GraphicExtension(TWicImage); if not SavePictureDialog1.Execute then exit; ExeStr := ExtractFileExt(SavePictureDialog1.FileName); if ExeStr = '' then begin // 拡張子がなかったら Findex := SavePictureDialog1.FilterIndex; // FilterIndexによる拡張子の設定 case Findex of 1, 3 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.jpg'); // 拡張子の設定 2 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.png'); // 拡張子の設定 4 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.gif'); // 拡張子の設定 5 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.bmp'); // 拡張子の設定 6 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.tif'); // 拡張子の設定 7 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.wdp'); // 拡張子の設定 end; end else Fname := SavePictureDialog1.FileName; ExeStr := ExtractFileExt(Fname); // 拡張子だけ取り出し if not WFormatSet then begin // 拡張子によるファイルフォーマット設定と確認 application.MessageBox('ファイルの拡張子が間違っています。','注意', 0); exit; end; FnameTop := ExtractFileName(Fname); // ファイル名だけ取り出し if Length(FnameTop) = Length(ExeStr) then begin // ファイル名の長さ確認 application.MessageBox('ファイル名がありません。','注意', 0); exit; end; if FileExists(Fname) then // ファイル名によるファイル検索 if MessageDlg('既に同じ名前のファイルがあります上書きしますか ' + ExtractFileName(Fname) + '?', mtConfirmation, [mbYes, mbNo], 0, mbNo) = IDNo then exit; WIC := TWicImage.Create; // TWicImage生成 try WIC.Assign(OutBitmap24); // TWicImageにビットマップデーター割り付け WIC.ImageFormat := WICF; // 保存フォーマットセット WIC.SaveTofile(Fname); // ファイルの書き出し finally WIC.Free; // TWicImage解放 end; end; // 初期設定 procedure TForm1.FormCreate(Sender: TObject); begin SepiaColorBtn.Enabled := False; FileSaveBtn.Enabled := False; GDiMatGrayBtn.Enabled := False; GammaBtn.Enabled := False; Image1.Width := ImageHW; Image1.Height := ImageHW; Image2.Width := ImageHW; Image2.Height := ImageHW; InBitmap := TBitmap.Create; // 入力画像用 OutBitmap24 := TBitmap.Create; // 24ビット出力用 InBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 OutBitmap24.PixelFormat := pf24bit; // 24ビットカラーに設定 TemplateMake; // Templateデーター作成 SepiaTemplate; // SepiaTemplateデーター作成 GammaTemplate; // SepiaGammaTemplateデーター作成 end; // ビットマップの解法 procedure TForm1.FormDestroy(Sender: TObject); begin InBitmap.Free; OutBitmap24.Free; end; end.