Python OpenCV 图像处理之直方图相关知识细节,学点细的

举报
梦想橡皮擦 发表于 2021/09/23 13:39:23 2021/09/23
【摘要】 Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 50 篇。 学在前面直方图在之前的博客中已经学习过一部分内容了,具体可以自行去回顾。直方图是图像处理过程中的一个分析工具,是使用灰度值或者从灰度级的角度统计图像的特征。如果从统计的角度看,直方图统计了图像各个灰度级出现的次数,直方图横坐标是像素灰度级,纵坐标是该灰度级的个数。如果存在一个 5x...

Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 50 篇。

学在前面

直方图在之前的博客中已经学习过一部分内容了,具体可以自行去回顾。

直方图是图像处理过程中的一个分析工具,是使用灰度值或者从灰度级的角度统计图像的特征。

如果从统计的角度看,直方图统计了图像各个灰度级出现的次数,直方图横坐标是像素灰度级,纵坐标是该灰度级的个数。

如果存在一个 5x5 的灰度图,那直方图就是对其中的各个像素值进行统计。

上面 2 张图就是关于直方图统计的说明,但直方图直观的呈现不是表格,而是图形,所以将上述数据进行绘制,即可得到直方图。
直方图中 x 轴表示的是 8 位位图的 256 个灰度级,y 轴表示的是对应灰度级的像素点格式。

基于此还要补充一个知识,叫做归一化的直方图,在很多时候,直方图中 x 轴表示灰度级,但是 y 轴表示灰度级出现的频率,具体就是将次数/总数,例如上表可以修改为:

直方图概念理解之后,还有几个新词需要注意下,DIMSBINSRANGE

  • DIMS:基于目前知识,只有一个灰度值,该值为 1,它表示的是收集直方图时,收集的参数数量;
  • RANGE:统计灰度级范围,对于灰度图,范围是 [0,255]
  • BINS:参数子级数目,大概含义可以理解为,将刚才的灰度级做分组处理,一般保持默认即可。

对于 BINS 参数在细说一下,对于灰度图像,灰度级的区间是 [0,255],其 BINS 默认是 256,如果按照 16 个灰度级为一组,可以分为 16 个 BINS

直方图绘制

Python OpenCV 图像处理之图像直方图,取经之旅第 25 天 本篇博客已经对直方图的绘制进行了说明,先来整体回顾一下。

代码基于 plt.hist() 函数实现直方图,该函数原型为 plt.hist(X,BINS)

运行下述代码,找图片的时候,尽量找颜色分布比较平均的,不要白色或者黑色所占区域特别大,否则得到的直方图效果不明显。

import cv2 as cv
import matplotlib.pyplot as plt

src = cv.imread("./2.png", 0)
cv.imshow("src", src)

plt.hist(src.ravel(), 256)
plt.show()

cv.waitKey()
cv.destroyAllWindows()

上述代码还有一个需要注意的就是 src.ravel() 函数了,该函数用于将二维数组降成一维数组,例如下图所示。

如果将 BINS 设置为 16 得到的结果如下。

Python OpenCV 中的直方图绘制

在 OpenCV 中使用 cv.calcHist 函数计算图像的直方图,本文重点说明一下该函数的返回值。

import cv2 as cv
import matplotlib.pyplot as plt

src = cv.imread("./2.png", 0)
cv.imshow("src", src)
hist = cv.calcHist([src], [0], None, [256], [0, 255])
print(hist)
print(len(hist))
cv.waitKey()
cv.destroyAllWindows()

hist 输出格式如下:

# 省略一部分输出数据
……
 [ 115.]
 [ 122.]
 [ 115.]
 [ 142.]
 [ 148.]
 [ 152.]
 [  61.]
 [   6.]
 [   0.]]
256

可以看到最后返回的 hist 是 256 个数字组成的数组,该数组内的元素是各个灰度级的统计个数。
得到 hist 之后,就可以通过 plt.plot 函数将其绘制出来啦。

import cv2 as cv
import matplotlib.pyplot as plt

src = cv.imread("./2.png", 0)
cv.imshow("src", src)
ret_hist = cv.calcHist([src], [0], None, [256], [0, 255])
print(ret_hist)
print(len(ret_hist))
plt.plot(ret_hist)
plt.show()
cv.waitKey()
cv.destroyAllWindows()

掩膜绘制直方图

接下来说明一下掩膜绘制直方图,通过掩膜就是选择图像的一部分区域进行绘制,掩膜白色区域表示透明,可显示图片,黑色区域表示不透明,无法显示图片。

掩膜的原理也可以翻阅以前的博客进行学习,总结下来就是下面两句话

  1. 原图像与掩膜中黑色位置对应的部分,这部分就不再显示了,灰度值被置为 0;
  2. 原图像与掩膜中白色位置对应的部分,保留原值。
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
src = cv.imread("./2.png", 0)
cv.imshow("src", src)
print(src.shape)
mask = np.zeros(src.shape, np.uint8)
# 前面是行,后面是列
mask[10:150, 40: 200] = 255
mask_img = cv.bitwise_and(src, mask)
cv.imshow("mask", mask_img)
ret_hist = cv.calcHist([src], [0], None, [256], [0, 255])
mask_hist = cv.calcHist([src], [0], mask, [256], [0, 255])

plt.plot(ret_hist)
plt.plot(mask_hist)
plt.show()
cv.waitKey()
cv.destroyAllWindows()

直方图均衡化相关知识补充

直方图均衡化,可以使图像拥有全部可能的灰度级,让像素值的灰度均匀分布,实现的具体步骤如下。

  1. 计算累计直方图
  2. 对累计直方图进行转换

可以参考下述灰度矩阵,该图像大小为 5x5,具有 8 个灰度级,范围是 [0,7],计算归一化的统计直方图与累计统计直方图分别如下。

基于上图,可以对原有灰度级空间进行转换,分为两种一种是在原有的灰度空间范围,即 [0,7],第二种是在更大范围进行转换。

首先看一下在原有范围内实现均衡化,用当前的累计概率乘以 7(灰度级的最大值),得到新的灰度级。

新灰度级与原灰度级个数可以对比得出。

未均衡化之前,灰度级如果区分成 2 组,数据如下:

  1. 0~3:4 个像素点,统计的像素个数是 19;
  2. 4~7:4 个像素点,统计的像素个数是 6。
    均衡化之后,灰度级如果区分成 2 组,数据如下:
  3. 0~3:4 个像素点,统计的像素个数是 11;
  4. 4~7:4 个像素点,统计的像素个数是 14。

如果希望得到更大范围的直方图,只需要将累计概率乘以更大的灰度级即可,具体可以自行尝试。

橡皮擦的小节

希望今天的 1 个小时你有所收获,我们下篇博客见~

相关阅读


技术专栏

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 94 / 100 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。