OpenCV4入门教程085:图像直方图反向投影

索引地址:系列索引

反向投影可以用来做图像分割,寻找感兴趣区间。它会输出与输入图像大小相同的图像,每一个像素值代表了输入图像上对应点属于目标对象的概率,简言之,输出图像中像素值越高的点越可能代表想要查找的目标。直方图投影经常与camshift(追踪算法)算法一起使用。

算法实现的方法,首先要为包含我们感兴趣区域的图像建立直方图(样例要找一片草坪,其他的不要)。被查找的对象最好是占据整个图像(图像里全是草坪)。最好使用颜色直方图,物体的颜色信息比灰度图像更容易被分割和识别。再将颜色直方图投影到输入图像查找目标,也就是找到输入图像中每一个像素点的像素值在直方图中对应的概率,这样就得到一个概率图像,最后设置适当的阈值对概率图像进行二值化。

这个过程可以笼统的说与计算图像直方图相反,由图像计算直方图的过程比较容易理解,就是统计图像中像素分布的概率,而反向投影正好反过来,是通过直方图来形成图像,其步骤有点类似于直方图均衡化,只不过直方图均衡化是将图像中的每个像素值由一个地方迁移到另外一个地方,而反向投影是直接去直方图中的值,如某种像素值在直方图中的值越大,在进行反向投影操作时其对应的像素值越大即月亮,反过来,如果某灰度值所占面积越小,其反向投影后像素值就会更小。

举个简单的例子来帮助理解这段话的意思。例如灰度图像的像素值如下如下:

1
2
3
4
0 1 2 3
4 5 6 7
8 9 10 11
8 9 14 15

对图像进行直方图统计(bin指定的区间为[0,3),[4,7),[8,11),[12,16))如下所示:

1
Histogram=4 4 6 2

也就是说在[0,3)这个区间的像素值有4个,其它含义相同

根据上述的直方图进行反向投影,得到反向投影图像像素值如下:

1
2
3
4
5
Back_Projection=
4 4 4 4
4 4 4 4
6 6 6 6
6 6 2 2

例如位置(0,0)上的像素值为0,对应的bin为[0,3),所以反向直方图在该位置上的值这个bin的值4,而在位置(3,3)上的像素为15,其在直方图中的统计为2,故其反向投影图像中的像素为2。

函数原型:

1
2
3
4
5
6
7
8
9
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

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

//-----------------------------------【宏定义部分】--------------------------------------------
// 描述:定义一些辅助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_NAME1 "Original" //为窗口标题定义的宏

//-----------------------------------【全局变量声明部分】--------------------------------------
// 描述:全局变量声明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage;
Mat g_hsvImage;
Mat g_hueImage;
int g_bins = 30; //直方图组距

//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
void on_BinChange(int, void *);

//--------------------------------------【main( )函数】-----------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main() {
//【1】读取源图像,并转换到 HSV 空间
g_srcImage = imread("1.jpg", 1);
if (!g_srcImage.data) {
printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n");
return false;
}
cvtColor(g_srcImage, g_hsvImage, COLOR_BGR2HSV);

//【2】分离 Hue 色调通道
g_hueImage.create(g_hsvImage.size(), g_hsvImage.depth());
int ch[] = {0, 0};
mixChannels(&g_hsvImage, 1, &g_hueImage, 1, ch, 1);

//【3】创建 Trackbar 来输入bin的数目
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
createTrackbar("色调组距 ", WINDOW_NAME1, &g_bins, 180, on_BinChange);
on_BinChange(0, 0); //进行一次初始化

//【4】显示效果图
imshow(WINDOW_NAME1, g_srcImage);

// 等待用户按键
waitKey(0);
return 0;
}

//-----------------------------------【on_HoughLines( )函数】--------------------------------
// 描述:响应滑动条移动消息的回调函数
//---------------------------------------------------------------------------------------------
void on_BinChange(int, void *) {
//【1】参数准备
MatND hist;
int histSize = MAX(g_bins, 2);
float hue_range[] = {0, 180};
const float *ranges = {hue_range};

//【2】计算直方图并归一化
calcHist(&g_hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());

//【3】计算反向投影
MatND backproj;
calcBackProject(&g_hueImage, 1, 0, hist, backproj, &ranges, 1, true);

//【4】显示反向投影
imshow("BackProject", backproj);

//【5】绘制直方图的参数准备
int w = 400;
int h = 400;
int bin_w = cvRound((double)w / histSize);
Mat histImg = Mat::zeros(w, h, CV_8UC3);

//【6】绘制直方图
for (int i = 0; i < g_bins; i++) {
rectangle(histImg, Point(i * bin_w, h),
Point((i + 1) * bin_w, h - cvRound(hist.at<float>(i) * h / 255.0)),
Scalar(100, 123, 255), -1);
}

//【7】显示直方图窗口
imshow("Histogram", histImg);
}

测试结果:

bakcproject


OpenCV4入门教程085:图像直方图反向投影
https://feater.top/opencv/opnecv-image-histogram-backproject/
作者
JackeyLea
发布于
2020年6月21日
许可协议