[python opencv 计算机视觉零基础到实战] 十二 直方图

2022-01-06 09:57:04 浏览数 (1)

一、学习目标

  1. 了解matplotlib绘图库的使用
  2. 了解如何通过折线图或者直方图对图表进行绘制
  3. 了解了通过图标对图片内容进行直观判断

如有错误欢迎指出~

二、了解图像直方图及其应用

2.1 了解matplotlib库

在了解图像直方图前我们需要了解一个matplotlib库,matplotlib库和numpy可谓是一对好伴侣,就像泡面伴侣火腿肠一样。

matplotlib是一个绘图库,我们将通过matplotlib绘制图像的直方图。为什么图像可以绘制直方图呢?我们可以想一下,图像是由一堆数据组成,既然是数据那就可以对这个图像进行可视化的图标操作。

首先,我们需要安装matplotlib。使用pip工具可以直接安装matplotlib。安装命令为:

代码语言:javascript复制
pip install matplotlib

安装完matplotlib后,可以在python的交互窗口中对其进行引入,若没问题那就肯定是安装好了:

代码语言:javascript复制
from matplotlib import pyplot as plt 

我们先简单了解一下matplotlib 的使用方法,查看以下示例:

代码语言:javascript复制
from matplotlib import pyplot as plt

x = [0, 5, 8, 13]
y = [2, 1, 1, 3]

print(x)
print(y)
plt.title("Matplotlib") 
plt.xlabel("x") 
plt.ylabel("y") 
plt.plot(x,y) 
plt.show()

首先引入进行引入,随后定义了x和y两个列表,这两个列表对应的是折线图中x和y的两个绘制点,其中x列表是当前坐标系中x的值,y列表则表示y坐标系的值。x列表和y列表相互对应,x[0]与y[0]构成一个坐标点,如x[0]与y[0]则表示(0,2),依次下去则是(5,1)、(8,1);随后使用plt.title设置折线图标题,plt.xlabel设置x标签、plt.ylabel设置y标签,再继续使用plot传入x和y的值,最后使用show方法进行展示。结果如下:

以上是一个Matplotlib折线图的基本用法,接下来我们开始正式的编写有关图像直方图的内容。

2.2 绘制图像直方图

图像直方图表示了一张图像像素的分布,对像素进行了统计,方便与直观的以图的形式对图片进行分析。一般横坐标表示图像像素的不同值,或者说不同的种类,纵坐标则表示了每一种颜色的个数或者百分比。直方图用这种表现方式显示图像的基本内容特征方便接下来对图像进行进行下一步的操作。一般直方图的表示是越靠近左侧纵坐标则表示当前图像的偏暗点数据分布,越靠近右侧则表示图像偏亮的像素点分布情况。

绘制图像直方图需要使用一个直方图方法hist方法,我们一般使用前两个参数;第一个参数为一维数组,第二个参数为需要多少个间隔。现在我们先读取一张图片:

代码语言:javascript复制
import cv2
import numpy as np

from matplotlib import pyplot as plt

img = cv2.imread(r'C:UsersmxDesktop1.jpg')
cv2.namedWindow("Image",cv2.WINDOW_NORMAL)
cv2.destroyWindow("Image")
cv2.imshow("Image", img)

随后使用hist方法,使用hist方法时由于img是一个三通道的数据,那如何转为一维数据呢?很简单,有一个ravel方法很方便的对img数据进行转变:

代码语言:javascript复制
img.ravel()

随后将该数据传入至hist方法中:

代码语言:javascript复制
plt.hist(img.ravel(), 256)

以上代码中,256是表示一共分为256个数据去显示其中的内容。最后添加show方法以及等待图像关闭的代码,完整代码如下:

代码语言:javascript复制
import cv2
import numpy as np

from matplotlib import pyplot as plt

img = cv2.imread(r'C:UsersmxDesktop1.jpg')
cv2.namedWindow("Image",cv2.WINDOW_NORMAL)
cv2.destroyWindow("Image")
cv2.imshow("Image", img)

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

cv2.waitKey (0)
cv2.destroyAllWindows()

运行代码结果如下:

由于我们这张图片是整体偏亮的,所以数据的分布都在右侧,左侧分布较少。这张图片如下:

也可以把第二个参数改为100,这样每次分段就是一半了:

代码语言:javascript复制
plt.hist(img.ravel(), 100)

效果如下:

hist的第三个参数是统计范围,你需要直方图显示哪个范围内的数据,例如[0,100],这时就可以写成:

代码语言:javascript复制
plt.hist(img.ravel(), 100,[0,100])

运行结果如下:

左边纵坐标数据则是个数,横坐标x是0逐渐增大,也就构成了一种越靠近左侧越黑,越靠近右侧越亮的这一种分布情况。我们也可以将以上的0到100范围改成0-256,这时即可恢复原样:

代码语言:javascript复制
plt.hist(img.ravel(), 256,[0,256])

2.3 3通道折线图绘制

以上只绘制了一个完整图像的直方图,现在我们来试一下绘制3个通道RGB的直方图图像。 绘制3通道的折线图使用一个方法可以很简便的进行绘制,那就是calcHist,calcHist可以通过你给的通道数、灰度范围、像素值范围可以获取图像的通道信息,也可以加入mask遮罩提取图像。 由于同一个直方图或者折线图中,使用同一种颜色绘制会分辨不清,我们可以通过三原色的红绿蓝分别绘制3跟不同颜色的线段进行表示。这里使用折线图首先进行图像绘制。 首先我们定义一个列表,存储红绿蓝三原色字符串,方便之后的绘图时传入色彩:

代码语言:javascript复制
color = ["blue", "green", "red"]

随后使用一个for循环,不过需要将color传入enumerate中,enumerate将会给予出两个值,一个是下标一个是值,那么这个时候就需要有两个变量接收,for循环可以写成:

代码语言:javascript复制
for i, v in enumerate(color):

随后使用calcHist方法,并且传入5个值:

代码语言:javascript复制
hist = cv2.calcHist([img], [i], None, [256], [0, 256])

值必须使用方括号传入,这是语法规定,若不使用你可能会出错。第一个值img是图像数据,第二个值i,由于每次都会循环作为下标,那么就是0、1、2、3这3个数据,这3个数据传入到calcHist后将会拿出红绿蓝三个不同的通道值。第三位填入NONE,因为我们不需要遮罩。然后[256]是一个灰度的范围空间,[0,256]表示像素的范围值。这时每次循环hist都将会是不同通道的值内容,这个时候就可以使用plot进行折线画图,并且传入颜色值,绘制不同颜色的线段:

代码语言:javascript复制
plt.plot(hist, color=v)

组后show图片,完整代码如下:

代码语言:javascript复制
import cv2
import numpy as np

from matplotlib import pyplot as plt

img = cv2.imread(r'C:UsersmxDesktop1.jpg')
cv2.namedWindow("Image",cv2.WINDOW_NORMAL)
cv2.destroyWindow("Image")
cv2.imshow("Image", img)
color = ["blue", "green", "red"]
for i, v in enumerate(color):
    hist = cv2.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(hist, color=v)

plt.show()
cv2.waitKey (0)
cv2.destroyAllWindows()

结果如下:

该系列首发于ebaina

三、总结

  1. 了解matplotlib对折线图、直方图的绘图方法
  2. 了解了matplotlib绘制的图标左侧偏暗右侧偏亮的特性

0 人点赞