详解为什么OpenCV的直方图计算函数calcHist()计算出的灰度值为255的像素个数为0
在使用OpenCV的直方图计算函数calcHist()时,发现灰度值为255的像素个数总是为0。
哪怕图像中灰度值为255的像素个数不为0,使用OpenCV的直方图计算函数calcHist()计算出的结果也为0。
一个例子如下:
//OpenCV版本3.0
//作者微信/QQ 2487872782
//有问题可以联系作者交流
//欢迎加入图像处理交流群,群号271891601
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
int main( )
{
// 读取源图像并转化为灰度图像
cv::Mat srcImage = cv::imread("F:/material/images/P0027-coin-01.png");
// 判断文件是否读入正确
if( !srcImage.data )
return 1;
cv::Mat ImageGray;
cv::cvtColor(srcImage,ImageGray,CV_BGR2GRAY);
// 定义直方图参数
const int channels[1]={0};
const int histSize[1]={256};
float pranges[2]={0,255};
const float* ranges[1]={pranges};
cv::MatND hist;
cv::calcHist(&ImageGray,1,channels,cv::Mat(),hist,1,histSize,ranges);
std::cout<<hist<<std::endl;
// 找到图像像素的最大值
double maxVal=0;
cv::minMaxLoc(ImageGray, 0, &maxVal, 0, 0);
std::cout<<"图像像素的最大值为:"<<maxVal<<std::endl;
return 0;
}
程序中用到的图片“P0027-coin-01.png”下载链接:
链接:https://pan.baidu.com/s/1k13r2DdhEXuXWlxV-IbxNA 提取码:kwgo
程序运行结果如下图所示:
从上面的截图可以看出,函数calcHist()计算出的灰度值为255的像素个数为0,但是从“图像像素的最大值为:255”这句话可以看出,其个数肯定不为0,至少是大于等于1的。
怎么会出现这样的情况呢?查阅函数calcHist()的官方文档,原型如下:
void cv::calcHist (const Mat * images,
int nimages,
const int * channels,
InputArray mask,
OutputArray hist,
int dims,
const int * histSize,
const float ** ranges,
bool uniform = true,
bool accumulate = false
)
注意到第8个参数“ranges”的说明:
“ranges Array of the dims arrays of the histogram bin boundaries in each dimension. When the histogram is uniform ( uniform =true), then for each dimension i it is enough to specify the lower (inclusive) boundary of the 0-th histogram bin and the upper (exclusive) boundary”
注意上面这段说明中我标注为红色的单词。从这段话可知,当采用均匀分割方法时,这个ranges确定的灰度统计范围区间是左闭右开区间。
为什么OpenCV的这个函数不将这个ranges确定的灰度统计范围区间设为左闭右闭区间呢?
这其实是一个国际惯例。在Python的numpy库中对ndarray进行切片操作时,区间也是左闭右开区间的。
为什么国际惯例要怎么做呢?我们从一个简单的例子来说。
假设我们把0~5的灰度值均匀划分成三个区间,因为灰度值都是整数,所以我们可以像下面这样划分:
第一种方法:
[0,1]∪[2,3]∪[4,5]
也可以像下面这样这样划分:
第二种方法:
[0,1)∪[1,3)∪[3,5)
这两种方法都存在一些不足。第一种方法我们发现假如这里不是灰度值,即不是整数,而是实数,那介于1~2和3~4之间的实数实际上就被遗漏了,这样的划分方法显然是存在严重的局限性,所以我们不用这种方法而用第二种方法。
第二种方法的问题就在于最右边的数会被遗漏,比如上面例子中的5。
两种相害取其轻,所以在各种语言和库中大家通常都用第二种方法,即采用左闭右开区间表示范围了。
当明白了第二种方法的问题后,我们就知道了,在上面的代码中,pranges[2]={0,255},histSize[1]={256},是按下面这样划分的:
把{0,255}按最小步进长度1进行划分,可以划分成下面这样:
[0,1)∪[1,2)∪[2,3)…[253,254)∪[254,255) ∪[255,255)
在上面的划分中,最后一个区间很奇怪,255既在里面又没在里面,此时这个函数把这个区间的统计个数直接置0处理。
从这个划分和处理方式可以看出,255灰度级的像素个数是不会被统计的,所以255灰度级的像素个数为0。
有时候我们又需要知道灰度值为255的像素个数,那怎么办呢?
很简单,把灰度值范围写为0~266就行了,即上面代码中的
float pranges[2]={0,255};
写为
float pranges[2]={0,256};
通过这样的改写,就按下面的区间进行划分了:
[0,1)∪[1,2)∪[2,3)…[253,254)∪[254,255) ∪[255,256)
我们对比一下:
范围为0~255时的划分:[0,1)∪[1,2)∪[2,3)…[253,254)∪[254,255)∪[255,255)
范围为0~256时的划分:[0,1)∪[1,2)∪[2,3)…[253,254)∪[254,255) ∪[255,256)
经过这样的改写,可见255灰度值就被包含进来了。
我们将上面的代码改成如下的代码,然后看下运行结果:
//OpenCV版本3.0
//作者微信/QQ 2487872782
//有问题可以联系作者交流
//图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
int main( )
{
// 读取源图像并转化为灰度图像
cv::Mat srcImage = cv::imread("F:/material/images/P0027-coin-01.png");
// 判断文件是否读入正确
if( !srcImage.data )
return 1;
cv::Mat ImageGray;
cv::cvtColor(srcImage,ImageGray,CV_BGR2GRAY);
// 定义直方图参数
const int channels[1]={0};
const int histSize[1]={256};
float pranges[2]={0,256};
const float* ranges[1]={pranges};
cv::MatND hist;
cv::calcHist(&ImageGray,1,channels,cv::Mat(),hist,1,histSize,ranges);
std::cout<<hist<<std::endl;
// 找到图像像素的最大值
double maxVal=0;
cv::minMaxLoc(ImageGray, 0, &maxVal, 0, 0);
std::cout<<"图像像素的最大值为:"<<maxVal<<std::endl;
return 0;
}
讲到这里,相信大家也就明白了“为什么OpenCV的直方图计算函数calcHist()计算出的灰度值为255的像素个数为0”。如果还是不明白,可以添加博主的微信/QQ 2487872782 交流。
相关文章
- python如何获取一个视频的帧率_python-使用OpenCV计算视频文件中的帧数?
- 一组基于OpenCV的图像处理函数
- 利用OpenCV对图像进行裁剪
- 利用OpenCV的函数calcHist()计算出图像的直方图数据后绘制图像的直方图
- 利用OpenCV的函数mean()和meanStdDev()计算图像的均值和标准差
- OpenCV函数subtract()使用心得及需要注意的地方
- 利用OpenCV实现旋转文本图像矫正的原理及OpenCV代码
- 对OpenCV的图像直方图计算函数calcHist()进行透彻解析
- 详解OpenCV的函数convexHull()和函数convexityDefects(),并利用它们)做凸包(凸壳)检测及凸包(凸壳)的缺陷检测
- 使用OpenCV计算图像的轮廓矩的代码
- OpenCV积分图函数:integral ()详解
- OpenCV进行图像相似度对比的几种办法
- opencv学习笔记——时间计算函数getTickCount()和getTickFrequency()
- opencv学习笔记——cv::line函数详解
- opencv学习笔记——minMaxIdx函数的含义及用法
- opencv中 int main(int argc,char* argv[])详解
- “recursion is detected during loading of “cv2“ binary extensions. Check OpenCV installation“问题的处理
- 如何在VS2019中配置OpenCV
- OpenCV图像的基础叠加