Opencv2系列學習筆記6(直方圖的應用)

前面我講述了直方圖的概念以及如何用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

  1.        MatND hist = getHistogram(image);  
  2. int i;  
  3. for(i = 0; i < 256; i++)  
  4. {  
  5.     cout << "Value" << i << ": " << hist.at<float>(i) << endl;  
  6. }  
  7. /*int dim(256);  
  8. Mat lut(1, &dim, CV_8U);  
  9. for(i = 0; i < 256; i++)  
  10. {  
  11.     lut.at<uchar>(i) = 255 - i;   
  12. }  
  13. Mat result = applyLookUp(image, lut);  

Result:

 

<2>提高圖像對比度

以下代碼定義了一個提高圖像對比度的查找表。方法就是通過拉伸直方圖,能得到擴展後的對比度。

Code:

得到圖像的直方圖:

  1. MatND getHistogram(Mat &image)  //  得到圖像的直方圖  
  2. {  
  3.     MatND hist;  
  4.     int channels[] = {0};  
  5.     int dims = 1;  
  6.     int histSize[] = {256};   
  7.     float granges[] = {0, 255};  
  8.     const float *ranges[] = {granges};  
  9.     calcHist(&image, 1, channels, Mat(), hist, dims, histSize, ranges);  
  10.     return hist;  
  11. }  
將圖像的直方圖展示出來:

  1. Mat getHistogramImage(Mat &image, int scaleX = 1, int scaleY = 1)   //  將圖像的直方圖展示出來  
  2. {  
  3.     MatND hist = getHistogram(image);  
  4.     Mat showImage(scaleY * 256, scaleX*256, CV_8U,Scalar(255));  
  5.     int i;  
  6.     double maxValue = 0;  
  7.     minMaxLoc(hist, 0, &maxValue, 0, 0);  
  8.     for(i = 0; i < 256; i++)  
  9.     {  
  10.         float value = hist.at<float>(i);  
  11.         int intensity = saturate_cast<int>(256*scaleY - 256*scaleY * (value/maxValue));  
  12.         rectangle(showImage, Point(i*scaleX, scaleY*256 - 1), Point((i+1)*scaleX-1, intensity), Scalar(0));  
  13.     }  
  14.     return showImage;  
  15. }  

得到並應用查找表:

  1. Mat stretch(Mat &image, int minValue = 0)  
  2. {  
  3.     // 首先計算直方圖  
  4.     MatND hist = getHistogram(image);  
  5.     int imin = 0,i = 0;  
  6.     // 尋找直方圖的左端  
  7.     for(; i < 256; i ++)  
  8.     {  
  9.         float iminValue = hist.at<float>(i);  
  10.         if(iminValue > minValue)  
  11.         {  
  12.             imin = i;  
  13.             break;  
  14.         }  
  15.     }  
  16.     //  尋找直方圖的右端  
  17.     int imax = 255;  
  18.     for(i = 255; i >= 0 ; i--)  
  19.     {  
  20.         float imaxValue = hist.at<float>(i);  
  21.         if(imaxValue > minValue){  
  22.             imax = i;  
  23.             break;  
  24.         }  
  25.     }  
  26.     // 創建查找表  
  27.     Mat lookUp(1, 256, CV_8U);  
  28.     // 填充查找表  
  29.     for(i = 0; i < 256; i++)  
  30.     {  
  31.         // 確保數值位於imin和imax之間  
  32.         if(i < imin)  
  33.             lookUp.at<uchar>(i) = 0;  
  34.         else if(i > imax)  
  35.             lookUp.at<uchar>(i) = 255;  
  36.         else     // 線性映射  
  37.             lookUp.at<uchar>(i) = saturate_cast<uchar>(255.0*(i - imin)/(imax - imin));   
  38.     }  
  39.     // 應用查找表  
  40.     Mat result;  
  41.     LUT(image, lookUp, result);  
  42.     return result;  
  43. }  
  44. Mat applyLookUp(Mat &image, Mat& lut)  
  45. {  
  46.     Mat result;  
  47.     LUT(image, lut, result);  
  48.     return result;  
  49. }  
Main.cpp

  1. int main()  
  2. {  
  3.     Mat image = imread("F:\\group.jpg", 0);  
  4.     if(!image.data)  
  5.     {  
  6.         cout << "fail to load image" << endl;  
  7.         return 0;  
  8.     }  
  9.     Mat showImage = getHistogramImage(image);  
  10.     Mat result = stretch(image, 100);  
  11.     Mat showImage2 = getHistogramImage(result);  
  12.     namedWindow("image");  
  13.     imshow("image", image);  
  14.     namedWindow("showImage");  
  15.     imshow("showImage", showImage);  
  16.     namedWindow("showImage2");  
  17.     imshow("showImage2", showImage2);  
  18.     namedWindow("result");  
  19.     imshow("result", result);  
  20.     waitKey(0);  
  21.     return 0;  
  22. }  

Result:

 


二:直方圖均衡化

直方圖均衡化的目的也是提高圖像的對比度,但它與通過查找表提高對比度方法不同。通過拉伸直方圖,使它覆蓋所有的取值範圍。這個策略確實可以簡單有效地提升圖像質量。然而,大多數情況下,圖像在視覺上的缺陷並非源於過窄的強度範圍,而是由於某些顏色值出現的頻率高於另外一些。事實上,一副高質量的圖片應該均衡使用所有的像素值。

Code:

Main.cpp

  1. int main()  
  2. {  
  3.     Mat image = imread("F:\\group.jpg", 0);  
  4.     if(!image.data)  
  5.     {  
  6.         cout << "fail to load image" << endl;  
  7.         return 0;  
  8.     }  
  9.     Mat result;  
  10.     equalizeHist(image, result);   // 直方圖的均衡化====增加圖像的對比度  
  11.     namedWindow("image");  
  12.     imshow("image", image);  
  13.     namedWindow("result");  
  14.     imshow("result", result);  
  15.     Mat imageHist = getHistogramImage(image);  
  16.     Mat resultHist = getHistogramImage(result);  
  17.   
  18.     namedWindow("imageHist");  
  19.     imshow("imageHist", imageHist);  
  20.     namedWindow("resultHist");  
  21.     imshow("resultHist", resultHist);  
  22.   
  23.     waitKey(0);  
  24.     return 0;  
  25. }  

Explaination:

opencv2中提供的函數爲equalizeHist();它的原理是這樣的,在一副完全均衡的直方圖中,所有的容器擁有同等數量的像素。公式爲:

       lookup.at<uchar>(i)= static_cast<uchar>(255*p[i]);;其中p[i]代表強度值小於i的像素所佔的比例。

Result:



三:反投影直方圖檢測特定圖像的內容

如果一副圖像的區域中顯示的是一種獨特的紋理或者是一個獨特的物體,那麼這個區域的直方圖可以看做一個概率函數,它給出了某個像素屬於該紋理或者物體的概率。直白的說,就是在一副圖像中將與某特定感興趣的區域全部找出來,使用的是概率。

Code:

Main.cpp:

  1. int main()  
  2. {  
  3.     Mat image = imread("F:\\clound.jpg", 0);  
  4.     if(!image.data)  
  5.     {  
  6.         cout << "Fail to load image" << endl;  
  7.         return 0;  
  8.     }  
  9.     Mat imageROI = image(Rect(80, 120, 40, 50));  
  10.     MatND histImage = getHistogram(imageROI);  
  11.     normalize(histImage, histImage, 1.0);   //歸一化該直方圖  
  12.     int channels[] = {0};  
  13.     Mat result;  
  14.     float granges[] = {0, 255};  
  15.     const float *ranges[] = {granges};  
  16.     calcBackProject(&image, 1, channels, histImage, result, ranges, 255.0);   
  17.     /*第二個參數:圖像數量   第三個參數: 通道數, histImage: 歸一化的直方圖,  result: 概率映射圖 
  18.     ranges: 維度的範圍 
  19.     */  
  20.     namedWindow("image", 0);  
  21.     imshow("image", image);  
  22.     namedWindow("result", 0);  
  23.     imshow("result", result);  
  24.     waitKey(0);  
  25.     return 0;  
  26. }  

Result:


四:使用meanshift算法<均值漂移>跟蹤物體

Meanshift算法的具體介紹可以看我轉載的一篇blog:http://blog.csdn.net/lu597203933/article/details/17042331  它就是以迭代的方式鎖定概率函數的局部最大值

主要分爲這三個步驟:首先需要得到目標的直方圖,其次用該直方圖對圖片進行反投影,後對反投影的圖像進行meanshift算法。其中迭代的次數和精度作爲收斂的條件。

Code:

  1. /*計算一幅圖像特定區域的hist*/  
  2. MatND getHueHistogram(Mat &image, int minSaturation = 0)  
  3. {  
  4.     MatND hist;  
  5.     Mat hsvColor;  
  6.     cvtColor(image, hsvColor, CV_BGR2HSV);  
  7.     vector<Mat> hsv;   
  8.     split(hsvColor, hsv);  
  9.     Mat mask;  
  10.     if(minSaturation > 0)  
  11.     {  
  12.         threshold(hsv[1], mask, minSaturation, 255, THRESH_BINARY);  
  13.         int channels[] = {0};  
  14.         int histSize[] = {181};  
  15.         float hRanges[] = {0, 180};  
  16.         const float *ranges[] = {hRanges};  
  17.         int dims = 1;  
  18.         calcHist(&hsv[0], 1, channels, mask, hist, dims, histSize, ranges);  
  19.     }  
  20.     normalize(hist, hist, 1.0);  
  21.     return hist;  
  22. }  
  23.   
  24. /*通過上面特定區域的直方圖得到圖像的反投影直方圖*/  
  25. Mat getBackProject(Mat &image, MatND colorHist)  
  26. {  
  27.     int channels[] = {0};  
  28.     Mat result;  
  29.     float hRanges[] = {0, 180};  
  30.     const float *ranges[] = {hRanges};  
  31.     calcBackProject(&image, 1, channels, colorHist, result, ranges, 255);  
  32.     int thre = 60;  
  33.     threshold(result, result, 60, 255,THRESH_BINARY);  
  34.     return result;  
  35. }  
  36. int main()  
  37. {  
  38.     // 載入第一幅圖像  
  39.     Mat image = imread("F:\\4.jpg",1);  
  40.     // 定義需要跟蹤的目標---感興趣的區域  
  41.     Mat imageROI = image(Rect(230, 320, 60, 35));  
  42.     // 得到目標的直方圖  ********  
  43.     int minSat = 65;  
  44.     MatND colorHist = getHueHistogram(imageROI, minSat);  
  45.   
  46.     //  載入需要跟蹤的圖片  
  47.     Mat image2 = imread("F:\\10.jpg", 1);  
  48.     // 轉換顏色到hsv空間  
  49.     Mat hsvColor;  
  50.     cvtColor(image2, hsvColor, CV_BGR2HSV);  
  51.     vector<Mat> hsv;  
  52.     split(image2, hsv);  
  53.     // 得到目標圖像的反投影直方圖  *********  
  54.     Mat result = getBackProject(hsv[0], colorHist);  
  55.     // 去除 低飽和區域  
  56.     threshold(hsv[1], hsv[1], minSat, 255, THRESH_BINARY);  
  57.     bitwise_and(result, hsv[1], result);  
  58.   
  59.     Rect rect(230, 320, 60, 35);  
  60.     rectangle(image, rect, Scalar(0,0,255));  
  61.     rectangle(image2, rect, Scalar(0,0,255));  
  62.     // 定義迭代的次數以及迭代的精度  
  63.     TermCriteria criteria(TermCriteria::MAX_ITER, 10, 0.01);  
  64.     // meanshift 算法 跟蹤目標 並且得到新目標的位置rect  
  65.     meanShift(result, rect, criteria);  
  66.     rectangle(image2, rect, cv::Scalar(0,255,0));  
  67.     namedWindow("image");  
  68.     imshow("image", image);  
  69.     namedWindow("result");  
  70.     imshow("result", result);  
  71.     namedWindow("image2", 0);  
  72.     imshow("image2", image2);  
  73.     waitKey(0);  
  74.     return 0;  
  75.   
  76. }  

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:

  1. int main()  
  2. {  
  3.     Mat imageSource = imread("F:\\test\\tongtong.jpg", 1);  
  4.     colorReduce(imageSource, 32);  
  5.     MatND sourceHist = getHistogram(imageSource);  
  6.     stringstream ss;  
  7.     string str;  
  8.     string strBest = "";  
  9.     double minDistance = 256*100*100*100;  
  10.     for(int i = 1; i < 10; i++)  
  11.     {  
  12.         str = "F:\\test\\";  
  13.         ss.clear();  
  14.         ss << str;  
  15.         ss << i;  
  16.         ss << ".jpg";  
  17.         ss >> str;  
  18.         Mat imageDst = imread(str, 1);  
  19.         colorReduce(imageDst, 1);  
  20.         MatND dstHist = getHistogram(imageDst);  
  21.         double distance = compareHist(sourceHist, dstHist, CV_COMP_INTERSECT);  
  22.         if(distance < minDistance)  
  23.         {  
  24.             strBest = str;  
  25.             minDistance = distance;  
  26.         }  
  27.     }  
  28.     Mat best = imread(strBest,1);  
  29.     namedWindow(strBest);  
  30.     imshow(strBest,best);  
  31.     waitKey(0);  
  32.     return 0;  
  33.   
  34. }  

Explaination:

<1>代碼中使用了降低顏色數。這是因爲直方圖的比較大多數都是基於逐個容器的,即比較直方圖容器時並不考慮相鄰容器的影像。因此,測量相似度之前減少顏色空間是很重要的。

<2>compareHist函數直接明瞭,只需提供兩個直方圖,函數邊返回測量距離。通過一個標誌參數可以指定測量方法。代碼中的參數CV_COMP_INTERSECT表示交叉測量法。即簡單比較每個直方圖容器的值,並保留最小的一個。相似性測量值只是這些最小值的和。


作者:小村長  出處:http://blog.csdn.net/lu597203933 歡迎轉載或分享,但請務必聲明文章出處。 (新浪微博:小村長zack, 歡迎交流!)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章