如果一幅图像的区域中显示的是一种结构纹理或者一个独特的物体,那么这个区域的直方图可以看作一个概率函数,他给的是某个像素属于该纹理或物体的概率。所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找测试图像中存在的该特征。
void cv::calcBackProject
(
const Mat * images,
int nimages,
const int * channels,
InputArray hist,
OutputArray backProject,
const float ** ranges,
double scale = 1,
bool uniform = true
)
const Mat* images:输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
int nimages:输入图像的数量
const int* channels:用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数
InputArray hist:输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
OutputArray backProject:目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
const float ranges**:直方图中每个维度bin的取值范围
double scale=1:可选输出反向投影的比例因子
bool uniform=true:直方图是否均匀分布(uniform)的标识符,有默认值true
#include
#include
#include
#include
using namespace cv;
using namespace std;
char file[] = "10.jpg";
char file2[] = "11.jpg";
string convertToString(double d);
int main(int argc, char** argv)
{
Mat img = imread(file, -1);
pyrDown(img, img, Size(img.cols/2, img.rows/2));
pyrDown(img, img, Size(img.cols/2, img.rows/2));
pyrDown(img, img, Size(img.cols/2, img.rows/2));
imshow("1",img);imwrite("img.jpg", img);
//对比阀值前后的效果差异
///
Mat hsv, hue;
int nChannels[] = {0, 0};
cvtColor(img, hsv, CV_BGR2HSV);
hue.create(hsv.size(), hsv.depth());
mixChannels(&hsv, 1, &hue, 1, nChannels, 1);//复制特定通道的图像
imshow("hue_before_threshold",hue);imwrite("hue_before_threshold.jpg", hue);
Mat h_hist;
int n = 15;
float range[] = {0, 180};
const float *ranges = {range};
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &n, &ranges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
int img_h = 700;//图片的高(行数)
int img_w = 512;//图片的宽(列数)
int line_w = 30;//线宽
Mat histImage_before_threshold(img_h, img_w, CV_8UC3, Scalar(0,0,0));//图片(画布)大小
for (int i=1; i(i-1)) - 200),
Point(i*line_w, img_h - cvRound(h_hist.at(i)) - 200),Scalar(0,0,255), 2);
}
imshow("histImage_before_threshold", histImage_before_threshold);imwrite("histImage_before_threshold.jpg", histImage_before_threshold);
Mat backPrjImage;
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &ranges, 1, true);
imshow("backPrjImage_before_threshold", backPrjImage);imwrite("backPrjImage_before_threshold.jpg", backPrjImage);
//阀值后
threshold(hue, hue, 80, 180, THRESH_BINARY);
imshow("hue",hue);imwrite("hue.jpg", hue);
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &n, &ranges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
Mat histImage(img_h, img_w, CV_8UC3, Scalar(0,0,0));//图片(画布)大小
for (int i=1; i(i-1)) - 200),
Point(i*line_w, img_h - cvRound(h_hist.at(i)) - 200),Scalar(0,0,255), 2);
}
imshow("histImage", histImage);imwrite("histImage.jpg", histImage);
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &ranges, 1, true);
imshow("backPrjImage", backPrjImage);imwrite("backPrjImage.jpg", backPrjImage);
//这里测试下同一个物体在两个图片里的效果
//注意,虽然是两个图片,这里我取得是同一相机拍摄的图片
Mat img2 = imread(file2, -1);
pyrDown(img2, img2, Size(img2.cols/2, img2.rows/2));
pyrDown(img2, img2, Size(img2.cols/2, img2.rows/2));
pyrDown(img2, img2, Size(img2.cols/2, img2.rows/2));
imshow("2",img2);imwrite("img2.jpg", img2);
Mat hsv2, hue2;
int nChannels2[] = {0, 0};
cvtColor(img2, hsv2, CV_BGR2HSV);
hue2.create(hsv2.size(), hsv2.depth());
mixChannels(&hsv2, 1, &hue2, 1, nChannels2, 1);
threshold(hue2, hue2, 70, 180, THRESH_BINARY);//注意,两个图片的预处理是不一样的
imshow("hue2",hue2);imwrite("hue2.jpg", hue2);
Mat backPrjImage2;
calcBackProject(&hue2, 1, 0, h_hist, backPrjImage2, &ranges, 1, true);
imshow("backPrjImage2", backPrjImage2);imwrite("backPrjImage2.jpg", backPrjImage2);
waitKey();
return 1;
}
总结:calcBackProject 函数是基于概率的一个反射函数,因此必须要求图片的概率特征明显,所以光源以及一定的预处理是必须的。其次可以通过预定良好的模板对物体进行识别,反射后得到的图片非常利于特征的抓取以及物体识别。
img.jpg
hue_before_threshold.jpg
histImage_before_threshold.jpg
backPrjImage_before_threshold.jpg
hue.jpg
histImage.jpg
backPrjImage.jpg
img2.jpg
hue2.jpg
backPrjImage2.jpg