写真の色相補正

 写真の色補正は、デジカメで撮影した時本来の色と変わってしまう場合、本来の色に戻す為のプログラムです。
光の三原色は、赤、緑、青となっていますが、単純に三色に分解して撮影すると、青より波長の短い紫の色が暗い青になってしまいます。
人の視覚は、青と、赤の光の混合色を紫として認識するので、赤のセンサーに波長の短い紫部分の感度を持たせれば良いのですが、コンパクトデジカメでは、省略されているか感度の低いものがあります。
赤のセンサーの紫部分の感度が無いか低い場合紫色の部分が暗くなってしまいますので、青のセンサーに対して紫部分の感度を上げる必要があり、これは技術的に容易です。
通常の写真の場合は殆ど問題は無いのですが、紫色の、花の写真は、青い色の花になってしまいます。
 販売されているデジカメで、高価な一眼レフカメラは紫に対する対策が取られていますが、デジカメのカタログには記されていません。
コンパクトデジカメの場合、価格、カタログ上の性能で判別がつきません、購入する場合は、紫色の物を持って行って、撮影モードで確認すれば良いでしょう。
安価なカメラでも紫の対策がされている物があります。
 紫が青になってしまう場合、照明や、撮影条件の変更で、紫に写せると説明をしているWebサイトがありますが、紫の色に対して赤と青の色に変換する機能の無いカメラの場合、絶対に紫になることはありません。
照明や、撮影条件で、紫に写るとしたら、その紫の色は、赤と青の光の混合色で、被写体が赤と青を反射している紫か、紫に対して赤と青に分解する機能を持っているカメラで、単に感度があっていない場合です。
 カメラによっては、色の無い、輝度用センサーと、赤、青フィルターのセンサーで構成し、YCbCr演算で色の再現をしているものもあるようです。
色の無い輝度用センサーを使用することにより暗いときの感度を上げることができますが、色の再現性はあまりよくないようです。



上の図の一番上のカラーバーはパソコンで表示しているカラーバーです。
中央のは、そのカラーバーを紫が青になってしまうデジカメで撮影したもので、一番下側のは、一応紫が紫に写るカメラで撮影したものです。
上図の紫は、赤と青の光の混合色ですが、紫が青になってしまうカメラの場合、青に対する感度が異常に高いことが分かります。

  色の補正は、青の部分だけ、紫側にずらし他の部分は補正しない様にします。
青い空が写っている場合は、空も紫色になってしまうので、花の部分だけ色相を変更するように範囲指定をします。
青い空の中に紫の花がある場合、同じ青になってしまい、分離が難しくなります。
クロマキーによる画像の分離が必要ですが、此処のプログラムでは採用していません。

 上の写真は、カシオのコンパクトデジカメとパナソニックのデジカメの写真を比べたものです。
パナソニックのデジカメの写真は、元の色に近い紫色ですが、カシオのコンパクトデジカメでは、青く成ってしまっています。
一番右は、カシオのコンパクトデジカメ写真の青い部分を色相補正して紫に変更したものです。
(現在、カシオはデジタルカメラの製造販売を終了しています。)

 色の補正は、色相をズラスことで行ないます。
補正をする範囲の上限と下限、ズラス色相の位置、移動量を指定します。
移動量は、指定点が最大となり、上限と下限に行くにしたがって小さくし、限度外は移動しない様にします。
上限下限がゼロ度をまたぐ場合は、計算に注意が必要です。
 輝度、彩度、色相の計算は、テレビで使用されていた、NTSC 方式(YSH)の計算です。
 一般的に使用されている色相環(HSV)の計算は、輝度(明度)の計算が視覚に対するものと外れている為に使用できません。
画像のRGBをHSVに変換し、そのまま元のRGBに逆変換すれば元の画像が得られますが、色相の補正をすると、輝度が変わってしまい、異常な画像になります。
NTSC(YSH)の輝度係数は視覚に補正した値になっています。
  輝度 Y = 赤 × 0.298912+ 緑 × 0.586611 + 青 × 0.114478
HSVは三色の明るさの最大値が明度となます。
  赤 = 0~1 緑 = 0~1 青 = 0~1

注) NTSCY,Cb,CrCb Cr から 彩度 S =  √(Cb2 + Cr2)    色相 H = tan-1(Cr/Cb) を計算し YSH(輝度、彩度、色相) としています。
   Cr = S × Sin(H), Cb = S × Cos(H)
   このYSHの呼称は、ここだけの呼び名で学術的に呼ばれているものではありません。
   YSHの計算式は、プログラムを、HSVについては、色コントロール(RGB HSV変換)を参照してください。

  上の元写真は、コンパクトデジカメでツクバトリカブトの花を撮影したものですが、紫の色が青に成ってしまっています。
HSV色相を使用して補正すると、明度と輝度の差で、明暗がおかしくなります。
Windowsに標準として入っているHSL変換を使用しても同じ結果です。
アナログテレビで使用されていたNTSC方式のYSH(YCbCr)変換を使用すれば、輝度の異常な変動がない為、色合いだけが素直に変更できます。
青い空が入っていると、空の青も紫側へ色合いが変わってしまうので注意が必要です。

 左の写真は、YSH輝度変換と、HSV明度変換です。
元写真の花の部分の青は、YSH輝度変換の場合、小さな値となりますが、HSV明度変換の場合大きな値となります。
又、HSV変換の場合、60°毎に変換計算が変わり60°の範囲を超えて色相が変換されると逆変換計算が変わってしまい、元の濃淡の再現出来ません。


 左図は、色相による色補正をするプログラムの実行画面です。
ColorRingの内側の表示は、基本色の表示で、外側は、補正後の表示です。
 色相補正値の、上限、下限、補正指定位置、補正量は、補正値入力のチェックボックスにチェックを入れ、色相環の補正したい色の付近をマウスでクリックすることにより、色相の角度を入力することが出来ます。
補正の指定は、三個指定が出来、三個 同時に補正をする事も可能です。
 色相環はNTSC方式と全く同じ角度と、赤、緑、青の角度を120°等分にする方式が選べます。
 紫の色が青になってしまうコンパクトデジカメの場合、当然、青は青として写るので、変更したくない青も紫に補正されてしまうので、その場合は、変更する範囲の指定をします。
左図では、範囲指定の例となっています、トリカブトの色が指定された範囲だけ紫色に替わっています。
 輝度、彩度の変更をしていすると、全体的に変更されます。
又、入力値による色相変更も、全体的に色相が入力値分変更されます。
 指定範囲だけの色相の変更は、ColorRing補正チェックボックスにチェックをいれまて行います。
指定範囲の入力は、上側の画像の上をマウスの、左ボタン押し下げ後ドラッグするだけです。
取り消しは、指定範囲の枠が表示されている状態で、上側の画像上でマウスの左ボタンを押し下げるだけです。
 本プログラムでは、上限下限指定の色相変更の場合、輝度、彩度の変更はしていませんが、色相により輝度、彩度も微妙に変化するので、色相の変化量に応じて、変更できるようにした方が良いでしょう。


プログラム

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtDlgs, Vcl.ExtCtrls, Vcl.StdCtrls, System.Math,
  Vcl.Buttons, System.UITypes, System.Types;

Type
  TDIarray   = array of array of Double;              // YSH画像用配列

type
  TRevarry  = array[0..2] of array[0..4] of double ;  // 色相補正値配列

type
  TForm1 = class(TForm)
    FileOpenbtn: TButton;
    Image1: TImage;
    OpenPictureDialog1: TOpenPictureDialog;
    RGBtoYSHtoRGBBtn: TButton;
    Image2: TImage;
    sat_imageBtn: TButton;
    hue_imageBtn: TButton;
    YmEdit: TLabeledEdit;
    SmEdit: TLabeledEdit;
    HdEdit: TLabeledEdit;
    CheckBox1: TCheckBox;
    yTograyBtn: TButton;
    tran_yshBtn: TBitBtn;
    FileSaveBtn: TButton;
    SavePictureDialog1: TSavePictureDialog;
    Image3: TImage;
    colorRingBtn: TButton;
    Memo1: TMemo;
    Panel1: TPanel;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    RadioButton3: TRadioButton;
    RadioButton4: TRadioButton;
    RadioButton5: TRadioButton;
    RadioButton6: TRadioButton;
    RadioButton7: TRadioButton;
    RadioButton8: TRadioButton;
    RadioButton9: TRadioButton;
    RadioButton10: TRadioButton;
    RadioButton11: TRadioButton;
    RadioButton12: TRadioButton;
    CheckBox3: TCheckBox;
    CheckBox4: TCheckBox;
    CheckBox5: TCheckBox;
    CheckBox6: TCheckBox;
    Panel2: TPanel;
    CheckBox7: TCheckBox;
    CheckBox2: TCheckBox;
    procedure FileOpenbtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure RGBtoYSHtoRGBBtnClick(Sender: TObject);
    procedure sat_imageBtnClick(Sender: TObject);
    procedure hue_imageBtnClick(Sender: TObject);
    procedure tran_yshBtnClick(Sender: TObject);
    procedure yTograyBtnClick(Sender: TObject);
    procedure FileSaveBtnClick(Sender: TObject);
    procedure colorRingBtnClick(Sender: TObject);
    procedure Image3MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure CheckBox4Click(Sender: TObject);
    procedure CheckBox5Click(Sender: TObject);
    procedure CheckBox6Click(Sender: TObject);
    procedure CheckBox3Click(Sender: TObject);
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  private
    { Private 宣言 }
    function  sat_image: Integer;               // 彩度データを濃淡画像化
    procedure hue_image;                        // 色相データを画像化
    procedure tran_ysh(ym, sm, hd :Double);     // 輝度,彩度,色相を変える
    procedure setarray;                         // 配列サイズ設定
    procedure hue_circle_revision(Mode: integer; rev: TRevarry; Lmode: boolean);  // 補正色相環作図
    procedure Revinitial;                                         // 色相補正値初期化
    function  RingShift(Hu: double; rev: TRevarry):double;        // 色相補正
    procedure RevDisp;                                            // 色相補正値表示
    procedure HueMsin(Q :double; I:integer);                      // 色相補正値範囲設定
    procedure PanelBtnset;                                        // 補正値入力選択ラジオボタン位置設定
    function HuRedCalc(Mode: integer): double;                    // 輝度変換係数による赤の色相計算
    procedure rgb_to_ysh;                                         // R,G,BからYSH(輝度,彩度,色相)に変換
    procedure ysh_to_rgb(hu, sa, yo: TDIarray);                   // YSHから R,G,Bに変換
  public
    { Public 宣言 }
  end;

type
  TPrgbarry = array[0..0] of Trgbtriple;                          // rgb配列ポインタ設定
  Prgbarry  = ^TPrgbarry;

var
  Form1: TForm1;

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   = 450;                                // 表示枠サイズ
  Image3HW  = 300;
  ButtonW   = 105;

  krD = 0.298912;                                 // R 輝度変換係数
  kgD = 0.586611;                                 // G 輝度変換係数
  kbD = 0.114478;                                 // B 輝度変換係数


var
  GHeight, GWidth : integer;                  // 画像サイズ
  Vrect           : Trect;
  InBitmap        : TBitmap;                  // 入力画像ビットマップ
  OutBitmap       : TBitmap;                  // 出力画像ビットマップ

  y               : TDIarray;                 // y  輝度
  sat             : TDIarray;                 // 彩度
  hue             : TDIarray;                 // 色相

  Out_y           : TDIarray;                 // y  輝度
  Out_sat         : TDIarray;                 // 彩度
  Out_Hue         : TDIarray;                 // 色相

  kr              : double;                   // = 0.299;  R 輝度変換係数
  kg              : double;                   // = 0.587;  G 輝度変換係数
  kb              : double;                   // = 0.114;  B 輝度変換係数

  revision        : TRevarry;                 // 色相補正角 三種類

  Drawing       : Boolean;                    // マウス選択枠表示フラグ
  Origin        : TPoint;                     // マウススタート位置
  EndPoint      : TPoint;                     // マウスアップ位置
  NewMove       : Boolean;                    // マウス新移動
  REMove        : Boolean;                    // 再枠表示

  OriginG       : TPoint;                     // マウススタート位置
  EndPointG     : TPoint;                     // マウスアップ位置
  GScale        : double;                     // 画像サイズ係数

//----------------------------------------
// 輝度変換係数による赤の色相計算
// 標準NTSC  赤 113.1°
// 120°当分  赤 120°
//----------------------------------------
function TForm1.HuRedCalc(Mode: integer): double;
var
  y, cr, cb : double;
begin
  // 変換係数設定
  if Mode = 0 then begin
    // 標準輝度変換係数 
    kr := krD;
    kg := kgD;
    kb := kbd;
  end
  else begin
    // 120°等分色相角度変換係数
    kr := 255 / (255 - 255 * tan(105 / 180 * pi));  // kr = kb = 0.211325
    kb := kr;
    kg := 1 - kr - kb;                              // kg = 0.57735
//    kr := 0.211325;
//    kg := 0.57735;
//    kb := 0.211325;
  end;
  // 赤の角度計算
  y := kr * 255;
  //  y := kr * r + kg * g + kb * b;
  cr := 255 - y;
  cb := 0 - y;
  result := arctan2(cr, cb) * 180 / pi;
end;

// ------------ 色相角度補正 ------------------------
// 指定された範囲と補正量で色相角度の補正をします。
// Hu  色相角度
// rev 補正指定配列
//---------------------------------------------------
function TForm1.RingShift(Hu: double; rev: TRevarry):double;
var
  J               : integer;
  J0, J1, J2, J3  : double;
  revkU, revkD    : double;
begin
  for J := 0 to 2 do begin
    J0 := rev[J,0];                                 // 下限値
    J1 := rev[J,1];                                 // 上限値
    J2 := rev[J,2];                                 // 移動指定位置
    J3 := rev[J,3];                                 // 移動量
    if (rev[J,4] <> 0) and (J0 <> J1) then begin  // rev[J,4] 修正有効 1 無効 0  &  下限<>上限
      if J1 < J0 then  begin                      // 上限が下限より小さかったら
        if Hu < J1 then Hu := Hu + 360;             // 色相が上限より小さかったら360°加算
        if J0 > J2 then J2 := J2 + 360;             // 移動指定位置が下限より小さかったら360°加算
        J1 := J1 + 360;                             // 上限値に360°加算   加算の順番に注意
      end;
      if (Hu >= J0) and (Hu < J1) then begin      // 色相が下限と上限の中に入っていたら
        if Hu >= J2 then begin                      // 色相が移動指定位置より大きかったら
          revkU := (J1 - (J2 + J3)) / (J1 - J2);    // 修正係数
          Hu   := J1 - (J1 - Hu) * revkU;           // 色相補正値計算
        end
        else begin                                  // 色相が移動指定位置より小さかったら
          revkD := ((J2 + J3) - J0) / (J2 - J0);    // 修正係数
          Hu   := (Hu - J0) * revkD + J0;           // 色相補正値計算
        end;
      end;
    end;
    if hu >= 360 then hu := hu - 360;
    if hu < 0    then hu := hu + 360;
  end;
  result := hu;
end;

//------------ colorring作図 -------------------------------------
// Mode 0   標準 
// Mode 1  120°等分
// rev      色相補正配列
// Lmode    False  補正無し内側色相Ring  True 補正外側色相Ring
// HSV 表示なので色は半時計周り
//----------------------------------------------------------------
procedure TForm1.hue_circle_revision(Mode: integer; rev: TRevarry; Lmode: boolean);
var
  i  : integer;
  hu, huRed : double;
  st : double;
  y, cb, cr     : double;
  fr, fg, fb    : double;
  kbg, krg      : double;
  r, g, b       : byte;
  max, min, ks  : double;
  N             : integer;
  huD, hub      : double;
  q1, q2        : integer;
  q1p, q2p      : double;
  RO, RI, RF    : integer;
begin
  if Lmode then begin         // 補正表示
    q1 := 4;
    q2 := q1 div 2 - 1;
    RO  := 95;
    RI  := 85;
  end
  else begin                  // 通常表示
    q1 := 8;
    q2 := q1 div 2 - 2;
    RO  := 80;
    RI  := 60;
  end;
  RF  := (RO + RI) div 2;
  q1p := q1 / 180 * pi;
  q2p := q2 / 180 * pi;
  // 変換係数設定
  huRed := HuRedCalc(Mode);
  // 逆変換係数計算
  kbg := kb / kg;
  krg := kr / kg;
  // 彩度値仮数
  st := 255;
  // 輝度値仮数
  y := 255;
  // 分割数計算
  N := 360 div q1 - 1;
  with Image3.canvas do begin
   // イメージ消去
    if not Lmode then begin
      Brush.Style := bsSolid;
      Brush.Color := clBlack;
      FillRect(Rect(0, 0, Width, Height));                          // Canvas 画像消去
      Pen.Width :=2;
    end
    else
      Pen.Width := 4;
    // 色相環作図
    for i := 0 to N do begin
      hub := i * q1;
      // 色補正表示だったら指定範囲シフト
      if Lmode then begin
        // 色相補正
        huD := RingShift(hub, rev);
        // 色相値表示
        memo1.Lines.Add(floatTostr(hub) + '  ' + floatTostr(huD));
      end
      else huD := hub;
      // 色相角度  赤をゼロ度に設定 修正せずに描画すると
      // 赤が真上になります
      // 座標でゼロ度の位置を赤にします。
      huD := huD + huRed;
      if huD > 0 then huD := huD - 360;
      hu := huD / 180 * pi;
      // 色係数 B - Y
      cb := st * cos(hu);
      // 色係数 R - Y
      cr := st * sin(hu);
      // R G B の 仮の値計算
      fr := y + cr;
      fg := y - kbg * cb - krg * cr;
      fb := y + cb;
      // 最大値最小値検索
      max := fr;
      min := fr;
      if fg > max then max := fg;
      if fb > max then max := fb;
      if fg < min then min := fg;
      if fb < min then min := fb;
      // R G B の値修正
      ks := 255 / (max - min);
      r := round((fr - min) * ks);
      g := round((fg - min) * ks);
      b := round((fb - min) * ks);
      // 描画色設定
      Pen.Color := rgb(r, g, b);
      brush.Color := rgb(r, g, b);
      // 色相環描画
      Moveto(150 + round(RI * cos(i * q1p - q2p)),150 - round(RI * sin(i * q1p - q2p)));
      Lineto(150 + round(RO * cos(i * q1p - q2p)),150 - round(RO * sin(i * q1p - q2p)));
      AngleArc(150, 150, RO, i* q1 - q2, q2 * 2);

      Moveto(150 + round(RI * cos(i * q1p - q2p)),150 - round(RI * sin(i * q1p - q2p)));
      AngleArc(150, 150, RI,i* q1 - q2, q2 * 2);
      Lineto(150 + round(RO * cos(i * q1p + q2p)),150 - round(RO * sin(i * q1p + q2p)));
      // 塗りつぶし
      if not Lmode then
        floodfill(150 + round(RF * cos(i * q1p)),150 - round(RF * sin(i * q1p)),rgb(r, g, b),fsBorder);
    end;
    if Lmode then begin
      font.Height := 18;
      brush.Color := clblack;
      font.Color  := clWhite;
      textout(150 + 105, 150 -   8, '0°');
      textout(150 -  10, 150 - 120, '90°');
      textout(150 - 140, 150 -   8, '180°');
      textout(150 -  10, 150 + 100, '270°');
    end;
  end;
end;

//----------------------------
// 色相角編集値の入力有効化
//-----------------------------
procedure TForm1.CheckBox3Click(Sender: TObject);
begin
  if checkbox1.Checked then begin
    hue_circle_revision(1, revision, false);
    hue_circle_revision(1, revision, true);
  end
  else begin
    hue_circle_revision(0, revision, false);
    hue_circle_revision(0, revision, true);
  end;
  RevDisp;    // 色相補正値の表示
end;

//---------------------------------
// 設定1の有効化
//---------------------------------
procedure TForm1.CheckBox4Click(Sender: TObject);
begin
  RevDisp;    // 色相補正値の表示
end;

//---------------------------------
// 設定2の有効化
//---------------------------------
procedure TForm1.CheckBox5Click(Sender: TObject);
begin
  RevDisp;    // 色相補正値の表示
end;

//---------------------------------
// 設定3の有効化
//---------------------------------
procedure TForm1.CheckBox6Click(Sender: TObject);
begin
  RevDisp;    // 色相補正値の表示
end;

//---------------------------------
// 色相補正値の表示
//---------------------------------
procedure TForm1.RevDisp;
var
  I : integer;
  Max, Cen : double;
begin
  memo1.Clear;
  if checkBox4.Checked then revision[0, 4] := 1
                       else revision[0, 4] := 0;
  if checkBox5.Checked then revision[1, 4] := 1
                       else revision[1, 4] := 0;
  if checkBox6.Checked then revision[2, 4] := 1
                       else revision[2, 4] := 0;
  for I := 0 to 2 do begin
    Memo1.Lines.Add('設定   ' + inttostr(I + 1));
    Memo1.Lines.Add(' 下限値  ' + floattostr(revision[I, 0]));
    Memo1.Lines.Add(' 上限値  ' + floattostr(revision[I, 1]));
    Memo1.Lines.Add(' 指定位置 ' + floattostr(revision[I, 2]));
    Memo1.Lines.Add(' 移動量  ' + floattostr(revision[I, 3]));
    if revision[I,4] <> 0 then Memo1.Lines.Add(' 有効   ' + floattostr(revision[I, 4]))
                          else Memo1.Lines.Add(' 無効   ' + floattostr(revision[I, 4]));
    Memo1.Lines.Add(' ');
  end;
    Memo1.Lines.Add(' ');
  for I := 0 to 2 do begin
    Max := revision[I, 1];                            // 上限値
    Cen := revision[I, 2];                            // 指定位置
    if revision[I, 0] > revision[I, 1] then begin     // 下限値が上限値より大きかったら
      Max := Max + 360;                                 // 上限値に360°加算
      if revision[I, 2] < revision[I, 0] then           // 指定値が上限値より小さかったら指定値に360°加算
        Cen := revision[I, 2] + 360;
    end;
    if revision[i, 3] >= 0 then begin                 // 移動量がプラスだったら上限をチェック
      if MAX <= Cen + revision[I, 3] then               // 指定位置プラス移動量が上限値を超えたら
        Memo1.Lines.Add('設定' + intTostr(I + 1) + '修正の範囲が上限値を超えています。');
    end
    else begin                                        // 移動量がマイナスだったら
      if revision[I, 0] >= Cen + revision[I, 3] then  // 指定位置プラス移動量が下限値を超えたら
        Memo1.Lines.Add('設定' + intTostr(I + 1) + '修正の範囲が下限値を超えています。');
    end;
    if (Max < Cen) or (revision[I, 0] > Cen) then
        Memo1.Lines.Add('設定' + intTostr(I + 1) + '指定位置が上下の範囲を超えています。');
  end;
end;

//---------------------------------
// 補正配列初期設定
//---------------------------------
procedure TForm1.Revinitial;
begin
  revision[ 0, 0] := 315;    // 下の値
  revision[ 0, 1] :=  90;    // 上の値
  revision[ 0, 2] :=  20;    // 補正位置
  revision[ 0, 3] :=  20;    // 補正量
  revision[ 0, 4] :=  0;     // 有効

  revision[ 1, 0] := 197;    // 下の値
  revision[ 1, 1] := 330;    // 上の値
  revision[ 1, 2] := 217;    // 補正位置
  revision[ 1, 3] :=  30;    // 補正量
  revision[ 1, 4] :=   1;    // 有効

  revision[ 2, 0] := 110;    // 下の値
  revision[ 2, 1] := 220;    // 上の値
  revision[ 2, 2] := 170;    // 補正位置
  revision[ 2, 3] := -30;    // 補正量
  revision[ 2, 4] :=  0;     // 有効
  if revision[0, 4] = 1 then checkBox4.Checked := true;
  if revision[1, 4] = 1 then checkBox5.Checked := true;
  if revision[2, 4] = 1 then checkBox6.Checked := true;
  RevDisp;                    // 色相補正値の表示
end;

//-----------------------------------------------------
// 色相環表示 1  標準      赤113.1°,緑225°,青352.7°
//           0  120°等分  赤105°  ,緑225°,青345°
//-----------------------------------------------------
procedure TForm1.colorRingBtnClick(Sender: TObject);
begin
  colorRingBtn.Enabled := False;
  memo1.Clear;
  if checkbox1.Checked then begin
    hue_circle_revision(1, revision, false);
    hue_circle_revision(1, revision, true);
  end
  else begin
    hue_circle_revision(0, revision, false);
    hue_circle_revision(0, revision, true);
  end;
  checkBox3.Checked := False;
  colorRingBtn.Enabled := True;
end;

//--- tran_ysh  輝度,彩度,色相を変えます ---------------
//    y:            入力データ配列 Y
//    sat:          入力データ配列 SAT
//    hue:          入力データ配列 HUE
//    out_y:        出力データ配列 Y
//    out_sat:      出力データ配列 SAT
//    out_hue:      出力データ配列 HUE
//    ym:           輝度の乗数
//    sm:           彩度の乗数
//    hd:           色相の増分
//------------------------------------------------------
procedure TForm1.tran_ysh(ym, sm, hd :Double);
var
  i, j  : integer;
  hub   : double;
  revi  : TRevarry;                          // 色相補正角 三種類
  huRed : double;
begin
  // チェック時 輝度変換係数を色相角度が120度毎になるように設定 R 105度 g 225度 b 345度
  // チェック無し                         R 113度 g 225度 b 353度
  if checkbox1.Checked then huRed := HuRedCalc(1)
                       else huRed := HuRedCalc(0);
  for i := 0 to 2 do                         // 三種類
    for j := 0 to 4 do begin                 // 五項目
      if j <= 2  then begin
        // YSHの赤の色相位置と補正値の赤の位置が違うので補正値側を修正します
        revi[i, j] := revision[i][j] + huRed;
        if revi[i, j] >= 360 then revi[i, j] := revi[i][j] - 360;
      end
      else revi[i, j] := revision[i, j]
    end;
  // 輝度、彩度、色相の補正計算
  for i := 0 to GHeight - 1 do
    for j := 0 to GWidth - 1 do begin
      out_y[i][j]   := y[i][j] * ym;                        // 輝度の計算
      out_sat[i][j] := sat[i][j] * sm;                      // 彩度の計算
      // 色相補正計算の選択
      if not checkBox2.Checked then begin                 // 全体補正
        hub := hue[i][j] + hd;                              // 色相の計算
        if hub >= 360 then hub := hub - 360;
        if hub <  0   then hub := hub + 360;
        out_hue[i][j] := hub;
      end
      else begin                                          // 部分補正
        if CheckBox7.Enabled then begin                       // 範囲指定だったら
          if not CheckBox7.Checked then begin                 // 指定範囲内補正
            if (j >= OriginG.X) and (j <= EndPointG.X)
              and (i >= OriginG.Y) and (i <= EndPointG.Y) then
                out_hue[i][j] := RingShift(hue[i][j], revi)   // 補正配列による色相修正
            else out_hue[i][j] := hue[i][j];
          end
          else                                                // 指定範囲外補正
            if (j >= OriginG.X) and (j <= EndPointG.X)
              and (i >= OriginG.Y) and (i <= EndPointG.Y) then
                out_hue[i][j] := hue[i][j]
              else
                out_hue[i][j] := RingShift(hue[i][j], revi);   // 補正配列による色相修正
        end
        else
          out_hue[i][j]:= RingShift(hue[i][j], revi);   // 補正配列による色相修正
      end;
    end;
  ysh_to_rgb(out_hue, out_sat, out_y);
  Image2.Canvas.StretchDraw(VRect, OutBitmap);        // 出力枠に変倍出力
end;

//------------- 輝度、彩度、色相を変えます -----------------
//    ym:           輝度の乗数
//    sm:           彩度の乗数
//    hd:           色相の増分
//--------------------------------------------------------
procedure TForm1.tran_yshBtnClick(Sender: TObject);
var
  ym    : Double;
  sm    : Double;
  hd    : Double;
  ch    : Integer;
begin
  CheckBox3.Checked := False;
  tran_yshBtn.Enabled := False;
  // チェック時 輝度変換係数を色相角度が120度毎になるように設定 R 105度 g 225度 b 345度
  // チェック無し                         R 113度 g 225度 b 353度
  if CheckBox1.Checked then huRedCalc(1)     // 105°
                       else huRedCalc(0);    // 113°
  setarray;                       // 配列サイズ設定
  rgb_to_ysh;
  val(ymEdit.Text, ym, ch);       // 輝度の乗数
  if ch <> 0 then ym := 1;
  ym := abs(ym);
  val(smEdit.Text, sm, ch);       // 彩度の乗数
  if ch <> 0 then sm := 1;
  sm := abs(sm);
  val(hdEdit.Text, hd, ch);       // 色相の増分
  if ch <> 0 then hd := 0;
  if hd > 360  then hd :=  360;   // 360度以内に設定
  if hd < -360 then hd := -360;
  tran_ysh(ym, sm, hd);           // 輝度,彩度,色相を変える
  filesavebtn.Enabled := True;
  tran_yshBtn.Enabled := True;
end;

//--- hue_image  色相データを画像化します ---------------------
//    sat:          彩度のデータ配列
//    hue:          色相のデータ配列
//    stdhue:       基準となる色相値
//    OutBitmap:    出力画像
//-------------------------------------------------------------
procedure TForm1.hue_image;
var
  i, j    : Integer;
  ihue    : Integer;
  delt    : Double;
  OutPrgbarray  : Prgbarry;
  stdhue  : double;
begin
  val(hdedit.Text, stdhue, i);
  if i <> 0 then stdhue := 0;
  if stdhue > 360  then stdhue :=  360;                       // 360度以内に設定
  if stdhue < -360 then stdhue := -360;
  for i := 0 to GHeight - 1 do begin
    OutPrgbarray := OutBitmap.ScanLine[i];
    for j := 0 to GWidth - 1 do begin
      if sat[i][j] > 0 then begin                             // 彩度がゼロ以上だったら
        delt := abs(hue[i][j] - stdhue);                      // 色相角度の絶対値
        if delt < 0 then delt := delt + 360;
        if delt > 180  then delt := 360.0 - delt;             // 180度以内に設定 
        ihue := Trunc(255.0 - delt * 255.0 / 180);            // 255から0に変換
        OutPrgbarray[j].rgbtBlue  := ihue;
        OutPrgbarray[j].rgbtGreen := ihue;
        OutPrgbarray[j].rgbtRed   := ihue;
      end
      else begin
        OutPrgbarray[j].rgbtBlue  := 0;
        OutPrgbarray[j].rgbtGreen := 0;
        OutPrgbarray[j].rgbtRed   := 0;
      end;
    end;
  end;
  Image2.Canvas.StretchDraw(VRect, OutBitmap);                // 出力枠に変倍出力
end;

//-----------------------------------------
// 色相角のデーター画像表示
//-----------------------------------------
procedure TForm1.hue_imageBtnClick(Sender: TObject);
begin
  hue_image;        // 色相データを画像化
end;

//------------------------------
// 色相補正値範囲設定
//------------------------------
procedure TForm1.HueMsin(Q :double; I:integer);
begin
  if revision[ I, 0] < revision[ I, 1]  then begin
    revision[ I, 3] := Q - revision[ I, 2];
  end
  else begin
    if Q < revision[ I, 1] then begin
      if revision[ I, 2] < revision[ I, 1] then
        revision[ I, 3] := Q -  revision[ I, 2];
      if revision[ I, 2] > revision[ I, 0] then
        revision[ I, 3] := Q +  (360 - revision[ I, 2]);
    end;
    if Q > revision[ I, 0] then begin
      if revision[ I, 2] > revision[ I, 0] then
        revision[ I, 3] := Q -  revision[ I, 2];
      if revision[ I, 2] < revision[ I, 1] then
        revision[ I, 3] := - (revision[ I, 2] + 360 - Q);
    end;
  end;
end;

//----------------------------------------------
// 入力画像でマウスにより色補正範囲指定
//----------------------------------------------
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if REMove then begin                                                    // 範囲指定があったら
    Image1.Canvas.Pen.Mode := pmNotXor;                                   // ペンモード Not Xor
    Image1.Canvas.Rectangle(Origin.X, Origin.Y, EndPoint.X, EndPoint.Y);  // 前の線消去
    REMove := False                                                       // 範囲指定取り消し
  end;
  Origin := Point(X, Y);                                                  // 座標の保存
  EndPoint := point(X, Y);                                                // 新しい座標を保存
  NewMove := True;                                                        // 新規移動
  Drawing := True;                                                        // 枠描画中セット
  Image1.Canvas.Pen.Mode := pmCopy;                                       // ペンモード通常に
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if Drawing and (Shift = [ssLeft]) then begin        // 枠描画中で左ボタンを押しながら移動
    Image1.Canvas.Pen.Mode := pmNotXor;                                     // ペンモード Not Xor
    if not NewMove then begin                         // 移動中なら
      Image1.Canvas.Rectangle(Origin.X, Origin.Y, EndPoint.X, EndPoint.Y);  // 前の線消去
    end;
    EndPoint := point(X, Y);                                                // 新しい座標を保存
    Image1.Canvas.Rectangle(Origin.X, Origin.Y, EndPoint.X, EndPoint.Y);
    NewMove := False;                                                       // 新規移動リセット
    Image1.Canvas.Pen.Mode := pmCopy;                                       // ペンモード通常に
  end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  OX, EX : integer;
  OY, EY : integer;
begin
  if Drawing then begin           // 枠描画中なら
    REMove := True;                 // 範囲指定セット
    Drawing := False;               // 枠描画中リセット
    NewMove := False;               // 新規移動リセット
    OX := Origin.X;
    OY := Origin.Y;
    EX := EndPoint.X;
    EY := EndPoint.Y;
    // 指定範囲座標の整理
    if OX > EX then begin
      Origin.X := EX;
      EndPoint.X := OX;
    end;
    if OY > EY then begin
      Origin.Y := EY;
      EndPoint.Y := OY;
    end;
    // 範囲がゼロなら範囲指定取り消し
    if (OX = EX) and (OY = EY) then begin
      CheckBox7.Enabled := False;   // 範囲指定方法チェックボックス デスエブル
      REMove := False;              // 範囲指定フラグリセット
    end
    else
      CheckBox7.Enabled := True;    // 範囲指定方法チェックボックス イネーブル
    // 指定範囲座標を画像座標に変換
    OriginG.X := round(Origin.X * Gscale);
    OriginG.Y := round(Origin.Y * Gscale);
    EndPointG.X := round(EndPoint.X * Gscale);
    EndPointG.Y := round(EndPoint.Y * Gscale);
  end;
end;

//----------------------------------------------
// 色相環表示のマウスの位置から色相編集値入力
//----------------------------------------------
procedure TForm1.Image3MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Xpos, Ypos : integer;
  Q          : double;
begin
  if not CheckBox3.Checked then exit;
  Xpos := X - 150;
  Ypos := 150 - Y;
  Q := arctan2(Ypos, Xpos);
  Q := int(Q / Pi * 180);
  if Q < 0 then Q := Q + 360;
  if RadioButton1.Checked then revision[ 0, 0] := Q;
  if RadioButton2.Checked then revision[ 0, 1] := Q;
  if RadioButton3.Checked then revision[ 0, 2] := Q;
  if RadioButton4.Checked then begin
    HueMsin(Q, 0);
  end;
  if RadioButton5.Checked then revision[ 1, 0] := Q;
  if RadioButton6.Checked then revision[ 1, 1] := Q;
  if RadioButton7.Checked then revision[ 1, 2] := Q;
  if RadioButton8.Checked then begin
    HueMsin(Q, 1);
  end;
  if RadioButton9.Checked then revision[ 2, 0] := Q;
  if RadioButton10.Checked then revision[ 2, 1] := Q;
  if RadioButton11.Checked then revision[ 2, 2] := Q;
  if RadioButton12.Checked then begin
    HueMsin(Q, 2);
  end;
  RevDisp;                // 色相補正値の表示
end;

//--- sat_image  彩度データを濃淡画像化します ----------------------------
//    sat:          彩度のデータ配列
//    OutBitmap:    出力画像
//------------------------------------------------------------------------
function TForm1.sat_image: Integer;
var
  i, j      : Integer;
  isat      : Integer;
  min, max  : double;
  OutPrgbarray  : Prgbarry;
begin
  min := 255;
  max := 0;
  // 彩度の最大値と最小値検索
  for i := 0 to  GHeight - 1 do
    for j := 0 to GWidth - 1 do begin
      if sat[i][j] > max then max := sat[i][j];
      if sat[i][j] < min then min := sat[i][j];
    end;
  // 彩度がなかったら終了
  if min = max then begin
    result := -1;
    exit;
  end;
  for i := 0 to GHeight - 1 do begin
    OutPrgbarray := OutBitmap.ScanLine[i];
    for j := 0 to GWidth - 1 do begin
      isat := Trunc(255 * (sat[i][j] - min) / (max - min));   // 彩度の値を0~255に変換
      OutPrgbarray[j].rgbtRed   := isat;
      OutPrgbarray[j].rgbtGreen := isat;
      OutPrgbarray[j].rgbtBlue  := isat;
    end;
  end;
  Image2.Canvas.StretchDraw(VRect, OutBitmap);                // 出力枠に変倍出力
  result := 0;
end;

//---------------------------------------------
// 彩度データーの画像表示
//---------------------------------------------
procedure TForm1.sat_imageBtnClick(Sender: TObject);
var
  Ret : Integer;
begin
  Ret := sat_image;                                           // 彩度データを濃淡画像化
  if Ret = -1 then application.MessageBox('彩度がありません。','情報',0);
end;

// グレイ画像(輝度)表示
procedure TForm1.yTograyBtnClick(Sender: TObject);
var
  i, j          : Integer;
  iglay         : Integer;
  OutPrgbarray  : Prgbarry;
begin
  for i := 0 to GHeight - 1 do begin
    OutPrgbarray := OutBitmap.ScanLine[i];
    for j := 0 to GWidth - 1 do begin
      iglay := round(y[i,j]);
      OutPrgbarray[j].rgbtRed   := iglay;
      OutPrgbarray[j].rgbtGreen := iglay;
      OutPrgbarray[j].rgbtBlue  := iglay;
    end;
  end;
  Image2.Canvas.StretchDraw(VRect, OutBitmap);                // 出力枠に変倍出力
end;

//---------------------------------------------
// 輝度、彩度、色相 から RGBに変換します
// hu     色相配列
// sa     彩度配列
// yo     輝度配列
//---------------------------------------------
procedure TForm1.ysh_to_rgb(hu, sa, yo: TDIarray);  // YSHから R,G,Bに変換
var
  i, j        : Integer;
  rad, c1, c2 : Double;
  ir, ig, ib    : Integer;
  OutPrgbarray  : Prgbarry;
  a, b          : Double;
begin
  a := kr / kg;                                            // a = 0.299 / 0.587
  b := kb / kg;                                            // b = 0.114 / 0.587
  for i := 0 to GHeight - 1 do begin
    OutPrgbarray := OutBitmap.ScanLine[i];
    for j := 0 to GWidth - 1 do begin
      rad := PI * hu[i][j] / 180;
      c1 := sa[i][j] * sin(rad);
      c2 := sa[i][j] * cos(rad);
      ir := Trunc(yo[i][j] + c1);                    // R = Y + (R - Y)
      if ir > 255 then ir := 255;
      if ir < 0   then ir := 0;
      ig := Trunc(yo[i][j] - a * c1 - b * c2);       // G = Y - a(R - Y) - b(B - Y)
      if ig > 255 then ig := 255;
      if ig < 0   then ig := 0;
      ib := Trunc(yo[i][j] + c2);                    // B = Y + (B - Y)
      if ib > 255 then ib := 255;
      if ib < 0   then ib := 0;
      OutPrgbarray[j].rgbtRed   := ir;
      OutPrgbarray[j].rgbtGreen := ig;
      OutPrgbarray[j].rgbtBlue  := ib;
    end;
  end;
end;

//----------------------------------------------------
// RGBを 輝度value、彩度sat、色相hueに変更します。
//----------------------------------------------------
procedure TForm1.rgb_to_ysh;                        // R,G,Bから輝度,彩度,色相変換
var
  i, j        : Integer;
  fr, fg, fb  : Double;
  c1, c2      : Double;
  Inprgbarry  : Prgbarry;                               // rgb配列ポインタ
begin
  for i := 0 to GHeight - 1 do begin
    Inprgbarry := inBitmap.ScanLine[i];
    for j := 0 to GWidth - 1 do begin
      fr := Inprgbarry[j].rgbtRed;
      fg := Inprgbarry[j].rgbtGreen;
      fb := Inprgbarry[j].rgbtBlue;
      y[i][j]  :=  kr * fr + kg * fg + kb * fb;         // Y                輝度
      c1 :=  fr - y[i][j];                              // R - Y  (fr - y)  色差C1
      c2 :=  fb - y[i][j];                              // B - Y  (fb - y)  色差C2
      sat[i][j] := sqrt(c1 * c1 + c2 * c2);             // 彩度
      hue[i][j] := arctan2(c1, c2) * 180 / pi;          // 色相
      if hue[i][j] < 0 then  hue[i][j] := hue[i][j] + 360;
    end;
  end;
end;

//---------- 配列の長さセット---------------------
//    y:            データ配列 Y
//    sat:          彩度のデータ配列
//    hue:          色相のデータ配列
//    out_y         出力輝度のデータ配列
//    out_sat:      出力彩度のデータ配列
//    out_hue:      出力色相のデータ配列
//------------------------------------------------
procedure TForm1.setarray;
begin
  setlength(y,   GHeight, GWidth);
  setlength(sat, GHeight, GWidth);
  setlength(hue, GHeight, GWidth);
  setlength(out_sat, GHeight, GWidth);
  setlength(out_hue, GHeight, GWidth);
  setlength(out_y,   GHeight, GWidth);
  OutBitmap.Width  := GWidth;
  OutBitmap.Height := GHeight;
end;

//------- 各変換後元へ戻して画像表示確認--------------------
procedure TForm1.RGBtoYSHtoRGBBtnClick(Sender: TObject);
begin
  // チェック時 輝度変換係数を色相角度が120度毎になるように設定 R 105度 g 225度 b 345度
  // チェック無し                         R 113度 g 225度 b 353度
  if CheckBox1.Checked then huRedCalc(1)     // 105°
                       else huRedCalc(0);    // 113°
  setarray;                                                   // 配列サイズ設定
  rgb_to_ysh;
  ysh_to_rgb(hue, sat, y);
  Image2.Canvas.StretchDraw(VRect, OutBitmap);                // 出力枠に変倍出力
  sat_imageBtn.Enabled  := True;
  hue_imageBtn.Enabled  := True;
  yTograyBtn.Enabled    := True;
end;

//------- 画像ファイルオープン 表示---------------------------
procedure TForm1.FileOpenbtnClick(Sender: TObject);
var
  WIC         : TWICImage;
  InFilename  : String;
  IHeight     : Integer;
  IWidth      : Integer;
begin
  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);
        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ビットに変換
      finally
        WIC.Free;                                             // TWICImage 解放
      end;
    end
    else exit;
  GScale := GWidth / Image1.Width;
  REMove := False;
  CheckBox7.Enabled := False;
  RGBtoYSHtoRGBBtn.Enabled    := True;
  sat_imageBtn.Enabled  := False;
  hue_imageBtn.Enabled  := False;
  tran_yshBtn.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.PanelBtnset;
const
  Btnp = 20;
var
  I : integer;
begin
  for I := 1 to 12 do begin
    TRadioButton(FindComponent('RadioButton' + IntToStr(I))).Height := 15;
    TRadioButton(FindComponent('RadioButton' + IntToStr(I))).Width := 100;
    TRadioButton(FindComponent('RadioButton' + IntToStr(I))).Left := 16;
  end;
  checkBox3.Height := 15;
  checkBox3.Width := 100;
  checkBox3.Top := 5;
  checkBox3.left := 5;

  checkBox4.Height := 15;
  checkBox4.Width := 100;
  checkBox4.Left := 10;
  checkBox5.Height := 15;
  checkBox5.Width := 100;
  checkBox5.Left := 10;
  checkBox6.Height := 15;
  checkBox6.Width := 100;
  checkBox6.Left := 10;

  RadioButton1.Top := checkBox3.Top + Btnp + 5;
  RadioButton2.Top := RadioButton1.Top + Btnp;
  RadioButton3.Top := RadioButton2.Top + Btnp;
  RadioButton4.Top := RadioButton3.Top + Btnp;
  checkBox4.Top := RadioButton4.Top + Btnp;

  RadioButton5.Top := checkBox4.Top + Btnp + 20;
  RadioButton6.Top := RadioButton5.Top + Btnp;
  RadioButton7.Top := RadioButton6.Top + Btnp;
  RadioButton8.Top := RadioButton7.Top + Btnp;
  checkBox5.Top := RadioButton8.Top + Btnp;

  RadioButton9.Top := checkBox5.Top + Btnp + 20;
  RadioButton10.Top := RadioButton9.Top + Btnp;
  RadioButton11.Top := RadioButton10.Top + Btnp;
  RadioButton12.Top := RadioButton11.Top + Btnp;
  checkBox6.Top := RadioButton12.Top + Btnp;
  panel1.Height := 5 + checkBox6.Top + checkBox6.Height + 5;
end;

//----------------------------------
// Form内の設定
//----------------------------------
procedure TForm1.FormCreate(Sender: TObject);
var
  bitmap : Tbitmap;
begin
  Form1.ClientHeight := ImageHW * 2 + 40;
  Form1.ClientWidth := 20 + ImageHW + 20 + Image3HW + 20 + ButtonW + 20;
  Image1.Width  := ImageHW;
  Image1.Height := ImageHW;
  Image2.Width  := ImageHW;
  Image2.Height := ImageHW;
  Image3.Width  := Image3HW;
  Image3.Height := Image3HW;
  bitmap := Tbitmap.Create;
  bitmap.Width := ImageHW;
  bitmap.Height := ImageHW;
  Image1.Picture.Graphic := bitmap;
  Image2.Picture.Graphic := bitmap;
  bitmap.Width := Image3HW;
  bitmap.Height := Image3HW;
  Image3.Picture.Graphic := bitmap;
  Image1.Left   := 20;
  Image2.Left   := 20;
  Image3.Left   := 20 + ImageHW + 20;
  memo1.Width := Image3HW div 2 + 20;
  memo1.Left := 20 + ImageHW + 20;
  panel1.Left := memo1.Left + memo1.Width + 10;
  Image1.Top := 10;
  Image2.Top := 10 + Image2.Height + 20;
  FileOpenBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  RGBtoYSHtoRGBbtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  sat_imageBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  hue_imageBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  YmEdit.Left := 20 + ImageHW + 20 + Image3HW + 20;
  SmEdit.Left := 20 + ImageHW + 20 + Image3HW + 20;
  HdEdit.Left := 20 + ImageHW + 20 + Image3HW + 20;
  Panel2.Left := 20 + ImageHW + 20 + Image3HW + 15;
  CheckBox1.Left := 20 + ImageHW + 20 + Image3HW + 20;
  CheckBox2.Left := 5;
  CheckBox7.Left := 5;
  yTograyBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  tran_yshBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  FileSaveBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  colorRingBtn.Left := 20 + ImageHW + 20 + Image3HW + 20;
  InBitmap  := TBitmap.Create;
  OutBitmap := TBitmap.Create;
  InBitmap.PixelFormat  := pf24bit;
  OutBitmap.PixelFormat := pf24bit;
  RGBtoYSHtoRGBBtn.Enabled    := False;
  sat_imageBtn.Enabled  := False;
  hue_imageBtn.Enabled  := False;
  tran_yshBtn.Enabled   := False;
  yTograyBtn.Enabled    := False;
  filesavebtn.Enabled   := False;
  CheckBox7.Enabled := False;
  GScale := 1;
  memo1.Clear;
  Revinitial;
  tran_yshBtn.Caption := 'Color調整' + #13#10 + 'YSH to RGB';
  panel1.Caption := '';
  Form1.Caption := 'Y(輝度) S(彩度) H(色相) による色の調整';
  Form1.Top := (screen.Height - Form1.Height) div 2;
  Form1.Left := (screen.Width - Form1.Width) div 2;
  PanelBtnset;
  Drawing := False;
  REMove  := False;
  bitmap.Free;
end;

//------------ 終了処理 ------------------------
procedure TForm1.FormDestroy(Sender: TObject);
begin
  InBitmap.Free;
  OutBitmap.Free;
end;

end.


   download ColorRingControl.zip


画像処理一覧へ戻る

    最初に戻る