2016. 8.. 2.
石立 喬

OpenCV3.1.0とVisual C++ 2015による画像処理と認識(5)

----- 画像のヒストグラムを取得し、コントラストの改善と画像の検索を行う -----

 これは、「OpenCVとVisual C++による画像処理と認識(5)」の続編と言えるもので、すでに紹介したequalizeHist関数による画像のコントラスト改善の他に、compareHist関数による画像検索(image retrieval)を新しく追加した。OpenCV3.1.0で変わった部分は、比較方法を指定する文字列程度である。

ヒストグラムとは
 ヒストグラムは、デジカメや画像ソフトでおなじみで、画像が明るいか暗いか、メリハリがあるかないかなどを知ることができる。
 画像全体のピクセルに対し、個々のピクセルの値に応じてカウンタの計数値を増やして行き、全体の傾向をグラフ化したのがヒストグラムである。グレイスケール画像には、濃度のみのヒストグラムしかないが、カラー画像では、R、G、B別にヒストグラムを採ることが多い。HSV系やYCrCb系でのヒストグラムもありうる。
 ヒストグラムを平坦化(equalize)すると、画像のコントラストを補正することができ、画像が見やすくなる。
 ヒストグラムを比較する関数compareHistを使うと、画像の全般的な相似性を調べることができ、応用例として、CBIR(Content-Based Image Retrieval、内容による画像検索)がある。傾きや回転に強いのが特徴である。

ヒストグラムの取得と表示のためのユーザ関数createHistogramの作成
 図1は、OpenCV3.1.0の関数を使用して作成したユーザ関数createHistogramで、下記から成っている。
1)引数として受け取ったMatクラスのimage_srcを、Matクラスのchannels[0]〜[2](B、G、Rに対応)に分解する。
2)グレイスケール化したヒストグラムを取得するために、image_srcからimage_grayを得る。
3)各ピクセルのR、G、B値(0〜255)などに対応する、Matクラスのカウンタr_histなどを用意する。
4)calcHist関数の引数に必要なサイズ(カウンタの数、256)、範囲(0〜256、256とすることに注意)などを用意する。
5)calcHistを用いて、channels毎のヒストグラムを採り、結果をr_histなどに入れる。
6)normalize関数を用いて、r_histなどを0〜50になるよう正規化する。
   0〜1でないのは、その値を線長としてヒストグラムを描画するためである。
7)引数として受け取ったMatクラス画像image_histに、ヒストグラムを描画する。

使用した関数の説明
◎calcHist関数
 原画像をR、G、B別に分けたMatクラスのchannelsの個々のヒストグラムを求めるのに使用する。この関数は、汎用過ぎて、簡単なヒストグラムを採るには、やや難解である。複数の画像からヒストグラムを取得でき、RGBの組み合わせなど3次元の ヒストグラムも採れる。ヒストグラムを採る全体の範囲とカウンタの数を異なった値に設定することもできる。一次元の、最も簡単な場合を、以下に「一般的」と称す。
 関数の形は、
  calcHist(
       images ------- const Mat*  1次元Matクラスへの参照、これがヒストグラムを採る対象である
       nimages ------ int        Matクラスの数、一般的には1
       channels ----- const int*   チャンネルの番号、一般的には0番目なので0
       mask --------- InputArray  マスクを使用する際のMatクラス、一般的には空のMat()
       hist --------- OutputArray  ヒストグラムを出力するMatクラス、ここへヒストグラムが出力される
       dims --------- int        ヒストグラムの次元、一般的には1
       histSize ----- const int*    ヒストグラム・サイズ(カウンタの数)への参照(下記に詳述)
       ranges ------- const float**  ヒストグラム範囲への参照(下記に詳述)
       uniform = true           ヒストグラムの採り方が一様に連続的であればtrue
       accumulate = false         ヒストグラムを累積させるときはtrue、その都度クリヤーする時はfalse
  )
 ヒストグラム・サイズ(カウンタの数)とヒストグラム範囲は分かり難い。ヒストグラム範囲が0〜255(OpenCVでは、0〜256として設定する)であっても、ヒストグラム・サイズは256とは限らない。ヒストグラム・サイズを64にして、0〜3、4〜7、・・・のようにまとめてヒストグラムを採るように設定できるからである。
 calcHist関数に渡す引数の準備も、汎用ゆえに難解である。
 一般的な記述法に従えば、一次元の場合は
   カウンタの数 ------------ int bins =256; int histSize = {bins};
   ヒストグラムの範囲 ------ float ranges[] = {0, 256}; const float* histRange = { range };
となるところであるが、{ }で包む必要はない。多くのOpenCV資料では、多次元用の一般用法に準じて、習慣的に、一次元の場合でも{ }を付けているものが多い。
◎normalize関数
 これも汎用関数なので、ヒストグラムの正規化に使用する場合は、デフォルト値を用いてはいけない引数(*で示す)があるので、注意を要する。関数の形は、
  normalize(
       src ------------ InputArray      一次元のMatクラス
       dst ------------ InputOutpuArray  一般には、入力アレイと同じにする
       alpha = 1 ------ double         * ヒストグラムの場合は、正規化後の下限値(例えば0)を入れる
       beta = 0 ------- double         * ヒストグラムの場合は、正規化後の上限値(例えば1や50)を入れる
       norm_type = NORM_L2          * ヒストグラムの場合は、NORM_MINMAXを使う
       dtype = -1 ----- int、          負の時は出力アレイが入力アレイと同じタイプ
       mask = noArray()              ユーザマスクを用いない
  )
◎line関数
 関数の形は、
  line(
      img ------ InputOutputArray 描画する対象のMatクラス
      pt1 ------ Point         始点の座標
      pt2 ------ Point         終点の座標
      color ----- const Scalar&   線の色
      thickness = 1 ---- int      線幅
      lineType = LINE_8、        8連接(旧バージョンでは、単に8としていた。アンチエリアスの旧CV_AAはLINE_AAになった)
      shift = 0               線の座標の小数点以下のビット数
  )
 線幅が細い場合は連接を気にする必要はなく、小数点以下の桁数は0で良い。これらはいずれもデフォルト値となっており、省略できる。



図1 ヒストグラム取得とグラフ作成のcreateHistogram関数

ユーザ関数createHistogramを使って、画像のヒストグラムを表示する
 図2は、使用したプログラムである。特に説明の必要はないと思われる。


図2 createHistogram関数を呼び出し、結果を表示するプログラム


得られた結果  図3は得られた結果で、ヒストグラムは、上から、R、G、B成分およびグレイスケール化した濃度Dである。正規化されているので、ピークの高さは同じである。





図3 図2のプログラムで得られた結果




ヒストグラム平坦化を利用した画像のコントラストの改善
 OpenCVには、equalizeHistと言う、便利な関数がある。これを使えば、特定の1個のチャンネルについて、ヒストグラムを平坦化できる。カラー画像の場合、R、G、B各成分についてヒストグラムを採り、それらを個別に平坦化すると、色のバランスが崩れる恐れがあり、適当でない。そこで、明るさに注目して、HSV系のVのみのヒストグラムを採り、それについて平坦化して、残りのH、Sはそのままにして、新しいVとと共にBGR系に戻す。
 図4は、そのプログラムである。


図4 Vヒストグラムを平坦化し、createHistogramで結果を確認する


◎明暗のコントラストが強すぎる例
 図5は、明暗の激しい画像をヒストグラム平坦化で改善した例で、暗部が見やすくなり、その割には、明部が飛んでいない。図7は、それをヒストグラムで確認したもので、ヒストグラムの分布が、より一様になっている。


図5 コントラストの強い画像が見やすくなった



図6 図5の画像に対応したヒストグラム(右はヒストグラムが中央部に移っている)


◎コントラストに乏しい例
 図7は、明暗の差が少ない画像をヒストグラム平坦化で改善した例で、明暗がはっきりしている。彩度(saturation)は増やしていないので、色はグレイスケールに近い。図8は、それをヒストグラムで確認したもので、ヒストグラムの分布範囲が広くなっている。


図7 コントラストの弱い画像が、はっきりした



図8 図7の画像に対応したヒストグラム(右はヒストグラムが全体に広がっている)


ヒストグラムを比較するcompareHist関数
 compareHist関数は、二つのヒストグラムを比較して、その相似性を数値で表すことができる。HSV系のH(色相、hue)のみの一次元ヒストグラムや、HとS(彩度、saturation)の二次元ヒストグラムを使うと、CBIR(Content-Based Image Retrieval、内容による画像検索)への応用ができ、多くの試みがある。
 関数の形は、
  double compareHist(
       H1 ------- InputArray  一番目の比較対象ヒストグラム、Matクラス
       H2 ------- InputArray  二番目の比較対象ヒストグラム、Matクラス
       method --- int        ヒストグラムの比較方法
  )
 ヒストグラムの比較方法には、
    HISTCMP_CORREL ( =0)     相関を取る、等しければ1になる
    HISTCMP_CHISQR ( =1)     カイ二乗法、等しければ0になる
    HISTCMP_INTERSECT ( =2)   交差法、比較して小さい方の合計、等しければ、総ピクセル数、計算が速い
その他がある。
 なお、これらは、旧バージョンでは、CV_COMP_CORRELなどとされていたものである。

compareHist関数を用いて画像の類似性を判断するプログラム
 図9は、作成したユーザ関数createHueHistと、それを使用して2画像を比較するプログラムで、createHueHist関数の内容は以下の通りである。
1)引数として受け取ったMatクラスのimageを、Matクラスのimage_hsvに変換する。
2)image_hsvをchannels[0]〜[2]に分解する。
3)hue( = channels[0])の範囲(0〜180)に対応させるため、36個のカウンタから成るh_histを用意する。
  カウンタの数が180個でないのは、僅かの違いに敏感にさせないためである。
4)calcHistを用いて、channels[0]のヒストグラムを採り、結果をh_histに入れる。
5)normalize関数を用いて、h_histを正規化する。
7)h_histを戻り値として返す。



図9 hueのヒストグラムを採るユーザ関数と、二つのヒストグラムを比較するプログラム


得られた結果
 図9のプログラムにより得られた結果を図10に示す。
 図で、上段左は原画像と同じものを比較したもの、上段右は上下反転画像、中段左は類似画像、中段右は風車のある別画像、下段左は無関係の写真、右は色調が全く異なる写真である。








図10 比較した画像と、比較結果(相関の場合)


 図10の結果は、図9のプログラムにより、比較規準として相関を用いたもの(図9の44行で、HISTCMP_CORRELを使用)であるが、別途、交差を用いた場合(HISTCOMP_INTERSECT使用)も試みた。結果を、相関の場合と比較しながら表にしたのが表1である。
 ここで使用した画像と、方法に関する限り、相関を用いた方が、人の目による判断に近いことが分かった。

表1 相関と交差による類似性判断結果(小数点以下3桁で表示)



結 論
 ヒストグラムの取得と表示を行うユーザ関数createHistogramを作成した上で、equalizeHistを用いて、画像のコントラストの改善を行った。原画像のBGR系をHSV系に変換後、V(value、明度)のヒストグラムのみを平坦化(equalize)して、元のBGR系に戻すと、期待通りの結果が得られた。
 CBIR(Content-Based Image Retrieval、内容による画像検索)の一例として、compareHist関数を使って、色相(hue)のヒストグラムだけを比較して、画像の類似性の判断を試みた、ここで使用した画像に限っては、比較方法に相関(HISTCMP_CORREL)を用いた方が、交差(HISTCMP_INTERSECT)を用いるよりも、人の目による判断に近かった。

参考文献
 Ritendra Datta他、"Content-Based Image Retrieval - Approaches Trends of the New Age" (2005)
 http://infolab.stanford.edu/~wangz/project/imsearch/review/ACM05/datta.pdf

 P. Halder他、"Content Based Image Retrieval Using Histogram, Color and Edge" (2012)
 http://research.ijcaonline.org/volume48/number11/pxc3880366.pdf

 A. R. Kumar他、"ContentBased Image Retrieval Using Color Histogram" (2013)
 http://ijcsit.com/docs/Volume%204/Vol4Issue2/ijcsit20130402011.pdf






「Visual C++の勉強部屋」(目次)へ