'meanshift'에 해당되는 글 1건

  1. 2011.09.14 OpenCV 2.3 Computer vision (4) 4
Computer Vision2011. 9. 14. 13:33
Using the mean shift algorithm to find an object

 Histogram projection으로 원하는 위치의 Histogram을 바탕으로 움직이는 물체를 추적할 수 있습니다. 이에 대한 대표적인 알고리듬이 Mean Shift Algorithm입니다.  이 절에서는 이것을 이용해서 실제 움직이는 물체를 추적하는 것을 해보겠습니다.

별도의 복잡한 알고리듬을 계속 사용하는 것이 아니라서 추적에 약간의 버그가 있음을 미리 알려드립니다.
- 책에 있는 내용에서 버그를 약간 수정한 버전입니다.


QT에서 QT Console로 프로젝트를 만듭니다. 
- 반드시 Qt일 필요는 없지만, 따로 빌드 스크립트 만드는게 귀찮아서 여기서합니다.
- 까짓거 속도가 좀느려도 어떠냐 라는 생각도 있습니다. ㅋㅋ


코드는 아래 것을 보시기 바라고 코드를 그대로 읽어보면 아래와 같습니다.

STEP 1 일단 이미지를 읽어들여서 관심 영역을 설정합니다.
 여기서는 움직이는 차량을 대상으로 합니다.

안타깝게도, 관심 영역은 자동으로 하지 못하므로 일일이 손으로 지정해야 하는 단점이 있습니다.
GIMP에서 픽셀단위로 위치를 확인합니다.


    // Set ROI
    cv::Mat imageROI = image(cv::Rect(127, 170 , 120 , 80));
    cv::rectangle(image,cv::Rect(127, 170 , 120 , 80),cv::Scalar(0,0,255));

그리고 화면에 보이기 위해서 사각형을 하나 그립니다.

그것이 이 아래의 그림입니다.



Step 2 : Hue의 Saturation을 구합니다. 

코드는 아래와 같습니다.

    // Get HUE histogram
    ColorHistogram hc;
    int minSat = 65;

    //cv::MatND colorhist = hc.getHueHistogram( imageROI ,  minSat );
    cv::MatND colorhist = hc.getHueHistogram( imageROI );


실세로는 Class로 선언된 ColorHistogram class에서 GetHueHistogram을 호출하여 가지고 옵니다.
그리고 그 대상은 관심 영역을 대상으로 합니다.

해당 함수는 아래와 같습니다.

	// Computes the 1D Hue histogram with a mask.
	// BGR source image is converted to HSV
	cv::MatND getHueHistogram(const cv::Mat &image) {

		cv::MatND hist;

		// Convert to Lab color space
		cv::Mat hue;
		cv::cvtColor(image, hue, CV_BGR2HSV);

		// Prepare arguments for a 1D hue histogram
		hranges[0]= 0.0;
		hranges[1]= 180.0;
		channels[0]= 0; // the hue channel 

		// Compute histogram
		cv::calcHist(&hue, 
			1,			// histogram of 1 image only
			channels,	// the channel used
			cv::Mat(),	// no mask is used
			hist,		// the resulting histogram
			1,			// it is a 1D histogram
			histSize,	// number of bins
			ranges		// pixel value range
		);

		return hist;
	}

이미지를 RGB에서 HSV로 바꾼뒤에 이중에서 Hue에 대한 것만 찾아서 Histogram을 만들어갑니다. 
Histogram을 만드는 함수는 다시 OpenCV내에 함수를 사용했습니다.
여하튼 이렇게 해서 Hue에 대한 Histogram을 얻어냅니다.

Step 3  ObjectFinder에서 finder를 선언합니다.
그리고 Search에 필요한 히스토그램 데이터를 설정합니다.
코드는 아래와 같습니다.

    // Object Finder
    ObjectFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);


setHistogram은 아래와 같은 함수입니다.

	// Sets the reference histogram
	void setHistogram(const cv::MatND& h) {

		isSparse= false;
		histogram= h;
		cv::normalize(histogram,histogram,1.0);
	}

주어진 histogram데이터를 0~1사이에서  normalize시켜서 보관한다.

finder.setThreshold(0.2f);

은 그냥 내부 변수중 Threshld값을 0.2f로 설정해준다. 이 값은 나중에 연산할 때 사용한다.

Step 4 : 이미지를 HSV로 변경한 후에 이미지를 분할한다. 

    //convert to HSV space
    cv::cvtColor(image, hsv, CV_BGR2HSV);

    //split image
    vector<cv::Mat> v;
    cv::split(hsv,v);

HSV의 image를 다시 각각의 컬러 값들을 V plane으로 분리시켜둡니다.
이는 H값만을 대상으로 작업하기 편하게 만든 것입니다.
v[1]이 H값입니다.

Step 5 : Threshold를 찾아서 마스킹 시킵니다.

    // Identify pixel with low saturation
    cv::threshold(v[1],v[1], minSat, 255,cv::THRESH_BINARY);

이렇게 해서 얻은 이미지는 아래와 같다.



Step 6 HUE에 대한 Backproject을 얻습니다.

    // Let's obtain backrojection of the hue channel
    // Get back-projection of hue histogram
    int ch[1]={0};
    cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
    cv::namedWindow("Result Hue");
    cv::imshow("Result Hue",result);

 

결과 이미지는 아래와 같다.


- 약간 뭔가 이상한 그림이긴 하지만, 하여튼 결과는 위와 같다.

그리고 Low와 Saturation시킨 그림을 마스킹 시켜서 이미지를 다시 얻는다.

    // Eliminate low saturation pixels
    cv::bitwise_and(result,v[1],result);
    cv::namedWindow("Result Hue and");
    cv::imshow("Result Hue and",result);

 이미지는 아래와 같다.

앞서의 이미지보다는 약간 정돈된 느낌을 주지만, 뭐...



Step 8 : 두번째 이미지를 읽어들여서 HSV로 바꾼뒤에 다시 H 값을 얻습니다.
이 코드는 앞서 설명한 코드와 동일한 부분이므로,  그냥 Pass
그리고 나머지 부분도 그대로 앞서와 동일한 코드이므로 그대로 진행합니다.


Step 9 :  마지막으로 MeanShift를 수행합니다.

     cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
     cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;

이렇게 얻은 사각형을 이미지에 그리고 화면에 출력합니다.
이렇게 얻어낸 이미지는 이 글의 마지막에 있습니다.



완성된 코드는 아래와 같습니다.

#include <iostream>
#include <vector>
using namespace std;
 

#include <QtCore/QCoreApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/contrib/contrib.hpp>

#include "colorhistogram.h"
#include "objectFinder.h"
 



int main(int argc, char *argv[])
{
    cv::Mat hsv;

    QCoreApplication a(argc, argv);

    // Read Data
    cv::Mat image = cv::imread( "./0172.jpg" );
    if (!image.data)
            return 0;
 

    // Set ROI
    cv::Mat imageROI = image(cv::Rect(127, 170 , 120 , 80));
    cv::rectangle(image,cv::Rect(127, 170 , 120 , 80),cv::Scalar(0,0,255));
 

    //Display Origitanl Image
    cv::namedWindow("Image 1 :: Orignal");
    cv::imshow("Image 1 :: Orignal",image);
 


    // Get HUE histogram
    ColorHistogram hc;
    int minSat = 65;
 

    //cv::MatND colorhist = hc.getHueHistogram( imageROI ,  minSat );
    cv::MatND colorhist = hc.getHueHistogram( imageROI );
 


    // Object Finder
    ObjectFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);
 


    //convert to HSV space
    cv::cvtColor(image, hsv, CV_BGR2HSV);
 

    //split image
    vector<cv::Mat> v;
    cv::split(hsv,v);
 

    // Identify pixel with low saturation
    cv::threshold(v[1],v[1], minSat, 255,cv::THRESH_BINARY);
    cv::namedWindow("Saturation");
    cv::imshow("Saturation",v[1]);
 

    // Let's obtain backrojection of the hue channel
    // Get back-projection of hue histogram
    int ch[1]={0};
    cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
    cv::namedWindow("Result Hue");
    cv::imshow("Result Hue",result);
 

    // Eliminate low saturation pixels
    cv::bitwise_and(result,v[1],result);
    cv::namedWindow("Result Hue and");
    cv::imshow("Result Hue and",result);
 


    // load second image
    image = cv::imread("./0181.jpg");

    // Display image
     cv::namedWindow("Image 2");
     cv::imshow("Image 2",image);
 

     // Convert to HSV space
     cv::cvtColor(image, hsv, CV_BGR2HSV);

     // Split the image
     cv::split(hsv,v);

     // Eliminate pixels with low saturation
     cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
     cv::namedWindow("Saturation");
     cv::imshow("Saturation",v[1]);
 

     // Get back-projection of hue histogram
     result= finder.find(hsv,0.0f,180.0f,ch,1);

     cv::namedWindow("Result Hue");
     cv::imshow("Result Hue",result);
 

     // Eliminate low stauration pixels
     cv::bitwise_and(result,v[1],result);
     cv::namedWindow("Result Hue and");
     cv::imshow("Result Hue and",result);
 

     // Get back-projection of hue histogram
     finder.setThreshold(-1.0f);
     result= finder.find(hsv,0.0f,180.0f,ch,1);
     cv::bitwise_and(result,v[1],result);
     cv::namedWindow("Result Hue and raw");
     cv::imshow("Result Hue and raw",result);
 

     cv::Rect rect(127, 170 , 120 , 80);
     cv::rectangle(image, rect, cv::Scalar(0,0,255));

     cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
     cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;

     cv::rectangle(image, rect, cv::Scalar(0,255,0));
 

    // Display Image
    cv::namedWindow("Image Result");
    cv::imshow("Image Result",image);
 

    cv::waitKey();
    return 0;


}


트래킹된 결과는 아래와 같습니다.


 위의 두개의 이미지를 가지고 자동차의 움직임을 추적하는 것입니다.




'Computer Vision' 카테고리의 다른 글

OpenCV를 이용한 Face Detection  (0) 2013.05.19
3D Noise Reduction algorithm test  (0) 2011.10.05
OpenCV 2.3 Computer vision (3)  (0) 2011.09.06
OpenCV 2.3 Computer vision (2)  (0) 2011.09.05
OpenCV 2.3 Computer vision (1)  (2) 2011.09.05
Posted by GUNDAM_IM