2013년 1월 12일 토요일

OpenCV #4-6 Example (히스토그램 비교를 이용한 유사 영상 검색)

 - 내용 기반 영상 검색은 주어진 질의 영상과 유사한 내용을 갖는 영상 집단을 찾는 과정으로 이루어 짐.
 - 히스토그램은 영상의 내용을 특성화하는 효율적인 방법으로 구성되어있다.
 - 즉, 내용 기반 검색 문제를 해결하는 데 사용할수 있다.

 - 핵심은 히스토그램을 간단하게 비교함으로 써 두 영상 간의 유사도를 측정하는 데 있음.
 - 측정 기능은 얼마나 다른지, 또는 얼마나 유사한지를 평가하는 측정 함수에서 두 히스토그램을 정의할 필요가 있다.
 - OpenCV는 cv::compareHist 함수를 구현할 때 몇 가지 제안을 반영했다.

 - 영상 컬렉션에 있는 참조 영상을 비교하고 질의 영상과 상당히 유사한 영상들을 찾기 위해 ImageComparator 클래스를 만듬.
 - 이 클래스에는 질의 영상과 입력 영상에 대한 참조를 비롯해 두 개의 히스토그램(cv::NatND 인스턴스)이 함께 들어있다.
 - 또한 컬러 히스토그램을 사용해 비교를 수행하며, ColorHistogram 클래스를 사용


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

class ColorHistogram {
private:
int histSize[3];
float hranges[2];
    const float* ranges[3];
    int channels[3];
public:
ColorHistogram() {
// 컬러 히스토그램을 위한 인자 준비
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0;    // BRG 범위
hranges[1]= 255.0;
ranges[0]= hranges; // 모든 채널은 같은 범위를 가짐 
ranges[1]= hranges; 
ranges[2]= hranges; 
channels[0]= 0; // 세 가지 채널 
channels[1]= 1; 
channels[2]= 2; 
}

// 히스토그램 계산.
cv::MatND getHistogram(const cv::Mat &image) {

cv::MatND hist;

// BGR 컬러 히스토그램
hranges[0]= 0.0;    // BRG 범위
hranges[1]= 255.0;
channels[0]= 0; // 세 가지 채널
channels[1]= 1; 
channels[2]= 2; 

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

return hist;
}

// 히스토그램 계산.
cv::SparseMat getSparseHistogram(const cv::Mat &image) {

cv::SparseMat hist(3,histSize,CV_32F);

// BGR 컬러 히스토그램
hranges[0]= 0.0;    // BRG 범위
hranges[1]= 255.0;
channels[0]= 0; // 세 가지 채널 
channels[1]= 1; 
channels[2]= 2; 

// Compute histogram
cv::calcHist(&image, 
1, // 단일 영상의 히스토그램만
channels, // 대상 채널
cv::Mat(), // 마스크 사용하지 않음      
hist, // 결과 히스토그램   
3, // 3D 히스토그램          
histSize, // 빈도수                 
ranges // 화소값 범위            
);

return hist;
}

cv::Mat colorReduce(const cv::Mat &image, int div=64) {

 int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
 // 화소값 반올림 하기 위해 사용하는 마스크
 uchar mask= 0xFF<<n; // 예, div=16이면 mask= 0xF0

 cv::Mat_<cv::Vec3b>::const_iterator it= image.begin<cv::Vec3b>();
 cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();

 // 결과 영상 설정(항상 1-채널)
 cv::Mat result(image.rows,image.cols,image.type());
 cv::Mat_<cv::Vec3b>::iterator itr= result.begin<cv::Vec3b>();

 for ( ; it!= itend; ++it, ++itr) {
        
        (*itr)[0]= ((*it)[0]&mask) + div/2;
        (*itr)[1]= ((*it)[1]&mask) + div/2;
        (*itr)[2]= ((*it)[2]&mask) + div/2;
 }

 return result;
}
};

class ImageComparator {
private:
cv::Mat reference; // 원본
cv::Mat input; // 비교할 대상
cv::MatND refH;
cv::MatND inputH;

ColorHistogram hist;
int div;

  public:
ImageComparator() : div(32) { // div=32 초기화
}

// 신뢰할 수 있는 유사도 측정 방법을 얻으려면 컬러 개수를 반드시 감축해야 함
// 클래스에 컬러 감축 인자를 포함하며, 질의와 입력 영상에 모두 적용
void setColorReduction( int factor) {
// 컬러 감축 인자
// 각 차원별 인자에 의해 컬러 공간 감축 시킨 영상 비교
div= factor;
}

int getColorReduction() {
return div;
}

void setReferenceImage(const cv::Mat& image) {
// 질의 영상도 적절한 세터를 사용해 지정, 질의 영상에 대해 컬러를 감축
reference= hist.colorReduce(image,div);
refH= hist.getHistogram(reference);
}

double compare(const cv::Mat& image) {
// compare 메소드는 지정한 입력 영상과 참조 영상을 비교
// 두 영상이 얼마나 유사한지 알려주는 점수 반환
input= hist.colorReduce(image,div);
inputH= hist.getHistogram(input);

return cv::compareHist(refH,inputH,CV_COMP_INTERSECT);
}
};

int main()
{

cv::Mat image= cv::imread("batman.jpg"); // 참조 영상 읽기
cv::namedWindow("Bat man");
cv::imshow("Bat man", image); // 영상 띄워 보기

ImageComparator c;
c.setReferenceImage(image);
// 이 클래스는 주어진 질의 영상과 유사한 영상을 검사하는 데 사용

cv::Mat input= cv::imread("batgirl.jpg");
cv::namedWindow("Bat girl");
cv::imshow("Bat girl", input); // 영상 띄워 보기

std::cout << "Bat man vs Bat girl: " << c.compare(input) << std::endl;
// 참조 영상과 비교

cv::waitKey(0);

return 0;
}


  • Result

  • 예제 분석

 - 대부분 히스토그램 비교 측정은 빈도 간의 비교를 기반으로 함.
 - 히스토그램의 빈도를 비교할 때 이웃한 빈도를 사용하지 않는다.
 - 따라서 두 컬러 히스토그램의 유사도를 측정하기 전에 컬러 공간을 감축하는 것이 중요.

 - cv::compareHist를 호출하는 방법은 쉽다.
 - 두 히스토그램을 바로 입력하고 이 함수에서 측정된 거리를 반환한다.
 - 플래그를 사용하길 원하는 측정 방법을 지정한다.
 - ImageComparator 클래스에서 인터섹션 방법을 사용(CV_Comp_INTERSECT 플래그를 함께 사용)한다.
 - 이 방법은 간단하게 각 빈도, 즉 각 히스토그램 내의 두 개 값을 비교하고, 그 중 최솟값을 유지, 유사도 측정은 최소값의 합으로 간단해진다.
 - 이런 경우 모두 컬러가 전혀 없되 화소의 총 개수가 동일한 두 히스토그램을 갖는 두 영상의 인터섹션 값은 0이다.

 - 사용할 수 있는 다른 방법은 카이 제곱(CV_COMP_CHISQR 플래그)이며, 빈도 간 정규 제곱 차이의 합으로 구한다.
 - 카이 제곱 공식
 - 상관관계 방법(CV_COMP_CORREL 플래스)은 신호 처리에서 두 신호 간의 유사도를 측정하기 위해 사용하는 정규 상관관계수 연산자.
 - 바타챠르야 측정(CV_COMP_BHATTACHARYYA 플래스)은 두 확률 분포 간의 유사도를 측정하기 위한 통계로 사용.

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