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 |