モノカラー

 色相を指定して、指定した色で画像を表示するプログラムを作成してみました。

 複写機のコピーモードに、モノカラーがありますが、複写機の場合は、黒と他の色 一色でコピーするモードをモノカラーとよんでいます。
コピーする紙が、白である為、暗い色を表現するためには、黒のトナーが必要となるため、2色となります。
カラーの複写機で、モノカラーコピーを行う場合、トナーの色は、マゼンタ、シアン、イエロー、ブラックであり、赤のモノカラーは、マゼンタ、イエロー、ブラックの三色が使用されることになります。
マゼンタ、シアン、イエローを混ぜると、ブラックになりそうですが、実際には暗いグレーとなり、ブラックにはなりません。
その為、カラーのプリンター、複写機では、最低でも四色が使用されます。

パソコンの場合は、発光が小さくなれば黒になるので、単色でも可能です。
しかし、複写機でのモノカラーをパソコンで再現するためには、白発光も必要になり、結局は青、緑、赤の全色発光となり、見ためじょうの単色表示となります。

ここで、取り上げた、モノカラーの方法で、YSH変換、HSV変換によるものは、最大再度時は、一色、又は二色の混合色で表され、HSL変換は、明度の値によりグレイ+カラーとなり三色が使用されることになります。
彩度を下げた場合は、どの変換方式でも、グレー+色相色となります。

NTSCの輝度を使用する場合、特殊なのでYSHモードとの、関係についてプログラムを作成表示してみました。
変換の詳細については、色コントロールを参照してください。

HSVtoYSH

上のカラーバーはHSVモードで表示、それをYSH変換して、更にRGBに戻して、カラーバーを再現しています。
全く同じカラーバーが再現されているのが分かります。
しかし、彩度、輝度に関しては、色相によって変化してしまいます、輝度、彩度の計算方法が全く違うためです。

まず、YSH変換を利用して、モノカラーのプログラムを作成を検討してみました。
輝度値を変えて、モノカラーを再現しようとしましたが、輝度値によって彩度が変わるので、うまくいきません。
結局、色相の最大彩度から、色の比率を変えずに、階調化して、再現する事にしました。

YSHモノカラー

彩度を下げると、色によっては輝度値が小さいので、暗い画像となってしまいます。
赤の場合輝度変換係数が0.298912なので、グレー画像の最大の値が、255×0.298912=76 となります。

 HSV変換の場合

この場合は、明度を変えて、モノカラー画像を作成します。

HSVモノカラー

HSV変換の場合は、彩度がゼロの場合、輝度値がそのまま反映され、正しいグレー画像となります。

 HSL変換の場合

HSV変換と同じように、明度により、画像を再現します。

HSLモノカラー

グレー画像に色相色を加算したような、感じになり、明度0.5が、純色となります。
カラーバーは、明度0.5で作図していますので、彩度をゼロにすると、グレーになりますが、画像としては、輝度値が明度にそのまま反映されるので、正しいグレー画像となります。
HLS変換だと、色に関係なく、最大明度1は、白になります。
モノカラー画像としては、HLS変換を利用するのが、一番良い結果となるようです。
これが、HLSをWindowsの標準にしている理由でしょう。

HLS変換のサンプル。

KLS画像サンプル

どの色相でも、見やすいモノカラー画像が再現されています。
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.


    download MonoColor.zip

画像処理一覧へ戻る

      最初に戻る