ヒストグラム均一化と、2値化の検討

ヒストグラムの均一化

画像濃度のヒストグラムを均一化することで、画像の濃度を調整します。

ヒストグラム均一化均一化グラフ
画像の濃度が幅広く分布し、コントラストが大きくなった画像になります。

計算方法を、いろいろ検索をしたのですが、計算が簡単な、基本的な画像処理手法について にあるものを使用しました。

 w = INT( ( v(u) - vmin ) x ( L - 1 ) / ( 1 - vmin ) )

  w: 変換後の画素値、
  v(u): 変換前画素値uに対する累積度数÷画面の総画素数
  vmin: v(u)の最小値
  L: 階調数(256)
  

ヒストグラムの作成と、グラフ表示 
 此処では、カラーをグレーに変換した配列データーをしようします。
配列データーは、ファイルをオープンした時に、作成しておきます。
HistMat[]が最初のヒストグラム、RuisMat[]が累積度数です。

//******************************
// グレイヒストグラム表示
//******************************
procedure TForm1.HistogramChart; 
// ヒストグラムの作成と表示
var
  I , j: Integer;
begin
  for I := 0 to 255 do HistMat[I] := 0;
  for I := 0 to GHeight - 1 do
    for J := 0 to GWidth - 1 do
      inc(HistMat[GrayMat[I, J]]);
  I := 0;
  for J := 0 to 255 do begin
    I := I + HistMat[J];
     RuisMat[J] := I;
 end;
  Series1.clear;
  for I := 0 to 255 do begin
    Series1.Add(HistMat[I]);
  end;
end;

ヒストグラムの均一化実行と表示

 //*******************************************************
// ヒストグラム平均化
// w = INT( ( v(u) - vmin ) x ( L - 1 ) / ( 1 - vmin ) )
//
//  w: 変換後の画素値、
//  v(u): 変換前画素値uに対する累積度数÷画面の総画素数
//  vmin: v(u)の最小値
//  L: 階調数(256)
//  INT(): 整数化関数
//
//*******************************************************
procedure TForm1.HistoavreageBtnClick(Sender: TObject);
var
  HH, WW : integer;
  PBa : PByteArray;
  DW : Integer;
  ALLdot: Integer;
  Vmin : double; // 最小値
begin
  ALLdot := Gheight * GWidth;
  // ゼロ以外の最小値
  Vmin := 0;
  for HH := 0 to 255 do begin
    if RuisMat[HH] > 0 then begin
      Vmin := RuisMat[HH] / ALLdot;
      break;
    end;
  end;
  // ヒストグラム平均化
  for HH := 0 to Gheight - 1 do
    for WW := 0 to GWidth - 1 do begin
      GOSAMat[HH, WW] := Round((RuisMat[GrayMat[HH, WW]] / ALLdot - Vmin) * 255 / (1 - vmin));
      if GOSAMat[HH, WW] < 0 then GOSAMat[HH, WW] := 0;
    end;
  // 平均化ご画像表示
  OutputBitmap.Assign(nil);
  OutputBitmap.PixelFormat := pf24bit;
  OutputBitmap.Width := GWidth;
  OutputBitmap.Height := GHeight;
  for HH := 0 to GHeight - 1 do begin
    PBa := OutputBitmap.ScanLine[HH];
    for WW := 0 to GWidth - 1 do begin
      DW := WW * 3;
      PBa[DW ]    := GOSAMat[HH, WW];
      PBa[DW + 1] := GOSAMat[HH, WW];
      PBa[DW + 2] := GOSAMat[HH, WW];
    end;
  end;
  Image1.Canvas.StretchDraw(VRect, OutputBitmap); // 出力枠に変倍出力
  for HH := 0 to 255 do HistMat[HH] := 0;
  for HH := 0 to GHeight - 1 do
    for WW := 0 to GWidth - 1 do
      inc(HistMat[GOSAMat[HH, WW]]);
  Series1.clear;
  for HH := 0 to 255 do begin
    Series1.Add(HistMat[HH]);
  end;
end;

 カラー画像の検討

 カラーの場合、RGB毎に、ヒストグラムの均一化を行うと、カラーバランスが崩れて、画像が乱れてしまいしまいます。
そこで、画像の輝度値は、グレーの輝度値に沿って、元のRGBの割合を崩さないようにして新しい値とするようにしました。
もし、RGBのどれかが、255を超える場合は、最大値を255として、他の色は、元の割合になるように補正し、255を越えないようにします。
カラーヒストグラム均一化ヒストグラム
上の画像の場合は、それなりにコントラストが付いた画像になりましたが、画像によっては、均一化した結果、悪くなってしまう場合もあります。
悪い例
それぞれ右側が均一化後の画像ですが、黒くなったり、明るくなりすぎています。
カラーによる問題かと考え、グレー画像でも行ってみましたが、同じ様な傾向にあるようです。
コントラストの調整は、微調整の出来る、変換曲線の指定による補正の方が良い結果が得られるようです。

2値化の検討

 パソコンの記憶容量が大きくなり、画像を2値化するのは、画像を利用した測定ぐらいに限られるようになりました。
以外に、簡単なことなのですが、自動測定を行う場合、2値化も自動的に行いたいので、その方法について少し検討してみました。

2値化画像
2値化サンプル2
閾値の指定
 
単純にシキイ値を指定して2値化をしています。
シキイ値を固定していると画像によっては、非常に悪い画像になる事があります。
閾値127の画像は結構良い画像となっていますが、たまたま取り上げたサンプルが良かったのかと思います。

比率の指定
 黒50パーセントは、ヒストグラムを作成し、ヒストグラムの輝度値小さいほうの値と大きい方の値で、小さいほうの値の合計が全体の値に対して指定されたパーセントになるように閾値を設定します。

 //*********************************
// 黒パーセント指定2値化
//*********************************
procedure TForm1.PercentBtnClick(Sender: TObject);
var
  threshold : Integer;
  AllAdd : Integer;
  Dadd : Integer;
  Percent : Double;
  Percent100 : Double;
  ch : Integer;
  HH, WW : Integer;
  PBa : PByteArray;
  DW : Integer;
begin
  val(PercentEdit.Text, Percent, Ch);
  if ch <> 0 then begin
    Application.MessageBox('パーセントの値に間違いがあります', '注意', 0);
    exit;
  end;
  Percent100 := Percent / 100;
// AllAdd := 0;
  Dadd := 0;
  threshold := 0;
// AllAddは画像縦ドット数×横ドット数と同じになります
  AllAdd := GWidth * GHeight;
// for HH := 0 to 255 do AllAdd := AllAdd + HistMat[HH];
// 指定%と同じが越えた位置を検出し閾値を決定します
  for WW := 0 to 255 do begin
    Dadd := Dadd + HistMat[WW];
    if Dadd / AllAdd >= Percent100 then begin
      threshold := WW;
      break;
    end;
  end;
// 閾値により2値化します
  OutputBitmap.Assign(nil);
  OutputBitmap.PixelFormat := pf8bit;
  OutputBitmap.Width := GWidth;
  OutputBitmap.Height := GHeight;
  for HH := 0 to GHeight - 1 do begin
    PBa := OutputBitmap.ScanLine[HH];
    for WW := 0 to GWidth - 1 do begin
      DW := WW;
      if GrayMat[HH, WW] >= threshold Then
        PBa[DW] := $FF
      else
        PBa[DW] := $00;
    end;
  end;
  Image1.Canvas.StretchDraw(VRect, OutputBitmap); // 出力枠に変倍出力
end;


 判別分析法は、閾値を境にして黒側の分散、白側の分散を求め、分離度が最大になる点に閾値を設定する方法ですが、詳しくは、 イメージソリューションを参照して下さい。
 誤差拡散法は、測定とは関係ないのですが、2値化するとハーフトーンの再現が出来ないので、ドットにしてハーフトーンの再現が出来る様にしたものです。
画像処理 誤差拡散法で検索すれば沢山見つかります。

    download histogram.zip

画像処理一覧へ戻る

        最初に戻る