Continuously Adaptive Mean Shift

CamShiftDetectionDlg.h

  • Header를 추가합니다.
#include "CamOpenCV.h"
#include "CamShift.h"
  • member 변수를 추가합니다.
private:
	CCamOpenCV *_camera;
	CCamShift *_camShift;
  • member 함수를 추가합니다.
private:
	void DrawRecognizedInfo(const BYTE *image, int width, int height, int pixelByte, sRecResult objectInfo);
public:
	afx_msg void OnDestroy();
	afx_msg void OnTimer(UINT_PTR nIDEvent);

CamShiftDetectionDlg.cpp

  • 전역변수(Global variable)를 추가합니다.
CvRect	gRect;			// 색 추적 알고리즘의 초기 영역을 선택할 사각형
bool	detectionStart;		// 색 추적 알고리즘의 초기 영역을 적용하라는 flag
bool	regionSelecting;	// 색 추적 알고리즘의 초기 영역을 선택하는 중인지 알려주는 flag
  • 전역함수(Global function)를 추가합니다.
void on_mouse( int event, int x, int y, int flags, void* param )
{
    switch( event )
    {
    case CV_EVENT_LBUTTONDOWN:
		gRect.x = x;
		gRect.y = y;
		gRect.width = 1;
		gRect.height = 1;
		regionSelecting = true;
		break;
    case CV_EVENT_LBUTTONUP:
		gRect.width = abs(gRect.x-x);
		gRect.height = abs(gRect.y-y);
		gRect.x = min(gRect.x, x);
		gRect.y = min(gRect.y, y);
		regionSelecting = false;
		detectionStart = true;
		break;
    case CV_EVENT_MOUSEMOVE:
		gRect.width = abs(gRect.x-x);
		gRect.height = abs(gRect.y-y);
		gRect.x = min(gRect.x, x);
		gRect.y = min(gRect.y, y);
        break;
    }
}
  • OnInitDialog() 함수에 다음 코드를 추가합니다.
_camera = new CCamOpenCV("OpenCV_Camera", 0, 640, 480);	// Camera 장치 클래스를 생성합니다
if( _camera ) {						// 장치가 잘 잡혔으면
	SetTimer(1000, 100, NULL);			// 33ms마다 Timer를 구동시켜 영상 처리를 합니다.
}
_camShift = new CCamShift();				// 색추적 알고리즘 클래스를 생성합니다.
cvNamedWindow("CamShiftDetection");			// 결과 영상을 출력하기 위한 윈도우를 생성합니다.
 
cvSetMouseCallback( "CamShiftDetection", on_mouse, 0 );	// OpenCV에서 제공하는 윈도우에서 마우스 이벤트를 처리하는 함수를 설정합니다.
 
// 전역으로 선언한 변수를 초기화
gRect.x = 0;
gRect.y = 0;
gRect.width = 639;
gRect.height = 479;
detectionStart = false;
regionSelecting = false;
  • OnDestroy() 함수에 다음 코드를 추가합니다.
cvDestroyWindow("CamShiftDetection");	// 결과 영상을 출력하기 위한 윈도우를 종료합니다.
delete _camShift;			// 물체인식 알고리즘 클래스를 메모리에서 제거합니다.
delete _camera;				// 카메라 장치 클래스를 메모리에서 제거합니다.
  • OnTimer() 함수에 다음 코드를 추가합니다.
if( _camera ) {									// 카메라 장치 클래스가 잡혀있으면
	if( detectionStart ) {							// 카메라 장치 클래스에 영상을 한프레임 저장한 후
		_camShift->Add(gRect.x, gRect.y, gRect.width, gRect.height);	// 카메라 장치의 영상 정보를 가져오고
		detectionStart = false;						// 카메라 장치의 이미지를 가져옵니다.
	}
 
	if( _camera->CaptureImage() ) {
		BITMAPINFO *bi = _camera->GetBitmapInfo();
		const BYTE *image = _camera->GetImage();
 
		IplImage *cvImage = cvCreateImageHeader( cvSize(bi->bmiHeader.biWidth, bi->bmiHeader.biHeight), IPL_DEPTH_8U, bi->bmiHeader.biBitCount/8 );
		cvImage->imageData = (char *)image;
 
		vector<sRecResult> result = _camShift->Recognize( image, cvImage->width, cvImage->height, cvImage->nChannels );	// 색추적을 수행하고
		for( unsigned int i = 0  ;  i < result.size()  ;  i++ ) {
			DrawRecognizedInfo(image, cvImage->width, cvImage->height, cvImage->nChannels, result[i]);	// 수행 된 결과를 영상에 표시합니다.
		}
 
		if( regionSelecting ) {		// 마우스로 초기 영역을 설정할 때는
			cvRectangle(cvImage, cvPoint(gRect.x, gRect.y), cvPoint(gRect.x+gRect.width, gRect.y+gRect.height), CV_RGB(255,255,0));	// 설정하는 초기 영억을 표시합니다.
		}
 
		cvShowImage("CamShiftDetection", cvImage);	// 결과 영상을 모니터에 출력합니다.
		cvReleaseImageHeader(&cvImage);
	}
}
  • DrawRecognizedInfo() 함수에 다음 코드를 추가합니다.
IplImage *cvImage = cvCreateImageHeader( cvSize(width, height), IPL_DEPTH_8U, pixelByte );
cvImage->imageData = (char *)image;
 
CvFont	cvFont;
cvInitFont( &cvFont, CV_FONT_HERSHEY_SIMPLEX, 0.5f, 0.5f, 0, 1, 8 );
 
CvPoint pt1, pt2;
pt1.x = objectInfo.rectangle[0].x;
pt1.y = objectInfo.rectangle[0].y-3;
cvPutText( cvImage, objectInfo.name.c_str(), pt1, &cvFont, CV_RGB(0, 255, 0) );
 
pt1.x =	objectInfo.rectangle[0].x;
pt1.y = objectInfo.rectangle[0].y;
 
for( int j = 1  ;  j < 4  ;  j++ )
{
	pt2.x =	objectInfo.rectangle[j].x;
	pt2.y = objectInfo.rectangle[j].y;
	cvLine( cvImage, pt1, pt2, CV_RGB(0, 255, 0), 5, 8, 0 );
	pt1 = pt2;
}
pt2.x =	objectInfo.rectangle[0].x;
pt2.y = objectInfo.rectangle[0].y;
cvLine( cvImage, pt1, pt2, CV_RGB(0, 255, 0), 5, 8, 0 );
 
cvReleaseImageHeader( &cvImage );

CamShift.h

/// @struct sRecResult
/// @brief 인식된 결과를 저장
struct sRecResult{
	std::string	name;	///< 인식된 이름
	CPoint	rectangle[4];	///< 인식된 영역
	double	x, y, z;	///< 인식된 위치
};
 
/// @brief CamShift 알고리즘을 사용하여 색을 추적한다.
class CCamShift
{
public:
	/// @brief 클래스를 초기화 한다.
	CCamShift ();
	~CCamShift ();
 
	/// @brief CamShift 알고리즘을 적용할 초기 영역을 설정한다.
	void Add( int x,	///< 선택할 영역에서 좌상단 점의 x좌표
		  int y,	///< 선택할 영역에서 좌상단 점의 y좌표
		  int width,	///< 선택할 영역의 너비
		  int height	///< 선택할 영역의 높이
		 );
 
	/// @brief 영상에서 색을 추적한다.
	/// @return 인식된 색 정보가 있는 구조체
	vector<sRecResult> Recognize( const BYTE *img,	///< 인식할 영상의 주소 값
				      int width,	///< 인식할 영상의 가로 해상도
				      int height,	///< 인식할 영상의 세로 해상도
				      int pixelByte	///< 인식할 영상의 픽셀당 바이트 수
				    );
 
private:
	int	_bBeingTracked;		// Flag for traking object.
 
	CvPoint	_cvPtOrigin;
	CvRect	_cvRtSelected;		// Zone Selected.
	CvRect	_cvRtTracked;		// Zone Tracked.
 
	CvBox2D	_cvTrackBox;		// 2D Track Box
	CvConnectedComp _cvTrackComp;	//Track Connected Comp
	CvHistogram* _cvHist;		// Histogram
 
	IplImage* _iplImage;
	IplImage* _iplHsv;
	IplImage* _iplHue;
	IplImage* _iplMask;
	IplImage* _iplBackproject;
	IplImage* _iplHist;
 
	int _iSMin;
	int _iVMin;
	int _iVMax;
 
	float	__hranges_arr[2];
	float*	_hranges;
	int	_hdims;
};

CamShift.cpp

CCamShift::CCamShift ()
:_iSMin(50), _iVMin(10), _iVMax(256)
{
	_bBeingTracked = 0;
 
	_iplImage = NULL;
	_iplHsv = NULL;
	_iplHue = NULL;
	_iplMask = NULL;
	_iplBackproject = NULL;
	_iplHist = NULL;
 
	__hranges_arr[0] = 0;
	__hranges_arr[1] = 180;
	_hranges = __hranges_arr;
	_hdims = 16;
}
 
CCamShift::~CCamShift ()
{
	cvReleaseImage(&_iplImage);
	cvReleaseImage(&_iplHsv);
	cvReleaseImage(&_iplHue);
	cvReleaseImage(&_iplBackproject);
}
 
void CCamShift::Add( int x, int y, int width, int height )
{
	_cvRtSelected.x = x;
	_cvRtSelected.y = y;
	_cvRtSelected.width = width;
	_cvRtSelected.height = height;
	if( x && y && width && height )
		_bBeingTracked = -1;
}
 
 
vector<sRecResult> CCamShift::Recognize(const BYTE *image, int width, int height, int pixelByte )
{
	vector<sRecResult> recResult;
	recResult.clear ();
 
	IplImage *frame= cvCreateImageHeader( cvSize(width, height), IPL_DEPTH_8U, pixelByte );
	frame->imageData = (char *)image;
 
	// _iplImage이 초기화되지 않았을 때  
	if( _iplImage == NULL )
	{
		// _iplImage 초기화 
		_iplImage = cvCreateImage( cvGetSize(frame), 8, pixelByte );
		_iplImage->origin = frame->origin;
 
		// 여러가지 영상 데이터 초기화 
		_iplHsv = cvCreateImage( cvGetSize(frame), 8, 3 );
		_iplHue = cvCreateImage( cvGetSize(frame), 8, 1 );
		_iplMask = cvCreateImage( cvGetSize(frame), 8, 1 );
		_iplBackproject = cvCreateImage( cvGetSize(frame), 8, 1 );
 
		// 히스토그램 초기화 
		_cvHist = cvCreateHist( 1, &_hdims, CV_HIST_ARRAY, &_hranges, 1 );
	}
 
	// 카메라에서 넘겨온 frame을 _iplImage로 복사한 후, HSV 컬러 공간으로 변환한다.
	cvCopy( frame, _iplImage, 0 );
	cvCvtColor( _iplImage, _iplHsv, CV_BGR2HSV );
 
	// 추적이 시작되었다면
	if( _bBeingTracked )
	{
 
		// 주어진 3개( Value 채널의 max, min, Saturation 채널의 min )를 갖고
		// 다시 HSV 컬러 공간내 범위를 줄인다.
		cvInRangeS( _iplHsv, cvScalar(0, _iSMin, MIN(_iVMin, _iVMax),0),
			cvScalar(180,256,MAX(_iVMin,_iVMax),0), _iplMask );
 
		// HSV 컬러 공간을 분할하여, Hue 채널만 가져온다.
		cvSplit( _iplHsv, _iplHue, 0, 0, 0 );
 
		if( _bBeingTracked < 0 )
		{
			float max_val = 0.f;
 
			// 객체 설정한 영역 정보를 갖고, 관심 영역으로 간주하여 추출한다.
			cvSetImageROI( _iplHue, _cvRtSelected );
			cvSetImageROI( _iplMask, _cvRtSelected );
 
			// 관심 영역의 히스토그램을 계산한다.
			cvCalcHist( &_iplHue, _cvHist, 0, _iplMask );
			cvGetMinMaxHistValue( _cvHist, 0, &max_val, 0, 0 );
 
			// 관심 영역의 히스토그램을 재계산한다.
			cvConvertScale( _cvHist->bins, _cvHist->bins, 
							max_val ? 255. / max_val : 0., 0 );
 
			// 관심 영역을 다시 되돌려서 원래의 크기로 반환한다.
			cvResetImageROI( _iplHue );
			cvResetImageROI( _iplMask );
 
			// 추적 대상의 영역 정보를 넘긴다.
			_cvRtTracked = _cvRtSelected;
 
			// Hue 채널 내 영역을 계속 재설정하는 논리적 오류를 방지한다.
			_bBeingTracked = 1;
		}
 
		// Hue 채널의 이미지와 히스토그램 값을 넣고 backproject한 영상을 반환한다.
		cvCalcBackProject( &_iplHue, _iplBackproject, _cvHist );
 
		// backproject한 영상과 AND 연산해서 갱신한다.
		cvAnd( _iplBackproject, _iplMask, _iplBackproject, 0 );
 
		// CAMSHIFT 알고리즘을 이용해 추적한다.
		cvCamShift( _iplBackproject, _cvRtTracked,
					cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
					&_cvTrackComp, &_cvTrackBox );
 
		// 새롭게 추적한 범위를 다시 할당한다.
		_cvRtTracked = _cvTrackComp.rect;
 
		if( _iplImage->origin )
			_cvTrackBox.angle = -_cvTrackBox.angle;
 
		sRecResult color;
		color.rectangle[0].x = _cvTrackComp.rect.x;
		color.rectangle[0].y = _cvTrackComp.rect.y;
		color.rectangle[1].x = _cvTrackComp.rect.x+_cvTrackComp.rect.width;
		color.rectangle[1].y = _cvTrackComp.rect.y;
		color.rectangle[2].x = _cvTrackComp.rect.x+_cvTrackComp.rect.width;
		color.rectangle[2].y = _cvTrackComp.rect.y+_cvTrackComp.rect.height;
		color.rectangle[3].x = _cvTrackComp.rect.x;
		color.rectangle[3].y = _cvTrackComp.rect.y+_cvTrackComp.rect.height;
		recResult.push_back(color);
	}
 
	// 할당한 메모리 해제한다.
	if( _iplImage == NULL )
	{
		cvReleaseHist( &_cvHist );
		cvReleaseImage( &_iplHsv );
		cvReleaseImage( &_iplHue );
		cvReleaseImage( &_iplMask );
		cvReleaseImage( &_iplBackproject );
	}
 
	cvReleaseImageHeader( &frame );
 
	return recResult;
}



[출처]
CamShift (OpenCV)|작성자 린연

'OpenSTUDY > CameraVision' 카테고리의 다른 글

cvFlip  (0) 2011.11.05
CreateVideoWriter  (0) 2011.11.02
CaptureFromAVI  (0) 2011.11.02
Canny Edge  (0) 2011.11.02
openCV 2.1 시작  (0) 2011.10.22

+ Recent posts