1 图像直方图
1.1 定义
统计各个像素值,在整幅图像中出现次数的一个分布函数。
1.2 标准化
$quad p_r(r_k) = frac{n_k}{MN} qquad k = 0, 1, 2, ..., L -1 $
$r_{k}$ - 第 k 个像素灰度值; $n_{k}$ - 像素灰度值为 rk 的像素数目;
MN - 图像中总的像素个数; [0, L-1] - 像素灰度值的范围
1.3 直方图均衡化
1.3.1 定义
直方图均衡化,是将给定图像的直方图改造成均匀分布的直方图,从而扩大像素灰度值的动态范围,达到增强图像对比度的效果。
$quad s_k = frac{(L - 1)}{MN} sumlimits_{j=0}^k n_j qquad k = 0, 1, 2, ..., L - 1 $
1.3.2 实例
一幅灰度值范围是[0, 7],64行64列的数字图像,其灰度分布如下表所示,求直方图均衡化之后的灰度分布。
r(k) | n(k) | P(rk) |
r(0) = 0 | 790 | 0.19 |
r(1) = 1 | 1023 | 0.25 |
r(2) = 2 | 850 | 0.21 |
r(3) = 3 | 656 | 0.16 |
r(4) = 4 | 329 | 0.08 |
r(5) = 5 | 245 | 0.06 |
r(6) = 6 | 122 | 0.03 |
r(7) = 7 | 81 | 0.02 |
根据上述公式得, s(0)=1.33≈1,s(1)=3.08≈3,s(2)≈5,s(3)≈6,s(4)≈6,s(5)≈7,s(6)≈7,s(7)≈7
因为 r(k) -> s(k),所以 s(0)=1 对应有790个像素值。因为r(3), r(4) 分别对应 s(3), s(4),且 s(3)=s(4)=6,
故像素值为6的像素数为 (656+329)个,同理可计算像素值为7的像素数。
将不同像素值对应的的像素数除以MN(图像的像素总数),便得到均衡化之后的灰度直方图,如下所示:
2 四个参数
H1 和 H2 为两个待比较的直方图。1) 和 2) 的值越大,二者越匹配;而 3) 和 4) 的值越小,两者越匹配。
1) Correlation
2) Intersection
3) Chi-square
4) Bhattacharyya distance
3 OpenCV中的函数
3.1 equalizeHist
void equalizeHist (
InputArray src, // 输入图像
OutputArray dst // 输出图像
);
源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void cv::equalizeHist( InputArray _src, OutputArray _dst ) { CV_Assert( _src.type() == CV_8UC1 ); if (_src.empty()) return; CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), ocl_equalizeHist(_src, _dst)) Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); Mutex histogramLockInstance; const int hist_sz = EqualizeHistCalcHist_Invoker::HIST_SZ; int hist[hist_sz] = {0,}; int lut[hist_sz]; EqualizeHistCalcHist_Invoker calcBody(src, hist, &histogramLockInstance); EqualizeHistLut_Invoker lutBody(src, dst, lut); cv::Range heightRange(0, src.rows); if(EqualizeHistCalcHist_Invoker::isWorthParallel(src)) parallel_for_(heightRange, calcBody); else calcBody(heightRange); int i = 0; while (!hist[i]) ++i; int total = (int)src.total(); if (hist[i] == total) { dst.setTo(i); return; } float scale = (hist_sz - 1.f)/(total - hist[i]); int sum = 0; for (lut[i++] = 0; i < hist_sz; ++i) { sum += hist[i]; lut[i] = saturate_cast<uchar>(sum * scale); } if(EqualizeHistLut_Invoker::isWorthParallel(src)) parallel_for_(heightRange, lutBody); else lutBody(heightRange); }
3.2 calcHist
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void cv::calcHist( const Mat * images, int nimages, const int * channels, InputArray mask, OutputArray hist, int dims, const int * histSize, const float ** ranges, bool uniform = true, bool accumulate = false )
3.3 compareHist
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
double cv::compareHist ( InputArray H1, InputArray H2, int method )
4 实例
4.1 直方图计算
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgcodecs/imgcodecs.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; int main( int, char** argv ) { Mat src, dst; // 1) Load image src = imread("left.png"); if(src.empty()) { return -1; } // 2) Separate the image in 3 places ( B, G and R ) std::vector<Mat> bgr_planes; split( src, bgr_planes ); // 3) Establish the number of bins int histSize = 256; // 4) Set the ranges (for B,G,R) float range[] = { 0, 256 } ; const float* histRange = { range }; bool uniform = true; bool accumulate = false; Mat b_hist, g_hist, r_hist; // 5) Compute the histograms calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate ); calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate ); calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate ); // 6) Draw the histograms for B, G and R int hist_w = 512; int hist_h = 400; int bin_w = cvRound( (double) hist_w/histSize ); Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) ); // 7) Normalize the result to [ 0, histImage.rows ] normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); // 8) Draw for each channel for( int i = 1; i < histSize; i++ ) { line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ), Scalar( 255, 0, 0), 2, 8, 0 ); line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ), Scalar( 0, 255, 0), 2, 8, 0 ); line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ), Scalar( 0, 0, 255), 2, 8, 0 ); } // 9) Display imshow("calcHist Demo", histImage ); waitKey(0); }
4.2 直方图均衡化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "opencv2/imgcodecs.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> using namespace cv; using namespace std; int main( int, char** argv ) { Mat src, dst; const char* source_window = "Source image"; const char* equalized_window = "Equalized Image"; // Load image src = imread( argv[1], 1 ); if( src.empty() ) { cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl; return -1; } // Convert to grayscale cvtColor( src, src, COLOR_BGR2GRAY ); // Apply Histogram Equalization equalizeHist( src, dst ); // Display results namedWindow( source_window, WINDOW_AUTOSIZE ); namedWindow( equalized_window, WINDOW_AUTOSIZE ); imshow( source_window, src ); imshow( equalized_window, dst ); // Wait until user exits the program waitKey(0); return 0; }
4.3 直方图比较
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "opencv2/imgcodecs.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> using namespace std; using namespace cv; /** * @function main */ int main( int argc, char** argv ) { Mat src_base, hsv_base; Mat src_test1, hsv_test1; Mat src_test2, hsv_test2; Mat hsv_half_down; /// Load three images with different environment settings if( argc < 4 ) { printf("** Error. Usage: ./compareHist_Demo <image_settings0> <image_setting1> <image_settings2> "); return -1; } src_base = imread( argv[1], 1 ); src_test1 = imread( argv[2], 1 ); src_test2 = imread( argv[3], 1 ); /// Convert to HSV cvtColor( src_base, hsv_base, COLOR_BGR2HSV ); cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV ); cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV ); hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); /// Using 50 bins for hue and 60 for saturation int h_bins = 50; int s_bins = 60; int histSize[] = { h_bins, s_bins }; // hue varies from 0 to 179, saturation from 0 to 255 float h_ranges[] = { 0, 180 }; float s_ranges[] = { 0, 256 }; const float* ranges[] = { h_ranges, s_ranges }; // Use the o-th and 1-st channels int channels[] = { 0, 1 }; /// Histograms MatND hist_base; MatND hist_half_down; MatND hist_test1; MatND hist_test2; /// Calculate the histograms for the HSV images calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false ); normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false ); normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false ); normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); /// Apply the histogram comparison methods for( int i = 0; i < 4; i++ ) { int compare_method = i; double base_base = compareHist( hist_base, hist_base, compare_method ); double base_half = compareHist( hist_base, hist_half_down, compare_method ); double base_test1 = compareHist( hist_base, hist_test1, compare_method ); double base_test2 = compareHist( hist_base, hist_test2, compare_method ); printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f ", i, base_base, base_half , base_test1, base_test2 ); } printf( "Done " ); return 0; }
参考资料
<Digital Image Processing> 3rd
OpenCV Tutorials / Image Processing (imgproc module) / Histogram Calculation