2013년 1월 7일 월요일

OpenCV #4-3 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 equalize(const cv::Mat &image) {
// 원 영상을 평활화

cv::Mat result;
cv::equalizeHist(image,result);

return result;
}
};

int main()
{
cv::Mat image = cv::imread("batgirl.jpg", 0);

if(!image.data)
return 0;

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

Histogram1D h; // 히스토그램 객체

cv::Mat eq= h.equalize(image); // 영상 평활화

cv::namedWindow("Equalized Image"); // 결과 보기
cv::imshow("Equalized Image",eq);

cv::namedWindow("Equalized Histogram"); // 새로운 히스토그램 보기
cv::imshow("Equalized Histogram",h.getHistogramImage(eq));

cv::waitKey(0);

return 0;
}


  • Result

 - 완벽한 균등 히스토그램의 모든 빈도는 화소 개수와 모두 같다.
 - 화소의 50%는 128보다 작은 명암도를 갖고, 25%는 64보다 작은 명암도를 갖는다.
 - 균등 히스토그램에서 화소의 p%에 속한 명암도 값을 갖되 255*p%보다 적거나 같아야 한다는 게 히스토그램을 평활화 하는 데 사용하는 규칙.
 - 명암도 i는 i보다 적은 명암도 값을 갖는 화소의 %에 대응하는 명암도로 매핑.
 - 즉, 다음 공식을 적용해 구축된 룩업 테이블 필요.

 - lookup.at<uchar>(i) = static_cast<uchar>(255.0*p[i]);
 - p[i]는 i보다 적거나 같은 명암도를 갖는 화소 개수.
 - p[i]는 종종 누적 히스토그램으로 불림.
 - 즉, 지정된 명암도와 적거나 같은 화소 개수를 포함하는 히스토그램으로, 해당 화소값을 갖는 화소 개수를 대신함.

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