OpenCV入门(二十三)快速学会OpenCV 22 直方图

Posted 小幽余生不加糖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV入门(二十三)快速学会OpenCV 22 直方图相关的知识,希望对你有一定的参考价值。

OpenCV入门(二十三)快速学会OpenCV 22 直方图

作者:Xiou

直方图是图像处理过程中的一种非常重要的分析工具。直方图从图像内部灰度级的角度对图像进行表述,包含十分丰富而重要的信息。从直方图的角度对图像进行处理,可以达到增强图像显示效果的目的。

1.直方图概述

从统计的角度讲,直方图是图像内灰度值的统计特性与图像灰度值之间的函数,直方图统计图像内各个灰度级出现的次数。从直方图的图形上观察,横坐标是图像中各像素点的灰度级,纵坐标是具有该灰度级(像素值)的像素个数。

一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图描述了图像中灰度的分布情况,能够很直观地展示出图像中各个灰度级所占的多少。


上图就是一个灰度图像的灰度直方图,横坐标代表的是像素的灰度值的范围[0,255],越接近0表示图像越暗,越接近255表示图像越亮。纵坐标代表的是每一个灰度值在图像所有像素中出现的次数。曲线越高,表示该像素值在图像中出现的次数越多。

2.绘制直方图

Python的模块matplotlib.pyplot中的hist()函数能够方便地绘制直方图,我们通常采用该函数直接绘制直方图。除此以外,OpenCV中的cv2.calcHist()函数能够计算统计直方图,还可以在此基础上绘制图像的直方图。下面分别讨论这两种方式。

calcHist,该函数能够同时计算多个图像、多个通道、不同灰度范围的灰度直方图。为了更为通用,该函数的参数有些复杂,具体函数声明如下:

    calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])

其中,参数
images表示图像矩阵数组,这些图像必须有相同大小和相同的深度;
channels表示要计算直方图的通道个数;
mask表示可选的掩码,不使用时可设为空,mask必须是一个8位的数组并且和images的数组大小相同,在进行直方图计算的时候只会统计该掩码不为0的对应像素;
histSize表示直方图每个维度的大小;
hist表示输出的直方图;
ranges表示直方图每个维度要统计的灰度级的范围;
accumulate表示累积标志,默认值为False。

OpenCV中用calHist函数得到直方图数据后就可以将其绘制出来了。

代码实例1:得到某图像的灰度直方图。

测试原图:

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

img = cv2.imread('test.jpg',0)
plt.hist(img.ravel(),256,[0,256]);
plt.show()

输出结果:


代码实例2:统计时使用cv2.calcHist()函数。绘制BGR直方图

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg',1)
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

输出结果:

代码实例3:直方图+mask

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

plt.style.use("fivethirtyeight")

# 读取图片, 并转换成灰度图
img = cv2.imread("girl1.jpg", 0)

# 创建mask
mask = np.zeros(img.shape, np.uint8)
mask[280:1000, 420:1500] = 255

# 获取mask后的图像
masked_img = cv2.bitwise_and(img, img, mask=mask)

# 直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])

# 图片展示
f, ax = plt.subplots(2, 2, figsize=(12, 9))
ax[0, 0].imshow(img, 'gray')
ax[0, 0].set_title("original image")
ax[0, 1].imshow(mask, 'gray')
ax[0, 1].set_title("mask")
ax[1, 0].imshow(masked_img, 'gray')
ax[1, 0].set_title("masked image")
ax[1, 1].plot(hist_full)
ax[1, 1].plot(hist_mask)
ax[1, 1].set_title("original vs masked hist")

plt.show()

输出结果:

3.直方图均衡化

直方图均衡化是一种常见的增强图像对比度的方法,可以增强局部图像的对比度,在数据较为相似的图像中的作用更加明显。直方图均衡化处理的“中心思想”是:把原始图像的灰度直方图从比较集中的某个灰度区间,变成在全部灰度范围内的均匀分布。

直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改成“均匀”分布。

为什么要直方图均衡化?很多时候,图片看起来的效果不是那么清晰,这时可以对图像进行一些处理来扩大图像像素值显示的范围。例如,有些图像整体像素值偏低,图像中的一些特征不是很清晰,只是隐约看到一些轮廓痕迹,这时可以经过图像直方图均衡化之后,使图像看起来亮一些,也便于后续处理。

直方图均衡化是灰度变换的一个重要应用,它高效且易于实现,广泛应用于图像增强处理中。图像的像素灰度变化是随机的,直方图的图形高低不齐,直方图均衡化就是用一定的算法使直方图大致平和的方法。

直方图均衡化的算法主要包括两个步骤:
(1)计算累计直方图。
(2)对累计直方图进行区间转换在此基础上,再利用人眼视觉达到直方图均衡化的目的。

下面我们实现直方图均衡化来改善图像质量,通常有两种方法:
一种是不通过OpenCV函数;
另外一种是通过OpenCV函数equalizeHist(这种方式不懂原理也可以使用,只要把原图像输入即可得到均衡化的图像输出)。
equalizeHist函数对图像进行直方图均衡化(归一化图像亮度和增强图像对比度),声明如下:

     equalizeHist(src[, dst])

代码实例:

import cv2
from matplotlib import pyplot as plt

plt.style.use("fivethirtyeight")

# 读取图片, 并转换成灰度图
img = cv2.imread("girl1.jpg", 0)

# 均衡化
img_equ = cv2.equalizeHist(img)

# 直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
ax[0, 0].imshow(img, "gray")
ax[0, 0].set_title("before")
ax[0, 1].imshow(img_equ, "gray")
ax[0, 1].set_title("after")
ax[1, 0].hist(img.ravel(), 256)
ax[1, 1].hist(img_equ.ravel(), 256)

plt.show()

输出结果:

OpenCV入门快速学会OpenCV5图像处理基础像素处理

OpenCV入门(六)快速学会OpenCV5图像处理基础(二)

作者:Xiou

像素是图像构成的基本单位,像素处理是图像处理的基本操作,可以通过位置索引的形式对图像内的元素进行访问、处理。

1.二值化操作

需要说明的是,在OpenCV中,最小的数据类型是无符号的8位数。

因此,在OpenCV中实际上并没有二值图像这种数据类型,二值图像经常是通过处理得到的,然后使用0表示黑色,使用255表示白色。可以将二值图像理解为特殊的灰度图像。

在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。

声明如下:

ret, dst = cv2.threshold(src, thresh, maxval, type)

参数:

src: 需要转换的图
thresh: 阈值
maxval: 超过阈值所赋的值
type: 二值化操作类型
返回值:

ret: 输入的阈值
dst: 处理好的图片

原图如下所示:

1.1 Binary

大于阈值的设为 255, 低于或等于阈值的为 0。

实例代码:

import cv2 as cv

img_gray = cv.imread("test.jpg")

# 二值化
ret, thresh1 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)

# 图片展示
cv.imshow("thresh1", thresh1)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

1.2 Binary Inverse

与 Binary 相反。

代码实例:

import cv2 as cv

img_gray = cv.imread("test.jpg")

# 二值化
ret, thresh1 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
# 图片展示
cv.imshow("thresh1", thresh1)
cv.imshow("thresh2", thresh2)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

1.3 Trunc

大于阈值的设为 255, 低于或等于阈值的不变。

代码实例:

import cv2 as cv

img_gray = cv.imread("test.jpg")

# 二值化
ret, thresh1 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img_gray, 127, 255, cv.THRESH_TRUNC)
# 图片展示
cv.imshow("thresh1", thresh1)
cv.imshow("thresh2", thresh2)
cv.imshow("thresh3", thresh3)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

1.4 Tozero

大于阈值部分不变, 否则设为 0。

代码实例:

import cv2 as cv

img_gray = cv.imread("test.jpg")

# 二值化
ret, thresh1 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img_gray, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO)
# 图片展示
cv.imshow("thresh1", thresh1)
cv.imshow("thresh2", thresh2)
cv.imshow("thresh3", thresh3)
cv.imshow("thresh4", thresh4)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

1.5 Tozero Inverse

与 Tozero 相反。

代码实例:

import cv2 as cv

img_gray = cv.imread("test.jpg")

# 二值化
ret, thresh1 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img_gray, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO)
ret, thresh5 = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO_INV)
# 图片展示
cv.imshow("thresh1", thresh1)
cv.imshow("thresh2", thresh2)
cv.imshow("thresh3", thresh3)
cv.imshow("thresh4", thresh4)
cv.imshow("thresh5", thresh5)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

2.颜色转换

灰度转换:

如果拍照时曝光不足或曝光过度,照片会灰蒙蒙的或者过白,这实际上是因为对比度太小、输入图像亮度分量的动态范围较小造成的,例如x光照片或陆地资源卫星多光谱图像。改善这些图像的质量可以采用灰度变换法,通过扩展输入图像的动态范围达到图像增强的目的。

灰度变换是图像处理最基本的方法之一,灰度变换可使图像动态范围加大、图像对比度增强、图像清晰、特征明显,是图像增强的重要手段。图像的灰度变换又称为灰度增强,是根据某种目标条件按一定变换关系逐点改变原图像中每一个像素灰度值的方法。

灰度变换主要利用点运算来修正像素灰度,由输入像素点的灰度值确定相应输出点的灰度值,是一种基于图像变换的操作。灰度变换不改变图像内的空间关系,除了灰度级的改变是根据特定的灰度函数变换进行之外,可以看作是“从像素到像素”复制操作,基于点运算的灰度变换可表示为s=T®。

其中,T是灰度变换函数,描述输入灰度值和输出灰度值之间的关系,一旦灰度变换函数确定,该灰度变换就被完全确定下来;r是变换前的灰度;s是变换后的像素。

灰度变换的作用:
(1)改善图像的质量,使图像能够显示更多的细节,提高图像的对比度(对比度拉伸)。
(2)有选择地突出图像感兴趣的特征,或者抑制图像中不需要的特征。
(3)可以有效地改变图像的直方图分布,使像素的分布更为均匀。

灰度变换函数描述了输入灰度值和输出灰度值之间的变换关系,一旦灰度变换函数确定下来,那么其输出的灰度值也就确定了。灰度变换函数的性质决定了灰度变换所能达到的效果。

cv.cvtColor可以帮助我们转换图片通道。

声明如下:

cv2.cvtColor(src, code[, dst[, dstCn]])

参数:

src: 需要转换的图片
code: 颜色空间转换码
dst: 输出图像大小深度相同, 可选参数
desCn: 输出图像的颜色通道, 可选参数

2.1 灰度转换

RGB 到灰度图转换公式:

Y' = 0.299 R + 0.587 G + 0.114 B

代码实例:

import cv2 as cv

img = cv.imread("test.jpg")

# 转换成灰度图
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# 输出维度
print(img_gray.shape)  # (554, 640)

# 展示图像
cv.imshow("img_gray", img_gray)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

2.2 HSV

HSV (Hue, Saturation, Value) 是根据颜色的直观特性由 A.R. Smith 在 1978 年创建的一种颜色空间。

代码实例:

import cv2 as cv

img = cv.imread("test.jpg")

# 转换成灰度图
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 转换成hsv
img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# 输出维度
print(img_gray.shape)  # (554, 640)
print(img_hsv.shape)  # (554, 640, 3)
# 展示图像
cv.imshow("img_gray", img_gray)
cv.imshow("img_hsv", img_hsv)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

2.3 YUV

YUV 是一种颜色编码的方法, 主要用在视频, 图形处理流水线中。

代码实例:

import cv2 as cv

img = cv.imread("test.jpg")

# 转换成灰度图
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 转换成hsv
img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# 转换成hsv
img_yuv = cv.cvtColor(img, cv.COLOR_BGR2YUV)

# 输出维度
print(img_gray.shape)  # (554, 640)
print(img_hsv.shape)  # (554, 640, 3)
print(img_yuv.shape)  # (554, 640, 3)
# 展示图像
cv.imshow("img_gray", img_gray)
cv.imshow("img_hsv", img_hsv)
cv.imshow("img_yuv", img_yuv)
cv.waitKey(0)
cv.destroyAllWindows()

输出结果:

以上是关于OpenCV入门(二十三)快速学会OpenCV 22 直方图的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV竟然可以这样学!成神之路终将不远(二十三)

OpenCV竟然可以这样学!成神之路终将不远(二十三)

《PyInstaller打包实战指南》第二十三节 单文件模式打包OpenCV-Python

《PyInstaller打包实战指南》第二十三节 单文件模式打包OpenCV-Python

OpenCV探索之路(二十三):特征检测和特征匹配方法汇总

Qt+OpenCV联合开发(二十三)--图像直方图(calcHist)