Python OpenCV 图像处理之图像直方图,取经之旅第 25 天丨【百变AI秀】

举报
梦想橡皮擦 发表于 2021/09/18 09:21:33 2021/09/18
【摘要】 Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。 基础知识铺垫截止到本篇博客,已经第二次听到直方图这个概念了,有必要将其搞懂。图像直方图(histogram)是图像统计学特征,用来统计像素值出现的频次,常用在分析图像的基本特征。创建直方图一般分为两个步骤:统计数据绘制直方图直方图的定义横坐标:图像中各个像素点的灰度级纵坐标:该灰度级的像素个数绘制直方图需要 mat...

Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。

基础知识铺垫

截止到本篇博客,已经第二次听到直方图这个概念了,有必要将其搞懂。

图像直方图(histogram)是图像统计学特征,用来统计像素值出现的频次,常用在分析图像的基本特征。

创建直方图一般分为两个步骤:

  1. 统计数据
  2. 绘制直方图

直方图的定义

  • 横坐标:图像中各个像素点的灰度级
  • 纵坐标:该灰度级的像素个数

绘制直方图需要 matplotlib 库,这个需要自行安装一下。

matplotlib 中 pyplot 绘制直方图

pyplot 中提供了一个绘制直方图的函数,名称为 hist

函数原型介绍

matplotlib.pyplot.hist() 函数原型如下

(n, bins, patches)=matplotlib.pyplot.hist(x, bins=None, range=None, density=None, weights=None,
cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical',
rwidth=None, log=False, color=None, label=None, stacked=False, normed=None, *, data=None, **kwargs)

参数非常多,实际应用中只需掌握几个重要参数。

最简单的测试代码如下:

import numpy as np
import matplotlib.pyplot as plt

# 生成数据,以 10000 组均值为0,标准差为 1 的高斯分布数据为例
data = np.random.normal(0,1,10000)

n, bins, patches = plt.hist(data)
plt.show()

运行效果如下:

其中,np.random.normal(0,1,10000) 函数说明如下,np.random.normal() 是一个正态分布,normal这里是正态的意思。

该函数原型为:

numpy.random.normal(loc=0.0, scale=1.0, size=None)

参数说明如下:

  • loc:概率分布的均值,对应着整个分布的中心 center
  • scale:概率分布的标准差,对应于分布的宽度,scale 越大,越矮胖,scale 越小,越瘦高
  • size:数据类型为 int or tuple of ints, 输出的 shape,默认为 None,只输出一个值

其实该函数的目的就是,输出为高斯分布的一组数或一个值。
简单案例:

data = np.random.normal(loc=0, scale=1, size=2)
print(data)

继续回顾 matplotlib.pyplot.hist() 函数的相关参数(官网说明):

只选取其中比较重要的几个参数如下:

  • x:(n,) array or sequence of (n,) arrays
    指定要绘制直方图的数据,必须是一维数组.使用.ravel()将你的通道值转为一维数组
  • bins:integer or sequence or ‘auto’, optional
    指定直方图条形的个数,integer 或 auto,也可以不设置.举例[1,2,3,4],则第一个柱为取值[1,2),一次类推,最后一个是取值[3,4].默认 taken from the rcParam hist.bins.
  • range:tuple or None, optional
    数组或者不给.给出数组将指定直方图数据的上下界,超出范围的舍弃.不设置的话包含绘图数据的最大值和最小值;默认为 None

基于上述内容,将一副图像的直方图显示出来。

做一些准备工作

  • x: 图像,必须是一维数组
  • 其中函数 ravel b = a.ravel()
    功能: 将多维数组降为一维数组
    格式: 一维数组=多维数组.revel()
  • bins: 一般是 256,指[0, 255]

以上内容掌握之后,就可以处理图像的直方图了,代码如下:

import cv2
from matplotlib import pyplot as plt


def plot_demo(image):
    # numpy 的 ravel 函数功能是将多维数组降为一维数组
    plt.hist(image.ravel(), 256, [0, 256])
    plt.show()


if __name__ == "__main__":
    img = cv2.imread("./106.jpg")
    cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE)
    cv2.imshow("input image", img)
    plot_demo(img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

python opencv 直方图(histogram)

函数原型介绍

在 Python OpenCV 中实现直方图的函数为cv2.calcHist,原型如下:

# 返回 hist
cv2.calcHist(img, channels, mask, histSize, ranges[, hist[, accumulate ]])

参数说明:

  • img:图像,方括号方式传入,即[img]
  • channels:选取图像的哪个通道,用方括号给出的,计算直方图的 channel 的索引,如果输入时灰度图,值就是 [0],对于彩色图片,你可以传 [0][1][2] 来分别计算蓝色,绿色和红色通道的直方图;
  • mask:掩膜,如果要找整个图像的直方图,这里传入"None"。如果你想找到特定区域图片的直方图,需要使用掩膜,只计算值>0 的位置上像素的颜色直方图
  • histSize:直方图大小,BINS 数量(BINS 是啥,下面细说),要方括号传入,对于全刻度,传入 [256]
  • ranges:直方图范围,一般来说是[0,256]

关于上文提及的 BINS 等内容涉及直方图如下概念:

  • BINS: 在上面的直方图当中,如果像素值是 0 到 255,则需要 256 个值来显示直方图。
    但是,如果不需要知道每个像素值的像素数目,只想知道两个像素值范围内的像素点数目即可?首先像素值在 0--15 之间的像素点数目,然后是 16--31 ……直到 240--255,即每次间隔 16 个数字,将 256 个值分成 16 份,每份计算综合。每个分成的小组就是一个 BIN(箱)。在 opencv 中使用 histSize 表示 BINS。

彩色图像,不同通道的直方图

首先绘制蓝色通道的直方图,代码如下:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('./106.jpg')
hist = cv.calcHist([img], [0], None, [256], [0,256])

plt.plot(hist, label='B', color='b')
plt.show()

运行结果如下:

三个通道同时绘制代码如下:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('./106.jpg')
color = ('b', 'g', 'r')
for i, col in enumerate(color):

    hist = cv.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(hist, color=col)
    plt.xlim([0, 256])
plt.show()

BGR 直方图如下:

直方图均衡化 (Histogram Equalization)

如果图像的灰度分布不均匀,集中在一个比较窄的范围内,这样图像的细节就会不清晰,对比度低。

这种情况可以使用直方图均衡化,对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等。

执行之后,直方图中间峰值部分对比度增强,两侧谷底部分对比度降低。图像的灰度范围拉伸之后,灰度均匀分布,反差增大,增强图像细节。

理论的东西就是上面那些了,实操起来才可以看到效果。

函数原型介绍

使用 cv2.equalizeHist 方法来得到直方图均衡化之后的图像,函数原型如下:

cv2.equalizeHist(src[, dst])

参数说明:

  • src:源图像。图像必须是灰度图。
  • dst:目标图像。

测试代码如下:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

image = cv.imread("2o.jpg")
# 将图片转换为灰度图
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow("gray", gray)

# 直方图绘制
hist = cv.calcHist([gray], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()

# 应用直方图均衡化
dst = cv.equalizeHist(gray)
cv.imshow("dst", dst)

# 直方图绘制
hist = cv.calcHist([dst], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()

运行结果与直方图:

运行直方图均衡化之后的图像如下:

上述案例为灰度图直方图均衡化,对于彩色图像一样可以进行图像增强操作。

import cv2 as cv
import numpy as np
img = cv.imread('th.jpeg')
cv.imshow('img', img)
b, g, r = cv.split(img)
bH = cv.equalizeHist(b)
gH = cv.equalizeHist(g)
rH = cv.equalizeHist(r)
dst = cv.merge((bH, gH, rH))
cv.imshow('dst', dst)
cv.waitKey(0)

使用cv2.split函数分离图像的颜色通道,分别得到各个通道的直方图,再使用cv2.merge函数合并各通道,得到彩色图像的直方图均衡化。

以上提及的叫做全局直方图均衡化,下面为大家在介绍一下局部直方图均衡化。

局部直方图均衡化在有的地方也被叫做 CLAHE 有限对比适应性直方图均衡化

大概实现过程如下:
整幅图像会被分成很多小块,这些小块被称为 “tiles”(tiles 的默认大小是 8x8),然后再对每一个小块分别进行直方图均衡化。

函数原型如下:

cv2.createCLAHE(clipLimit, tileGridSize)

参数说明:

  • clipLimit:对比度限制的阈值
  • tileGridSize:图像分割每块的尺寸,默认 8x8

运行下述代码,得到的结果可以与全局直方图均衡化做一下比较。

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

image = cv.imread("2o.jpg")
# 将图片转换为灰度图
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow("gray", gray)

# 直方图绘制
hist = cv.calcHist([gray], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()

# 应用直方图均衡化
# 1. 实例化自适应直方图均衡化函数
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
# 2. 进行自适应直方图均衡化
dst = clahe.apply(gray)
cv.imshow("dst", dst)

# 直方图绘制
hist = cv.calcHist([dst], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()

橡皮擦的小节

今天重点学习了一下直方图,写了这么多,只有一个原因,就是这是第二次碰到了,当一个知识点再次出现时,就要在进一步的学习下,因为大概率它是重点知识。

今天是持续写作的第 65 / 100 天。
如果你有想要交流的想法、技术,欢迎在评论区留言。


博主 ID:梦想橡皮擦,希望大家点赞评论收藏

【百变AI秀】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/296704

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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