Color コントロール(RGB HSV)
カラーの調整をRGB to YSH(輝度、彩度、色相) 変換で行う方法を、Color Control
で載せてありますが、ここでは、RGB を HSV(色相、彩度、明度)に変換して、係数により変更 RGBに戻す方法についてのプログラムを載せています。
計算式は全く違うのですが、 調整した結果は、ほとんど同じような結果になります。
計算の方法は、イメージングソリューション"に詳しく乗っているのでそちらを参照してください。
Delphi には、RGBtoHSL HSLtoRGB 関数が用意されているので、簡単に変換ができるのですが、ここでは、プログラムの参考として、RGB
HSV変換を載せています。
鮮やかさだけを、調整するならば、鮮やかさ専用のプログラムがあるので、そちらの方が良いと思われます。
一般の色相環に対して逆周りとなっています。
作図順を逆にすれば良いのですが、ここでは、角度の定義に従いました。
作図の角度はYSH変換とHSV変換で同じなのですが、計算方式の違いにより、角度に対する色の位置にズレがあります。
サンプルプログラム
unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, system.Math, Vcl.ExtDlgs, Vcl.ExtCtrls, Vcl.StdCtrls, System.UITypes; type TForm1 = class(TForm) FileOpenBtn: TButton; ScrollBox1: TScrollBox; ScrollBox2: TScrollBox; Image1: TImage; Image2: TImage; OpenPictureDialog1: TOpenPictureDialog; RGBtoHSVbtn: TButton; RadioGroup1: TRadioGroup; SavePictureDialog1: TSavePictureDialog; FileSaveBtn: TButton; LabeledEdit1: TLabeledEdit; LabeledEdit2: TLabeledEdit; LabeledEdit3: TLabeledEdit; ColorControlBtn: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FileOpenBtnClick(Sender: TObject); procedure RGBtoHSVbtnClick(Sender: TObject); procedure FileSaveBtnClick(Sender: TObject); procedure ColorControlBtnClick(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} type TPrgbarry = array[0..0] of Trgbtriple; // 24ビットカラーレコード 32ビット用はTRGBQuadArray Prgbarray = ^TPrgbarry; // ポインター // 配列のポインターが必要なだけなので、長さは1 [0..0]で問題ありません。 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'; var InBitmap : TBitmap; // ビットマップ OutBitmap : TBitmap; GHeight, GWidth : integer; // ソース画像サイズ // RGB から HSV 変換ルーチン Hsu 色相 Saturation 彩度 Value 明度 procedure RGBtoHSV(Red, Green, Blue: Integer; var Hue, Saturation: Double; var Value: Integer); var CMax, CMin, CG: Integer; begin Hue := 0; CMax := Max(Red, Max(Green, Blue)); // 最大値検索 CMin := Min(Red, Min(Green, Blue)); // 最小値検索 Value := CMax; // 最大値が明度の値 // Value(明度) = 0 の時 if Value = 0 then begin Saturation := 0; Value := 0; exit; end; // Value(明度) <> 0 の時 Saturation := (CMax - CMin) / CMax; // 彩度の計算 if CMax <> CMin then begin // 色相の計算 CMAXとCMINが等しい場合は H(色相) = 0 CG := CMax - CMin; if Red = CMax then Hue := 60 * (Green - Blue) / CG else if Green = CMax then Hue := 60 * (Blue - Red) / CG + 120 else if Blue = CMax then Hue := 60 * (Red - Green) / CG + 240; if Hue < 0 then Hue := Hue + 360; end; end; // HSV から RGB 変換ルーチン Hsu 色相 Saturation 彩度 Value 明度 procedure HSVtoRGB(Hue, Saturation: Double; Value: Integer; var Red, Green, Blue: Byte); var P, Q, T, Hi : Integer; F : Double; begin if Saturation = 0 then // 彩度がゼロの場合はグレー 三色同じ値です begin Red := Value; Green := Value; Blue := Value; end else begin // Saturation <> 0 Hi := Trunc(Hue / 60); // 60で割った整数部の値 F := Frac(Hue / 60); // 60で割った小数部の値 P := Round(Value * (1 - Saturation)); Q := Round(Value * (1 - Saturation * F)); T := Round(Value * (1 - Saturation * (1 - F))); if P < 0 then P := 0; if P > 255 then P:= 255; if Q < 0 then Q := 0; if Q > 255 then Q:= 255; if T < 0 then T := 0; if T > 255 then T:= 255; case Hi of 0: begin Red := Value; Green := T; Blue := P; end; 1: begin Red := Q; Green := Value; Blue := P; end; 2: begin Red := P; Green := Value; Blue := T; end; 3: begin Red := P; Green := Q; Blue := Value; end; 4: begin Red := T; Green := P; Blue := Value; end; 5: begin Red := Value; Green := P; Blue := Q; end; end; end; end; // RGB から HSV 変換 表示 procedure TForm1.RGBtoHSVbtnClick(Sender: TObject); var i, j : integer; D, V : integer; hf, Sf : Double; pb, pbi : Prgbarray; begin for i := 0 to Gheight - 1 do begin pbi := Inbitmap.ScanLine[i]; pb := OutBitmap.ScanLine[i]; D := 0; for j := 0 to GWidth - 1 do begin RGBtoHSV(Pbi[j].rgbtRed, pbi[j].rgbtGreen, pbi[j].rgbtBlue, Hf, Sf, V); // HSV 変換 case RadioGroup1.ItemIndex of // ラジオボタンの位置で表示変更 0 : D := Round(Hf * 255 / 360); // 色相 1 : D := Round(Sf * 255); // 彩度 2 : D := V; // 明度 end; pb[j].rgbtRed := D; // 三色同じ値をセットしてグレー表示 pb[j].rgbtGreen := D; pb[j].rgbtBlue := D; end; end; image2.Picture.Bitmap := OutBitmap; // 画像表示 end; // ColorConrol procedure TForm1.ColorControlBtnClick(Sender: TObject); var Hue, sat, value : Double; k : integer; Hi, sati : Double; valuei : Integer; X, Y : Integer; Pb, Pbi : Prgbarray; begin val(LabeledEdit1.Text, Hue, K); if K <> 0 then begin Application.MessageBox('色相角度の入力値に誤りがあります。','色相',0); exit; end; if abs(hue) > 360 then begin Application.MessageBox('色相角度値が大きすぎます。','色相',0); exit; end; val(LabeledEdit2.Text, sat, K); if K <> 0 then begin Application.MessageBox('彩度Gain係数の入力値に誤りがあります。','彩度',0); exit; end; // if sat < 0 then begin // Application.MessageBox('彩度Gain係数はゼロ以下ではいけません。','彩度',0); // exit; // end; val(LabeledEdit3.Text, value, K); if K <> 0 then begin Application.MessageBox('明度Gain係数の入力値に誤りがあります。','明度',0); exit; end; if value < 0 then begin Application.MessageBox('明度Gain係数はゼロ以下ではいけません。','明度',0); exit; end; for Y := 0 to GHeight - 1 do begin pbi := Inbitmap.ScanLine[Y]; pb := OutBitmap.ScanLine[Y]; for X := 0 to GWidth - 1 do begin RGBtoHSV(pbi[X].rgbtRed, pbi[X].rgbtGreen, pbi[X].rgbtBlue, hi, Sati, valuei); // R G B を H(色相) S(彩度) V(明度)に変換 hi := hi + Hue; // 色相の処理 if hi < 0 then hi := hi + 360; if hi > 360 then hi := hi - 360; sati := sati * sat; // 彩度の処理 if sati > 1 then sati := 1; valuei := Round(valuei * value); // 明度の処理 if valuei > 255 then valuei := 255; HSVtoRGB(hi, sati, valuei, pb[X].rgbtRed, pb[X].rgbtGreen, pb[X].rgbtBlue); // H S V を R G B に変換 end; end; image2.Picture.Bitmap := OutBitmap; // 画像表示 FileSaveBtn.Enabled := True; end; // ファイルの指定オープン procedure TForm1.FileOpenBtnClick(Sender: TObject); var WIC : TWICImage; InFilename : String; begin 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; // 画像幅 InBitmap.Width := GWidth; // 画像高さ InBitmap.Height := GHeight; InBitmap.Canvas.Draw(0, 0, WIC); // DrawでInBitmapに入力画像設定フォーマット24ビットに変換されます OutBitmap.Width := GWidth; // 出力画像幅 OutBitmap.Height := GHeight; // 出力画像高さ image1.Picture.Bitmap := InBitmap; // 画像表示 finally WIC.Free; // TWICImage 解放 end; end else Exit; RGBtoHSVbtn.Enabled := True; // ボタンイネーブル ColorControlBtn.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 // XE3,XE4の場合はアンシャープマスキングを参照してください WIC.Free; // TWicImage解放 end; end; // 初期設定 procedure TForm1.FormCreate(Sender: TObject); begin Top := (Screen.Height - Height) div 2; // 表示位置設定 Left := (Screen.Width - Width) div 2; InBitmap := TBitmap.Create; // 入力画像用 OutBitmap := TBitmap.Create; // 出力画像用 InBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 OutBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 RGBtoHSVbtn.Enabled := False; // ボタンディスエブル FileSaveBtn.Enabled := False; ColorControlBtn.Enabled := False; end; // 終了時ビットマップの破棄 procedure TForm1.FormDestroy(Sender: TObject); begin InBitmap.Free; OutBitmap.Free; end; end.