影醉阏轩窗

衣带渐宽终不悔,为伊消得人憔悴。
扩大
缩小

直方图的相关知识(第九天)

---直方图的理解---


貌似小学和初中及见过柱状图,高中就开始学习柱状图了,我们当时的学习就是为了很明显的看出数据的变化和对比,直观明了。

opencv用直方图的叫法代替柱状图,其实一个意思!其作用不单单是看着好看,也是为了图像的操作更加方便,比如用直方图显示之后,想把图像增强(明亮一点),那就直接在直方图上操作,然后通过映射关系作用到图像上,这样做非常简单。我现在也只知道这一个用途,其他的以后再补充。。。

---直方图的绘制---

   1.dims: 需要统计的特征的数目,channels (),在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像)。

   2.bins: 每个特征空间 子区段 的数目,在上例中, bins = 16

   3.range: 每个特征空间的取值范围,在上例中, range = [0,255]  4.  b1--b16所占用X的距离是我们自己定义的,b1--b16占用的高度也是我们自己定义的(这个高度是比例高度,就是相对于图像的比例高度)

 1 #include<iostream>
 2 #include <opencv2/opencv.hpp>
 3 #include <math.h>
 4 using namespace cv;
 5 using namespace std;
 6 
 7 int main(int argc, char**argv)
 8 {
 9     const Mat input_image = imread("1.jpg");
10     if (input_image.data == NULL) {
11         return -1; cout << "can't open image.../";
12     }
13     imshow("Sourse image", input_image);
14     Mat output_image;
15     vector<Mat> vector_test;
16     split(input_image, vector_test);
17 
18     const int histSize = 255;
19     float range[] = { 0, 255 };
20     const float* histRange = { range };
21     bool uniform = true;
22     bool accumulate = false;
23     Mat r_hist, g_hist, b_hist;
24     calcHist(&vector_test[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, true, false);
25     calcHist(&vector_test[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, true, false);
26     calcHist(&vector_test[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, true, false);
27     
28     int hist_h = 400;
29     int hist_w = 512;
30     int hist_b = hist_w / histSize;
31     Mat histImage(hist_w,hist_h,CV_8UC3,Scalar(0,0,0));
32     normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
33     normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
34     normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
35 
36     for (size_t i = 1; i < histSize; i++)
37     {
38         line(histImage, Point((i - 1)*hist_b, hist_h - cvRound(b_hist.at<float>(i - 1))), Point(i*hist_b, cvRound(hist_h - b_hist.at<float>(i))), Scalar(0, 255, 255));
39         line(histImage, Point((i - 1)*hist_b, hist_h - cvRound(g_hist.at<float>(i - 1))), Point(i*hist_b, cvRound(hist_h - g_hist.at<float>(i))), Scalar(50, 25, 255));
40         line(histImage, Point((i - 1)*hist_b, hist_h - cvRound(r_hist.at<float>(i - 1))), Point(i*hist_b, cvRound(hist_h - r_hist.at<float>(i))), Scalar(155, 25, 55));
41     }
42     imshow("Destinate3 image", histImage);
43     waitKey(0);
44     return 0;
45 }

补充说明一下几个点:

A. 这个写法是opencv的API调用要求的,calcHist()的histRange是一个二级指针float**,这个定义是有原因的,因为这个函数想要的是a=[0,255],b=[1,200],c=[3,100]...这些数全部放在一个变量里面调用,其中一级指针只能显示一个范围,二级指针可以显示多个数的范围,其实目的就是为了函数的书写好看而且方便。如果你用一个类在里面定义多个数组也是可以的,但是麻烦啊!其实opencv还给了一个重载类型:const std::vector<float>& ranges,你用这个容器也是可以的。

1 float range[] = { 0, 255 };
2 const float* histRange = { range };

 

B.这个hist_b的作用是每个小块在X轴占用的长度,其中dims=256,也就代表有256个小段,hist_h就代表宽度,两者相处之后那就是把整个图像的X轴充满,显示好看。

1 int hist_b = hist_w / histSize;

 

C.归一化的作用:因为图像是个很庞大的数据体,假如在[200-205]这个像素段有10000像素点,而在[100-105]只有1个像素点,那么在图上显示怎么办呢?一个高的看不见,一个低的看不见,这不是最主要的,主要的是这个显示图你怎么选?有10000个高度的图像吗?归一化之后都在你定义的图像高度范围,这样显示才好看。

1 normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
2 normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
3 normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());

 D.二维直方图:这个比较特殊,如果是H-S图像那就整体计算(其实这个图像的色彩空间我不了解没办法解释,以后用到再去查资料),如果是RGB那就得分开计算R/G/B图像的每个空间。

 

 

 

 ---直方图的比较---

这部分内容主要是四个概率比较公式,直接看看公式和参考数学书本就行了。。。

 

---直方图反向投影---

 

反向投影的理解(引用http://m.blog.csdn.net/article/details?id=18308681)

 

1.经过上面的分析,下面给出反向投影的作用:反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
2.反向投影查找原理:查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
3.反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。
可以这样理解:对于calcBackProjectPatch,也就是是基于块的反向投影形式,利用直方图做匹配,类似于模板匹配,只不过这些模板转换为直方图,而原图中以某点为基准,抠出来作对比的部分也转换为直方图,两个直方图作匹配,匹配的结果作为此点的值。 结果会是一张灰度图。
4.如果输入图像和模板图像一样大,那么反向投影相当于直方图对比。如果输入图像比模板图像还小,无法进行反向投影。

 

说一下mixChannels()这个函数:

 1 Mat rgba( 3, 4, CV_8UC4, Scalar(1,2,3,4) );  
 2 Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );  
 3 Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );  
 4   
 5 // forming an array of matrices is a quite efficient operation,  
 6 // because the matrix data is not copied, only the headers  
 7 Mat out[] = { bgr, alpha };  
 8 // rgba[0] -> bgr[2], rgba[1] -> bgr[1],  
 9 // rgba[2] -> bgr[0], rgba[3] -> alpha[0]  
10 int from_to[] = { 0,2, 1,1, 2,0, 3,3 };  
11 mixChannels( &rgba, 1, out, 2, from_to, 4 );

运行的结果:

其实这个函数最难理解的就是那个映射关系:from_to[]  :

 比如我要把一个两个通道的RGB分离成两个R、G、B----->>>int from_to[] = {0,0,1,1,2,2};其实从这个上面看不出来是把RGB分离成(RG、B)或者(R、GB)或者(R、G、B)的,就是看你怎么定义接受矩阵了,两个通道+一个通道=(RG、B)、一个通道+两个通道=(R、GB)、一个通道+一个通道+一个通道=(R、G、B)。

比如把RGBA分裂成RGB+A,  int from_to[] = {0,0,1,1,2,2,3,3};定义接受矩阵就是三个通道+一个通道

比如把RG和BA加起来为GRAB,int from_to[] = {0,1,1,0,2,3,3,2};定义的矩阵是一个四通道的

 

posted on 2017-04-08 20:46  影醉阏轩窗  阅读(...)  评论(...编辑  收藏

导航

/* 线条鼠标集合 */ /* 鼠标点击求赞文字特效 */ //带头像评论