前面我講述了直方圖的概念以及如何用opencv實現一維和二維的直方圖。詳見這兩篇blog: Mat 格式:http://blog.csdn.net/lu597203933/article/details/16884409
cv:http://blog.csdn.net/lu597203933/article/details/14104505
這次主要想講點直方圖的應用,其中包括使用查找表修改圖像的外觀、直方圖的均衡化、反投影直方圖檢測特定圖像的內容、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++)
-
{
-
-
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);
-
-
-
-
namedWindow("image", 0);
-
imshow("image", image);
-
namedWindow("result", 0);
-
imshow("result", result);
-
waitKey(0);
-
return 0;
-
}
Result:
四:使用meanshift算法<均值漂移>跟蹤物體
Meanshift算法的具體介紹可以看我轉載的一篇blog:http://blog.csdn.net/lu597203933/article/details/17042331 它就是以迭代的方式鎖定概率函數的局部最大值
主要分爲這三個步驟:首先需要得到目標的直方圖,其次用該直方圖對圖片進行反投影,後對反投影的圖像進行meanshift算法。其中迭代的次數和精度作爲收斂的條件。
Code:
-
-
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);
-
-
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(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表示交叉測量法。即簡單比較每個直方圖容器的值,並保留最小的一個。相似性測量值只是這些最小值的和。
作者:小村長 出處:http://blog.csdn.net/lu597203933 歡迎轉載或分享,但請務必聲明文章出處。 (新浪微博:小村長zack, 歡迎交流!)