2014.12.20.
石立 喬
OpenCVとVisual C++による画像処理と認識(5)
----- ヒストグラムを作成し、equalizeHist関数で画像を補正する -----
OpenCVの関数を使って、実際にヒストグラムを採る機会はあまりない。ヒストグラムを採らなくても、ヒストグラムを平均化するequalizeHist関数を使えば画像のコントラストを補正することができるし、compareHist関数で、画像の類似性を調べることもできる。
ここでは、ヒストグラムの仕組みを理解するために、まずOpenCVの関数をなるべく使わないでヒストグラムを作成し、次いで、便利なOpenCV関数をフルに活用して同様の処理を試みる。カラー画像の表色系を変えて、equalizeHist関数で画像を改善する例も示す。
ヒストグラムとは
ヒストグラムは、デジカメや画像ソフトでおなじみで、画像が明るいか暗いか、メリハリのあるかないかなどを知ることができる。
画像全体のピクセルに対し、個々のピクセルの値に応じてカウンタbinの計数値を増やして行き、全体の傾向をグラフ化したのがヒストグラムである。グレイスケール画像には、濃度のみのヒストグラムしかないが、カラー画像には、R、G、B別にヒストグラムを採ることが多い。HSV系やYCrCb系でのヒストグラムもありうる。
ヒストグラムを平坦化したり拡張したりすると、画像のコントラストを補正することができ、画像が見やすくなる。
ヒストグラムを比較する関数compareHistを使うと、画像の全般的な相似性を調べることができ、応用例として、良品と不良品を区別する検査がある。傾きや回転に強いのが特徴である。
ヒストグラムの取得と表示(なるべくOpenCV関数を使わない)
ヒストグラムの採り方と画像としての表示法方法を理解するには、OpenCVの関数に頼らない方が、仕組みがわかり易い。後述の全面的にOpenCVを使用する方法と比較して欲しい。
図1は、ヒストグラム作成部分にOpenCVのを使用しないプログラムで、下記から成っている。3)~5)は、便利なOpenCV関数によらないで作成した。
1)原画像を、画像src_imageとして読み込んで表示する(OpenCV使用)。
2)R、G、B別にヒストグラムを作成するために、Matクラスのsrc_imageを、Matクラスのchannelsに分解する(OpenCV使用)。
3)ピクセルの各濃度(0~255)に応じて、濃度別のカウンタr_histなどを用意し、念のために、0クリヤしておく。
4)ヒストグラムを採る。全ピクセルをスキャンし、ピクセルの濃度channels[2].at<uchar>(y,
x)に応じて、対応するr_histを加算する(R成分のみについて説明するが、G、B成分についても同じ)。Matクラスのchannelsを使用しているため、channels[2].at<uchar>(y,
x)は、OpenCV流である。
5)ヒストグラムの描画に必要なため、r_histの最大値r_hist_maxを求め(必要なグラフの縦軸が決まる)、その値が1になるように、各r_histをr_hist__maxで割り、r_histfとする。
6)r_histfを使って、ヒストグラムを描画する(OpenCV使用)。
ヒストグラムの描画に使用したOpenCV関数の説明
◎rectangle関数
ヒストグラムの背景をグレイの矩形で塗りつぶすために用いた。
rectangle(描画する対象のMatクラス、左上の座標、右下の座標、色、-1)
のように引数を与えて使用する。最後の-1は、塗りつぶしを意味し、正の値の場合は、塗りつぶしてはなく、矩形の枠の線の太さを表す。
◎line関数
ヒストグラムを縦線の集まりで描くのに用いた。
line(描画する対象のMatクラス、始点の座標、終点の座標、色、1、8、0)
を用い、1は線幅、8は8連結、0は座標を示す小数点以下の桁数である。線幅が細い場合は連結を気にする必要はなく、小数点以下の桁数は0で良い。これらはいずれもデフォルト値となっており、省略できる。
図1 原理が分かり易いヒストグラム描画プログラム
図2は得られた結果で、R、G、B各成分のヒストグラムの最大値に合わせてグラフが描かれている。
図2 図1のプログラムで得られた結果
OpenCVの関数を積極的に利用したヒストグラムの取得と表示
図3は、OpenCVの関数を全面的に使用したプログラムで、図1のプログラムと全く同等であり、下記から成っている。
1)原画像を読み込んで表示する。
2)R、G、B別にヒストグラムを作成するために、Matクラスのsrc_imageを、Matクラスのchannels[0]~[2]に分解する。
3)各ピクセルの濃度(0~255)に対応する、Matクラスの濃度別のカウンタr_histなどを用意する。
4)calcHist関数の引数に必要な値を用意する。
5)calcHistを用いて、channels毎のヒストグラムを採る。結果は、r_histなどに入る。
6)normalize関数を用いて、r_histなどを正規化する。
7)ヒストグラムの描画に必要なMatクラス画像hist_imageを用意し、ヒストグラムを描画する。
なお、これによって得られた結果は、図2と全く同じであったので、省略する。
ヒストグラムを採り、結果を正規化するのに使用したOpenCV関数の説明
◎calcHist関数
原画像をR、G、B別に分けたMatクラスのchannelsの個々のヒストグラムを求めるのに使用した。この関数は、汎用過ぎて、簡単なヒストグラムを採るには、やや難解である。複数の画像からヒストグラムを取得でき、RGBの組み合わせなど3次元の
ヒストグラムも採れる。ヒストグラムを採る全体の範囲とカウンタの数を異なった値に設定することもできる。
関数の形は、
calcHist(1次元Matクラスへの参照、Matクラスの数、チャンネル数、ヒストグラムを出力するMatクラス、ヒストグラムの次元、ヒストグラム・サイズへの参照、ヒストグラム範囲への参照、true、false)
となっている。
後方のtrueとfalseはデフォルト値で、一般的には省略できる。最初のtrueは、ヒストグラムがuniform(範囲が分割されていない、これが一般的)であることを意味し、後のfalseは、ヒストグラムを累積させない(実行ごとにカウンタをクリヤする、これが一般的)ことを表す。
ヒストグラム・サイズとヒストグラム範囲は分かり難い。ヒストグラム範囲が0~255(OpenCVでは、0~256として設定する)であっても、ヒストグラム・サイズは256とは限らない。ヒストグラム・サイズを64にして、0~3、4~7、・・・のようにまとめてヒストグラムを採ることができるからである。
calcHist関数に渡す引数の準備も、汎用ゆえに難解である。表1によって分かりやすく説明する。
一般的な記述法に従えば、一次元の場合は
カウンタの数 ------------ int bins =256; int histSize = {bins};
ヒストグラムの範囲 ------ float ranges[] = {0, 256}; const float*
histRange = { range }
となるところであるが、表1に示すように、{ }で包む必要はない。多くのOpenCV資料では、一般用法に準じて、一次元の場合でも{
}を付けているものが多い。
表1 calcHist関数への引数の準備方法
◎normalize関数
関数の形は、
normalize(入力1次元Matクラス、出力1次元Matクラス、正規化後の下限(alpha)、正規化後の上限(beta)、MORM_MINMAX、-1、Mat())
となっている。
ヒストグラム描画の場合は、正規化の種類としてNORM_MINIMAXを指定し、alphaには正規化後の下限値(0.0)、betaには上限値(1.0)を与える。
-1は、入力と出力が同じタイプのMatクラスであることを意味し、Mat()はユーザマスクを用いないことを意味する。この二つはデフォルト値なので、省略しても良い。
図3 OpenCV関数を積極的に使用したプログラム
ヒストグラム平坦化を利用した画像のコントラストの改善
OpenCVには、equalizeHistと言う、便利な関数がある。これを使えば、特定のチャンネル1個について、ヒストグラムを平坦化できる。カラー画像の場合、R、G、B各成分についてヒストグラムを採り、それらを個別に平坦化すると、色のバランスが崩れる恐れがあり、適当でない。そこで、明るさに注目して、YCrCb系のY、またはHSV系のVのみのヒストグラムを採り、それについて平坦化して、画像に戻すと、良好な結果が得られる。
図6は、これらの2種類を用いて結果を比較するためのプログラムである。
図4 ヒストグラム平坦化のプログラム
図5は、使用した原画像を表示したもので、 図6は、プログラムの実行結果である。この場合は、原画像の明暗が激しい(コントラストが強く、ヒストグラムが両端に偏っている)ので、コントラストが平坦化されて中間調部分が増えている。CrCbを使用したものは、やや明るい部分がやや飛んでいる感があり、HSVを使用した方が色の乗りが良い。
図5 ヒストグラム平坦化に使用した原画像
図6 ヒストグラム平坦化の結果
結 論
ヒストグラムを採ること自身は、画像処理にも、画像認識にもそれほど重要ではないが、ヒストグラムの原理を理解できるように、ヒストグラムの取得と表示を行った。画像からヒストグラムを取得しなくても、OpenCVに備わっているequalizeHistを使えば、簡単に画像の補正ができる。
その他の便利な関数については、別項目で紹介する。
「Visual C++の勉強部屋」(目次)へ