HOG特徴量の仕組みについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
takezawaakiragm
記事: 26
登録日時: 8年前

HOG特徴量の仕組みについて

#1

投稿記事 by takezawaakiragm » 8年前

現在HOG特徴量について研究しています。
コードを確認し、論文などを読んでもわからないことがあります。
HOG特徴量とは角度別の勾配強度を導き、これによって勾配ヒストグラムの作成が可能であることは分かりました。
質問
1 角度を180/9などにした場合20度ずつになりますが、この20度というのは0°~20°,・・160°~180°と中間の10度の等の角度もぞれ求めるのでしょうか。もしくは単に0,20,40度と単体の角度を求めているのでしょうか。

2角度の求め方はセルの中央から360求め、0°と180°は同一、20°と200°は同一とのように対称の角度は同一としているのでしょうか。

3黒をべた塗の画像の場合(画像ファイル)、次のような結果になりました。HOGは人認識などに使われるようですが、エッジに強く反応するというのは、いわゆる隣あった画素の輝度が違う場合に輝度強度が増えていくという解釈でよいうのでしょうか。
輝度の変化を読み取っているのか、それとも輝度の連続性を読み取っているのでしょうか。
輝度が連続性を見ているのであればべた塗画像では、相当高い数値になるはずなのですが、結果は最初のセルの20°では値が0であり、その他の角度も高いとはいえませんでした。

何方か是非ご教授お願いいたします。

コード:

#include<iostream>
#include<opencv/cv.h>
#include<opencv/highgui.h>
#include <vector>
#include<opencv2/opencv.hpp>
#include<fstream>

using namespace std;
using namespace cv;

IplImage* image;
// ヒストグラムのビン数
//180の約数[1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 18, 20, 30, 36, 45, 60, 90, 180]
//360の約数[1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360]
#define N_BIN 9
// 何度ずつに分けて投票するか(分解能)
#define THETA (180 / N_BIN)

// 積分画像生成
vector<Mat> calculateIntegralHOG(const Mat& image) {
	// X, Y方向に微分
	Mat xsobel, ysobel;
	Sobel(image, xsobel, CV_32F, 1, 0);
	Sobel(image, ysobel, CV_32F, 0, 1);

	// 角度別の画像を生成しておく
	vector<Mat> bins(N_BIN);
	for (int i = 0; i < N_BIN; i++)
		bins[i] = Mat::zeros(image.size(), CV_32F);

	// X, Y微分画像を勾配方向と強度に変換
	Mat Imag, Iang;
	cartToPolar(xsobel, ysobel, Imag, Iang, true);
	// 勾配方向を[0, 180)にする
	add(Iang, Scalar(180), Iang, Iang < 0);
	add(Iang, Scalar(-180), Iang, Iang >= 180);
	// 勾配方向を[0, 1, ..., 8]にする準備(まだfloat)
	Iang /= THETA;

	// 勾配方向を強度で重みをつけて、角度別に投票する
	for (int y = 0; y < image.rows; y++) {
		for (int x = 0; x < image.cols; x++) {
			int ind = Iang.at<float>(y, x);
			bins[ind].at<float>(y, x) += Imag.at<float>(y, x);
		}
	}

	// 角度別に積分画像生成
	vector<Mat> integrals(N_BIN);
	for (int i = 0; i < N_BIN; i++) {
		// 積分画像をつくる、OpenCVの関数がある
		integral(bins[i], integrals[i]);
	}

	return integrals;
}

// ある矩形領域の勾配ヒストグラムを求める
// ここでいう矩形はHOG特徴量のセルに該当
void calculateHOGInCell(Mat& hogCell, Rect roi, const vector<Mat>& integrals) {
	int x0 = roi.x, y0 = roi.y;
	int x1 = x0 + roi.width, y1 = y0 + roi.height;

	printf("(%d,%d)~(%d,%d)セルの中でのHOG\n\n", x0, y0, x1, y1);
	//excelに出力
	//ofstream ofs("位置.csv", ios::app);
	//ofs <<"("<< cv::format(x0, cv::Formatter::FMT_CSV) <<","<< cv::format(y0, cv::Formatter::FMT_CSV)<<","<<")~(" 
	//	<< cv::format(x1, cv::Formatter::FMT_CSV) <<","<< cv::format(y1, cv::Formatter::FMT_CSV)<<")" << endl;
	
	for (int i = 0; i < N_BIN; i++) {
		Mat integral = integrals[i];
		float a = integral.at<double>(y0, x0);
		float b = integral.at<double>(y1, x1);
		float c = integral.at<double>(y0, x1);
		float d = integral.at<double>(y1, x0);
		hogCell.at<float>(0, i) = (a + b) - (c + d);
		//std::cout << 20 * i << hogCell << std::endl << std::endl;
	}
	
}

// セルの大きさ(ピクセル数)
#define CELL_SIZE 10
// ブロックの大きさ(セル数)奇数
#define BLOCK_SIZE 1
// ブロックの大きさの半分(ピクセル数)
#define R (CELL_SIZE*(BLOCK_SIZE)*0.5)

// HOG特徴量を計算する
// pt: ブロックの中心点
Mat getHOG(ofstream& ofs, Point pt, const vector < Mat > &integrals)  {
	// ブロックが画像からはみ出していないか確認
	if (pt.x - R < 0 ||
		pt.y - R < 0 ||
		pt.x + R >= integrals[0].cols ||
		pt.y + R >= integrals[0].rows
		) {
		return Mat();
	}

	// 与点を中心としたブロックで、
	// セルごとに勾配ヒストグラムを求めて連結
	Mat hist(Size(N_BIN*BLOCK_SIZE*BLOCK_SIZE, 1), CV_32F);

	Point tl(0, pt.y - R);
	int c = 0;
	for (int i = 0; i < BLOCK_SIZE; i++) {
		tl.x = pt.x - R;
		for (int j = 0; j < BLOCK_SIZE; j++) {
			calculateHOGInCell(hist.colRange(c, c + N_BIN),
				Rect(tl, tl + Point(CELL_SIZE, CELL_SIZE)),
				integrals);
			tl.x += CELL_SIZE;
			c += N_BIN;
		}
		tl.y += CELL_SIZE;

	}

	// L2ノルムで正規化
	normalize(hist, hist, 1, 0, NORM_L2);
	/*//hist2のmatを生成 histで得た勾配ヒストグラムの値を随時代入し、
	//それぞれの相関係数をもとめ、その値を比較する。
	
	for (int i = 0; i < BLOCK_SIZE; i++) {
		for (int j = 0; j < BLOCK_SIZE; j++) {
		Mat hist2(Size(N_BIN*BLOCK_SIZE*BLOCK_SIZE, 1), CV_32F);
		}
	}*/
	///////////////////
	//cmdに出力
	std::cout << "正規化後のHOG\n"  << hist << std::endl << std::endl;
	
	//excelに出力
	//ofstream ofs("HOG.csv",ios::app);
	ofs << cv::format(hist, cv::Formatter::FMT_CSV) << endl;
	return hist;

}

int main(int argv,char **argc) {

	// 画像をグレイスケールで読み込む
	string fileName = "black20x20.bmp";
	Mat originalImage = imread(fileName, CV_LOAD_IMAGE_GRAYSCALE);
	Mat originalImage2 = imread(fileName, CV_LOAD_IMAGE_COLOR);
	
	// 積分画像生成
	vector<Mat> integrals = calculateIntegralHOG(originalImage);
	// ある点(x, y)のHOG特徴量を求めるには
	// Mat hist = getHOG(Point(x, y), integrals);
	// とする。histはSize(81, 1) CV_32FのMat


	/* ****************** *
	* 以下、表示のための処理
	* ****************** */

	// 表示用画像を用意(半分の輝度に)
	Mat image = originalImage.clone();
	image *= 0.5;

	// 格子点でHOG計算
	Mat meanHOGInBlock(Size(N_BIN, 1), CV_32F);
	ofstream ofs("kesson.csv");
	for (int y = CELL_SIZE / 2; y < image.rows; y += CELL_SIZE) {
		for (int x = CELL_SIZE / 2; x < image.cols; x += CELL_SIZE) {
			// (x, y)でのHOGを取得
			Mat hist = getHOG(ofs, Point(x, y), integrals);
			// ブロックが画像からはみ出ていたら continue
			if (hist.empty()) continue;

			// ブロックごとに勾配方向ヒストグラム生成
			meanHOGInBlock = Scalar(0);
			for (int i = 0; i < N_BIN; i++) {
				for (int j = 0; j < BLOCK_SIZE*BLOCK_SIZE; j++) {
					meanHOGInBlock.at<float>(0, i) += hist.at<float>(0, i + j*N_BIN);
				}
			}
			// L2ノルムで正規化(強い方向が強調される)
			normalize(meanHOGInBlock, meanHOGInBlock, 1, 0, CV_L2);
			
			
			
			//printf("輝度勾配の始点と終点");

			// 角度ごとに線を描画
			Point center(x, y);
			for (int i = 0; i < N_BIN; i++) {
				double theta = (i * THETA + 90.0) * CV_PI / 180.0;
				Point rd(CELL_SIZE*0.5*cos(theta), CELL_SIZE*0.5*sin(theta));
				Point rp = center - rd;
				Point lp = center - -rd;
				//  line(画像,開始xとy,終点xとy,色,太さ,)
				line(image, rp, lp, Scalar(255 * meanHOGInBlock.at<float>(0, i), 255, 255));

				//輝度勾配の角度、及び強度の出力
				//printf("rd=%d:rp=%d:lp=%d\n",rd,rp,lp);

				//(x, y)でのHOGの出力
				//printf("描画開始位置x軸%d:y軸%d\n特徴量の角度\n\n",x,y);
				//std::cout << 20*i  << hist << std::endl << std::endl;


			}
			printf("描画開始位置x軸%d:y軸%d\n--------------------------------------------\n", x, y);
		}
	}
	
	// 表示
	imshow("out", image);
	waitKey(0);

	// 画像を保存する
	cv::imwrite("outkesson.png", image);

	


	return 0;
}


添付ファイル
角度あたりの輝度強度.PNG
セルの中の角度あたりの輝度強度
角度あたりの輝度強度.PNG (48.71 KiB) 閲覧数: 4842 回
black20x20.png
黒 20x20
black20x20.png (401 バイト) 閲覧数: 4842 回
outkesson.png
20x20の黒画像をセル(10x10)で実行した画像
outkesson.png (413 バイト) 閲覧数: 4842 回

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: HOG特徴量の仕組みについて

#2

投稿記事 by usao » 8年前

詳しくないので,間違っていたらご容赦ください(&修正願います).

>1
勾配方向ヒストグラムですから
セル内の各画素に関して,その画素位置での輝度勾配方向に対応するbinのカウントを増やす という感じでしょう.
binが9つあるなら,例えば勾配方向が10度の場合には1つ目のbinに含めるという形かと思います.

>2
>セルの中央から360求め
という話が何を指しているかわかりません.
必要な「角度」は,輝度勾配の方向であって,画素位置的な話ではないです.
対称の方向は同一と見なす(ことが多い?)と思います.

>3
ある画像の 勾配の方向がどのような分布であるか を表す感じ.
例えば人であれば,
頭のあたりにおおよそ丸い輪郭があって(0~180度の範囲のいろんな方向の輝度勾配がある,ということになるかな?)
胴体の左右におおよそ縦線(勾配方向が横方向)な輪郭があって…
というような情報になるのかな,と思います.
なので,べた塗り画像だと,まともな情報が存在しない感じ(全部0とか)になるのではないかと予想します.

takezawaakiragm
記事: 26
登録日時: 8年前

Re: HOG特徴量の仕組みについて

#3

投稿記事 by takezawaakiragm » 8年前

大変参考になりました!
ありがとうございます。

takezawaakiragm
記事: 26
登録日時: 8年前

Re: HOG特徴量の仕組みについて

#4

投稿記事 by takezawaakiragm » 8年前

黒のべた塗画像のHOG特徴量を調べた結果(添付ファイルのcmdキャプチャ画像)、まばらな数値が出てきてしまいよくわかりませんでした。
分かる方是非、ご教授お願いいたします。

takezawaakiragm
記事: 26
登録日時: 8年前

Re: HOG特徴量の仕組みについて

#5

投稿記事 by takezawaakiragm » 8年前

黒のべた塗画像のHOG特徴量を調べた結果(添付ファイルのcmdキャプチャ画像)、まばらな数値が出てきてしまいよくわかりませんでした。
分かる方是非、ご教授お願いいたします。

Math

Re: HOG特徴量の仕組みについて

#6

投稿記事 by Math » 8年前

[参考]http://opencv.jp/cookbook/opencv_img.htmlにある[ 人を検出する HOG ]がOpenCV3.1で動作しますよ。(VS2015 C++)

コード:

#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>

int main(int argc, char *argv[])
{
	const char *imagename = argc > 1 ? argv[1] : "d:/dat/people_detect2.png";
	cv::Mat img = cv::imread(imagename, 1);
	if (img.empty()) return -1;

	cv::HOGDescriptor hog;
	hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());

	std::vector<cv::Rect> found;
	// 画像,検出結果,閾値(SVMのhyper-planeとの距離),
	// 探索窓の移動距離(Block移動距離の倍数),
	// 画像外にはみ出た対象を探すためのpadding,
	// 探索窓のスケール変化係数,グルーピング係数
	hog.detectMultiScale(img, found, 0.2, cv::Size(8, 8), cv::Size(16, 16), 1.05, 2);

	std::vector<cv::Rect>::const_iterator it = found.begin();
	std::cout << "found:" << found.size() << std::endl;
	for (; it != found.end(); ++it) {
		cv::Rect r = *it;
		// 描画に際して,検出矩形を若干小さくする
		r.x += cvRound(r.width*0.1);
		r.width = cvRound(r.width*0.8);
		r.y += cvRound(r.height*0.07);
		r.height = cvRound(r.height*0.8);
		cv::rectangle(img, r.tl(), r.br(), cv::Scalar(0, 255, 0), 3);
	}

	// 結果の描画
	cv::namedWindow("result", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);
	cv::imshow("result", img);
	cv::waitKey(0);
}

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: HOG特徴量の仕組みについて

#7

投稿記事 by usao » 8年前

>黒のべた塗画像

black20x20.png のことでしょうか.
DLして見た感じだと,「べた塗り」にはなっていないように見えます.

takezawaakiragm
記事: 26
登録日時: 8年前

Re: HOG特徴量の仕組みについて

#8

投稿記事 by takezawaakiragm » 8年前

ありがとうございます。
確かにべた塗ができていませんでした。
黒のべた塗を作成後ためしたところ、すべて0の値になりました。

大変ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る