OpenCV图像直方图的理解和验证
2017-03-22 20:55
183 查看
昨天看到了图像直方图的东西,看了很久都没看懂,今天接着看,看懂了一部分,虽然理解的可能不完善,但也贴出来分享一下,帮助一下后人吧
首先贴出代码计算一维直方图
#include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace cv; #include<iostream> using namespace std; int main(int argc, char** argv) { Mat src, hsv; if (argc != 2 || !(src = imread(argv[1], 1)).data || src.channels() != 3) return -1; //颜色空间的转换BGR转HSV cvtColor(src, hsv, CV_BGR2HSV); //把H通道分为30个bin,把S通道等分为32bin int hbins = 30; //int sbins = 32; //int histSize[] = {hbins,sbins}; int histSize[] = { hbins }; //H的取值范围 0-179 float hranges[] = { 0, 180 }; //S的取值范围 0-255 //float sranges [] ={0,255}; //const float* ranges [] = {hranges,sranges}; const float* ranges[] = { hranges }; MatND hist; //我们根据图像第一个通道一维直方图 int channels[] = { 0 }; calcHist(&hsv, 1, channels, Mat(), hist, 1, histSize, ranges, true, false); //输出直方图 cout << hist << endl; return 0; } /* 运算的结果 =========================================== [60571; 12194; 180311; 1035634; 172737; 23720; 6520; 4793; 3744; 2124; 3045; 2641; 2475; 2896; 2941; 7646; 228236; 2946426; 3027; 1289; 3502; 1012; 1256; 640; 322; 200; 830; 865; 1160; 1645] 通过这个答案,我们可以看到,对应于hsv的通道0来说,histSize数组的的长度表示直方图的维数 这个程序中,histSize数组长度为1,只有一个元素,表明是一维直方图 histSize[0]的长度,即30表示这个一维直方图的所分成的桶的数目 输出直方图,之后,我们可以看到 [60571;12194;180311;1035634;172737;23720;6520;4793;3744;2124;3045;2641;2475;2896;2941;7646;228236;2946426;3027;1289;3502;1012;1256;640;322;200;830;865;1160;1645] 正好一共30个数据,表示的是相对于第i个桶的像素个数; 例如第一数是60571,因为算法中使用的是平均划分,即180/30等6, 所划分的区间为:[0,6),[6,12),[12,18)……[174,180) 表示的是H在[0,6)之间的像素点个数为60571 =========================================== */
然后进一步二维直方图的代码
#include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace cv; #include<iostream> using namespace std; int main(int argc, char** argv) { Mat src, hsv; if (argc != 2 || !(src = imread(argv[1], 1)).data || src.channels() != 3) return -1; //颜色空间的转换BGR转HSV cvtColor(src, hsv, CV_BGR2HSV); //把H通道分为30个bin,把S通道等分为32bin cout << hsv.rows<<" "<<hsv.cols<<" "<<hsv.rows*hsv.cols << endl; int hbins = 5; int sbins = 4; int histSize[] = { hbins, sbins }; //H的取值范围 0-179 float hranges[] = { 0, 180 }; //S的取值范围 0-255 float sranges[] = { 0, 256 }; //每一维直方图的大小 const float* ranges[] = { hranges, sranges }; MatND hist; //我们根据图像第一个通道和第二通道,计算二维直方图 int channels[] = { 0, 1 }; //第二个数组表示源图像的个数 //channels表示使用图像的对应通道数来进行多维直方图的计算,均匀划分 calcHist(&hsv, 1, channels, Mat(), hist, 2, histSize, ranges, true, false); //输出直方图 cout << hist << endl; return 0; } /* ==================== 480 640 307200 [71449, 12113, 641, 56; 138975, 35175, 1653, 32; 37541, 5286, 27, 0; 2825, 18, 0, 0; 1390, 19, 0, 0],这列数字的所有和正好是307200; ==================== 这是一个五行四列的二维直方图,第一行第一列的71449 代表的是H和S的值在[0,36)X[0,63),第二行第一列的12113代表H和S的值在[36,72)X[0,63) 。 也就是说这个二维数组的第一行为H的值在[0,36) 第二行H在[36,72) 第三行H在[72,108) 第四行H在[108,144) 第五行在[144,180) */
插入所使用的图像
在看代码的过程中,我始终弄不明白这个二维直方图是怎么计算的。虽然看到上面两个代码时,我已经感觉理解有5,6分了,但还是无法理解的透彻,所以我进一步的创建了一个矩阵,使用的是5X5的尺寸;数据如下
5 | 20 | 30 | 4 | 1 |
9 | 16 | 18 | 20 | 23 |
22 | 40 | 70 | 80 | 99 |
20 | 4 | 3 | 1 | 2 |
8 | 70 | 90 | 60 | 40 |
20 | 30 | 49 | 40 | 20 |
19 | 17 | 8 | 7 | 33 |
24 | 26 | 19 | 5 | 2 |
47 | 39 | 21 | 13 | 12 |
5 | 1 | 1 | 2 | 3 |
#include<highgui.h> #include<cv.h> using namespace std; using namespace cv; int main(void) { Mat src,src0,src1,src2; src = cvCreateMat(5, 5, CV_8UC3); src0 = cvCreateMat(5, 5, CV_8UC1); src1 = cvCreateMat(5, 5, CV_8UC1); src2 = cvCreateMat(5, 5, CV_8UC1); src.empty(); uchar data0[] = { 5,20,30,4,1, 9,16,18,20,23, 22,40,70,80,99, 20,4,3,1,2, 8,70,90,60,40 }; src0.data = data0; uchar data1[] = { 20,30,49,40,20, 19,17,8,7,33, 24,26,19,5,2, 47,39,21,13,12, 5,1,1,2,3 }; src1.data = data1; std::vector<cv::Mat> m; m.push_back(src0); m.push_back(src1); m.push_back(src2); merge(m, src); imshow("zhou",src); waitKey(0); imwrite("zhou.jpg",src); int hbins = 5; int sbins = 4; const int histSize[] = { hbins, sbins }; float hranges[] = { 0, 100 }; float sranges[] = { 0, 50 }; //为何必须是const const float *ranges[] = {hranges, sranges}; int channels[] = {0, 1}; MatND mHist; calcHist(&src,1,channels,Mat(),mHist,2,histSize,ranges,true,false); cout << mHist << endl; return 0; } /* ========================== [3, 6, 0, 2; 1, 1, 2, 2; 1, 0, 1, 0; 2, 1, 0, 0; 3, 0, 0, 0] 实验证明确实是每一行的和为11,6,2,3,3,与我在纸上所的出的结论一致 第一行的3,6,0,2也与我得出的结论一致。 这就说明,我们在计算二维直方图的过程中 计算每一维每个桶中的个数时,以本例子中的5X5,5X5两个通道为例。 使用均匀分布,所以0-100,在使用五个桶统计时,分别划分的区间是 [0,19],[20, 39],[40,59],[60,79],[80,99] { 5,20,30,4,1, 9,16,18,20,23, 22,40,70,80,99, 20,4,3,1,2, 8,70,90,60,40 } data0数组中的这25个数,分别投入相应的桶,可以得到各自桶中的数目为11,6,2,3,3 接着看第二维,以第一维第一个桶为例: 我们可以看到5,4,1,9,16,18,4,3,1,2,8 它们的位置分别为[1,1],[1,4],[1,5],[2,1],[2,2],[2,3],[4,2],[4,3],[4,4],[4,5],[5,1] 所以对这11个数再进行第二维等分的时候,由于4个桶,4等分 所以每个桶以此存放的区间是[0,12],[13,25],[26,39],[40,52] 我们以第二维第一个桶为例子,即在sranges中属于[0,12]的个数 { 20,30,49,40,20, 19,17,8,7,33, 24,26,19,5,2, 47,39,21,13,12, 5,1,1,2,3 }; ========================== 对于5对应的位置[1,1]而言,它的s值为20,不在[0,12]区间内 对于4对应的位置[1,4]而言,它的s值为40,不在[0,12]区间内 …… 而对于[2,3],它的s值为8,在区间[0,12]内 对于2对应的位置[4,5]而言,它的s值为12,在区间[0,12] 对于最后数字8而言,它对应的位置[5,1]而言,这个位置的s值为5,在区间[0,12] 所以对于第二维第一个桶[0,12],一共有3个像素满足 其他以此可得出结论 */
算法的结果的得出,在实验结果的分析中较为详细,尤其要感谢
前两个代码的来源,我也是在这个代码开始理解的
这个是opencv的官网上给出的例子
通道合并参考的是http://blog.csdn.net/rongrongyaofeiqi/article/details/52575717
另外要读懂直方图的含义,可以读读这个文档:http://academy.fengniao.com/533/5339606.html
我对于opencv图像直方图中,开始理解二维直方图是在这个文档上
http://lib.csdn.net/article/opencv/25683
弄懂了这些,大概大家的直方图就差不多理解了。
相关文章推荐
- OPenCV直方图均衡 和 图像缩放
- OpenCV学习笔记(八)——图像处理之直方图ImgProc
- opencv学习之(五)-直方图计算和绘制图像直方图
- Opencv Python版学习笔记(一)图像直方图
- 彩色图像直方图均衡化及颜色直方图显示 opencv实现 完整代码及详细注释
- opencv相关问题理解(4)直方图操作!!
- opencv中直方图的理解
- vs2008 + OpenCV 2.3.1 下 显示图像和直方图
- opencv二维图像直方图
- OpenCV之彩色直方图反向投影(图像相似性检测)
- 对OpenCV直方图的数据结构CvHistogram的理解
- 彩色图像直方图均衡化及颜色直方图显示 opencv实现 完整代码及详细注释
- OpenCV直方图图像增强算法
- OpenCV基于直方图特征的图像搜索
- 笔记:OpenCV 读取摄像头并显示图像的R、G、B三个通道的直方图
- opencv中直方图的理解
- 使用openCV画出一幅图像的直方图
- 基础学习笔记之opencv(19):有关图像序列的直方图计算
- 图像直方图均衡化和直方图匹配的理解
- OpenCV之灰度直方图反向投影(图像相似性检测)