Robinit

IT関連メモ

OpenCVでヒストグラム計算する

OpenCVを利用しヒストグラム計算する

以下のように、Webカメラで取得した画像にヒストグラムのグラフを重ねて表示するアプリを作成する。
f:id:wonder_three:20180127231011p:plain

開発環境の構築は以下参照。
OS:Windows
言語:C++
IDE:Visual Stdio2015
robinit.hatenablog.com

ヒストグラムとは??

画像処理の分野では、各濃度値に対してその濃度値を持った画素数を求めたもので、
濃度ヒストグラムまたは単純にヒストグラムという。
詳細は以下参照
http://izumi-math.jp/sanae/inf_box/histgram/histgram.htm

ヒストグラム比較による近似確認や濃度変換など、
ヒストグラムは画像処理で様々なケースで利用される。

ヒストグラムは主に横軸が濃度値、縦軸に画素数をとったグラフで表現される

カメラ画像取得

VideoCapture cap(0) でカメラをオープン。引数の"0"はデフォルトのカメラを意味する。
2台目のカメラを接続しオープンする場合は、"1"を指定する。

VideoCapture::isOpened() でカメラがオープンされたかチェック

cap >> frame で現在のフレームをcv::Matに取得。

VideoCapture cap(0); // デフォルトカメラをオープン					
if (!cap.isOpened())  // 成功したかどうかをチェック					
	return -1;
do {
	cap >> frame;	 // カメラから新しいフレームを取得
} while (frame.empty());

グレースケール化

ヒストグラムの計算に使用する画像はグレイスケール化が必要
OpenCVの cv::cvtColor を利用してグレースケール化する

Mat gray;
cvtColor(frame, gray, CV_BGR2GRAY);

ヒストグラム計算

以下のようにOpenCVの cv::calcHist を利用してヒストグラムを求め、
ヒストグラムのグラフ画像を作成する

// ヒストグラムを描画する画像割り当て					
const int ch_width = 256, ch_height = 128;
Mat hist_img(Size(ch_width, ch_height), CV_8UC3, Scalar::all(255));

const int hdims[] = { 256 }; // 次元毎のヒストグラムサイズ					
const float hranges[] = { 0,256 };
const float* ranges[] = { hranges }; // 次元毎のビンの下限上限					
double max_val = .0;

// シングルチャンネルのヒストグラム計算					
// 画像(複数可),画像枚数,計算するチャンネル,マスク,ヒストグラム(出力),					
// ヒストグラムの次元,ヒストグラムビンの下限上限					
calcHist(&gray, 1, 0, Mat(), hist, 1, hdims, ranges);

// 最大値の計算					
minMaxLoc(hist, 0, &max_val);

// ヒストグラムのスケーリング					
hist = hist * (max_val ? ch_height / max_val : 0.);
for (int j = 0; j < hdims[0]; ++j) {
	int bin_w = saturate_cast<int>((double)ch_width / hdims[0]);
	rectangle(hist_img, Point(j * bin_w, hist_img.rows), Point((j + 1) * bin_w, hist_img.rows - saturate_cast<int>(hist.at<float>(j))), Scalar::all(128),-1);
}
// ヒストグラム画像のROI表示重ね合わせ					
Mat roi_img(targetImg, Rect(20, targetImg.rows - hist_img.rows - 20, ch_width, ch_height));
hist_img.copyTo(roi_img);

サンプル実装

以下サンプル実装

#include "opencv2/opencv.hpp"						

using namespace cv;

/*
 ヒストグラムを計算し、ヒストグラムのグラフを描画する
 第一引数: gray ヒストグラムを計算するグレイスケール画像 (in)
 第二引数: targetImg グラフを重畳する画像 (inout)
 第三引数: hist ヒストグラムの計算結果 (out)
*/
void drawHist(const Mat& gray, Mat& targetImg, Mat& hist) {

	// ヒストグラムを描画する画像割り当て					
	const int ch_width = 256, ch_height = 128;
	Mat hist_img(Size(ch_width, ch_height), CV_8UC3, Scalar::all(255));

	const int hdims[] = { 256 }; // 次元毎のヒストグラムサイズ					
	const float hranges[] = { 0,256 };
	const float* ranges[] = { hranges }; // 次元毎のビンの下限上限					
	double max_val = .0;

	// シングルチャンネルのヒストグラム計算					
	// 画像(複数可),画像枚数,計算するチャンネル,マスク,ヒストグラム(出力),					
	// ヒストグラムの次元,ヒストグラムビンの下限上限					
	calcHist(&gray, 1, 0, Mat(), hist, 1, hdims, ranges);

	// 最大値の計算					
	minMaxLoc(hist, 0, &max_val);

	// ヒストグラムのスケーリング					
	hist = hist * (max_val ? ch_height / max_val : 0.);
	for (int j = 0; j < hdims[0]; ++j) {
		int bin_w = saturate_cast<int>((double)ch_width / hdims[0]);
		rectangle(hist_img, Point(j * bin_w, hist_img.rows), Point((j + 1) * bin_w, hist_img.rows - saturate_cast<int>(hist.at<float>(j))), Scalar::all(128), -1);
	}

	// ヒストグラム画像のROI表示重ね合わせ					
	Mat roi_img(targetImg, Rect(20, targetImg.rows - hist_img.rows - 20, ch_width, ch_height));
	hist_img.copyTo(roi_img);

}

int main()
{
	VideoCapture cap(0); // デフォルトカメラをオープン					
	if (!cap.isOpened())  // 成功したかどうかをチェック					
		return -1;

	Mat frame;
	while (1) {
		do {
			cap >> frame;	 // カメラから新しいフレームを取得
		} while (frame.empty());

		// frame画像対象の処理				
		// グレースケール画像に変換				
		Mat gray;
		cvtColor(frame, gray, CV_BGR2GRAY);

		// ヒストグラム計算
		Mat hist;
		drawHist(gray, frame, hist);

		imshow("CatImg", frame);

		int keyVal = waitKey(30);
		if (keyVal == 27) { // Escキー				
			break;
		}
	}
	return 0;
}