一、直方图均衡化
直方图均衡化是灰度变换的一个重要应用,广泛应用在图像增强处理中,它是以累计分布函数变换为基础的直方图修正法,可以产生一幅灰度级分布具有均匀概率密度的图像,扩展了像素的取值动态范围。许多图像的灰度值是非均匀分布的,其中灰度值集中在一个小区间内的图像是很常见的,直方图均衡化是一种通过重新均匀地分布各灰度值来增强图像对比度的方法,经过直方图均衡化的图像对二值化阈值选取十分有利。一般来说,直方图修正能提高图像的主观质量,因此在处理艺术图像时非常有用。直方图均衡化处理的中心思想是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。
opencv中的调用就是下面这个函数,很方便
<span style="font-size:18px;">void equalizeHist(InputArray src,OutputArraydst );</span>
(1)通常用来增加许多图像的全局对比度,尤其是当图像的有用数据的对比度相当接近的时候。
(2)亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。
(3)对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。
(4)一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。
(5)一个缺点是它对处理的数据不加选择,它可能会增加背景噪声的对比度并且降低有用信号的对比度。
单独使用直方图均衡化得到图像
<span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; using namespace std; int main( int argc, const char** argv ) { Mat img = imread("lena.jpg", CV_LOAD_IMAGE_COLOR); //open and read the image if (img.empty()) { cout << "Image cannot be loaded..!!" << endl; return -1; } cvtColor(img, img, CV_BGR2GRAY); //change the color image to grayscale image Mat img_hist_equalized; equalizeHist(img, img_hist_equalized); //equalize the histogram //create windows namedWindow("Original Image", CV_WINDOW_AUTOSIZE); namedWindow("Histogram Equalized", CV_WINDOW_AUTOSIZE); //show the image imshow("Original Image", img); imshow("Histogram Equalized", img_hist_equalized); waitKey(0); //wait for key press destroyAllWindows(); //destroy all open windows return 0; }</span>
因为还是直方图均衡化,所以会用到直方图,如果单独使用直方图均衡化的话,就只有一个函数,所以这里可以处理图像后显示直方图和均衡化图像
<span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> #include<opencv2\core\core.hpp> using namespace cv; using namespace std; int img_Hist(Mat& image) { if(!image.data) { cout << "fail to load image" << endl; return 0; } Mat img_gray; //GRAY if(image.channels()==3) { cvtColor(image, img_gray, CV_BGR2GRAY); } else { image.copyTo(img_gray); } cv::imwrite("img_gray.jpg",img_gray); MatND hist; int dims = 1; float hranges[] = {0, 255}; const float *ranges[] = {hranges}; // 这里需要为const类型 int size = 256; int channels = 0; // 计算图像的直方图 calcHist(&img_gray, 1, &channels, Mat(), hist, dims, &size, ranges); // cv 中是cvCalcHist int scale = 1; Mat imageShow(size * scale, size, CV_8U, Scalar(0)); // 获取最大值和最小值 double minVal = 0; double maxVal = 0; minMaxLoc(hist,&minVal, &maxVal, 0, 0); // cv中用的是cvGetMinMaxHistValue //显示直方图的图像 int hpt = saturate_cast<int>(0.9 * size); for(int i = 0; i < 256; i++) { float value = hist.at<float>(i); // 注意hist中是float类型 cv中用cvQueryHistValue_1D int realValue = saturate_cast<int>(value * hpt/maxVal); rectangle(imageShow,Point(i*scale, size - 1), Point((i+1)*scale - 1, size - realValue), Scalar(255)); } namedWindow("Hist"); imshow("Hist", imageShow); cv::imwrite("hist.jpg",imageShow); Mat equalize_Hist; cv::equalizeHist(img_gray,equalize_Hist); namedWindow("equalize_Hist"); imshow("equalize_Hist", equalize_Hist); cv::imwrite("equalize_Hist.jpg",equalize_Hist); // 计算图像的直方图 calcHist(&equalize_Hist, 1, &channels, Mat(), hist, dims, &size, ranges); // cv 中是cvCalcHist Mat imageShow_equal(size * scale, size, CV_8U, Scalar(0)); // 获取最大值和最小值 minMaxLoc(hist,&minVal, &maxVal, 0, 0); // cv中用的是cvGetMinMaxHistValue //显示直方图的图像 hpt = saturate_cast<int>(0.9 * size); for(int i = 0; i < 256; i++) { float value = hist.at<float>(i); // 注意hist中是float类型 cv中用cvQueryHistValue_1D int realValue = saturate_cast<int>(value * hpt/maxVal); rectangle(imageShow_equal,Point(i*scale, size - 1), Point((i+1)*scale - 1, size - realValue), Scalar(255)); } namedWindow("Hist_equalize"); imshow("Hist_equalize", imageShow_equal); cv::imwrite("Hist_equalize.jpg",imageShow_equal); waitKey(0); return 0; } int main (int args, char** argv) { Mat image = imread("lena.jpg", 1); // 这里也可以是BGR 但是想想提取轮廓 效果是一样的 imshow("original", image); img_Hist(image); waitKey(); return 0; }</span>
二、直方图拉伸
变换函数:将图像的一种灰度值经过变换得到另一个灰度。
直方图变换的核心就是变换函数,s=T(r),r是变换前的灰度值,s是变换后的灰度值,如要我们想将[a,b]区间的灰度变换到[0,255]范围内,则变换函数是:T(r)=255*(r-a)/(b-a)。
先要找到imin imax
<span style="font-size:18px;">int imax,imin; for(imin=0;imin<256;imin++) { if(hist.at<uchar>(imin)>minValue) break; } for(imax=255;imax>-1;imax--) { if(hist.at<uchar>(imax)>minValue) break; } </span>
然后有一个映射函数
<span style="font-size:18px;"> Mat lookup(1,256,CV_8U); for(int i=0;i<256;i++) { if(lut.at<uchar>(i)<imin) lut.at<uchar>(i)=0; else if(lut.at<uchar>(i)>imax) lut.at<uchar>(i)=255; else lut.at<uchar>(i)=static_cast<uchar>( 255.0*(i-imin)/(imax-imin)+0.5); } </span>
最后用LUT
<span style="font-size:18px;">LUT(image,lut,result);</span>
好文章分享http://blog.csdn.net/cv_ronny/article/details/17507671