OpenCV学习二十五:calcBackProject 直方图反射函数

如果一幅图像的区域中显示的是一种结构纹理或者一个独特的物体,那么这个区域的直方图可以看作一个概率函数,他给的是某个像素属于该纹理或物体的概率。所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找测试图像中存在的该特征。

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 <opencv2/opencv.hpp>  
#include <stdio.h>  
#include <stdlib.h>  
#include <iostream>

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<n; i++)  
	{   
		line(histImage_before_threshold, Point((i-1)*line_w, img_h - cvRound(h_hist.at<float>(i-1)) - 200),  
			Point(i*line_w, img_h - cvRound(h_hist.at<float>(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<n; i++)  
	{   
		line(histImage, Point((i-1)*line_w, img_h - cvRound(h_hist.at<float>(i-1)) - 200),  
			Point(i*line_w, img_h - cvRound(h_hist.at<float>(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

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV提供了calcHist()函数来计算图像的直方图,它可以对整幅图像进行直方图处理,也可以对图像的某个区域进行直方图处理。 对图像的某个区域进行直方图处理,需要先定义一个ROI(Region of Interest)区域,然后将该区域作为参数传递给calcHist()函数。定义ROI区域的方法有两种: - 使用cv::Rect定义矩形区域 - 使用cv::Mat定义掩模区域 定义矩形区域的代码如下: ```cpp cv::Mat image = cv::imread("image.jpg"); cv::Rect roi_rect(100, 100, 200, 200); // (x, y, w, h) cv::Mat roi_image = image(roi_rect); cv::Mat hist; int channels[] = {0}; // 只处理图像的一个通道 int histSize[] = {256}; // 直方图的bin数量 float range[] = {0, 256}; // 像素值的范围 const float* ranges[] = {range}; cv::calcHist(&roi_image, 1, channels, cv::Mat(), hist, 1, histSize, ranges); ``` 定义掩模区域的代码如下: ```cpp cv::Mat image = cv::imread("image.jpg"); cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1); cv::Rect roi_rect(100, 100, 200, 200); // (x, y, w, h) mask(roi_rect) = cv::Scalar(255); // 将ROI区域设置为白色 cv::Mat hist; int channels[] = {0}; // 只处理图像的一个通道 int histSize[] = {256}; // 直方图的bin数量 float range[] = {0, 256}; // 像素值的范围 const float* ranges[] = {range}; cv::calcHist(&image, 1, channels, mask, hist, 1, histSize, ranges); ``` 在上述代码中,cv::calcHist()函数的第4个参数是掩模区域,如果不需要使用掩模区域,可以传递cv::Mat()作为该参数。 处理完直方图之后,可以使用cv::normalize()函数直方图归一化到一定范围内,例如[0,1]或[0,255]。归一化后的直方图可以用于图像增强、颜色量化等应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值