直方图的相关知识(第九天)
---直方图的理解---
貌似小学和初中及见过柱状图,高中就开始学习柱状图了,我们当时的学习就是为了很明显的看出数据的变化和对比,直观明了。
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)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
说一下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};定义的矩阵是一个四通道的
-------------------------------------------
个性签名:衣带渐宽终不悔,为伊消得人憔悴!
如果觉得这篇文章对你有小小的帮助的话,记得关注再下的公众号,同时在右下角点个“推荐”哦,博主在此感谢!