OpenCVメモ(2)

今日は色を認識するところまでできました。
完全にopenFrameworks WikiのColorTrackingの頁の写経ですが、コメントをつけてうpします。

元ページ
http://wiki.openframeworks.cc/index.php?title=Color_Tracking

ヘッダファイル testApp.h

#ifndef _TEST_APP
#define _TEST_APP

#include "ofMain.h"
#include "ofxVectorMath.h"
#include "ofxOpenCv.h"

//色の基本的な情報を持ったクラスを作ります。
class Color {
public:
	float hue, sat, bri;
	ofxVec2f pos;
};


class testApp : public ofBaseApp{

	public:
		//この辺はいつも通り
		void setup();
		void update();
		void draw();

		void keyPressed  (int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void windowResized(int w, int h);

	
	//カメラの映像を取得するためのオブジェクト
	ofVideoGrabber vidGrabber;
	
	//カメラの幅と高さ
	int camWidth;
	int camHeight;
	
	//もともとの映像情報
	ofxCvColorImage colorImg;
	
	//HSV系に変換した映像情報
	ofxCvColorImage colorImgHSV;
	
	//HSV系の色相、彩度、明度のマップ
	ofxCvGrayscaleImage hueImg;
	ofxCvGrayscaleImage satImg;
	ofxCvGrayscaleImage briImg;

	//色を追跡して輪郭を出すための映像情報
	ofxCvGrayscaleImage reds;
	
	//追跡する色です。
	Color one;
	
	//もとの映像情報のピクセルの彩度と明度が
	//指定した色に近ければ255を代入、遠ければ0を代入
	unsigned char * colorTrackedPixelsRed;

	//二値画像
	ofTexture trackedTextureRed;
	
	//輪郭を判別してくれるメチャクチャ便利なやつです。
	ofxCvContourFinder finderRed;
};

#endif

CPPファイル testApp.cpp

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
	//最初は色が原点にあることにします
	one.pos = ofxVec2f(0,0);
	
	//カメラの大きさを指定
	camWidth = 320;
	camHeight = 240;

	//それぞれの映像情報の大きさを指定してあげます。
	colorImg.allocate(camWidth, camHeight);
	colorImgHSV.allocate(camWidth, camHeight);
	
	hueImg.allocate(camWidth, camHeight);
	satImg.allocate(camWidth, camHeight);
	briImg.allocate(camWidth, camHeight);
	
	reds.allocate(camWidth, camHeight);
	
	//二値画像を作るための配列の大きさを指定
	colorTrackedPixelsRed =new unsigned char [camWidth*camHeight];

	//二値画像の大きさ
	trackedTextureRed.allocate(camWidth, camHeight, GL_LUMINANCE);

	//Grabberの「何か」と大きさ設定
	//setVerboseってなんだろ? Verbose:冗長な、言葉数の多い
	vidGrabber.setVerbose(true);
	vidGrabber.initGrabber(camWidth, camHeight);
}

//--------------------------------------------------------------
void testApp::update(){
	//映像を取得!
	vidGrabber.grabFrame();

	//colorImgの中身をピクセルごとに指定
	colorImg.setFromPixels(vidGrabber.getPixels(), camWidth, camHeight);

	//HSV系に変換
	colorImgHSV = colorImg;
	colorImgHSV.convertRgbToHsv();

	//色相、彩度、明度にマッピング
	colorImgHSV.convertToGrayscalePlanarImages(hueImg, satImg, briImg);

	//ここが何やってんのか分からん。
	hueImg.flagImageChanged();
	satImg.flagImageChanged();
	briImg.flagImageChanged();

	//ピクセルの配列をそれぞれに作成
	unsigned char * huePixels = hueImg.getPixels();
	unsigned char * satPixels = satImg.getPixels();
	unsigned char * briPixels = briImg.getPixels();

	//ピクセルの数
	int nPixels = camWidth*camHeight;

	//ピクセルの色が指定した色と色相と彩度が近ければ、
	//colorTrackedPixelsRedに255を、遠ければ0を代入。
	for (int i=0; i<nPixels; i++) {
		if ( (huePixels[i]>=one.hue-12 && huePixels[i] <= one.hue + 12) &&
			 (satPixels[i]>=one.sat-24 && satPixels[i]<=one.sat+200)){
			colorTrackedPixelsRed[i] = 255;
		}else {
			colorTrackedPixelsRed[i]=0;
		}

	}

	//colorTrackedPixelsRedをもとにredsを作成
	//redsは輪郭線を求めるためだけにあるのかな?
	reds.setFromPixels(colorTrackedPixelsRed, camWidth, camHeight);

	//輪郭線を見つける
	finderRed.findContours(reds, 10, nPixels/3, 1, false, true);

	//colorTrackedPixelsRedをもとにtrackedTextureRedを作成
	//これが二値画像になってるっぽい
	trackedTextureRed.loadData(colorTrackedPixelsRed,
				 camWidth, camHeight, GL_LUMINANCE);

	//追跡する色の位置を中心にあわせる
	if (finderRed.blobs.size()>0) {
		one.pos = ofxVec2f(finderRed.blobs[0].centroid.x,
				finderRed.blobs[0].centroid.y);
	}
}

//--------------------------------------------------------------
void testApp::draw(){
	//背景色を指定
	ofBackground(100, 100, 100);

	ofSetColor(0xffffff);

	//元映像を表示
	vidGrabber.draw(0, 0);

	//HSV系に変換したものを表示
	colorImgHSV.draw(340, 0);

	//二値画像を表示
	trackedTextureRed.draw(20, 300);
	ofDrawBitmapString("red", 20, 280);

	//元映像に輪郭線を表示
	finderRed.draw();

	//二値画像の方に輪郭線表示
	glPushMatrix();
		glTranslatef(20, 300, 0);
		finderRed.draw();
	glPopMatrix();

	//追跡する色の位置を表示
	if (finderRed.blobs.size()>0) {
		char tempStr1[255];
		sprintf(tempStr1, "x:%f\ny:%f",
		finderRed.blobs[0].centroid.x,
		finderRed.blobs[0].centroid.y);
		ofDrawBitmapString(tempStr1, 20, 250);
	}
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
.
.
.
.

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
	unsigned char * huePixels = hueImg.getPixels();
	unsigned char * satPixels = satImg.getPixels();
	unsigned char * briPixels = briImg.getPixels();

	//クリックした場所の色を追跡する色に設定。
	x=MIN(x,hueImg.width-1);
	y=MIN(y,hueImg.height-1);
	
	if (button==0) {
		one.hue = huePixels[x+(y*hueImg.width)];
		one.sat = satPixels[x+(y*satImg.width)];
		one.bri = briPixels[x+(y*briImg.width)];
	}
}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
.
.
.

これで、色の追跡ができています。実行結果は次のようになります。

ここでは紫色を追跡色に設定しています。ちゃんと追跡できていますね。