IntelliJ IDEA 跨域 npm安装 makefile acm codeigniter dictionary generics webview uiviewcontroller pip static drupal7 testng bootstrap时间轴 python转16进制 cpm怎么计算 pcie高速固态硬盘 svn更新本地代码 python开发安卓应用 kubernetes集群 python操作mysql java类与对象 java重载和重写 java正则表达式详解 java语言代码大全 java集合图 java线程死锁 java八大基本数据类型 java中long java字符 java循环list java接口调用 opengl编程指南 redis入门指南 hexworkshop 计价软件 摩斯密码翻译器 小米手环充电多久 linux运维之道
当前位置: 首页 > 学习教程  > python

[Python图像处理] 三十七.OpenCV直方图统计两万字详解(掩膜直方图、灰度直方图对比、黑夜白天预测)

2021/2/7 10:24:31 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,后期结合深度学习研究图像识别、图像分类应用。希望文章对您有所帮助…

该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,后期结合深度学习研究图像识别、图像分类应用。希望文章对您有所帮助,如果有不足之处,还请海涵~

前面一篇文章介绍了图像几何变换,包括:图像平移变换、图像缩放变换、图像旋转变换、图像镜像变换、图像仿射变换和图像透视变换;这篇文章将详细介绍直方图统计,包括Matplotlib和OpenCV绘制直方图、掩膜直方图、灰度直方图对比及通过直方图预测黑夜白天,万字长文整理,希望对您有所帮助。 同时,该部分知识均为作者查阅资料撰写总结,并且开设成了收费专栏,为小宝赚点奶粉钱,感谢您的抬爱。当然如果您是在读学生或经济拮据,可以私聊我给你每篇文章开白名单,或者转发原文给你,更希望您能进步,一起加油喔~

  • https://github.com/eastmountyxz/ImageProcessing-Python

文章目录

  • 一.图像直方图概述
  • 二.Matplotlib绘制直方图
  • 三.OpenCV绘制直方图
  • 四.掩膜直方图
  • 五.图像灰度变换直方图对比
    • 1.灰度上移变换图像直方图对比
    • 2.灰度减弱图像直方图对比
    • 3.图像反色变换直方图对比
    • 4.图像对数变换直方图对比
    • 5.图像阈值化处理直方图对比
  • 六.图像H-S直方图
  • 七.直方图判断黑夜白天
  • 八.总结

前文参考:

  • [Python图像处理] 一.图像处理基础知识及OpenCV入门函数
  • [Python图像处理] 二.OpenCV+Numpy库读取与修改像素
  • [Python图像处理] 三.获取图像属性、兴趣ROI区域及通道处理
  • [Python图像处理] 四.图像平滑之均值滤波、方框滤波、高斯滤波及中值滤波
  • [Python图像处理] 五.图像融合、加法运算及图像类型转换
  • [Python图像处理] 六.图像缩放、图像旋转、图像翻转与图像平移
  • [Python图像处理] 七.图像阈值化处理及算法对比
  • [Python图像处理] 八.图像腐蚀与图像膨胀
  • [Python图像处理] 九.形态学之图像开运算、闭运算、梯度运算
  • [Python图像处理] 十.形态学之图像顶帽运算和黑帽运算
  • [Python图像处理] 十一.灰度直方图概念及OpenCV绘制直方图
  • [Python图像处理] 十二.图像几何变换之图像仿射变换、图像透视变换和图像校正
  • [Python图像处理] 十三.基于灰度三维图的图像顶帽运算和黑帽运算
  • [Python图像处理] 十四.基于OpenCV和像素处理的图像灰度化处理
  • [Python图像处理] 十五.图像的灰度线性变换
  • [Python图像处理] 十六.图像的灰度非线性变换之对数变换、伽马变换
  • [Python图像处理] 十七.图像锐化与边缘检测之Roberts算子、Prewitt算子、Sobel算子和Laplacian算子
  • [Python图像处理] 十八.图像锐化与边缘检测之Scharr算子、Canny算子和LOG算子
  • [Python图像处理] 十九.图像分割之基于K-Means聚类的区域分割
  • [Python图像处理] 二十.图像量化处理和采样处理及局部马赛克特效
  • [Python图像处理] 二十一.图像金字塔之图像向下取样和向上取样
  • [Python图像处理] 二十二.Python图像傅里叶变换原理及实现
  • [Python图像处理] 二十三.傅里叶变换之高通滤波和低通滤波
  • [Python图像处理] 二十四.图像特效处理之毛玻璃、浮雕和油漆特效
  • [Python图像处理] 二十五.图像特效处理之素描、怀旧、光照、流年以及滤镜特效
  • [Python图像处理] 二十六.图像分类原理及基于KNN、朴素贝叶斯算法的图像分类案例
  • [Python图像处理] 二十七.OpenGL入门及绘制基本图形(一)
  • [Python图像处理] 二十八.OpenCV快速实现人脸检测及视频中的人脸
  • [Python图像处理] 二十九.MoviePy视频编辑库实现抖音短视频剪切合并操作
  • [Python图像处理] 三十.图像量化及采样处理万字详细总结(推荐)
  • [Python图像处理] 三十一.图像点运算处理两万字详细总结(灰度化处理、阈值化处理)
  • [Python图像处理] 三十二.傅里叶变换(图像去噪)与霍夫变换(特征识别)万字详细总结
  • [Python图像处理] 三十三.图像各种特效处理及原理万字详解(毛玻璃、浮雕、素描、怀旧、流年、滤镜等)
  • [Python图像处理] 三十四.数字图像处理基础与几何图形绘制万字详解(推荐)
  • [Python图像处理] 三十五.OpenCV图像处理入门、算数逻辑运算与图像融合(推荐)
  • [Python图像处理] 三十六.OpenCV图像几何变换万字详解(平移缩放旋转、镜像仿射透视)
  • [Python图像处理] 三十七.OpenCV和Matplotlib绘制直方图万字详解(掩膜直方图、H-S直方图、黑夜白天判断)

图像灰度直方图(Histogram)是灰度级分布的函数,是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率并绘制相关图形。本章主要讲解Matplotlib和OpenCV绘制直方图的两种方法,对比了灰度处理算法前后的直方图,实现掩膜直方图绘制、图像H-S直方图、直方图判断黑夜白天等内容。

一.图像直方图概述

灰度直方图是灰度级的函数,描述的是图像中每种灰度级像素的个数,反映图像中每种灰度出现的频率。假设存在一幅6×6像素的图像,接着统计其1至6灰度级的出现频率,并绘制如图1所示的柱状图,其中横坐标表示灰度级,纵坐标表示灰度级出现的频率。

在这里插入图片描述

如果灰度级为0-255(最小值0为黑色,最大值255为白色),同样可以绘制对应的直方图,如图2所示,左边是一幅灰度图像(Lena灰度图),右边是对应各像素点的灰度级频率。

在这里插入图片描述

为了让图像各灰度级的出现频数形成固定标准的形式,可以通过归一化方法对图像直方图进行处理,将待处理的原始图像转换成相应的标准形式[3]。假设变量r表示图像中像素灰度级,归一化处理后会将r限定在下述范围:

在这里插入图片描述

在灰度级中,r为0时表示黑色,r为1时表示白色。对于一幅给定图像,每个像素值位于[0,1]区间之内,接着计算原始图像的灰度分布,用概率密度函数P®实现。为了更好地进行数字图像处理,必须引入离散形式。在离散形式下,用rk表示离散灰度级,P(rk)代替P®,并满足公式(2)。

在这里插入图片描述

公式中,nk为图像中出现rk这种灰度的像素数,n是图像中像素总数,是概率论中的频数,l是灰度级总数(通常l为256级灰度)。接着在直角坐标系中做出rk和P(rk)的关系图,则成为灰度级的直方图。

假设存在一幅3×3像素的图像,其像素值如公式(9-3)所示,则归一化直方图的步骤如下:

在这里插入图片描述

  • 首先统计各灰度级对应的像素个数。用x数组统计像素点的灰度级,y数组统计具有该灰度级的像素个数。其中,灰度为1的像素共3个,灰度为2的像素共1个,灰度为3的像素共2个,灰度为4的像素共1个,灰度为5的像素共2个。

在这里插入图片描述

  • 接着统计总像素个数,如公式(5)所示。

在这里插入图片描述

  • 最后统计各灰度级的出现概率,通过公式(6)进行计算,其结果如下:

在这里插入图片描述

绘制的归一化图行如图3所示,横坐标表示图像中各个像素点的灰度级,纵坐标表示出现这个灰度级的概率。

在这里插入图片描述

直方图被广泛应用于计算机视觉领域,在使用边缘和颜色确定物体边界时,通过直方图能更好地选择边界阈值,进行阈值化处理。同时,直方图对物体与背景有较强对比的景物的分割特别有用,可以应用于检测视频中场景的变换及图像中的兴趣点,简单物体的面积和综合光密度IOD也可以通过图像的直方图计算而得。


二.Matplotlib绘制直方图

Matplotlib是Python强大的数据可视化工具,主要用于绘制各种2D图形。本小节Python绘制直方图主要调用matplotlib.pyplot库中hist()函数实现,它会根据数据源和像素级绘制直方图。其函数主要包括五个常用的参数,如下所示:

  • n, bins, patches = plt.hist(arr, bins=50, normed=1, facecolor=‘green’, alpha=0.75)
    – arr表示需要计算直方图的一维数组
    – bins表示直方图显示的柱数,可选项,默认值为10
    – normed表示是否将得到的直方图进行向量归一化处理,默认值为0
    – facecolor表示直方图颜色
    – alpha表示透明度
    – n为返回值,表示直方图向量
    – bins为返回值,表示各个bin的区间范围
    – patches为返回值,表示返回每个bin里面包含的数据,是一个列表

图像直方图的Python实现代码如下所示,该示例主要是通过matplotlib.pyplot库中的hist()函数绘制的。注意,读取的“picture.bmp”图像的像素为二维数组,而hist()函数的数据源必须是一维数组,通常需要通过函数ravel()拉直图像。

# -*- coding: utf-8 -*-
# By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np  
import matplotlib.pyplot as plt
 
#读取图片
img = cv2.imread('lena.bmp')

#灰度转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
#直方图均衡化处理
result = cv2.equalizeHist(gray)

#显示图像
plt.subplot(221)
plt.imshow(gray, cmap=plt.cm.gray), plt.axis("off"), plt.title('(a)') 
plt.subplot(222)
plt.imshow(result, cmap=plt.cm.gray), plt.axis("off"), plt.title('(b)') 
plt.subplot(223)
plt.hist(img.ravel(), 256), plt.title('(c)') 
plt.subplot(224)
plt.hist(result.ravel(), 256), plt.title('(d)') 
plt.show()

读取显示的“lena”灰度图像如图4所示。

在这里插入图片描述

最终的灰度直方图如图5所示,它将Lena图256级灰度和各个灰度级的频数绘制出来,其中x轴表示图像的256级灰度,y轴表示各个灰度级的频数。

在这里插入图片描述

如果调用下列函数,则绘制的直方图是经过标准化处理,并且颜色为绿色、透明度为0.75的直方图,如图6所示。

  • plt.hist(src.ravel(), bins=256, density=1, facecolor=‘green’, alpha=0.75)

在这里插入图片描述

彩色直方图是高维直方图的特例,它统计彩色图片RGB各分量出现的频率,即彩色概率分布信息。彩色图片的直方图和灰度直方图一样,只是分别画出三个通道的直方图,然后再进行叠加,其代码如下所示。Lena彩色原始图像如图7所示。

在这里插入图片描述

#coding:utf-8
# By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
src = cv2.imread('Lena.png')

#获取BGR三个通道的像素值
b, g, r = cv2.split(src)
print(r,g,b)

#绘制直方图
plt.figure("Lena")
#蓝色分量
plt.hist(b.ravel(), bins=256, density=1, facecolor='b', edgecolor='b', alpha=0.75)
#绿色分量
plt.hist(g.ravel(), bins=256, density=1, facecolor='g', edgecolor='g', alpha=0.75)
#红色分量
plt.hist(r.ravel(), bins=256, density=1, facecolor='r', edgecolor='r', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.show()

#显示原始图像
cv2.imshow("src", src)
cv2.waitKey(0)
cv2.destroyAllWindows()

绘制的彩色直方图如图8所示,包括红色、绿色、蓝色三种对比。

在这里插入图片描述

如果希望将三个颜色分量的柱状图分开绘制并进行对比,则使用如下代码实现,调用plt.figure(figsize=(8, 6))函数绘制窗口,以及plt.subplot()函数分别绘制4个子图。

#coding:utf-8
# By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#读取图像
src = cv2.imread('Lena.png')

#转换为RGB图像
img_rgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

#获取BGR三个通道的像素值
b, g, r = cv2.split(src)
print(r,g,b)

plt.figure(figsize=(8, 6))

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#原始图像
plt.subplot(221)
plt.imshow(img_rgb)
plt.axis('off')
plt.title("(a)原图像")

#绘制蓝色分量直方图
plt.subplot(222)
plt.hist(b.ravel(), bins=256, density=1, facecolor='b', edgecolor='b', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)蓝色分量直方图")

#绘制绿色分量直方图
plt.subplot(223)
plt.hist(g.ravel(), bins=256, density=1, facecolor='g', edgecolor='g', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(c)绿色分量直方图")

#绘制红色分量直方图
plt.subplot(224)
plt.hist(r.ravel(), bins=256, density=1, facecolor='r', edgecolor='r', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(d)红色分量直方图")
plt.show()

最终输出的图形如图9所示,,图9(a)表示原图像,图9(b)表示蓝色分量直方图,图9©表示绿色分量直方图,图9(d)表示红色分类直方图。

在这里插入图片描述


三.OpenCV绘制直方图

前一小节讲解了如何调用matplotlib库绘制直方图,接下来讲解使用OpenCV库绘制直方图的方法。在OpenCV中可以使用calcHist()函数计算直方图,计算完成之后采用OpenCV中的绘图函数,如绘制矩形的rectangle()函数,绘制线段的line()函数来完成。其中,cv2.calcHist()的函数原型及常见六个参数如下:

  • hist = cv2.calcHist(images, channels, mask, histSize, ranges, accumulate)
    – hist表示直方图,返回一个二维数组
    – images表示输入的原始图像
    – channels表示指定通道,通道编号需要使用中括号,输入图像是灰度图像时,它的值为[0],彩色图像则为[0]、[1]、[2],分别表示蓝色(B)、绿色(G)、红色(R)
    – mask表示可选的操作掩码。如果要统计整幅图像的直方图,则该值为None;如果要统计图像的某一部分直方图时,需要掩码来计算
    – histSize表示灰度级的个数,需要使用中括号,比如[256]
    – ranges表示像素值范围,比如[0, 255]
    – accumulate表示累计叠加标识,默认为false,如果被设置为true,则直方图在开始分配时不会被清零,该参数允许从多个对象中计算单个直方图,或者用于实时更新直方图;多个直方图的累积结果用于对一组图像的直方图计算

接下来的代码是计算图像各灰度级的大小、形状及频数,接着调用plot()函数绘制直方图曲线。

#encoding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#读取图像
src = cv2.imread('lena.bmp')

#计算256灰度级的图像直方图
hist = cv2.calcHist([src], [0], None, [256], [0,255])

#输出直方图大小、形状、数量
print(hist.size)
print(hist.shape)
print(hist)

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#显示原始图像和绘制的直方图
plt.subplot(121)
plt.imshow(src, 'gray')
plt.axis('off')
plt.title("(a)Lena灰度图像")

plt.subplot(122)
plt.plot(hist, color='r')
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)直方图曲线")
plt.show()

上述代码绘制的“Lena”灰度图像所对应的直方图曲线如图10所示,图10(a)表示原图像,图10(b)表示对应的灰度直方图曲线。

在这里插入图片描述

同时输出直方图的大小、形状及数量,如下所示:

256
(256L, 1L)
[[7.000e+00]
 [1.000e+00]
 [0.000e+00]
 [6.000e+00]
 [2.000e+00]
 ....
 [1.000e+00]
 [3.000e+00]
 [2.000e+00]
 [1.000e+00]
 [0.000e+00]]

彩色图像调用OpenCV绘制直方图的算法与灰度图像一样,只是从B、G、R三个放量分别进行计算及绘制,详见代码。

#encoding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#读取图像
src = cv2.imread('Lena.png')

#转换为RGB图像
img_rgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

#计算直方图
histb = cv2.calcHist([src], [0], None, [256], [0,255])
histg = cv2.calcHist([src], [1], None, [256], [0,255])
histr = cv2.calcHist([src], [2], None, [256], [0,255])

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#显示原始图像和绘制的直方图
plt.subplot(121)
plt.imshow(img_rgb, 'gray')
plt.axis('off')
plt.title("(a)Lena原始图像")

plt.subplot(122)
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)直方图曲线")
plt.show()

最终绘制的“Lena”彩色图像及其对应的彩色直方图曲线如图11所示,其中图11(a)表示Lena原始图像,图11(b)表示对应的彩色直方图曲线。

在这里插入图片描述


四.掩膜直方图

如果要统计图像的某一部分直方图,就需要使用掩码(蒙板)来进行计算。假设将要统计的部分设置为白色,其余部分设置为黑色,然后使用该掩膜进行直方图绘制,其完整代码如下所示。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#读取图像
img = cv2.imread('yxz.png')

#转换为RGB图像
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

#设置掩膜
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:300] = 255
masked_img = cv2.bitwise_and(img, img, mask=mask)

#图像直方图计算
hist_full = cv2.calcHist([img], [0], None, [256], [0,256]) #通道[0]-灰度图

#图像直方图计算(含掩膜)
hist_mask = cv2.calcHist([img], [0], mask, [256], [0,256])

plt.figure(figsize=(8, 6))

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#原始图像
plt.subplot(221)
plt.imshow(img_rgb, 'gray')
plt.axis('off')
plt.title("(a)原始图像")

#绘制掩膜
plt.subplot(222)
plt.imshow(mask, 'gray')
plt.axis('off')
plt.title("(b)掩膜")

#绘制掩膜设置后的图像
plt.subplot(223)
plt.imshow(masked_img, 'gray')
plt.axis('off')
plt.title("(c)图像掩膜处理")

#绘制直方图
plt.subplot(224)
plt.plot(hist_full)
plt.plot(hist_mask)
plt.title("(d)直方图曲线")
plt.xlabel("x")
plt.ylabel("y")
plt.show()

其运行结果如图12所示,它使用了一个200×200像素的掩膜进行实验。其中图12(a)表示原始图像,图12(b)表示200×200像素的掩膜,图12©表示原始图像进行掩膜处理,图12(d)表示直方图曲线,蓝色曲线为原始图像的灰度值直方图分布情况,绿色波动更小的曲线为掩膜直方图曲线。

在这里插入图片描述


五.图像灰度变换直方图对比

前面详细介绍了图像灰度变换和阈值变换,本小节将结合直方图分别对比图像灰度变换前后的变化,方便读者更清晰地理解灰度变换和阈值变换。

  • [Python图像处理] 三十一.图像点运算处理两万字详细总结(灰度化处理、阈值化处理)

1.灰度上移变换图像直方图对比

图像灰度上移变换使用的表达式为:

  • DB=DA+50

该算法将实现图像灰度值的上移,从而提升图像的亮度,结合直方图对比的实现代码如下所示。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
result = np.zeros((height, width), np.uint8)

#图像灰度上移变换 DB=DA+50
for i in range(height):
    for j in range(width):
        if (int(grayImage[i,j]+50) > 255):
            gray = 255
        else:
            gray = int(grayImage[i,j]+50)
            
        result[i,j] = np.uint8(gray)

#计算原图的直方图
hist = cv2.calcHist([img], [0], None, [256], [0,255])

#计算灰度变换的直方图
hist_res = cv2.calcHist([result], [0], None, [256], [0,255])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(221), plt.imshow(img, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制掩膜
plt.subplot(222), plt.plot(hist), plt.title("(b)"), plt.xlabel("x"), plt.ylabel("y")

#绘制掩膜设置后的图像
plt.subplot(223), plt.imshow(result, 'gray'), plt.title("(c)"), plt.axis('off')

#绘制直方图
plt.subplot(224), plt.plot(hist_res), plt.title("(d)"), plt.xlabel("x"), plt.ylabel("y")
plt.show()

其运行结果如图13所示,其中(a)表示原始图像,(b)表示对应的灰度直方图,(c)表示灰度上移后的图像,(d)是对应的直方图。对比发现,图13(d)比图13(b)的灰度级整体高了50,曲线整体向右平移了50个单位。

在这里插入图片描述


2.灰度减弱图像直方图对比

该算法将减弱图像的对比度,使用的表达式为:

  • DB=DA×0.8

Python结合直方图实现灰度对比度减弱的代码如下所示。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
result = np.zeros((height, width), np.uint8)

#图像对比度减弱变换 DB=DA×0.8
for i in range(height):
    for j in range(width):
        gray = int(grayImage[i,j]*0.8)
        result[i,j] = np.uint8(gray)

#计算原图的直方图
hist = cv2.calcHist([img], [0], None, [256], [0,255])

#计算灰度变换的直方图
hist_res = cv2.calcHist([result], [0], None, [256], [0,255])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(221), plt.imshow(img, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制掩膜
plt.subplot(222), plt.plot(hist), plt.title("(b)"), plt.xlabel("x"), plt.ylabel("y")

#绘制掩膜设置后的图像
plt.subplot(223), plt.imshow(result, 'gray'), plt.title("(c)"), plt.axis('off')

#绘制直方图
plt.subplot(224), plt.plot(hist_res), plt.title("(d)"), plt.xlabel("x"), plt.ylabel("y")
plt.show()

其运行结果如图14所示,其中(a)和(b)表示原始图像和对应的灰度直方图,(c)和(d)表示灰度减弱或对比度缩小的图像及对应的直方图。图14(d)比图14(b)的灰度级整体缩小了0.8倍,绘制的曲线更加密集。

在这里插入图片描述


3.图像反色变换直方图对比

该算法将图像的颜色反色,对原图像的像素值进行反转,即黑色变为白色,白色变为黑色,使用的表达式为:

  • DB=255-DA

实现代码如下所示。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
result = np.zeros((height, width), np.uint8)

#图像灰度反色变换 DB=255-DA
for i in range(height):
    for j in range(width):
        gray = 255 - grayImage[i,j]
        result[i,j] = np.uint8(gray)

#计算原图的直方图
hist = cv2.calcHist([img], [0], None, [256], [0,255])

#计算灰度变换的直方图
hist_res = cv2.calcHist([result], [0], None, [256], [0,255])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(221), plt.imshow(img, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制掩膜
plt.subplot(222), plt.plot(hist), plt.title("(b)"), plt.xlabel("x"), plt.ylabel("y")

#绘制掩膜设置后的图像
plt.subplot(223), plt.imshow(result, 'gray'), plt.title("(c)"), plt.axis('off')

#绘制直方图
plt.subplot(224), plt.plot(hist_res), plt.title("(d)"), plt.xlabel("x"), plt.ylabel("y")
plt.show()

其运行结果如图15所示,其中(a)和(b)表示原始图像和对应的灰度直方图,(c)和(d)表示灰度反色变换图像及对应的直方图。图15(d)与图15(b)是反相对称的,整个灰度值满足DB=255-DA表达式。

在这里插入图片描述


4.图像对数变换直方图对比

该算法将增加低灰度区域的对比度,从而增强暗部的细节,使用的表达式为:

在这里插入图片描述

下面的代码实现了图像灰度的对数变换及直方图对比。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
result = np.zeros((height, width), np.uint8)

#图像灰度对数变换
for i in range(height):
    for j in range(width):
        gray = 42 * np.log(1.0 + grayImage[i,j])
        result[i,j] = np.uint8(gray)

#计算原图的直方图
hist = cv2.calcHist([img], [0], None, [256], [0,255])

#计算灰度变换的直方图
hist_res = cv2.calcHist([result], [0], None, [256], [0,255])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(221), plt.imshow(img, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制原始图像直方图
plt.subplot(222), plt.plot(hist), plt.title("(b)"), plt.xlabel("x"), plt.ylabel("y")

#灰度变换后的图像
plt.subplot(223), plt.imshow(result, 'gray'), plt.title("(c)"), plt.axis('off')

#灰度变换图像的直方图
plt.subplot(224), plt.plot(hist_res), plt.title("(d)"), plt.xlabel("x"), plt.ylabel("y")
plt.show()

其运行结果如图16所示,其中(a)和(b)表示原始图像和对应的灰度直方图,(c)和(d)表示灰度对数变换图像及对应的直方图。

在这里插入图片描述


5.图像阈值化处理直方图对比

该算法原型为:

  • threshold(Gray,127,255,cv2.THRESH_BINARY)

当前像素点的灰度值大于thresh阈值时(如127),其像素点的灰度值设定为最大值(如9位灰度值最大为255);否则,像素点的灰度值设置为0。二进制阈值化处理及直方图对比的Python代码如下所示。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
result = np.zeros((height, width), np.uint8)

#图像灰度对数变换
for i in range(height):
    for j in range(width):
        gray = 42 * np.log(1.0 + grayImage[i,j])
        result[i,j] = np.uint8(gray)

#计算原图的直方图
hist = cv2.calcHist([img], [0], None, [256], [0,255])

#计算灰度变换的直方图
hist_res = cv2.calcHist([result], [0], None, [256], [0,255])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(221), plt.imshow(img, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制原始图像直方图
plt.subplot(222), plt.plot(hist), plt.title("(b)"), plt.xlabel("x"), plt.ylabel("y")

#灰度变换后的图像
plt.subplot(223), plt.imshow(result, 'gray'), plt.title("(c)"), plt.axis('off')

#灰度变换图像的直方图
plt.subplot(224), plt.plot(hist_res), plt.title("(d)"), plt.xlabel("x"), plt.ylabel("y")
plt.show()

其运行结果如图17所示,其中(a)和(b)表示原始图像和对应的灰度直方图,(c)和(d)表示图像阈值化处理及对应的直方图,图17(d)中可以看到,灰度值仅仅分布于0(黑色)和255(白色)两种灰度级。

在这里插入图片描述


六.图像H-S直方图

为了刻画图像中颜色的直观特性,常常需要分析图像的HSV空间下的直方图特性。HSV空间是由色调(Hue)、饱和度(Saturation)、以及亮度(Value)构成,因此在进行直方图计算时,需要先将源RGB图像转化为HSV颜色空间图像,然后将对应的H和S通道进行单元划分,再其二维空间上计算相对应直方图,再计算直方图空间上的最大值并归一化绘制相应的直方图信息,从而形成色调-饱和度直方图(或H-S直方图)。该直方图通常应用在目标检测、特征分析以及目标特征跟踪等场景。

由于H和S分量与人感受颜色的方式是紧密相连,V分量与图像的彩色信息无关,这些特点使得HSV模型非常适合于借助人的视觉系统来感知彩色特性的图像处理算法。下面代码是具体的实现代码,使用matplotlib.pyplot库中的imshow()函数来绘制具有不同颜色映射的2D直方图。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread('Lena.png')

#转换为RGB图像
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

#图像HSV转换
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

#计算H-S直方图
hist = cv2.calcHist(hsv, [0,1], None, [180,256], [0,180,0,256])

#原始图像
plt.figure(figsize=(8, 6))
plt.subplot(121), plt.imshow(img_rgb, 'gray'), plt.title("(a)"), plt.axis('off')

#绘制H-S直方图
plt.subplot(122), plt.imshow(hist, interpolation='nearest'), plt.title("(b)")
plt.xlabel("x"), plt.ylabel("y")
plt.show()

图18(a)表示原始输入图像,图18(b)是原图像对应的彩色直方图,其中X轴表示饱和度(S),Y轴表示色调(H)。在直方图中,可以看到H=140和S=130附近的一些高值,它对应于艳丽的色调。

在这里插入图片描述


七.直方图判断黑夜白天

接着讲述两个应用直方图的案例,第一个是通过直方图来判断一幅图像是黑夜或白天。常见的方法是通过计算图像的灰度平均值、灰度中值或灰度标准差,再与自定义的阈值进行对比,从而判断是黑夜还是白天。

  • 灰度平均值:该值等于图像中所有像素灰度值之和除以图像的像素个数。
  • 灰度中值:对图像中所有像素灰度值进行排序,然后获取所有像素最中间的值,即为灰度中值。
  • 灰度标准差:又常称均方差,是离均差平方的算术平均数的平方根。标准差能反映一个数据集的离散程度,是总体各单位标准值与其平均数离差平方的算术平均数的平方根。如果一幅图看起来灰蒙蒙的, 那灰度标准差就小;如果一幅图看起来很鲜艳,那对比度就很大,标准差也大。

下面的代码是计算灰度“Lena”图的灰度平均值、灰度中值和灰度标准差。

#coding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#函数: 获取图像的灰度平均值
def fun_mean(img, height, width):
    sum_img = 0
    for i in range(height):
        for j in range(width):
            sum_img = sum_img + int(img[i,j])
    mean = sum_img / (height * width)
    return mean

#函数: 获取中位数
def fun_median(data):
    length = len(data)
    data.sort()
    if (length % 2)== 1: 
        z = length // 2
        y = data[z]
    else:
        y = (int(data[length//2]) + int(data[length//2-1])) / 2
    return y

#读取图像
img = cv2.imread('lena.bmp')

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]

#计算图像的灰度平均值
mean = fun_mean(grayImage, height, width)
print("灰度平均值:", mean)

#计算图像的灰度中位数
value = grayImage.ravel() #获取所有像素值
median = fun_median(value)
print("灰度中值:", median)

#计算图像的灰度标准差
std = np.std(value, ddof = 1)
print("灰度标准差", std)

其运行结果如图9-19所示,图9-19(a)为原始图像,图9-19(b)为处理结果。其灰度平均值为123,灰度中值为129,灰度标准差为48.39。

在这里插入图片描述

下面讲解另一种用来判断图像是白天还是黑夜的方法,其基本步骤如下:

  • 读取原始图像,转换为灰度图,并获取图像的所有像素值;
  • 设置灰度阈值并计算该阈值以下的像素个数。比如像素的阈值设置为50,统计低于50的像素值个数;
  • 设置比例参数,对比该参数与低于该阈值的像素占比,如果低于参数则预测为白天,高于参数则预测为黑夜。比如该参数设置为0.8,像素的灰度值低于阈值50的个数占整幅图像所有像素个数的90%,则认为该图像偏暗,故预测为黑夜;否则预测为白天。

具体实现的代码如下所示。

#encoding:utf-8
#By:Eastmount CSDN 2021-02-05
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#函数: 判断黑夜或白天
def func_judge(img):
    #获取图像高度和宽度
    height = grayImage.shape[0]
    width = grayImage.shape[1]
    piexs_sum = height * width
    dark_sum = 0  #偏暗像素个数
    dark_prop = 0 #偏暗像素所占比例
    
    for i in range(height):
        for j in range(width):
            if img[i, j] < 50: #阈值为50
                dark_sum += 1

    #计算比例
    print(dark_sum)
    print(piexs_sum)
    dark_prop = dark_sum * 1.0 / piexs_sum 
    if dark_prop >=0.8:
        print("This picture is dark!", dark_prop)
    else:
        print("This picture is bright!", dark_prop)
               
#读取图像
img = cv2.imread('day.png')

#转换为RGB图像
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#计算256灰度级的图像直方图
hist = cv2.calcHist([grayImage], [0], None, [256], [0,255])

#判断黑夜或白天
func_judge(grayImage)

#显示原始图像和绘制的直方图
plt.subplot(121), plt.imshow(img_rgb, 'gray'), plt.axis('off'), plt.title("(a)")
plt.subplot(122), plt.plot(hist, color='r'), plt.xlabel("x"), plt.ylabel("y"), plt.title("(b)")
plt.show()

第一张测试图输出的结果如图20所示,其中图20(a)为原始图像,图20(b)为对应直方图曲线,最终输出结果为“(‘This picture is bright!’, 0.010082704388303882)”,该预测为白天。

在这里插入图片描述

第二张测试图输出的结果如图21所示,其中图21(a)为原始图像,图21(b)为对应直方图曲线,最终输出结果为“(‘This picture is dark!’, 0.8511824175824175)”,该预测为黑夜。

在这里插入图片描述

最后补充一段3D直方图代码,也请同学们下来进行深入的理解及尝试。

# -*- coding: utf-8 -*-
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter

#读取图像
img = cv.imread("yxz.png")
img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
imgd = np.array(img)      #image类转numpy

#准备数据
sp = img.shape
h = int(sp[0])        #图像高度(rows)
w = int(sp[1])        #图像宽度(colums) of image

#绘图初始处理
fig = plt.figure(figsize=(16,12))
ax = fig.gca(projection="3d")

x = np.arange(0, w, 1)
y = np.arange(0, h, 1)
x, y = np.meshgrid(x,y)
z = imgd
surf = ax.plot_surface(x, y, z, cmap=cm.coolwarm)  

#自定义z轴
ax.set_zlim(-10, 255)
ax.zaxis.set_major_locator(LinearLocator(10))   #设置z轴网格线的疏密
#将z的value字符串转为float并保留2位小数
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) 

# 设置坐标轴的label和标题
ax.set_xlabel('x', size=15)
ax.set_ylabel('y', size=15)
ax.set_zlabel('z', size=15)
ax.set_title("surface plot", weight='bold', size=20)

#添加右侧的色卡条
fig.colorbar(surf, shrink=0.6, aspect=8)  
plt.show()

输出结果如下图所示:

在这里插入图片描述


八.总结

写到这里,本文就介绍完毕。这篇文章主要讲解图像直方图相关知识点,包括Matplotlib和OpenCV两种统计及绘制直方图的方法,接着讲解了掩膜直方图和H-S直方图,并结合灰度变换对比了常见算法变换前后的直方图,应用直方图实现黑夜和白天的判断。同时,读者可以尝试之前的文章和直方图进行各种绘制,比如均衡化处理、特效处理等等,下图就是图像均衡化的直方图。

在这里插入图片描述

  • 源代码下载地址,记得帮忙点star和关注喔。
    https://github.com/eastmountyxz/ImageProcessing-Python

时光嘀嗒嘀嗒的流失,这是我在CSDN写下的第八篇年终总结,比以往时候来的更早一些。《敏而多思,宁静致远》,仅以此篇纪念这风雨兼程的一年,这感恩的一年。转眼小宝白天了,哈哈~提前祝大家新年快乐!

  • 2020年总结:敏而多思,宁静致远——纪念这风雨兼程的一年

2020年8月18新开的“娜璋AI安全之家”,主要围绕Python大数据分析、网络空间安全、人工智能、Web渗透及攻防技术进行讲解,同时分享CCF、SCI、南核北核论文的算法实现。娜璋之家会更加系统,并重构作者的所有文章,从零讲解Python和安全,写了近十年文章,真心想把自己所学所感所做分享出来,还请各位多多指教,真诚邀请您的关注!谢谢。

在这里插入图片描述

(By:Eastmount 2021-02-06 晚上12点 http://blog.csdn.net/eastmount/ )


参考文献,在此感谢这些大佬,共勉!

  • [1] 罗子江, 杨秀璋. Python中的图像处理[M]. 2020.
  • [2]冈萨雷斯. 数字图像处理(第3版)[M]. 北京:电子工业出版社, 2013.
  • [3]张恒博, 欧宗瑛. 一种基于色彩和灰度直方图的图像检索方法[J]. 计算机工程, 2004.
  • [4]Eastmount. [数字图像处理] 四.MFC对话框绘制灰度直方图[EB/OL]. (2015-05-31). https://blog.csdn.net/eastmount/article/details/46237463.
  • [5]苗锡奎, 孙劲光, 张语涵. 图像归一化与伪Zernike矩的鲁棒水印算法研究[J]. 计算机应用研究, 2010.
  • [6]阮秋琦. 数字图像处理学(第3版)[M]. 北京:电子工业出版社, 2008.
  • [7]Eastmount. [Python图像处理] 十一.灰度直方图概念及OpenCV绘制直方图[EB/OL]. (2018-11-06). https://blog.csdn.net/Eastmount/article/details/83758402.
  • [8]李立源, 龚坚. 基于二维灰度直方图最佳一维投影的图像分割方法[J]. 自动化学报, 1996.
  • [9]杨秀璋, 颜娜. Python网络数据爬取及分析从入门到精通(分析篇)[M]. 北京:北京航天航空大学出版社, 2018.
  • [10]毛星云, 冷雪飞. OpenCV3编程入门[M]. 北京:电子工业出版社, 2015.
  • [11]深思海数_willschang. Opencv-Python学习笔记七——图像直方图 calcHist,直方图均衡化equalizeHist[EB/OL]. (2018-08-26). https://www.jianshu.com/p/bd12c4273d7d.
  • [12]ZJE_ANDY. python3+opencv 利用灰度直方图来判断图片的亮暗情况[EB/OL]. (2018-06-20). https://blog.csdn.net/u014453898/article/details/80745987.

本文链接: http://www.dtmao.cc/news_show_2000220.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?