Opencv2系列學習筆記6(直方圖的應用)
這次主要想講點直方圖的應用,其中包括使用查詢表修改影象的外觀、直方圖的均衡化、反投影直方圖檢測特定影象的內容、meanshift演算法<均值漂移>跟蹤物體和利用影象直方圖檢索相似影象<可靠性比較低>。
一:使用查詢表修改影象的外觀
查詢表是簡單的一對一(或者多對一)函式,定義瞭如何將畫素值轉換為新畫素值。Opencv的cv::LUT對影象應用查詢表以生成新影象。公式為: result[i] = lookup[image[i]].
<1>影象反轉
Code:
Main.cpp
MatND hist = getHistogram(image); int i; for(i = 0; i < 256; i++) { cout << "Value" << i << ": " << hist.at<float>(i) << endl; } /*int dim(256); Mat lut(1, &dim, CV_8U); for(i = 0; i < 256; i++) { lut.at<uchar>(i) = 255 - i; } Mat result = applyLookUp(image, lut);
Result:
<2>提高影象對比度
以下程式碼定義了一個提高影象對比度的查詢表。方法就是通過拉伸直方圖,能得到擴充套件後的對比度。
Code:
得到影象的直方圖:
將影象的直方圖展示出來:MatND getHistogram(Mat &image) // 得到影象的直方圖 { MatND hist; int channels[] = {0}; int dims = 1; int histSize[] = {256}; float granges[] = {0, 255}; const float *ranges[] = {granges}; calcHist(&image, 1, channels, Mat(), hist, dims, histSize, ranges); return hist; }
Mat getHistogramImage(Mat &image, int scaleX = 1, int scaleY = 1) // 將影象的直方圖展示出來 { MatND hist = getHistogram(image); Mat showImage(scaleY * 256, scaleX*256, CV_8U,Scalar(255)); int i; double maxValue = 0; minMaxLoc(hist, 0, &maxValue, 0, 0); for(i = 0; i < 256; i++) { float value = hist.at<float>(i); int intensity = saturate_cast<int>(256*scaleY - 256*scaleY * (value/maxValue)); rectangle(showImage, Point(i*scaleX, scaleY*256 - 1), Point((i+1)*scaleX-1, intensity), Scalar(0)); } return showImage; }
得到並應用查詢表:
Mat stretch(Mat &image, int minValue = 0)
{
// 首先計算直方圖
MatND hist = getHistogram(image);
int imin = 0,i = 0;
// 尋找直方圖的左端
for(; i < 256; i ++)
{
float iminValue = hist.at<float>(i);
if(iminValue > minValue)
{
imin = i;
break;
}
}
// 尋找直方圖的右端
int imax = 255;
for(i = 255; i >= 0 ; i--)
{
float imaxValue = hist.at<float>(i);
if(imaxValue > minValue){
imax = i;
break;
}
}
// 建立查詢表
Mat lookUp(1, 256, CV_8U);
// 填充查詢表
for(i = 0; i < 256; i++)
{
// 確保數值位於imin和imax之間
if(i < imin)
lookUp.at<uchar>(i) = 0;
else if(i > imax)
lookUp.at<uchar>(i) = 255;
else // 線性對映
lookUp.at<uchar>(i) = saturate_cast<uchar>(255.0*(i - imin)/(imax - imin));
}
// 應用查詢表
Mat result;
LUT(image, lookUp, result);
return result;
}
Mat applyLookUp(Mat &image, Mat& lut)
{
Mat result;
LUT(image, lut, result);
return result;
}
Main.cpp
int main()
{
Mat image = imread("F:\\group.jpg", 0);
if(!image.data)
{
cout << "fail to load image" << endl;
return 0;
}
Mat showImage = getHistogramImage(image);
Mat result = stretch(image, 100);
Mat showImage2 = getHistogramImage(result);
namedWindow("image");
imshow("image", image);
namedWindow("showImage");
imshow("showImage", showImage);
namedWindow("showImage2");
imshow("showImage2", showImage2);
namedWindow("result");
imshow("result", result);
waitKey(0);
return 0;
}
Result:
二:直方圖均衡化
直方圖均衡化的目的也是提高影象的對比度,但它與通過查詢表提高對比度方法不同。通過拉伸直方圖,使它覆蓋所有的取值範圍。這個策略確實可以簡單有效地提升影象質量。然而,大多數情況下,影象在視覺上的缺陷並非源於過窄的強度範圍,而是由於某些顏色值出現的頻率高於另外一些。事實上,一副高質量的圖片應該均衡使用所有的像素值。
Code:
Main.cpp
int main()
{
Mat image = imread("F:\\group.jpg", 0);
if(!image.data)
{
cout << "fail to load image" << endl;
return 0;
}
Mat result;
equalizeHist(image, result); // 直方圖的均衡化====增加影象的對比度
namedWindow("image");
imshow("image", image);
namedWindow("result");
imshow("result", result);
Mat imageHist = getHistogramImage(image);
Mat resultHist = getHistogramImage(result);
namedWindow("imageHist");
imshow("imageHist", imageHist);
namedWindow("resultHist");
imshow("resultHist", resultHist);
waitKey(0);
return 0;
}
Explaination:
opencv2中提供的函式為equalizeHist();它的原理是這樣的,在一副完全均衡的直方圖中,所有的容器擁有同等數量的畫素。公式為:
lookup.at<uchar>(i)= static_cast<uchar>(255*p[i]);;其中p[i]代表強度值小於i的畫素所佔的比例。
Result:
三:反投影直方圖檢測特定影象的內容
如果一副影象的區域中顯示的是一種獨特的紋理或者是一個獨特的物體,那麼這個區域的直方圖可以看做一個概率函式,它給出了某個畫素屬於該紋理或者物體的概率。直白的說,就是在一副影象中將與某特定感興趣的區域全部找出來,使用的是概率。
Code:
Main.cpp:
int main()
{
Mat image = imread("F:\\clound.jpg", 0);
if(!image.data)
{
cout << "Fail to load image" << endl;
return 0;
}
Mat imageROI = image(Rect(80, 120, 40, 50));
MatND histImage = getHistogram(imageROI);
normalize(histImage, histImage, 1.0); //歸一化該直方圖
int channels[] = {0};
Mat result;
float granges[] = {0, 255};
const float *ranges[] = {granges};
calcBackProject(&image, 1, channels, histImage, result, ranges, 255.0);
/*第二個引數:影象數量 第三個引數: 通道數, histImage: 歸一化的直方圖, result: 概率對映圖
ranges: 維度的範圍
*/
namedWindow("image", 0);
imshow("image", image);
namedWindow("result", 0);
imshow("result", result);
waitKey(0);
return 0;
}
Result:
四:使用meanshift演算法<均值漂移>跟蹤物體
主要分為這三個步驟:首先需要得到目標的直方圖,其次用該直方圖對圖片進行反投影,後對反投影的影象進行meanshift演算法。其中迭代的次數和精度作為收斂的條件。
Code:
/*計算一幅影象特定區域的hist*/
MatND getHueHistogram(Mat &image, int minSaturation = 0)
{
MatND hist;
Mat hsvColor;
cvtColor(image, hsvColor, CV_BGR2HSV);
vector<Mat> hsv;
split(hsvColor, hsv);
Mat mask;
if(minSaturation > 0)
{
threshold(hsv[1], mask, minSaturation, 255, THRESH_BINARY);
int channels[] = {0};
int histSize[] = {181};
float hRanges[] = {0, 180};
const float *ranges[] = {hRanges};
int dims = 1;
calcHist(&hsv[0], 1, channels, mask, hist, dims, histSize, ranges);
}
normalize(hist, hist, 1.0);
return hist;
}
/*通過上面特定區域的直方圖得到影象的反投影直方圖*/
Mat getBackProject(Mat &image, MatND colorHist)
{
int channels[] = {0};
Mat result;
float hRanges[] = {0, 180};
const float *ranges[] = {hRanges};
calcBackProject(&image, 1, channels, colorHist, result, ranges, 255);
int thre = 60;
threshold(result, result, 60, 255,THRESH_BINARY);
return result;
}
int main()
{
// 載入第一幅影象
Mat image = imread("F:\\4.jpg",1);
// 定義需要跟蹤的目標---感興趣的區域
Mat imageROI = image(Rect(230, 320, 60, 35));
// 得到目標的直方圖 ********
int minSat = 65;
MatND colorHist = getHueHistogram(imageROI, minSat);
// 載入需要跟蹤的圖片
Mat image2 = imread("F:\\10.jpg", 1);
// 轉換顏色到hsv空間
Mat hsvColor;
cvtColor(image2, hsvColor, CV_BGR2HSV);
vector<Mat> hsv;
split(image2, hsv);
// 得到目標影象的反投影直方圖 *********
Mat result = getBackProject(hsv[0], colorHist);
// 去除 低飽和區域
threshold(hsv[1], hsv[1], minSat, 255, THRESH_BINARY);
bitwise_and(result, hsv[1], result);
Rect rect(230, 320, 60, 35);
rectangle(image, rect, Scalar(0,0,255));
rectangle(image2, rect, Scalar(0,0,255));
// 定義迭代的次數以及迭代的精度
TermCriteria criteria(TermCriteria::MAX_ITER, 10, 0.01);
// meanshift 演算法 跟蹤目標 並且得到新目標的位置rect
meanShift(result, rect, criteria);
rectangle(image2, rect, cv::Scalar(0,255,0));
namedWindow("image");
imshow("image", image);
namedWindow("result");
imshow("result", result);
namedWindow("image2", 0);
imshow("image2", image2);
waitKey(0);
return 0;
}
Explaination:
<1>程式碼中我們使用HSV顏色空間中的色調分量以描述所要搜尋的物體。
<2>還需要注意的是如果一個顏色的飽和度偏低,會導致色調資訊變得不穩定以及不可靠。這是因為低飽和度的顏色中,紅綠藍三個分量幾乎是相等的。
<3>meanshift:intcvMeanShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria,CvConnectedComp* comp );
引數
prob_image
目標直方圖的反向投影(見 cvCalcBackProject).
window
初始搜尋視窗
criteria
確定視窗搜尋停止的準則
五:通過比較直方圖檢索相似圖片
基本思想就是得到兩幅影象的直方圖,然後通過opencv提供的函式compareHist來得到它們的相似程度,返回的是個double值。
Code:
int main()
{
Mat imageSource = imread("F:\\test\\tongtong.jpg", 1);
colorReduce(imageSource, 32);
MatND sourceHist = getHistogram(imageSource);
stringstream ss;
string str;
string strBest = "";
double minDistance = 256*100*100*100;
for(int i = 1; i < 10; i++)
{
str = "F:\\test\\";
ss.clear();
ss << str;
ss << i;
ss << ".jpg";
ss >> str;
Mat imageDst = imread(str, 1);
colorReduce(imageDst, 1);
MatND dstHist = getHistogram(imageDst);
double distance = compareHist(sourceHist, dstHist, CV_COMP_INTERSECT);
if(distance < minDistance)
{
strBest = str;
minDistance = distance;
}
}
Mat best = imread(strBest,1);
namedWindow(strBest);
imshow(strBest,best);
waitKey(0);
return 0;
}
Explaination:
<1>程式碼中使用了降低顏色數。這是因為直方圖的比較大多數都是基於逐個容器的,即比較直方圖容器時並不考慮相鄰容器的影像。因此,測量相似度之前減少顏色空間是很重要的。
<2>compareHist函式直接明瞭,只需提供兩個直方圖,函式邊返回測量距離。通過一個標誌引數可以指定測量方法。程式碼中的引數CV_COMP_INTERSECT表示交叉測量法。即簡單比較每個直方圖容器的值,並保留最小的一個。相似性測量值只是這些最小值的和。
歡迎轉載或分享,但請務必宣告文章出處。 (新浪微博:小村長zack, 歡迎交流!)