http://blog.csdn.net/thefutureisour/article/details/7554716#comments
什麼是反向投影直方圖呢?簡單的說在灰度圖像的每個點(x,y),用它對應的直方圖的bin的值(就是有多少像素落在bin內)來代替它。所以·如果這個bin的值比較大,那麼反向投影顯示的結果會比較亮,否則就比較暗。
從統計學的角度,反輸出圖像象素點的值是觀測數組在某個分佈(直方圖)下的的概率。
所以加入我們已經得到了一個物體的直方圖,我們可以計算它在另一幅圖像中的反向投影,來判斷這幅圖像中是否有該物體。
OpenCV提供了計算反向投影直方圖的函數:calcBackProject來計算一幅圖像對於給定直方圖的反向投影。
下面上代碼:
一下是計算灰度圖像直方圖的類:
- #if!defined HISTOGRAM
- #define HISTOGRAM
- #include <opencv2/core/core.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <iostream>
- using namespace std;
- using namespace cv;
- class Histogram1D
- {
- private:
- //直方圖的點數
- int histSize[1];
- //直方圖的範圍
- float hranges[2];
- //指向該範圍的指針
- const float* ranges[1];
- //通道
- int channels[1];
- public:
- //構造函數
- Histogram1D()
- {
- histSize[0] = 256;
- hranges[0] = 0.0;
- hranges[1] = 255.0;
- ranges[0] = hranges;
- channels[0] = 0;
- }
- Mat getHistogram(const Mat &image)
- {
- Mat hist;
- //計算直方圖函數
- //參數爲:源圖像(序列)地址,輸入圖像的個數,通道數,掩碼,輸出結果,直方圖維數,每一維的大小,每一維的取值範圍
- calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);
- //這個函數雖然有很多參數,但是大多數時候,只會用於灰度圖像或者彩色圖像
- //但是,允許通過指明一個多通道圖像使用多幅圖像
- //第6個參數指明瞭直方圖的維數
- return hist;
- }
- Mat getHistogramImage(const Mat &image)
- {
- //首先計算直方圖
- Mat hist = getHistogram(image);
- //獲取最大值和最小值
- double maxVal = 0;
- double minVal = 0;
- //minMaxLoc用來獲得最大值和最小值,後面兩個參數爲最小值和最大值的位置,0代表不需要獲取
- minMaxLoc(hist,&minVal,&maxVal,0,0);
- //展示直方圖的畫板:底色爲白色
- Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));
- //將最高點設爲bin總數的90%
- //int hpt = static_cast<int>(0.9*histSize[0]);
- int hpt = static_cast<int>(histSize[0]);
- //爲每一個bin畫一條線
- for(int h = 0; h < histSize[0];h++)
- {
- float binVal = hist.at<float>(h);
- int intensity = static_cast<int>(binVal*hpt/maxVal);
- //int intensity = static_cast<int>(binVal);
- line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));
- }
- return histImg;
- }
- Mat applyLookUp(const Mat& image,const Mat& lookup)
- {
- Mat result;
- LUT(image,lookup,result);
- return result;
- }
- Mat strech(const Mat &image,int minValue = 0)
- {
- //首先計算直方圖
- Mat hist = getHistogram(image);
- //左邊入口
- int imin = 0;
- for(;imin< histSize[0];imin++)
- {
- cout<<hist.at<float>(imin)<<endl;
- if(hist.at<float>(imin) > minValue)
- break;
- }
- //右邊入口
- int imax = histSize[0]-1;
- for(;imax >= 0; imax--)
- {
- if(hist.at<float>(imax) > minValue)
- break;
- }
- //創建查找表
- int dim(256);
- Mat lookup(1,&dim,CV_8U);
- for(int 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) = static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);
- }
- }
- Mat result;
- result = applyLookUp(image,lookup);
- return result;
- }
- Mat equalize(const Mat &image)
- {
- Mat result;
- equalizeHist(image,result);
- return result;
- }
- };
- #endif
#if!defined HISTOGRAM
#define HISTOGRAM
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
class Histogram1D
{
private:
//直方圖的點數
int histSize[1];
//直方圖的範圍
float hranges[2];
//指向該範圍的指針
const float* ranges[1];
//通道
int channels[1];
public:
//構造函數
Histogram1D()
{
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0;
}
Mat getHistogram(const Mat &image)
{
Mat hist;
//計算直方圖函數
//參數爲:源圖像(序列)地址,輸入圖像的個數,通道數,掩碼,輸出結果,直方圖維數,每一維的大小,每一維的取值範圍
calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);
//這個函數雖然有很多參數,但是大多數時候,只會用於灰度圖像或者彩色圖像
//但是,允許通過指明一個多通道圖像使用多幅圖像
//第6個參數指明瞭直方圖的維數
return hist;
}
Mat getHistogramImage(const Mat &image)
{
//首先計算直方圖
Mat hist = getHistogram(image);
//獲取最大值和最小值
double maxVal = 0;
double minVal = 0;
//minMaxLoc用來獲得最大值和最小值,後面兩個參數爲最小值和最大值的位置,0代表不需要獲取
minMaxLoc(hist,&minVal,&maxVal,0,0);
//展示直方圖的畫板:底色爲白色
Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));
//將最高點設爲bin總數的90%
//int hpt = static_cast<int>(0.9*histSize[0]);
int hpt = static_cast<int>(histSize[0]);
//爲每一個bin畫一條線
for(int h = 0; h < histSize[0];h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal*hpt/maxVal);
//int intensity = static_cast<int>(binVal);
line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));
}
return histImg;
}
Mat applyLookUp(const Mat& image,const Mat& lookup)
{
Mat result;
LUT(image,lookup,result);
return result;
}
Mat strech(const Mat &image,int minValue = 0)
{
//首先計算直方圖
Mat hist = getHistogram(image);
//左邊入口
int imin = 0;
for(;imin< histSize[0];imin++)
{
cout<<hist.at<float>(imin)<<endl;
if(hist.at<float>(imin) > minValue)
break;
}
//右邊入口
int imax = histSize[0]-1;
for(;imax >= 0; imax--)
{
if(hist.at<float>(imax) > minValue)
break;
}
//創建查找表
int dim(256);
Mat lookup(1,&dim,CV_8U);
for(int 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) = static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);
}
}
Mat result;
result = applyLookUp(image,lookup);
return result;
}
Mat equalize(const Mat &image)
{
Mat result;
equalizeHist(image,result);
return result;
}
};
#endif
下面的類是計算彩色圖像的直方圖:
- #if!defined COLORHISTOGRAM
- #define COLORHISTOGRAM
- #include <opencv2/core/core.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- using namespace cv;
- class ColorHistogram
- {
- private:
- int histSize[3];
- float hranges[2];
- const float* ranges[3];
- int channels[3];
- public:
- //構造函數
- ColorHistogram()
- {
- histSize[0]= histSize[1]= histSize[2]= 256;
- hranges[0] = 0.0;
- hranges[1] = 255.0;
- ranges[0] = hranges;
- ranges[1] = hranges;
- ranges[2] = hranges;
- channels[0] = 0;
- channels[1] = 1;
- channels[2] = 2;
- }
- //計算彩色圖像直方圖
- Mat getHistogram(const Mat& image)
- {
- Mat hist;
- //BGR直方圖
- hranges[0]= 0.0;
- hranges[1]= 255.0;
- channels[0]= 0;
- channels[1]= 1;
- channels[2]= 2;
- //計算
- calcHist(&image,1,channels,Mat(),hist,3,histSize,ranges);
- return hist;
- }
- //計算顏色的直方圖
- Mat getHueHistogram(const Mat &image)
- {
- Mat hist;
- Mat hue;
- //轉換到HSV空間
- cvtColor(image,hue,CV_BGR2HSV);
- //設置1維直方圖使用的參數
- hranges[0] = 0.0;
- hranges[1] = 180.0;
- channels[0] = 0;
- //計算直方圖
- calcHist(&hue,1,channels,Mat(),hist,1,histSize,ranges);
- return hist;
- }
- //減少顏色
- Mat colorReduce(const Mat &image,int div = 64)
- {
- int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
- uchar mask = 0xFF<<n;
- Mat_<Vec3b>::const_iterator it = image.begin<Vec3b>();
- Mat_<Vec3b>::const_iterator itend = image.end<Vec3b>();
- //設置輸出圖像
- Mat result(image.rows,image.cols,image.type());
- Mat_<Vec3b>::iterator itr = result.begin<Vec3b>();
- for(;it != itend;++it,++itr)
- {
- (*itr)[0] = ((*it)[0]&mask) + div/2;
- (*itr)[1] = ((*it)[1]&mask) + div/2;
- (*itr)[2] = ((*it)[2]&mask) + div/2;
- }
- return result;
- }
- };
- #endif
#if!defined COLORHISTOGRAM
#define COLORHISTOGRAM
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
class ColorHistogram
{
private:
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
public:
//構造函數
ColorHistogram()
{
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
ranges[1] = hranges;
ranges[2] = hranges;
channels[0] = 0;
channels[1] = 1;
channels[2] = 2;
}
//計算彩色圖像直方圖
Mat getHistogram(const Mat& image)
{
Mat hist;
//BGR直方圖
hranges[0]= 0.0;
hranges[1]= 255.0;
channels[0]= 0;
channels[1]= 1;
channels[2]= 2;
//計算
calcHist(&image,1,channels,Mat(),hist,3,histSize,ranges);
return hist;
}
//計算顏色的直方圖
Mat getHueHistogram(const Mat &image)
{
Mat hist;
Mat hue;
//轉換到HSV空間
cvtColor(image,hue,CV_BGR2HSV);
//設置1維直方圖使用的參數
hranges[0] = 0.0;
hranges[1] = 180.0;
channels[0] = 0;
//計算直方圖
calcHist(&hue,1,channels,Mat(),hist,1,histSize,ranges);
return hist;
}
//減少顏色
Mat colorReduce(const Mat &image,int div = 64)
{
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
uchar mask = 0xFF<<n;
Mat_<Vec3b>::const_iterator it = image.begin<Vec3b>();
Mat_<Vec3b>::const_iterator itend = image.end<Vec3b>();
//設置輸出圖像
Mat result(image.rows,image.cols,image.type());
Mat_<Vec3b>::iterator itr = result.begin<Vec3b>();
for(;it != itend;++it,++itr)
{
(*itr)[0] = ((*it)[0]&mask) + div/2;
(*itr)[1] = ((*it)[1]&mask) + div/2;
(*itr)[2] = ((*it)[2]&mask) + div/2;
}
return result;
}
};
#endif
這裏面使用colorReduce函數減少了彩色圖像的顏色。在計算彩色圖像直方圖類中,還包括了一個計算HSV空間中hue分量的直方圖的函數。它在主函數中會使用到
有了計算直方圖的函數,我們再看計算反向投影直方圖的類:
- #if!defined CONTENTFINDER
- #define CONTENTFINDER
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- using namespace cv;
- class ContentFinder
- {
- private:
- float hranges[2];
- const float* ranges[3];
- int channels[3];
- float threshold;
- Mat histogram;
- public:
- ContentFinder():threshold(-1.0f)
- {
- //所有通道的範圍相同
- ranges[0] = hranges;
- ranges[1] = hranges;
- ranges[2] = hranges;
- }
- //設置門限參數[0,1]
- void setThreshold(float t)
- {
- threshold = t;
- }
- //獲取門限參數
- float getThreshold()
- {
- return threshold;
- }
- //設置參考的直方圖
- void setHistogram(const Mat& h)
- {
- histogram = h;
- normalize(histogram,histogram,1.0);
- }
- //簡單的利用反向投影直方圖尋找
- Mat find(const Mat& image)
- {
- Mat result;
- hranges[0] = 0.0;
- hranges[1] = 255.0;
- channels[0] = 0;
- channels[1] = 1;
- channels[2] = 2;
- calcBackProject(&image,1,channels,histogram,result,ranges,255.0);
- if (threshold>0.0)
- {
- cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
- }
- return result;
- }
- //複雜的利用反向投影直方圖,增加了一些參數
- Mat find(const Mat &image,float minValue,float maxValue,int *channels,int dim)
- {
- Mat result;
- hranges[0] = minValue;
- hranges[1] = maxValue;
- for(int i = 0;i < dim;i++)
- {
- this->channels[i] = channels[i];
- }
- calcBackProject(&image,1,channels,histogram,result,ranges,255.0);
- if(threshold >0.0)
- cv::threshold(result,result, 255*threshold,255,THRESH_BINARY);
- return result;
- }
- };
- #endif
#if!defined CONTENTFINDER
#define CONTENTFINDER
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
class ContentFinder
{
private:
float hranges[2];
const float* ranges[3];
int channels[3];
float threshold;
Mat histogram;
public:
ContentFinder():threshold(-1.0f)
{
//所有通道的範圍相同
ranges[0] = hranges;
ranges[1] = hranges;
ranges[2] = hranges;
}
//設置門限參數[0,1]
void setThreshold(float t)
{
threshold = t;
}
//獲取門限參數
float getThreshold()
{
return threshold;
}
//設置參考的直方圖
void setHistogram(const Mat& h)
{
histogram = h;
normalize(histogram,histogram,1.0);
}
//簡單的利用反向投影直方圖尋找
Mat find(const Mat& image)
{
Mat result;
hranges[0] = 0.0;
hranges[1] = 255.0;
channels[0] = 0;
channels[1] = 1;
channels[2] = 2;
calcBackProject(&image,1,channels,histogram,result,ranges,255.0);
if (threshold>0.0)
{
cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
}
return result;
}
//複雜的利用反向投影直方圖,增加了一些參數
Mat find(const Mat &image,float minValue,float maxValue,int *channels,int dim)
{
Mat result;
hranges[0] = minValue;
hranges[1] = maxValue;
for(int i = 0;i < dim;i++)
{
this->channels[i] = channels[i];
}
calcBackProject(&image,1,channels,histogram,result,ranges,255.0);
if(threshold >0.0)
cv::threshold(result,result, 255*threshold,255,THRESH_BINARY);
return result;
}
};
#endif
其中的兩個函數的區別在於一個計算的指定範圍內的反向投影直方圖。
最後我們看看主函數:
- #include "contentFinder.h"
- #include "histogram.h"
- #include "colorhistogram.h"
- int main()
- {
- //讀取圖像
- Mat image = imread("D:/picture/images/waves.jpg",0);
- if(!image.data)
- return -1;
- //定義感興趣區域
- Mat imageROI = image(Rect(360,55,40,50));
- //利用前面設計好的類計算感興趣區域的直方圖
- Histogram1D h;
- Mat hist = h.getHistogram(imageROI);
- //創建一個ContentFinder對象
- ContentFinder finder;
- finder.setHistogram(hist);
- finder.setThreshold(-1.0f);
- //獲取反向投影
- Mat result1;
- result1 = finder.find(image);
- Mat tmp;
- result1.convertTo(tmp,CV_8U,-1.0,255.0);
- imshow("反向投影結果",tmp);
- //獲取二值反向投影
- finder.setThreshold(0.12f);
- result1 = finder.find(image);
- imshow("灰度圖像檢測結果(1)",result1);
- //給源圖像加上矩形框
- rectangle(image,Rect(360,55,40,50),Scalar(0,0,0));
- imshow("源圖像",image);
- //換一幅圖像:這幅圖像中也有大量雲彩
- Mat image2 = imread("D:/picture/images/dog.jpg",0);
- Mat result2 = finder.find(image2);
- imshow("灰度圖像檢測結果(2)",result2);
- //**************以上檢測因爲沒有用到顏色信息,所以效果很差**************
- //獲取彩色直方圖
- //讀取彩色圖像
- ColorHistogram hc;
- Mat color = imread("D:/picture/images/waves.jpg");
- imshow("源圖像(1)",color);
- //爲了減少計算量,使用colorReduce函數
- color = hc.colorReduce(color,32);
- //定義感興趣區域:雲彩
- imageROI = color(Rect(0,0,165,75));
- //獲取直方圖
- Mat shist = hc.getHistogram(imageROI);
- finder.setHistogram(shist);
- finder.setThreshold(0.05f);
- //獲取反向投影直方圖
- result1 = finder.find(color);
- imshow("彩色圖像檢測結果(1)",result1);
- //讀取第二幅圖像並檢測
- Mat color2 = imread("D:/picture/images/dog.jpg");
- imshow("源圖像(2)",color2);
- color2 = hc.colorReduce(color2,32);
- result2 = finder.find(color2);
- imshow("彩色圖像檢測結果(2)",result2);
- //***************以上結果因爲考慮了顏色信息,所以效果比較好*********************
- //僅考慮色度信息,構成直方圖,進行反向投影檢測
- color = imread("D:/picture/images/waves.jpg");
- imageROI = color(Rect(0,0,165,75));
- Mat colorhist = hc.getHueHistogram(imageROI);
- finder.setHistogram(colorhist);
- finder.setThreshold(0.3f);
- Mat hsv;
- cvtColor(color,hsv,CV_BGR2HSV);
- int ch[2]={1,2};
- ch[0] = 0;
- result1 = finder.find(hsv,0.0f,180.0f,ch,1);
- imshow("使用色度的結果(1)",result1);
- //換一幅圖片
- color2 = imread("D:/picture/images/dog.jpg");
- cvtColor(color2,hsv,CV_BGR2HSV);
- result2 = finder.find(hsv,0.0f,180.0f,ch,1);
- imshow("使用色度檢測結果(2)",result2);
- waitKey(0);
- return 0;
- }
#include "contentFinder.h"
#include "histogram.h"
#include "colorhistogram.h"
int main()
{
//讀取圖像
Mat image = imread("D:/picture/images/waves.jpg",0);
if(!image.data)
return -1;
//定義感興趣區域
Mat imageROI = image(Rect(360,55,40,50));
//利用前面設計好的類計算感興趣區域的直方圖
Histogram1D h;
Mat hist = h.getHistogram(imageROI);
//創建一個ContentFinder對象
ContentFinder finder;
finder.setHistogram(hist);
finder.setThreshold(-1.0f);
//獲取反向投影
Mat result1;
result1 = finder.find(image);
Mat tmp;
result1.convertTo(tmp,CV_8U,-1.0,255.0);
imshow("反向投影結果",tmp);
//獲取二值反向投影
finder.setThreshold(0.12f);
result1 = finder.find(image);
imshow("灰度圖像檢測結果(1)",result1);
//給源圖像加上矩形框
rectangle(image,Rect(360,55,40,50),Scalar(0,0,0));
imshow("源圖像",image);
//換一幅圖像:這幅圖像中也有大量雲彩
Mat image2 = imread("D:/picture/images/dog.jpg",0);
Mat result2 = finder.find(image2);
imshow("灰度圖像檢測結果(2)",result2);
//**************以上檢測因爲沒有用到顏色信息,所以效果很差**************
//獲取彩色直方圖
//讀取彩色圖像
ColorHistogram hc;
Mat color = imread("D:/picture/images/waves.jpg");
imshow("源圖像(1)",color);
//爲了減少計算量,使用colorReduce函數
color = hc.colorReduce(color,32);
//定義感興趣區域:雲彩
imageROI = color(Rect(0,0,165,75));
//獲取直方圖
Mat shist = hc.getHistogram(imageROI);
finder.setHistogram(shist);
finder.setThreshold(0.05f);
//獲取反向投影直方圖
result1 = finder.find(color);
imshow("彩色圖像檢測結果(1)",result1);
//讀取第二幅圖像並檢測
Mat color2 = imread("D:/picture/images/dog.jpg");
imshow("源圖像(2)",color2);
color2 = hc.colorReduce(color2,32);
result2 = finder.find(color2);
imshow("彩色圖像檢測結果(2)",result2);
//***************以上結果因爲考慮了顏色信息,所以效果比較好*********************
//僅考慮色度信息,構成直方圖,進行反向投影檢測
color = imread("D:/picture/images/waves.jpg");
imageROI = color(Rect(0,0,165,75));
Mat colorhist = hc.getHueHistogram(imageROI);
finder.setHistogram(colorhist);
finder.setThreshold(0.3f);
Mat hsv;
cvtColor(color,hsv,CV_BGR2HSV);
int ch[2]={1,2};
ch[0] = 0;
result1 = finder.find(hsv,0.0f,180.0f,ch,1);
imshow("使用色度的結果(1)",result1);
//換一幅圖片
color2 = imread("D:/picture/images/dog.jpg");
cvtColor(color2,hsv,CV_BGR2HSV);
result2 = finder.find(hsv,0.0f,180.0f,ch,1);
imshow("使用色度檢測結果(2)",result2);
waitKey(0);
return 0;
}
在主函數中,我們對幾種法相投影直方圖的方法進行了對比:
只用灰度圖像的直方圖;用彩色圖像直方圖;以及HSV空間中色圖信息的直方圖。