ヒストグラム
■ ヒストグラム
ヒストグラムとは,値を持つ集合を任意の区間(OpenCVのリファレンスでは 「ビン」という用語が用いられることがある)に区切り,その区間毎のデータ の割合を棒グラフで表したものである. 画像処理の分野では,ある輝度値(あるいは,そのチャンネルにおける値)を 持つピクセルが,どのように分布しているかを示すものとして用いられること が多い.最近では,デジタルカメラなどにも,現在の画像に対するヒストグラ ムを表示する機能をもつものがある.サンプル
ヒストグラムの描画 cvCalcHist
入力画像のヒストグラムを計算し,結果のグラフを描画する
サンプルコード
#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, bin_w; int hist_size = 256; int sch = 0, ch_width = 260; float max_value = 0; float range_0[] = { 0, 256 }; float *ranges[] = { range_0 }; IplImage *src_img = 0, *dst_img[4] = { 0, 0, 0, 0 }, *hist_img; CvHistogram *hist; // (1)画像を読み込む if (argc < 2 || //(src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR))==0) (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_ANYCOLOR)) == 0) return -1; // (2)入力画像のチャンネル数分の画像領域を確保 sch = src_img->nChannels; for (i = 0; i < sch; i++) { dst_img[i] = cvCreateImage (cvSize (src_img->width, src_img->height), src_img->depth, 1); } // (3)ヒストグラム構造体,ヒストグラム画像領域を確保 hist = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist_img = cvCreateImage (cvSize (ch_width * sch, 200), 8, 1); // (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割 if (sch == 1) cvCopy (src_img, dst_img[0], NULL); else cvSplit (src_img, dst_img[0], dst_img[1], dst_img[2], dst_img[3]); // (5)ヒストグラム画像をクリア cvSet (hist_img, cvScalarAll (255), 0); for (i = 0; i < sch; i++) { // (6)ヒストグラムを計算して,スケーリング cvCalcHist (&dst_img[i], hist, 0, NULL); cvGetMinMaxHistValue (hist, 0, &max_value, 0, 0); cvScale (hist->bins, hist->bins, ((double) hist_img->height) / max_value, 0); // (7)ヒストグラムの描画 bin_w = cvRound ((double) ch_width / hist_size); for (j = 0; j < hist_size; j++) cvRectangle (hist_img, cvPoint (j * bin_w + (i * ch_width), hist_img->height), cvPoint ((j + 1) * bin_w + (i * ch_width), hist_img->height - cvRound (cvGetReal1D (hist->bins, j))), cvScalarAll (0), -1, 8, 0); } // (8)ヒストグラム画像を表示,キーが押されたときに終了 cvNamedWindow ("Image", CV_WINDOW_AUTOSIZE); cvShowImage ("Image", src_img); cvNamedWindow ("Histogram", CV_WINDOW_AUTOSIZE); cvShowImage ("Histogram", hist_img); cvWaitKey (0); cvDestroyWindow ("Histogram"); cvReleaseImage (&src_img); cvReleaseImage (&hist_img); for (i = 0; i < sch; i++) { cvReleaseImage (&dst_img[i]); } cvReleaseHist (&hist); return 0; }
// (1)画像を読み込む
コマンド引数で指定されたファイル名の画像(入力画像)をオープンし,関数cvLoadImage()で読み込む.
2番目の引数にCV_LOAD_IMAGE_ANYDEPTH |
CV_LOAD_IMAGE_ANYCOLORを指定することで,元画像のデプス,チャンネルを変更せずに読み込む.
// (2)入力画像のチャンネル数分の画像領域を確保
入力画像のチャンネル数と同じ数の画像領域を確保し,そのポインタを配列
dst_img[]に納める.また,この配列は0で初期化されているので,チャン
ネル数以上のインデックスを持つ要素の値は0になる.
// (3)ヒストグラム構造体,ヒストグラム画像領域を確保
ヒストグラム構造体 CvHistogram,ヒストグラム画像を描画する IplImageの領域を確保する.
今回計算されるのは,輝度値のみのヒストグラムなので,ヒストグラム構造体
は1次元,ビンの数は256(=hist_size),ビンの閾値は0〜255(=ranges)なので,
入力値の内,0〜255の範囲にある値(一つのチャンネルにつき8bit)のみが,
256個のビンに累積されていくことになる.また,最後の引数を1(非0の値)にすることで,等間隔なビンを持つヒストグラムとなる.
また,ranges[i][0]≤v<ranges[i][1]の範囲の値が入力として取られることに注意する.
// (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割
入力画像がマルチチャンネルの場合には,画像をチャンネル毎に分割して,そ
れぞれのヒストグラムを求める.ここで,分割後の画像配列
dst_img[]を最初に0で初期化しているので,そのまま関数cvSplit()に渡して
も良いことに注意する.
// (5)ヒストグラム画像をクリア
つぎに,まず,ヒストグラムを描画するIplImageを,輝度値255(白色)でク
リアする.つまり,ヒストグラムは,白色背景の画像になる.
// (6)ヒストグラムを計算して,スケーリング
各チャンネル毎にヒストグラムを計算する.計算後のヒストグラムの最大値を
取り出し,その値を用いてヒストグラムの高さが画像内に収まるようにスケーリングを行う.
ここでは,ヒストグラムのピーク(最大値)が,画像hist_imgの高さと等しく
なるように調整している.
// (7)ヒストグラムの描画
結果として得られたヒストグラムを,順次,画像に描画する.
// (8)ヒストグラム画像を表示,キーが押されたときに終了
全チャンネルのヒストグラムが描画された画像含むウィンドウを実際に表示し,
何かキーが押されるまで待つ.
実行結果例
入力画像(24ビット,3チャンネル)と,各チャンネルに対するヒストグラム
入力画像(24ビット,1チャンネル)と,それに対するヒストグラム
[左] 高い明度を持つように変更した画像とそのヒストグラム.グラフが高い輝度値側に寄っている.
[右] 低い明度を持つように変更した画像とそのヒストグラム.グラフが低い輝度値側に寄っている.
[左] 高いコントラストを持つように変更した画像とそのヒストグラム.最低輝度が0,
最高輝度が255となる広い分布を持つが,コントラストを強調しすぎて,特定
の範囲(ビン)の情報が欠けている.
[右] 低いコントラストを持つように変更した画像とそのヒストグラム.ヒストグラムが
中央部分に偏り,最低輝度と最高明度との差が小さくなっている.
ヒストグラム間の距離 cvCompareHist
二つの入力画像のヒストグラムの距離を計算し,その値を表示する
サンプルコード
#include <cv.h> #include <highgui.h> #include <math.h> #include <stdio.h> int main (int argc, char **argv) { char text[16]; int i, hist_size = 256, sch = 0; float range_0[] = { 0, 256 }; float *ranges[] = { range_0 }; double tmp, dist = 0; IplImage *src_img1 = 0, *src_img2 = 0, *dst_img1[4] = { 0, 0, 0, 0 }, *dst_img2[4] = { 0, 0, 0, 0}; CvHistogram *hist1, *hist2; CvFont font; CvSize text_size; // (1)二枚の画像を読み込む.チャンネル数が等しくない場合は,終了 if (argc >= 3) { src_img1 = cvLoadImage (argv[1], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); src_img2 = cvLoadImage (argv[2], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); } if (src_img1 == 0 || src_img2 == 0) return -1; if (src_img1->nChannels != src_img2->nChannels) return -1; // (2)入力画像のチャンネル数分の画像領域を確保 sch = src_img1->nChannels; for (i = 0; i < sch; i++) { dst_img1[i] = cvCreateImage (cvSize (src_img1->width, src_img1->height), src_img1->depth, 1); dst_img2[i] = cvCreateImage (cvSize (src_img2->width, src_img2->height), src_img2->depth, 1); } // (3)ヒストグラム構造体を確保 hist1 = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist2 = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); // (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割 if (sch == 1) { cvCopy (src_img1, dst_img1[0], NULL); cvCopy (src_img2, dst_img2[0], NULL); } else { cvSplit (src_img1, dst_img1[0], dst_img1[1], dst_img1[2], dst_img1[3]); cvSplit (src_img2, dst_img2[0], dst_img2[1], dst_img2[2], dst_img2[3]); } // (5)ヒストグラムを計算,正規化して,距離を求める for (i = 0; i < sch; i++) { cvCalcHist (&dst_img1[i], hist1, 0, NULL); cvCalcHist (&dst_img2[i], hist2, 0, NULL); cvNormalizeHist (hist1, 10000); cvNormalizeHist (hist2, 10000); tmp = cvCompareHist (hist1, hist2, CV_COMP_BHATTACHARYYA); dist += tmp * tmp; } dist = sqrt (dist); // (6)求めた距離を文字として画像に描画 snprintf (text, 16, "Distance=%.3f", dist); cvInitFont (&font, CV_FONT_HERSHEY_SIMPLEX, 2.0, 2.0, 0, 4, 8); cvPutText (src_img2, text, cvPoint (10, src_img2->height - 10), &font, cvScalarAll (0)); cvGetTextSize ("Input1", &font, &text_size, 0); cvPutText (src_img1, "Input1", cvPoint (10, text_size.height), &font, cvScalarAll (0)); cvPutText (src_img2, "Input2", cvPoint (10, text_size.height), &font, cvScalarAll (0)); // (7)二つの画像を表示,キーが押されたときに終了 cvNamedWindow ("Image1", CV_WINDOW_AUTOSIZE); cvNamedWindow ("Image2", CV_WINDOW_AUTOSIZE); cvShowImage ("Image1", src_img1); cvShowImage ("Image2", src_img2); cvWaitKey (0); cvDestroyWindow ("Image1"); cvDestroyWindow ("Image2"); for (i = 0; i < src_img1->nChannels; i++) { cvReleaseImage (&dst_img1[i]); cvReleaseImage (&dst_img2[i]); } cvReleaseImage (&src_img1); cvReleaseImage (&src_img2); cvReleaseHist (&hist1); cvReleaseHist (&hist2); return 0; }
// (1)二枚の画像を読み込む
「ヒストグラム画像の描画」の場合と同様に,コマンド引数で指定された画像
を,関数cvLoadImage()によって読み込む.今回は,ヒストグラム間の距離
を計算するので,二枚の画像を読み込む.
// (2)入力画像のチャンネル数分の画像領域を確保
チャンネル毎のヒストグラム間距離を計算するために,「ヒストグラム画像の
描画」の場合と同様に,チャンネル数分の画像構造体を確保する.
// (3)ヒストグラム構造体を確保
ヒストグラム構造体を確保.
// (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割
これも,「ヒストグラム画像の描画」の場合と同様に,各チャンネルを分割する.
// (5)ヒストグラムを計算,正規化して,距離を求める
関数cvCalcHist()によって,各画像のヒストグラムを計算したのちに,距離を
比較するために正規化を行う.どちらのヒストグラムも,全ビンの値の合計
が一定(ここでは,10000)になるように調整する.
実際に距離を計算する関数cvCompareHist()は,このように正規化されたヒ
ストグラムに対してのみ正しく実行されることに注意.
今回は,ヒストグラム比較の手法としてCV_COMP_BHATTACHARYYAを指定した
が,他にもCV_COMP_CORREL,CV_COMP_CHISQR,CV_COMP_INTERSECT などが指
定可能である.詳しくはリファレンスを参照すること.
また,このプログラムでは,マルチチャンネル画像の場合の最終的な距離と
して,チャンネル間の距離の自乗をの和の平方根を求めるようにしている.
ただし,これが唯一の距離の定義というわけでない.
// (6)求めた距離を文字として画像に描画
求めた距離を表示するために,関数cvPutText()を用いて,値をテ
キストとして2枚目の画像に上書きする.また,1枚目,2枚目の画像にそれぞ
れ,"Input1","Input2"の文字を描く.
// (7)二つの画像を表示,キーが押されたときに終了
2枚の画像を実際に表示し,何かキーが押されるまで待つ.終了時には,メモリを解放する.
実行結果例
入力画像(Input1, Input2)のヒストグラム間の距離
Input1 | 0.382 | 0.779 | 0.823 | 1.239 |
二次元のヒストグラム cvCreateHist
入力画像の色空間をHSVに変換し,H-Sのヒストグラムを描画する
サンプルコード
#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src, *hsv; IplImage *h_plane, *s_plane, *v_plane; IplImage *planes[2]; int h_bins = 30, s_bins = 32; int hist_size[] = { h_bins, s_bins }; float h_ranges[] = { 0, 181 }; /* 色相は0(0度,赤)から180(360度,赤)まで変化する */ float s_ranges[] = { 0, 256 }; /* 彩度は0(黒-グレー-白)から255(純粋なスペクトラムカラー)まで変化する */ float *ranges[] = { h_ranges, s_ranges }; int scale = 10; IplImage *hist_img = cvCreateImage (cvSize (h_bins * scale, s_bins * scale), 8, 3); CvHistogram *hist; float max_value = 0; int h, s; if (argc != 2 || (src = cvLoadImage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; hsv = cvCreateImage (cvGetSize (src), 8, 3); planes[0] = h_plane = cvCreateImage (cvGetSize (src), 8, 1); planes[1] = s_plane = cvCreateImage (cvGetSize (src), 8, 1); v_plane = cvCreateImage (cvGetSize (src), 8, 1); // (1)入力画像の色空間をRGBからHSVに変換する cvCvtColor (src, hsv, CV_BGR2HSV); cvCvtPixToPlane (hsv, h_plane, s_plane, v_plane, 0); // (2)ヒストグラムを計算 hist = cvCreateHist (2, hist_size, CV_HIST_ARRAY, ranges, 1); cvCalcHist (planes, hist, 0, 0); // (3)各ビンにおける値を求め,それを元に輝度値を算出して描画 cvGetMinMaxHistValue (hist, 0, &max_value, 0, 0); cvZero (hist_img); for (h = 0; h < h_bins; h++) { for (s = 0; s < s_bins; s++) { float bin_val = cvQueryHistValue_2D (hist, h, s); int intensity = cvRound (bin_val * 255 / max_value); cvRectangle (hist_img, cvPoint (h * scale, s * scale), cvPoint ((h + 1) * scale - 1, (s + 1) * scale - 1), CV_RGB (intensity, intensity, intensity), CV_FILLED); } } // (4)入力画像と二次元(H-S)ヒストグラムを表示して,何かキーが押されるまで待つ cvNamedWindow ("Source", 1); cvShowImage ("Source", src); cvNamedWindow ("H-S Histogram", 1); cvShowImage ("H-S Histogram", hist_img); cvWaitKey (0); cvDestroyWindow ("Source"); cvDestroyWindow ("H-S Histogram"); cvReleaseImage (&src); cvReleaseImage (&hsv); cvReleaseImage (&h_plane); cvReleaseImage (&s_plane); cvReleaseImage (&v_plane); cvReleaseImage (&hist_img); cvReleaseHist (&hist); return 0; }
// (1)入力画像の色空間をRGBからHSVに変換する
関数cvCvtColor()を用いて,入力されたカラー画像の色空間をRGB空間からHSV空間に変換する.
実際には,cvLoadImage()で読み込まれた画像は,BGRの順番に並んでいるので,
CV_BGR2HSVを指定して変換する.
次に,cvCvtPixToPlane()を用いて,H,S,Vの各チャンネル(色平面)に分解する.
この,cvCvtPixToPlane()は,cvcompat.h で
#define cvCvtPixToPlane cvSplitと定義されており,cvSplit()と等価である.
// (2)ヒストグラムを計算
今回は,2次元のヒストグラム構造体を作成し,関数cvCalcHist()では,二つ
の色平面(H,S)におけるヒストグラムを計算する.
// (3)各ビンにおける値を求め,それを元に輝度値を算出して描画
関数cvQueryHistValue_2D()によって,指定したH-Sにおけるビンの値を求め,
その値を用いて,最大値が255,最小値が0となるよに輝度値を算出する.
そして,その輝度値で塗りつぶされた矩形を逐次,画像hist_imgに書き込む.
// (4)入力画像と二次元(H-S)ヒストグラムを表示して,何かキーが押されるまで待つ
最後に,入力画像と描画された2次元(H-S)ヒストグラム画像を表示する.横
軸がH(色相),縦軸がS(彩度)を表す.
実行結果例
バックプロジェクションパッチ cvCalcBackProjectPatch
ヒストグラム間距離による,画像内のテンプレート探索
サンプルコード
#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, hist_size = 90; float h_ranges[] = { 0, 181 }; float *ranges[] = { h_ranges }; double min_val, max_val; CvSize dst_size; CvPoint min_loc, max_loc; IplImage *src_img, *tmp_img, *dst_img; IplImage *src_hsv, *tmp_hsv; IplImage **src_planes, *tmp_planes[3]; CvHistogram *hist = 0; if (argc != 3 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0 || (tmp_img = cvLoadImage (argv[2], CV_LOAD_IMAGE_COLOR)) == 0) return -1; src_planes = (IplImage **) cvAlloc (sizeof (IplImage *) * 3); for (i = 0; i < 3; i++) { src_planes[i] = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_8U, 1); tmp_planes[i] = cvCreateImage (cvGetSize (tmp_img), IPL_DEPTH_8U, 1); } // (1)2つ入力画像(探索画像,テンプレート画像)の色空間を,RGBからHSVに変換 src_hsv = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_8U, 3); tmp_hsv = cvCreateImage (cvGetSize (tmp_img), IPL_DEPTH_8U, 3); cvCvtColor (src_img, src_hsv, CV_BGR2HSV); cvCvtColor (tmp_img, tmp_hsv, CV_BGR2HSV); cvCvtPixToPlane (src_hsv, src_planes[0], src_planes[1], src_planes[2], 0); cvCvtPixToPlane (tmp_hsv, tmp_planes[0], tmp_planes[1], tmp_planes[2], 0); // (2)テンプレート画像のヒストグラムを計算 hist = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); cvCalcHist (&tmp_planes[0], hist, 0, 0); // (3)探索画像全体に対して,テンプレートのヒストグラムとの距離(手法に依存)を計算 dst_size = cvSize (src_img->width - tmp_img->width + 1, src_img->height - tmp_img->height + 1); dst_img = cvCreateImage (dst_size, IPL_DEPTH_32F, 1); cvCalcBackProjectPatch (src_planes, dst_img, cvGetSize (tmp_img), hist, CV_COMP_CORREL, 1.0); cvMinMaxLoc (dst_img, &min_val, &max_val, &min_loc, &max_loc, NULL); // (4)テンプレートに対応する位置に矩形を描画 cvRectangle (src_img, max_loc, cvPoint (max_loc.x + tmp_img->width, max_loc.y + tmp_img->height), CV_RGB (255, 0, 0), 3); cvNamedWindow ("Image", 1); cvShowImage ("Image", src_img); cvWaitKey (0); cvDestroyWindow ("Image"); cvReleaseImage (&src_img); cvReleaseImage (&dst_img); cvReleaseImage (&tmp_img); cvReleaseImage (&src_hsv); cvReleaseImage (&tmp_hsv); for (i = 0; i < 3; i++) { cvReleaseImage (&src_planes[i]); cvReleaseImage (&tmp_planes[i]); } cvFree (&src_planes); return 0; }
// (1)2つ入力画像(探索画像,テンプレート画像)の色空間を,RGBからHSVに変換
今回は,HSV空間におけるH(色相)のヒストグラムを比較するので,入力画像
の色空間をRGBからHSVに変換する.
比較するヒストグラムは,一つのチャンネル(色平面)に限らずとも良い.
// (2)テンプレート画像のヒストグラムを計算
最初に,テンプレートが像のヒストグラムをあらかじめ計算しておく.
関数cvCalcHist()については,前述のサンプルコードを参照すること.
// (3)探索画像全体に対して,テンプレートのヒストグラムとの距離(手法に依存)を計算
関数cvCalcBackProjectPatch()によって出力される画像は,32bit浮動小数点,
1チャンネルの画像であり,そのサイズは,
探索画像(の幅,高さ)- テンプレート画像(の幅高さ)+ 1
となる.この画像領域dst_imgを確保して,関数cvCalcBackProjectPatch()を実行する.
今回は,ヒストグラム比較の手法としてCV_COMP_CORREL(相関)を指定している.
結果画像中において最大値をもつ位置,つまりテンプレートのヒストグラムと
最も近いヒストグラムを持つ位置,をテンプレートに対応する位置とする.
画像中の値の最大,最小値,およびその位置は,関数cvMinMaxLoc()によって取り出す.
// (4)テンプレートに対応する位置に矩形を描画
前述の処理によって取り出された位置(座標)は,テンプレートの左上の座標
を表すので,そこにテンプレートサイズの矩形を描画する.
最後に,その画像を表示して,何かキーが押されるまで待つ.
実行結果例
以下の例では,探索画像の一部を切り出したものをテンプレート画像として扱っ ている(探索画像に直接ROIを設定しても同様の事が可能である). 従って,マッチング結果のピーク値は1.0になるが,実際にはこのような 完全にテンプレートと領域が入力画像内に存在することは稀である. マッチング位置のピークは1.0を下回り,また,ピーク自体が複数存在することも考えられる.
入力画像 | テンプレート画像 | 結果画像 |
ヒストグラムの均一化 cvEqualizeHist
グレースケール画像のヒストグラムを均一化する
サンプルコード
#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, bin_w, ch_width = 260; int hist_size = 256; float max_value = 0; float range_0[] = { 0, 256 }; float *ranges[] = { range_0 }; IplImage *src_img[2], *hist_img[2]; CvHistogram *hist; if (argc != 2 || (src_img[0] = cvLoadImage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; hist = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist_img[0] = cvCreateImage (cvSize (ch_width, 200), 8, 1); hist_img[1] = cvCreateImage (cvSize (ch_width, 200), 8, 1); src_img[1] = cvCreateImage (cvGetSize (src_img[0]), 8, 1); // (1)ヒストグラムを均一化する cvEqualizeHist (src_img[0], src_img[1]); for (i = 0; i < 2; i++) { cvSet (hist_img[i], cvScalarAll (255), 0); // (2)ヒストグラムを計算し,スケーリング cvCalcHist (&src_img[i], hist, 0, NULL); cvGetMinMaxHistValue (hist, 0, &max_value, 0, 0); cvScale (hist->bins, hist->bins, ((double) hist_img[i]->height) / max_value, 0); bin_w = cvRound ((double) ch_width / hist_size); // (3)ヒストグラムを描画 for (j = 0; j < hist_size; j++) cvRectangle (hist_img[i], cvPoint (j * bin_w, hist_img[i]->height), cvPoint ((j + 1) * bin_w, hist_img[i]->height - cvRound (cvGetReal1D (hist->bins, j))), cvScalarAll (0), -1, 8, 0); } // (4)均一化前後の画像と,それに対するヒストグラムを表示 cvNamedWindow ("Image1", CV_WINDOW_AUTOSIZE); cvShowImage ("Image1", src_img[0]); cvNamedWindow ("Histogram1", CV_WINDOW_AUTOSIZE); cvShowImage ("Histogram1", hist_img[0]); cvNamedWindow ("Image2", CV_WINDOW_AUTOSIZE); cvShowImage ("Image2", src_img[1]); cvNamedWindow ("Histogram2", CV_WINDOW_AUTOSIZE); cvShowImage ("Histogram2", hist_img[1]); cvWaitKey (0); cvDestroyWindow ("Image1"); cvDestroyWindow ("Image2"); cvDestroyWindow ("Histogram1"); cvDestroyWindow ("Histogram2"); cvReleaseImage (&src_img[0]); cvReleaseImage (&src_img[1]); cvReleaseImage (&hist_img[0]); cvReleaseImage (&hist_img[1]); cvReleaseHist (&hist); return 0; }
// (1)ヒストグラムを均一化する
関数cvEqualizeHist()により,入力画像を,そのヒストグラムが均一に分布するような画像に変換する.
この変換により,画像のコントラストがあがる.
しかし,変換後の画像の輝度値は,変換前の画像のヒストグラムを正規化した値の部分的な総和なので,
変換後はヒストグラムに抜けが生じる.
// (2)ヒストグラムを計算し,スケーリング
改めて,ヒストグラム均一化前後の画像にたいしてヒストグラムを計算し,ス
ケーリングを施す.
// (3)ヒストグラムを描画
計算したヒストグラムを描画する.
// (4)均一化前後の画像と,それに対するヒストグラムを表示
実際に,均一化前後の画像と,それに対するヒストグラムを表示し,何かキーが押されるまで待つ.