2013년 1월 8일 화요일

OpenCV #4-4 Example (영상 내 특정 내용을 감지하기 위한 히스토그램 역투영)

 - 특정 질감이나 특정 객체가 있는 영상 영역을 살펴보면 이 영역의 히스토그램은 특정 질감이나 특정 객체에 있는 화소가 속하는 분포를 제공함을 알 수 있다.
 - 영상 내의 특정 내용을 감지하는 데 유리하게 사용할 수 있는 방법을 알아본다.


  • Example
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

// 멤버 변수 정의

class Histogram1D {
private:
int histSize[1]; // 빈도수
float hranges[2]; // 최소/최대 화소값
const float* ranges[1];
int channels[1]; // 여기서 1채널만 사용
public :
Histogram1D() { // 1차원 히스토그램을 위한 인자 준비
histSize[0]= 256;
hranges[0]= 0.0;
hranges[1]= 255.0;
ranges[0]= hranges; 
channels[0]= 0; // 기본적으로 채널을 0으로 보기
 }

// 정의한 멤버 변수로 그레이레벨 영상의 히스토그램을 계산할 때는 다음 메소드를 사용해 수행

cv::MatND getHistogram(const cv::Mat &image) { 
// 1차원(1D) 히스토그램 계산.
cv::MatND hist;
cv::calcHist(&image, // 히스토그램 계산 
1,   // 단일 영상의 히스토그램만
channels, // 대상 채널               
cv::Mat(), // 마스크 사용하지 않음     
hist,  // 결과 히스토그램         
1,   // 1차원(1D) 히스토그램           
histSize, // 빈도수                  
ranges  // 화소값 범위             
);
return hist;
}

// 값의 순서만으로 의미를 파악하기 어려우므로 바 그래프를 사용

// 그래프를 생성하는 메소드
cv::Mat getHistogramImage(const cv::Mat &image){ 
// 1차원(1D) 히스토그램을 계산하고, 영상으로 반환
cv::MatND hist= getHistogram(image); // 먼저 히스토그램 계산

double maxVal=0; // 최대 빈도수 가져오기
double minVal=0; // 최소 빈도수 가져오기

cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);


cv::Mat histImg(histSize[0], histSize[0], CV_8U,cv::Scalar(255));

// 히스토그램을 출력하기 위한 영상

int hpt = static_cast<int>(0.9*histSize[0]);

// nbins의 90%를 최대점으로 설정

for(int h=0; h<histSize[0]; h++){ // 각 빈도에 대한 수직선을 그리기 
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal*hpt/maxVal);
cv::line(histImg,cv::Point(h,histSize[0]),cv::Point(h,histSize[0]-intensity),cv::Scalar::all(0));
// 두 점 간의 거리를 그리는 함수
}
return histImg;
}

cv::Mat applyCalcBackProject(const cv::Mat& image, const cv::Mat& imageROI){

cv::Mat result;
cv::Mat histogram = getHistogram(imageROI);

cv::normalize(histogram, histogram, 1.0);

// 히스토그램 정규화
// 정의한 영역에 속하는 명암도 값의 화소 확률을 제공하는 기능을 얻음

// 영투영 후의 히스토그램은 정규화를 거친 히스토그램에서 

// 읽은 확률 값을 입력 영상 내의 각 화소값으로 대치한 것으로 구성
cv::calcBackProject(&image,
                      1,            // 단일 영상
                      channels,     // 영상 채널에 속하는 히스토그램 차원인 벡터 지정
                      histogram,    // 히스토그램 사용
                      result,       // 역투영 영상 결과
                      ranges,       // 각 차원에 대한 값 범위
                      255.0         // 히스토그램을 1을 255로 매핑하기 위해 선택한 스케일링 인자
  );

return result;
}
};

int main()

{
cv::Mat image = cv::imread("cloud.jpg", 0);

if(!image.data)

return 0;

cv::namedWindow("Image");

cv::imshow("Image", image);

cv::Mat imageROI = image(cv::Rect(400, 40, 100, 100)); // 구름 영역

// 관심 영역을 얻는다.

cv::namedWindow("Reference"); // 참조 패치 띄워 보기

cv::imshow("Reference",imageROI);

Histogram1D h;

cv::MatND hist = h.getHistogram(imageROI);
cv::namedWindow("Reference Hist"); // 참조 히스토그램 찾기
cv::imshow("Reference Hist",h.getHistogramImage(imageROI));

cv::Mat result = h.applyCalcBackProject(image, imageROI);

// 히스토그램 정규화를 통해 정의한 영역에 속하는 명암도 값의 화소 확률을 제공하는 기능을 얻음
// 영투영 후의 히스토그램은 정규화를 거친 히스토그램에서 읽은 확률 값을 입력 영상 내의 각 화소값으로 대치한 것으로 구성
cv::namedWindow("Back Project Result");
cv::imshow("Back Project Result", result);
// 참조 영역에 속하는 밝은(낮은 확률) 부분부터 어두운(높은 확률) 부분까지의 확률이 함께 들어있음

cv::threshold(result, result, 35, 255, cv::THRESH_BINARY);

// 영상에 경계값을 적용하면 가장 가능성 있는 '구름' 화소를 얻음
cv::namedWindow("Decection Result");
cv::imshow("Decection Result", result);

cv::waitKey(0);


return 0;

}


  • Result


  • 예제 분석
 - 잘못 감지된 영역을 구름에 추가하면 결과가 다음과 같을 수 있다.
 - 간단한 그레이레벨 히스토그램에서 추출한 확률 함수를 이해함이 중요.
 - 영상 내 다른 많은 화소가 구름 화소와 같은 명암도를 공유하고 있고, 히스토그램을 역투영할 때 같은 확률 값을 동일한 명암도의 화소로 바꿨기 때문.
 - 해결책 : 컬러 정보를 사용해 결과를 감지해 개선하는 것. (cv::calcBackProject를 호출하는 부분을 수정할 필요가 있음)

 - cv::calcBackProject 함수는 cv::calcHist 함수와 유사.

 - 첫 번째 파라미터는 입력 영상을 지정.
 - 그 후 사용하길 원하는 채널 번호를 나열해야 함.
 - 히스토그램을 입력 파라미터로 함수에게 넘김.
 - 그 히스토그램은 정규화를 거치고, 차원은 채널 목록 배열 중 하나여야 하고, 마찬가지로 ranges 파라미터 중 하나여야 함.
 - 그 다음은 cv::calcHist와 마찬가지로 부동소수점 배열의 배열이되 채널별로 지정한 각 범위(최소와 최대값)다.
 - 출력 결과는 계산된 확률 맵인 영상.
 - 각 화소는 히스토그램에서 대응하는 빈도 위치에 있는 값으로 대치한 이후로, 결과 영상은 0.0과 1.0 (정규화된 히스토그램으로서 입력으로 제공했다고 가정) 사이의 값을 갖음.
 - 마지막 파라미터는 선택적으로 지정한 인자와 그 값을 곱해 다시 스케일링 함.


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook