画像認識(色認識)について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
yak

画像認識(色認識)について

#1

投稿記事 by yak » 14年前

画像認識(色認識)についての質問です

aerobeatなどのソフトは緑色の部分が複数あっても正確にターゲットのいちをとらえられている(ある程度の範囲が緑ならば認識している?)のですが・・
あれはどのような手順を踏んでいるのでしょうか。

画像認識(色認識)をしてそこから中心座標を取得したいのです。

緑色(に近い)部分を抽出するプログラムはつくれたのですがそこからどうしたらいいのか・・
やはりOpenCVなどのライブラリを使うことになるのでしょうか・・・

曖昧な質問ですが、よろしくお願いします。

アバター
kazuoni
記事: 17
登録日時: 14年前
住所: 愛知
連絡を取る:

Re: 画像認識(色認識)について

#2

投稿記事 by kazuoni » 14年前

Aerobeatの実装はよくわかりませんが、
線形移動する色認識ならば「パーティクルフィルタ」なんて検索かけると
幸せになれるかもしれません。

今緑色が抽出できるならば、物体中心はそれらのモーメントを計算すれば中心は出るかと思います。
ただ、似たような色がいくつかの箇所ででてしまっているなら
単純にモーメントではなくて、検出領域の重みで物体部分を決める(重みがしきい値以下ならそれは物体ではないとする)
などの処理が別途必要ですね。

開発はなんでもいいと思いますよ。(今現在不都合がないならば)
ただ、OpenCV2.2以降ではCudaが使えるようになったので、
ものすごい計算量の画像処理をするならばOpenCVに移行するのもありですね。
(あ、それ相応のグラボ積まないといけませんが・・・)

# 漢字ミス修正しました

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 画像認識(色認識)について

#3

投稿記事 by Dixq (管理人) » 14年前

Aerobeatの実装は適当なので、OpenCV使ってません。
カメラからの取得はDirectShowを使っていて、ピクセルごとに自前で計算してます。

仰るようにOpenCVを使う方がより正確に計算出来ると思います。
隣接している同色グループの塊にラベルを付け、ラベルを数えて一番大きい物をターゲットにすれば、部屋に邪魔な同じ色のものがあってもある程度除外出来ると思います。
ただ、その場合は、解像度を落としてから計算する等計算コストを考慮した設計が必要かと思います。

Aerobeatは複数ターゲットがあると同じ大きさの塊の場合、中間を示してしまいます。
また、色を判別する時は、RGBではなく、HSVに変換した方がいいです。
http://ja.wikipedia.org/wiki/HSV%E8%89% ... A%E9%96%93

しっかり画像処理をしたいならやはりCVは欠かせないと思います。

yak

Re: 画像認識(色認識)について

#4

投稿記事 by yak » 14年前

[quote="Dixq (管理人)"
隣接している同色グループの塊にラベルを付け、ラベルを数えて一番大きい物をターゲットにすれば、部屋に邪魔な同じ色のものがあってもある程度除外出来ると思います。 [/quote]

現在は配列に、赤に近い(1)か赤に近くない(0)が入ってるのですが・・
どのようにすれば「塊」を認識できるのでしょうか・・・

アバター
kazuoni
記事: 17
登録日時: 14年前
住所: 愛知
連絡を取る:

Re: 画像認識(色認識)について

#5

投稿記事 by kazuoni » 14年前

色の尤度を0,1にしている意図がわかりません。
0~1.0の尤度を持たせたほうが物体認識に向いていると思います。

暇ができたのでとりあえずyakさんの条件の下、「塊を認識」を目標に作ってみました。
楽なのでOpenCVを用いました。
ただ、1時間程度でささっと作ったので解説はあまりつけてません。
用いたのは領域拡張法です。詳細は調べていただければすぐにわかるかと思います。
簡単に言えば未操作の1点の周囲8近傍を探索して自分と同じ領域を外へ外へ拡張していくものです。

今はカメラ画像640*480を160*120に縮尺し、HSV空間に変換します。
次にyakさんにあわせて赤に近い(1)か赤に近くない(0)とし、マップ作成を行います。
このマップから領域拡張法を用いて「塊」に分割します。
最後にこれを面積降順上位4つとりだし、それを矩形領域で描画しています。
上から赤、緑、青、水色の順番です。
まぁそれなりに塊は検出できているのではないかなと思います。

現在では160*120上に結果を表示していますが、そのまま座標系を拡大してやれば640*480でも表示可能です。
自分の環境では現在はFPSをさほど落とさずに動作していますが、環境によっては重いかもしれません。
ソースコードの無駄な処理を減らせば早くなるかもしれません。
また、HSVの閾値が結構難しいです。そのあたりは試行錯誤してみてください。

コード:


#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <stack>
#include "define.h"

using namespace std;


template <class T>
int GetArrayMaxIndex(T* ary, int num){
	int m_id = 0;
	for(int i=0; i<num; ++i){
		if(ary[i]>ary[m_id]){
			m_id = i;
		}
	}
	return m_id;
}

int main (int argc, char **argv){
	CvCapture* capture = 0;
	IplImage* frame = 0;
	MyImage* colorbit, *devided;
	CvScalar col[4];
	int c;
	uchar H, S, V;


	if(argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))){
		capture = cvCreateCameraCapture (argc == 2 ? argv[1][0] - '0' : 0);
	}

	cvSetCaptureProperty (capture, CV_CAP_PROP_FRAME_WIDTH, CAPTURE_WIDTH);
	cvSetCaptureProperty (capture, CV_CAP_PROP_FRAME_HEIGHT, CAPTURE_HEIGHT);

	IplImage *dst = cvCreateImage(cvSize(CAPTURE_WIDTH*SCALE, CAPTURE_HEIGHT*SCALE), IPL_DEPTH_8U, 3);
	IplImage *dst_hsv = cvCreateImage(cvSize(CAPTURE_WIDTH*SCALE, CAPTURE_HEIGHT*SCALE), IPL_DEPTH_8U, 3);
	colorbit = new MyImage(CAPTURE_WIDTH*SCALE, CAPTURE_HEIGHT*SCALE, 1);
	devided = new MyImage(CAPTURE_WIDTH*SCALE, CAPTURE_HEIGHT*SCALE, 1);
	int* area = new int[colorbit->width*colorbit->height];
	vector<Area> area_vec;

	col[0] = CV_RGB(0xff, 0, 0);
	col[1] = CV_RGB(0, 0xff, 0);
	col[2] = CV_RGB(0, 0, 0xff);
	col[3] = CV_RGB(0, 0xff, 0xff);

	cvNamedWindow ("testAreaDivide", CV_WINDOW_AUTOSIZE);
	
	while (1) {

		// カメラ画像P1取得
		frame = cvQueryFrame (capture);

		// カメラ画像P1'縮尺
		cvResize(frame, dst, CV_INTER_LINEAR);

		// 赤に近い(1)か赤に近くない(0)map作成
		cvCvtColor(dst, dst_hsv, CV_BGR2HSV);
		for(int y=0; y<dst_hsv->height; ++y){
			for(int x=0; x<dst_hsv->width; ++x){
				// P1' RGB -> HSV
				H = (dst_hsv->imageData+y*dst_hsv->widthStep)[x*3];
				S = (dst_hsv->imageData+y*dst_hsv->widthStep)[x*3+1];
				V = (dst_hsv->imageData+y*dst_hsv->widthStep)[x*3+2];

				// P1'がHSV閾値の画素を記憶
				if( _MIN_H_<=H && H<=_MAX_H_ && 
					_MIN_S_<=S && S<=_MAX_S_ && 
					_MIN_V_<=V && V<=_MAX_V_ ){
					colorbit->val[x][y][0] = 0.0;
					colorbit->val[x][y][0] = 0.0;
					colorbit->val[x][y][0] = 0.0;
				}
				else{
					colorbit->val[x][y][0] = 1.0;
					colorbit->val[x][y][0] = 1.0;
					colorbit->val[x][y][0] = 1.0;
				}
				devided->val[x][y][0] = -1.0;
			}
		}

		// 領域拡張法
		int color_label = 0;
		stack<Pos> stack;
		area_vec.clear();
		for(int i=0; i<colorbit->width*colorbit->height; ++i){
			area[i] = 0;
		}
		for(int y=0; y<colorbit->height; ++y){
			for(int x=0; x<colorbit->width; ++x){
				if(colorbit->val[x][y][0]==0.0 && devided->val[x][y][0]==-1.0){
					// clear stack
					while(!stack.empty()){
						stack.pop();
					}
					// area expand
					stack.push(Pos(x,y));
					while(!stack.empty()){
						Pos p = stack.top();
						stack.pop();
						if(colorbit->val[p.x][p.y][0]==0.0 && devided->val[p.x][p.y][0]==-1.0){

							devided->val[p.x][p.y][0] = color_label;

							if(p.x+1<colorbit->width && colorbit->val[p.x+1][p.y][0]==0.0 ){
								stack.push(Pos(p.x+1,p.y));
							}
							if(p.x-1>=0 && colorbit->val[p.x-1][p.y][0]==0.0){
								stack.push(Pos(p.x-1,p.y));
							}
							if(p.y+1<colorbit->height && colorbit->val[p.x][p.y+1][0]==0.0){
								stack.push(Pos(p.x,p.y+1));
							}
							if(p.y-1>=0 && colorbit->val[p.x][p.y-1][0]==0.0){
								stack.push(Pos(p.x,p.y-1));
							}
						}
					}
					++color_label;
				}
			}
		}

		// 領域を上位(_EVALUATION_)個を抽出
		for(int y=0; y<devided->height; ++y){
			for(int x=0; x<devided->width; ++x){
				area[(int)devided->val[x][y][0]]++;
			}
		}
		for(int i=0; i<_EVALUATION_; ++i){
			int max_index = GetArrayMaxIndex(area, colorbit->width*colorbit->height);
			area_vec.push_back(Area(max_index, area[max_index]));
			area[max_index] = 0;
		}

		// 領域の左上と右下を探索
		for(int y=0; y<devided->height; ++y){
			for(int x=0; x<devided->width; ++x){
				for(int i=0; i<_EVALUATION_; ++i){
					if(devided->val[x][y][0]==area_vec[i].id){
						if(area_vec[i].p1.x==-1){
							area_vec[i].p1.x = area_vec[i].p2.x = x;
							area_vec[i].p1.y = area_vec[i].p2.y = y;
						}
						else{
							if(area_vec[i].p1.x<x){
								area_vec[i].p1.x = x;
							}
							else if(area_vec[i].p2.x>x){
								area_vec[i].p2.x = x;
							}
							if(area_vec[i].p1.y<y){
								area_vec[i].p1.y = y;
							}
							else if(area_vec[i].p2.y>y){
								area_vec[i].p2.y = y;
							}
						}
					}
				}
			}
		}

		// 上位4つを白で塗りつぶす
		/*
		for(int y=0; y<devided->height; ++y){
			for(int x=0; x<devided->width; ++x){
				for(int e=0; e<_EVALUATION_; ++e){
					if(devided->val[x][y][0]==area_vec[e].id){

						(dst->imageData+y*dst->widthStep)[x*3] = 255.0;
						(dst->imageData+y*dst->widthStep)[x*3+1] = 255.0;
						(dst->imageData+y*dst->widthStep)[x*3+2] = 255.0;
						break;
					}
				}
			}
		}
		*/

		// 上位4つを矩形領域で囲む
		for(int e=0; e<_EVALUATION_; ++e){
			cvRectangle(dst, area_vec[e].p1, area_vec[e].p2, col[e]);
		}

		cvShowImage ("testAreaDivide", dst);

		c = cvWaitKey (2);
		if (c=='\x1b'){
			break;
		}
		else if(c=='z'){
			IplImage *frame_min = cvCreateImage(cvSize(CAPTURE_WIDTH*SCALE, CAPTURE_HEIGHT*SCALE), IPL_DEPTH_8U, 3);
			cvResize(frame, frame_min, CV_INTER_LINEAR);
			cvSaveImage("before.jpg", frame_min);
			cvSaveImage("after.jpg", dst);
		}
	}

	cvReleaseCapture (&capture);
	cvDestroyWindow ("Capture");

	delete colorbit;
	delete devided;
	//delete[] area; 解放なぜかできない... in 環境VC++ 2010

	return 0;
}

コード:

#include <iostream>
#include <cv.h>

#pragma once

#define CAPTURE_WIDTH  (640)
#define CAPTURE_HEIGHT (480)
#define SCALE (0.25)
#define _MIN_H_ (0)
#define _MAX_H_ (5)
#define _MIN_S_ (120)
#define _MAX_S_ (255)
#define _MIN_V_ (0)
#define _MAX_V_ (200)
#define _EVALUATION_ (4)

class MyImage{
public:
	double ***val;
	int width, height, plane;

public:
	MyImage(int _width, int _height, int _plane)
		: width(_width), height(_height), plane(_plane), val(NULL){

		val = new double** [width];
		for(int x=0; x<width; ++x){
			val[x] = new double* [height];
			for(int y=0; y<height; ++y){
				val[x][y] = new double [plane];
				for(int p=0; p<plane; ++p){
					val[x][y][p] = 0.0;
				}
			}
		}
	}

	~MyImage(){
		for(int x=0; x<width; ++x){
			for(int y=0; y<height; ++y){
				delete[] val[x][y];
			}
			delete[] val[x];
		}
		delete[] val;
	}
};

class Pos{
public:
	int x, y;
	Pos(int _x, int _y) : x(_x), y(_y){
	}
};

class Area{
public:
	int id, area;
	CvPoint p1, p2;
	Area(int _id, int _area) : id(_id), area(_area){
		p1.x = p1.y = p2.x = p2.y = -1.0;
	}
};

画像

閉鎖

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