モノカラー
色相を指定して、指定した色で画像を表示するプログラムを作成してみました。
複写機のコピーモードに、モノカラーがありますが、複写機の場合は、黒と他の色 一色でコピーするモードをモノカラーとよんでいます。
コピーする紙が、白である為、暗い色を表現するためには、黒のトナーが必要となるため、2色となります。
カラーの複写機で、モノカラーコピーを行う場合、トナーの色は、マゼンタ、シアン、イエロー、ブラックであり、赤のモノカラーは、マゼンタ、イエロー、ブラックの三色が使用されることになります。
マゼンタ、シアン、イエローを混ぜると、ブラックになりそうですが、実際には暗いグレーとなり、ブラックにはなりません。
その為、カラーのプリンター、複写機では、最低でも四色が使用されます。
パソコンの場合は、発光が小さくなれば黒になるので、単色でも可能です。
しかし、複写機でのモノカラーをパソコンで再現するためには、白発光も必要になり、結局は青、緑、赤の全色発光となり、見ためじょうの単色表示となります。
ここで、取り上げた、モノカラーの方法で、YSH変換、HSV変換によるものは、最大再度時は、一色、又は二色の混合色で表され、HSL変換は、明度の値によりグレイ+カラーとなり三色が使用されることになります。
彩度を下げた場合は、どの変換方式でも、グレー+色相色となります。
NTSCの輝度を使用する場合、特殊なのでYSHモードとの、関係についてプログラムを作成表示してみました。
変換の詳細については、色コントロールを参照してください。
上のカラーバーはHSVモードで表示、それをYSH変換して、更にRGBに戻して、カラーバーを再現しています。
全く同じカラーバーが再現されているのが分かります。
しかし、彩度、輝度に関しては、色相によって変化してしまいます、輝度、彩度の計算方法が全く違うためです。
まず、YSH変換を利用して、モノカラーのプログラムを作成を検討してみました。
輝度値を変えて、モノカラーを再現しようとしましたが、輝度値によって彩度が変わるので、うまくいきません。
結局、色相の最大彩度から、色の比率を変えずに、階調化して、再現する事にしました。
彩度を下げると、色によっては輝度値が小さいので、暗い画像となってしまいます。
赤の場合輝度変換係数が0.298912なので、グレー画像の最大の値が、255×0.298912=76 となります。
HSV変換の場合
この場合は、明度を変えて、モノカラー画像を作成します。
HSV変換の場合は、彩度がゼロの場合、輝度値がそのまま反映され、正しいグレー画像となります。
HSL変換の場合
HSV変換と同じように、明度により、画像を再現します。
グレー画像に色相色を加算したような、感じになり、明度0.5が、純色となります。
カラーバーは、明度0.5で作図していますので、彩度をゼロにすると、グレーになりますが、画像としては、輝度値が明度にそのまま反映されるので、正しいグレー画像となります。
HLS変換だと、色に関係なく、最大明度1は、白になります。
モノカラー画像としては、HLS変換を利用するのが、一番良い結果となるようです。
これが、HLSをWindowsの標準にしている理由でしょう。
HLS変換のサンプル。
どの色相でも、見やすいモノカラー画像が再現されています。
YSH、HSVの場合、色相によっては、暗い画像になってしまいます。
サンプルプログラム
サンプルプログラムは、HSL変換を使用したプログラムです。
RGBtoHSL、HSLtoRGBメソッドは、Delphiに、組み込まれているので、プログラムは簡単です。
ダウンロードZipファイルには、参考として、YSH、HSV変換を使用したプログラムも入っています。
unit MonoMain; 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, System.UIConsts, Math; type TForm1 = class(TForm) FileOpen: TButton; Image1: TImage; OpenPictureDialog1: TOpenPictureDialog; MonoColorBtn: TButton; SavePictureDialog1: TSavePictureDialog; FileSaveBtn: TButton; ScrollBar1: TScrollBar; Label1: TLabel; Label2: TLabel; ScrollBar2: TScrollBar; Image2: TImage; procedure FormCreate(Sender: TObject); procedure FileOpenClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure MonoColorBtnClick(Sender: TObject); procedure FileSaveBtnClick(Sender: TObject); procedure ScrollBar2Change(Sender: TObject); procedure ScrollBar1Change(Sender: TObject); private { Private 宣言 } procedure TemplateMake; // Templateデーター作成 procedure ColorBar; // カラーバー作図 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; // グレースケール輝度係数 青 var InBitmap : TBitmap; // ビットマップ OutBitmap : TBitmap; GHeight, GWidth : integer; // ソース画像サイズ Vrect : Trect; // 表示枠 IHeight, IWidth : Integer; // 表示サイズ MonoColorTemp : array[0..255] of Trgbtriple; // テンプレート用配列 // カラーバー作図 procedure TForm1.ColorBar; var h : integer; hue : double; st : double; Cor : TColor; begin st := Scrollbar2.Position / 255; for h := 0 to 360 do begin hue := h / 360; Cor := HSLtoRGB(hue, st, 0.5); // 明度0.5 0.5 が純色 Image2.Canvas.Pen.Color := Cor and $00FFFFFF; Image2.Canvas.MoveTo(h, 0); Image2.Canvas.LineTo(h, image2.Height); end; end; // Templateデーター作成 procedure TForm1.TemplateMake; var L : integer; hu : double; st : double; Cor : TColor; begin // 彩度値 st := Scrollbar2.Position / 255; // 色相値 hu := Scrollbar1.Position / 360; // 明度値 for L := 0 to 255 do begin Cor := HSLtoRGB(hu, st, L / 255) and $00FFFFFF; MonoColorTemp[L].rgbtBlue := GetBValue(Cor); MonoColorTemp[L].rgbtGreen := GetGValue(Cor); MonoColorTemp[L].rgbtRed := GetRValue(Cor); end; end; // NTSC係数で輝度変換し更にMonocolorに変換します procedure TForm1.MonoColorBtnClick(Sender: TObject); // セピア色変換 var II, JJ : Integer; InpPrgbarray : Prgbarray; OutParray : Prgbarray; GrayByte : BYTE; begin ColorBar; TemplateMake; // Templateデーター作成 for II := 0 to GHeight - 1 do begin InpPrgbarray := InBitmap.ScanLine[II]; // ラインポインタ設定 OutParray := OutBitmap.ScanLine[II]; for JJ := 0 to GWidth - 1 do begin GrayByte := Round(InpPrgbarray[JJ].rgbtBlue * BK + InpPrgbarray[JJ].rgbtGreen * GK + InpPrgbarray[JJ].rgbtRed * RK); OutParray[JJ] := MonoColorTemp[GrayByte]; end; end; Image1.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 FileSaveBtn.Enabled := True; end; procedure TForm1.ScrollBar1Change(Sender: TObject); begin MonoColorBtnClick(nil); end; procedure TForm1.ScrollBar2Change(Sender: TObject); begin ColorBar; MonoColorBtnClick(nil); end; // ファイルのオープン WIC がファイルの種類が多いので使用 procedure TForm1.FileOpenClick(Sender: TObject); var WIC : TWICImage; InFilename : String; begin MonoColorBtn.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 画像消去 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); // 表示用ビットマップサイズの設定 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ビットに変換されます OutBitmap.Width := GWidth; OutBitmap.Height:= GHeight; finally WIC.Free; // TWICImage 解放 end; end else Exit; MonoColorBtn.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(OutBitmap); // TWicImageにビットマップデーター割り付け WIC.ImageFormat := WICF; // 保存フォーマットセット WIC.SaveTofile(Fname); // ファイルの書き出し finally WIC.Free; // TWicImage解放 end; end; // 初期設定 procedure TForm1.FormCreate(Sender: TObject); begin MonoColorBtn.Enabled := False; FileSaveBtn.Enabled := False; Image1.Width := ImageHW; Image1.Height := ImageHW; InBitmap := TBitmap.Create; // 入力画像用 OutBitmap := TBitmap.Create; // 24ビット出力用 InBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 OutBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 image2.Width := 361; scrollbar2.Position := 255; end; // ビットマップの解放 procedure TForm1.FormDestroy(Sender: TObject); begin InBitmap.Free; OutBitmap.Free; end; end.