在使用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 交流。

详解为什么OpenCV的直方图计算函数calcHist()计算出的灰度值为255的像素个数为0相关推荐

  1. 神奇。cv2.calcHist()函数返回值,灰度值为255的像素个数看似为0。

    cv2.calcHist()函数返回值,灰度值为255的像素个数看似为0的细节探究. import cv2 import matplotlib.pyplot as plt import numpy a ...

  2. python opencv 直方图均衡_详解python OpenCV学习笔记之直方图均衡化

    本文介绍了python OpenCV学习笔记之直方图均衡化,分享给大家,具体如下: 官方文档 – https://docs.opencv.org/3.4.0/d5/daf/tutorial_py_hi ...

  3. python中squeeze函数_详解pytorch中squeeze()和unsqueeze()函数介绍

    squeeze的用法主要就是对数据的维度进行压缩或者解压. 先看torch.squeeze() 这个函数主要对数据的维度进行压缩,去掉维数为1的的维度,比如是一行或者一列这种,一个一行三列(1,3)的 ...

  4. 前瞻: 拥抱量子计算时代!详解2020年全球十大杰出量子计算公司

    拥抱量子计算时代!详解2020年全球十大杰出量子计算公司 https://blog.csdn.net/Qtumist/article/details/105462463 分类专栏: 新闻资讯 文章标签 ...

  5. c语言标准库详解(七):字符串函数string.h

    c语言标准库详解(七):字符串函数<string.h> 头文件<string.h>中定义了两组字符串函数.第一组函数的名字以 str 开头:第二组函数的名字以 mem 开头.除 ...

  6. python中setattr用法_详解Python的hasattr() getattr() setattr() 函数使用方法

    hasattr(object, name) 判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False. 需要注意的是name要用括号括起来 ...

  7. js入门·表单详解一(修改表单属性,修改表单元素值)

    实在javascript入门·Document对象入门讲解(访问表单,创建新页,获取页标题) 一文中,我们已经把表单的一些基本访问等弄清楚了,下面我们深入的学下表单的属性以及对表单元素的简单操作! 演 ...

  8. (详解)opencv里的cv2.resize改变图片大小Python

    cv2.resize函数结构: image = cv2.resize(src, dsize, dst=None, fx=None, fy=None, interpolation=None) 功能: c ...

  9. 【ORACLE】详解oracle数据库UTL_RAW包各个函数的模拟算法

    前言 这篇文章可能是你至今(2022-02-11)能在互联网看到的,关于utl_raw包的逻辑说得最深入的一篇文章了. 由于最近在复刻oracle中自带的包到其他数据库,因此需要对oracle中的包的 ...

最新文章

  1. SignalR的另类实现技巧
  2. 使用 /proc 文件系统来访问 Linux 内核的内容
  3. php fpm xcache,php扩展xcache
  4. 征战蓝桥 —— 2013年第四届 —— C/C++A组第8题——买不到的数目
  5. Factory Method模式的误区:Factory Method模式是简化版的Abstract Factory吗?
  6. HTML5新特性-自定义属性(data-set)
  7. php获取页面中的指定内容,php 获取页面中指定内容的实现类
  8. MKcms4.4.3仿品优影视网站系统完整开源版自动采集可设置视频收费
  9. linux路由内核实现分析(一)----邻居子节点(2)
  10. c#位数不够0补充完_Java与C#比较,哪个语言更是适合你?
  11. Lync Server 2013企业版部署系列之一:部署环境介绍
  12. 环境判断:区别h5打开还是weixin打开?
  13. WAP 手机及开发技术调研(转)
  14. Ubuntu 安装显卡驱动
  15. Verilog 级联IIR滤波器设计
  16. Android Studio连接夜神模拟器
  17. 记一次产品需求:图片等比缩放和CSS自适应布局16:9
  18. 采样频率和带宽的关系_磁共振成像带宽
  19. 十张图,看数据分析如何赋能销售
  20. EasyExcel初了解

热门文章

  1. Webpack4 HappyPack增加编译进程数
  2. java中int转long
  3. 【Java】int类型强制转换成long
  4. 索尼便携无线随身存储服务器,新玩意儿!!索尼的黑科技 便携式个人空调设备,只为怕热的你们...
  5. CTF实战(隐写术):欢迎来到地狱
  6. 欠条的期限法律有没有明确的规定
  7. AI芯片发展现状及前景分析
  8. 期望的一个性质---可加性
  9. 7-2 简单计算器 分数 13分
  10. cinrad修改雷达图背景色