影象處理opencv2-灰度直方圖以及灰度直方圖均衡化
1灰度直方圖
1.1 概念
1.2 opencv表示 opencv提供了calcHist函式來計算影象直方圖。灰度直方圖是關於灰度級分佈的函式,是對影象中灰度級分佈的統計。
灰度直方圖是將數字影象中的所有畫素,按照灰度值的大小,統計其出現的頻率。
灰度直方圖是灰度級的函式,它表示影象中具有某種灰度級的畫素的個數,反映了影象中某種灰度出現的頻率。 如果將影象總畫素亮度(灰度級別)看成是一個隨機變數,則其分佈情況就反映了影象的統計特性,這可用probability density function (PDF)來刻畫和描述,表現為灰度直方圖。
其中C++的函式原型如下:
void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false );
引數解釋:
arrays:輸入的影象的指標,可以是多幅影象,所有的影象必須有同樣的深度(CV_8U or CV_32F)。同時一副影象可以有多個channes。
narrays:輸入的影象的個數。
channels:用來計算直方圖的channes的陣列。比如輸入是2副影象,第一副影象有0,1,2共三個channel,第二幅影象只有0一個channel,那麼輸入就一共有4個channes,如果int channels[3] = {3, 2, 0},那麼就表示是使用第二副影象的第一個通道和第一副影象的第2和第0個通道來計算直方圖。
mask:掩碼。如果mask不為空,那麼它必須是一個8位(CV_8U)的陣列,並且它的大小的和arrays[i]的大小相同,值為1的點將用來計算直方圖。
hist:計算出來的直方圖
dims:計算出來的直方圖的維數。
histSize:在每一維上直方圖的個數。簡單把直方圖看作一個一個的豎條的話,就是每一維上豎條的個數。
ranges:用來進行統計的範圍。比如 float rang1[] = {0, 20}; float rang2[] = {30, 40}; const float *rangs[] = {rang1, rang2};那麼就是對0,20和30,40範圍的值進行統計。
uniform:每一個豎條的寬度是否相等。
accumulate: 是否累加。如果為true,在下次計算的時候不會首先清空hist。
2 直方圖均衡化
1.1 概念
直方圖均衡化就是一種能僅靠輸入影象直方圖資訊自動達到這種效果的變換函式。
它的基本思想是對影象中畫素個數多的灰度級進行展寬,而對影象中畫素個數少的灰度進行壓縮,從而擴充套件像原取值的動態範圍,提高了對比度和灰度色調的變化,使影象更加清晰。
直方圖均衡化處理的“中心思想”是把原始影象的灰度直方圖從比較集中的某個灰度區間變成在全部灰度範圍內的均勻分佈。直方圖均衡化就是對影象進行非線性拉伸,重新分配影象畫素值,使一定灰度範圍內的畫素數量大致相同。直方圖均衡化就是把給定影象的直方圖分佈改變成“均勻”分佈直方圖分佈。
直方圖均衡化的基本思想是把原始圖的直方圖變換為均勻分佈的形式,這樣就增加了畫素灰度值的動態範圍從而可達到增強影象整體對比度的效果。
2.2 opencv表示
cv::equalizeHist(img_gray,equalize_Hist)
3.例程
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; // 在cv中用CvHistogram *hist = cvCreateHist
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("/home/odroid/TEST/111.jpg", 1); // 這裡也可以是BGR 但是想想提取輪廓 效果是一樣的
imshow("original", image);
img_Hist(image);
waitKey();
return 0;
}
原始圖 灰度圖
灰度直方圖 均衡化後的灰度直方圖
直方圖均衡化後的灰度圖