Color コントロール(RGB HSV)

 カラーの調整をRGB to YSH(輝度、彩度、色相) 変換で行う方法を、Color Control で載せてありますが、ここでは、RGB を HSV(色相、彩度、明度)に変換して、係数により変更 RGBに戻す方法についてのプログラムを載せています。
計算式は全く違うのですが、 調整した結果は、ほとんど同じような結果になります。
計算の方法は、イメージングソリューション"に詳しく乗っているのでそちらを参照してください。

Delphi には、RGBtoHSL HSLtoRGB 関数が用意されているので、簡単に変換ができるのですが、ここでは、プログラムの参考として、RGB HSV変換を載せています。

HSV0HSV1

 鮮やかさだけを、調整するならば、鮮やかさ専用のプログラムがあるので、そちらの方が良いと思われます。

ring 一般の色相環に対して逆周りとなっています。
作図順を逆にすれば良いのですが、ここでは、角度の定義に従いました。
作図の角度は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.

    download ColorControl(RGB_HSV).zip

画像処理一覧へ戻る

      最初に戻る