一、直方圖均衡化
直方圖均衡化是灰度變換的一個重要應用,廣泛應用在圖像增強處理中,它是以累計分佈函數變換爲基礎的直方圖修正法,可以產生一幅灰度級分佈具有均勻概率密度的圖像,擴展了像素的取值動態範圍。許多圖像的灰度值是非均勻分佈的,其中灰度值集中在一個小區間內的圖像是很常見的,直方圖均衡化是一種通過重新均勻地分佈各灰度值來增強圖像對比度的方法,經過直方圖均衡化的圖像對二值化閾值選取十分有利。一般來說,直方圖修正能提高圖像的主觀質量,因此在處理藝術圖像時非常有用。直方圖均衡化處理的中心思想是把原始圖像的灰度直方圖從比較集中的某個灰度區間變成在全部灰度範圍內的均勻分佈。
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>
<span style="font-size:18px;">LUT(image,lut,result);</span>
三、Matlab輔助
clear all;
I = imread('pout.tif');
subplot(2,2,1);imshow(I);
title('原始圖像');
J = histeq(I);
subplot(2,2,2);imshow(J);
title('圖像均衡化')
subplot(2,2,3);imhist(I)
title('原始圖像直方圖')
subplot(2,2,4);imhist(J)
title('均衡化圖像直方圖')
圖像識別算法交流 QQ羣:145076161,歡迎圖像識別與圖像算法,共同學習與交流