OpenCV 2.xでヒストグラム画像を生成する方法を調べました。結論から言うと、ヒストグラムを生成する関数はあるんですが、それを画像として表示する簡便な方法はないようです。いくつかのサイトを参考にして、自分でもヒストグラム画像を作成してみました。Windows 7 + OpenCV 2.4.8で動作を確認しています。
なお、OpenCVにこだわらないのであれば、PythonのmatplotlibやR言語を使うことも検討した方がよいと思います。
ヒストグラムの作成
白黒画像の輝度をもとにヒストグラムを作っていきます。まず白黒モードで画像を読み込みます。
cv::Mat img = cv::imread("..\\img\\baboon200.jpg", CV_LOAD_IMAGE_GRAYSCALE);
次に、ヒストグラムを作成する関数cv::calcHistを呼びます。引数が多くややこしい。今回はbinの数が64個のヒストグラムを作成します。
// ヒストグラムを生成するために必要なデータ int image_num = 1; // 入力画像の枚数 int channels[] = { 0 }; // cv::Matの何番目のチャネルを使うか 今回は白黒画像なので0番目のチャネル以外選択肢なし cv::MatND hist; // ここにヒストグラムが出力される int dim_num = 1; // ヒストグラムの次元数 int bin_num = 64; // ヒストグラムのビンの数 int bin_nums[] = { bin_num }; // 今回は1次元のヒストグラムを作るので要素数は一つ float range[] = { 0, 256 }; // 扱うデータの最小値、最大値 今回は輝度データなので値域は[0, 255] const float *ranges[] = { range }; // 今回は1次元のヒストグラムを作るので要素数は一つ // 白黒画像から輝度のヒストグラムデータ(=各binごとの出現回数をカウントしたもの)を生成 cv::calcHist(&img, image_num, channels, cv::Mat(), hist, dim_num, bin_nums, ranges);
作成したヒストグラムはcoutに流すと簡単にデバッグプリントできます。
// テキスト形式でヒストグラムデータを確認 std::cout << hist << std::endl;
出力例は以下の通りです。
[1; 0; 2; 8; 11; 21; 55; 55; 104; 125; 165; 208; 318; 264; 372; 416; 480; 507; 557; 632; 770; 815; 865; 950; 952; 1103; 1135; 1315; 1421; 1688; 1819; 1921; 1721; 1676; 1470; 1387; 1283; 1219; 1219; 1172; 1143; 1097; 1183; 1217; 1211; 1231; 974; 742; 456; 268; 183; 75; 16; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
ヒストグラム画像の作成
さきほど作ったヒストグラムからヒストグラム画像を作成します。多角形を描画する関数を使う方法もありますが、今回は単に矩形をビンの数だけ並べる簡単な方法を採用します。画像のカラーヒストグラムの描画 | OpenCV.jpを下敷きにしています。
// ヒストグラムデータを表示用の画像に変換 void make_histogram_image(cv::MatND& hist, cv::Mat& hist_img, int bin_num) { // histogramを描画するための画像領域を確保 int img_width = 512; int img_height = 512; hist_img = cv::Mat(cv::Size(img_width, img_height), CV_8UC3); // ヒストグラムのスケーリング // ヒストグラムのbinの中で、頻度数最大のbinの高さが、ちょうど画像の縦幅と同じ値になるようにする double max_val = 0.0; cv::minMaxLoc(hist, 0, &max_val); hist = hist * (max_val ? img_height / max_val : 0.0); // ヒストグラムのbinの数だけ矩形を書く for (int j = 0; j < bin_num; ++j){ // saturate_castは、安全に型変換するための関数。桁あふれを防止 int bin_w = cv::saturate_cast<int>((double)img_width / bin_num); cv::rectangle( hist_img, cv::Point(j*bin_w, hist_img.rows), cv::Point((j + 1)*bin_w, hist_img.rows - cv::saturate_cast<int>(hist.at<float>(j))), cv::Scalar::all(0), -1); } } int main(){ …略… // ヒストグラムデータを表示用の画像に変換 // OpenCVでは関数が用意されていないので自前で用意する必要がある cv::Mat hist_img; make_histogram_image(hist, hist_img, bin_num); …略… }
生成されるヒストグラム画像の例は以下のとおりです。
コード全文
#include <iostream> #include <vector> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" // cv::calcHist // 参考 // http://laconsigna.wordpress.com/2011/04/29/1d-histogram-on-opencv/ // http://opencv.jp/cookbook/opencv_img.html // ヒストグラムデータを表示用の画像に変換 void make_histogram_image(cv::MatND& hist, cv::Mat& hist_img, int bin_num) { // histogramを描画するための画像領域を確保 int img_width = 512; int img_height = 512; hist_img = cv::Mat(cv::Size(img_width, img_height), CV_8UC3); // ヒストグラムのスケーリング // ヒストグラムのbinの中で、頻度数最大のbinの高さが、ちょうど画像の縦幅と同じ値になるようにする double max_val = 0.0; cv::minMaxLoc(hist, 0, &max_val); hist = hist * (max_val ? img_height / max_val : 0.0); // ヒストグラムのbinの数だけ矩形を書く for (int j = 0; j < bin_num; ++j){ // saturate_castは、安全に型変換するための関数。桁あふれを防止 int bin_w = cv::saturate_cast<int>((double)img_width / bin_num); cv::rectangle( hist_img, cv::Point(j*bin_w, hist_img.rows), cv::Point((j + 1)*bin_w, hist_img.rows - cv::saturate_cast<int>(hist.at<float>(j))), cv::Scalar::all(0), -1); } } int main() { cv::Mat img = cv::imread("..\\img\\baboon200.jpg", CV_LOAD_IMAGE_GRAYSCALE); // ヒストグラムを生成するために必要なデータ int image_num = 1; // 入力画像の枚数 int channels[] = { 0 }; // cv::Matの何番目のチャネルを使うか 今回は白黒画像なので0番目のチャネル以外選択肢なし cv::MatND hist; // ここにヒストグラムが出力される int dim_num = 1; // ヒストグラムの次元数 int bin_num = 64; // ヒストグラムのビンの数 int bin_nums[] = { bin_num }; // 今回は1次元のヒストグラムを作るので要素数は一つ float range[] = { 0, 256 }; // 扱うデータの最小値、最大値 今回は輝度データなので値域は[0, 255] const float *ranges[] = { range }; // 今回は1次元のヒストグラムを作るので要素数は一つ // 白黒画像から輝度のヒストグラムデータ(=各binごとの出現回数をカウントしたもの)を生成 cv::calcHist(&img, image_num, channels, cv::Mat(), hist, dim_num, bin_nums, ranges); // テキスト形式でヒストグラムデータを確認 std::cout << hist << std::endl; // ヒストグラムデータを表示用の画像に変換 // OpenCVでは関数が用意されていないので自前で用意する必要がある cv::Mat hist_img; make_histogram_image(hist, hist_img, bin_num); cv::imshow("histogram image", hist_img); // 画像表示のためのwait cv::waitKey(-1); return 0; }