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 |