2013년 1월 6일 일요일

OpenCV #4-1 Example (히스토그램 계산 - 부연 설명) - 보류

 - Histogram1D 클래스는 1차원 히스토그램에 국한된 cv::calcHist 함수를 단순화 했고, 그레이레벨 영상에만 유용했음.
 - 유사하게 BGR 영상의 히스토그램을 계산하기 위한 클래스를 정의할 수 있음.


  • 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]; // 3채널 사용
public :
ColorHistogram() { // 컬러 히스토그램을 위한 인자 준비
// 3차원 히스토그램 (각 차원의 범위 설정해 줌)
// BGR 영상의 경우 세가지 채널은 같은 [0, 255] 범위를 갖음.
histSize[0] = histSize[1] = histSize[2] = 256;
hranges[0]= 0.0; // BGR 범위
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;

hranges[0] = 0.0; // BGR 범위
hranges[1] = 255.0;
channels[0] = 0; // 세 가지 채널
channels[1] = 1;
channels[1] = 2;

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

return hist;
} // 3차원 cv::Mat 인스턴스를 반환.
// (256)x3 요소를 갖으며, 1,600만 개 이상의 항목을 나타냄.

// 큰 히스토그램을 위해 컬러개수를 감추갛는 것이 좋음.
// 큰 희소행렬을 표한하기 위해 cv::SparseMat 데이터 구조 사용 가능
// calcHist 함수는 하나의 행렬을 반환하는 버전이 있음.
// 즉, cv::SparseMatrix를 사용하기 위한 이전 메소드를 수정하는 것이 간단.
/*
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; 

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;
}

// 값의 순서만으로 의미를 파악하기 어려우므로 바 그래프를 사용
// 그래프를 생성하는 메소드
/*
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;
}
*/
};

int main()
{
ColorHistogram hc; // 히스토그램 객체
cv::Mat color = cv::imread("batgirl.jpg");
color = hc.colorReduce(color, 32);
cv::namedWindow("Color Image");
cv::imshow("Color Image", color);

cv::MatND shist= hc.getHistogram(color);
// cv::SparseMat shist= hc.getSparseHistogram(color);
// OpenCV2.1에서 SparseMat이 있는 히스토그램을 수행하지 않음. 

/*
cv::MatND histo = h.getHistogram(image); // 히스토그램 계산

for(int i=0 ; i<256 ; i++) // 각 빈도 조회
std::cout << "Value " << i << " = " << histo.at<float>(i) << std::endl;

cv::namedWindow("Histogram");
cv::imshow("Histogram", h.getHistogramImage(image));
// 히스토그램을 영상으로 띄우기
// 가운데를 중심으로 왼쪽이 검정색, 오른쪽이 흰색값
// 가운데 봉우리 부분은 중간 명암도 값
// 왼쪽이 영상의 전경, 오른쪽이 배경

// 영상을 두 그룹으로 나누는 부분을 경계값으로 처리해 확인
cv::Mat thresholded; // 경계값으로 이진 영상 생성
cv::threshold(image,thresholded,60,255,cv::THRESH_BINARY);
// 영상을 경계화 하기 위해 히스토그램의 
// 높은 봉우리(명암값 60) 방향으로 증가하기 직전인 최소값으로 정함.
cv::namedWindow("Binary Image"); // 경계화된 영상 띄워 보기
cv::imshow("Binary Image",thresholded); // 배경과 전경이 분할됨
*/

cv::waitKey(0);

return 0;
}

보류...



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