数字图像可以用大小一定的矩阵来表示,矩阵中每个元素的大小表示图像中每个像素的明暗程度。查找矩阵中的最大值就是寻找图像中灰度值最大的像素,计算矩阵的平均值就是计算图像的平均灰度(图像的整体亮暗程度可以用平均灰度来表示)。因此,统计矩阵数据的特征值具有一定的意义。
OpenCV 4中提供了寻找图像像素最大值、最小值的函数 cv.minMaxLoc()
#cv.minMaxLoc()函数原型
minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(src
[, mask])
其中各返回值和参数的含义分别为:
minVal:图像中的最小值
maxVal:图像中的最大值
minLoc:图像最小值在矩阵中的坐标
maxLoc:图像最大值在矩阵中的最表
src:需要寻找最大值和最小值的图像或者矩阵
mask:图像掩模(可选参数)
需要注意的是如果图像中存在多个最大值或最小值,则返回的最值坐标为按行扫描从左到右第1次检查到的最值位置。
示例代码
#cv.minMaxLoc()函数原型
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
if __name__ == '__main__':
# 新建矩阵array
array = np.array([1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0])
# 将array调整为3*4的单通道图像
img1 = array.reshape((3, 4))
minval_1, maxval_1, minloc_1, maxloc_1 = cv.minMaxLoc(img1)
print('图像img1中最小值为:{}, 其位置为:{}' .format(minval_1, minloc_1))
print('图像img1中最大值为:{}, 其位置为:{}' .format(maxval_1, maxloc_1))
# 先将array调整为为3*2*2的多通道图像
img2 = array.reshape((3, 2, 2))
# 再利用-1的方法调整尺寸
img2_re = img2.reshape((1, -1))
minval_2, maxval_2, minloc_2, maxloc_2 = cv.minMaxLoc(img2_re)
print('图像img2中最小值为:{}, 其位置为:{}'.format(minval_2, minloc_2))
print('图像img2中最大值为:{}, 其位置为:{}'.format(maxval_2, maxloc_2))
图像的均值可以用于表示图像整体的亮暗程度,图像的均值越大,图像整体越亮。
图像的标准差表示图像中明暗变化程度,标准差越大,表示图像中明暗变化越明显。
OpenCV4中提供了计算图像均值和标准差的函数。
(1)cv.mean()函数可计算图像的均值。
#cv.mean()函数原型
retal = cv.mean(src
[,mask])
其中各返回值和参数的含义分别为:
retal:为一个长度为4的元组,四个位置分别代表相应通道的均值,若通道不存在,则对应值为0.0
src:需要计算均值的图像或者矩阵
mask:图像掩模(可选参数)
该函数计算的原理如下式所示:
N = ∑ I , mask ( I ) ≠ 0 1 M c = ( ∑ I , mask ( I ) ≠ 0 src ( I ) c ) / N \begin{aligned} N &=\sum_{I, \operatorname{mask}(I) \neq 0} 1 \\ M_{c} &=\left(\sum_{I, \operatorname{mask}(I) \neq 0} \operatorname{src}(I)_{c}\right) / N \end{aligned} NMc=I,mask(I)=0∑1=⎝⎛I,mask(I)=0∑src(I)c⎠⎞/N
其中, M c M_c Mc表示第c个通道的均值, I I I表示输入图像; s r c ( I ) c src(I)_c src(I)c表示第c个通道中像素的灰度值。
(2)cv.meanStdDev()函数可同时计算图像的均值和标准差。
#cv.meanStdDev()函数原型
mean, stddev = cv.meanStdDev(src
[, mean
[, stddev
[, mask]]])
其中各返回值参数的含义分别为:
mean:图像每个通道的均值
stddev:图像每个通道的标准差
src:需要计算均值的图像或者矩阵
mask:图像掩模(可选参数)
该函数计算的原理如下式所示:
N = ∑ I , mask ( I ) ≠ 0 1 M c = ( ∑ I , mask ( I ) ≠ 0 src ( I ) c ) / N stddev c = ∑ I , mask ( I ) ≠ 0 ( src ( I ) c − M c ) 2 / N \begin{aligned} N &=\sum_{I, \text { mask }(I) \neq 0} 1 \\ M_{c} &=\left(\sum_{I, \text { mask }(I) \neq 0} \operatorname{src}(I)_{c}\right) / N \\ \operatorname{stddev}_{c} &=\sqrt{\sum_{I, \operatorname{mask}(I) \neq 0}\left(\operatorname{src}(I)_{c}-M_{c}\right)^{2} / N} \end{aligned} NMcstddevc=I, mask (I)=0∑1=⎝⎛I, mask (I)=0∑src(I)c⎠⎞/N=I,mask(I)=0∑(src(I)c−Mc)2/N
示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
if __name__ == '__main__':
# 新建矩阵array
array = np.array([1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0])
# 将array调整为3*4的单通道图像img1
img1 = array.reshape((3, 4))
# 将array调整为3*2*2的多通道图像img2
img2 = array.reshape((3, 2, 2))
# 分别计算图像img1和图像img2的平均值和标准差
mean_img1 = cv.mean(img1)
mean_img2 = cv.mean(img2)
mean_std_dev_img1 = cv.meanStdDev(img1)
mean_std_dev_img2 = cv.meanStdDev(img2)
# 输出cv.mean()函数计算结果
print('cv.mean()函数计算结果如下:')
print('图像img1的均值为:{}'.format(mean_img1))
print('图像img2的均值为:{}\n第一个通道的均值为:{}\n第二个通道的均值为:{}'
.format(mean_img2, mean_img2[0], mean_img2[1]))
print('*' * 30)
# 输出cv.meanStdDev()函数计算结果
print('cv.meanStdDev()函数计算结果如下:')
print('图像img1的均值为:{}\n标准差为:{}'.format(mean_img1[0], float(mean_std_dev_img1[1])))
print('图像img2的均值为:{}\n第一个通道的均值为:{}\n第二个通道的均值为:{}\n'
'标准差为:{}\n第一个通道的标准差为:{}\n第二个通道的标准差为:{}\n'
.format(mean_img2, mean_img2[0], mean_img2[1],
mean_std_dev_img2[1], float(mean_std_dev_img2[1][0]), float(mean_std_dev_img2[1][0])))
OpenCV4中提供了求取两幅图中较大或较小灰度值的cv.max()、cv.min()函数,这两个函数依此比较两幅图像中灰度值的大小,返回值为两幅图像中较大(较小)的灰度值。
#cv.max()和cv.min()函数原型
dst = cv.max(src1,
src2,
[, dst])
dst = cv.min(src1,
src2,
[, dst])
其中各返回值和参数的含义分别为:
dst:保留对应位置上较大(较小)灰度值后的图像,尺寸、通道数和数据类型与输入参数一致
src1:第一幅图像,可以是任意通道数的矩阵
src2:第二幅图像,图像的尺寸、通道数以及数据类型需要与第一幅图像一致
OpenCV4提供了针对两幅图像像素之间的“与”、“或”、“异或”及“非”运算的函数。具体运算规则如下图所示。
像素逻辑运算函数的原型
//对像素求"与"运算
dst = cv.bitwise_and(src1,
src2,
[, dst
[, mask]])
//对像素求"或"运算
dst = cv.bitwise_or(src1,
src2,
[, dst
[, mask]])
//对像素求"异或"运算
dst = cv.bitwise_xor(src1,
src2,
[, dst
[, mask]])
//对像素求"非"运算
dst = cv.bitwise_not(src1,
src2,
[, dst
[, mask]])
其中各返回值和参数的含义分别为:
dst:逻辑运算之后的结果,尺寸、通道数和数据类型与输入参数一致
src1:第一幅图像,可以是任意通道数的矩阵
src2:第二幅图像,图像的尺寸、通道数以及数据类型需要与第一幅图像一致
mask:掩模矩阵,用于设置图像或矩阵中逻辑运算的范围
示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
if __name__ == '__main__':
# 创建两个黑白图像
img1 = np.zeros((200, 200), dtype='uint8')
img2 = np.zeros((200, 200), dtype='uint8')
img1[50:150, 50:150] = 255
img2[100:200, 100:200] = 255
# 进行逻辑运算
Not = cv.bitwise_not(img1)
And = cv.bitwise_and(img1, img2)
Or = cv.bitwise_or(img1, img2)
Xor = cv.bitwise_xor(img1, img2)
# 展示结果
cv.imshow('img1', img1)
cv.imshow('img2', img2)
cv.imshow('Not', Not)
cv.imshow('And', And)
cv.imshow('Or', Or)
cv.imshow('Xor', Xor)
cv.waitKey(0)
cv.destroyAllWindows()
在前面的程序中,我们生成了只有黑色和白色的图像,我们将这种只有两种灰度值的图像称为二值图像。二值图像的色彩种类少,可以进行高度压缩,以节省存储空间。将非二值图像经过计算变成二至图像的过程称为图像的二值化或阈值化。OpenCV4提供了cv.threshold()和cv.adapticeThredhold()函数用于实现图像二值化
(1)cv.threshold()
#cv.threshold()函数原型
retval , dst = cv.threshold(src,
thresh,
maxval,
type,
[, dst])
其中各返回值和参数的含义分别为:
dst:二值化后的图像,尺寸、通道数和数据类型与输入图像src一致
src:需要二值化的图像,图像的数据类型只能是uint8或float32,这与图像通道数目的要求和选择的二值化方法相关
thresh:二值化的阈值
maxval:二值化过程中的最大值,此参数只有选择 THRESH_BINARY 和 THRESH_BINARY_INV这两种二值化方法时才发挥作用,但是在使用其他方法时也需要输入参数
type:图像二值化的方法
具体图像二值化方法标志如下标所示
标志 | 简记 | 作用 |
---|---|---|
THRESH_BINARY | 0 | 若灰度值大于阈值,取最大值,对于其他值,取0 |
THRESH_BINARY_INV | 1 | 若灰度值大于阈值,取0 ,对于其他值,取最大值 |
THRESH_TRUNC | 2 | 若灰度值大于阈值,取阈值,对于其他值,不变 |
THRESH_TOZERO | 3 | 若灰度值大于阈值, 不变,对于其他值,取0 |
THRESH_TOZERO_INV | 4 | 若灰度值大于阈值,取0,对于其他值,不变 |
THRESH_OTSU | 8 | 使用大津法自动寻求全局阈值 |
THRESH_TRIANGLE | 16 | 使用三角形法自动寻求全局阈值 |
不同二值化方法示例如下图所示
(2)cv.adaptiveThreshold()
#cv.adaptiveThreshold()函数原型
dst = cv.adaptiveThreshold(src,
maxValue,
adaptiveMethod,
thresholdType,
blockSize,
C,
[, dst])
其中各返回值和参数的含义分别为:
dst:二值化后的图像,尺寸、通道数和数据类型与输入图像src一致
src:需要二值化的图像,图像的数据类型只能是uint8单通道类型
adaptiveMethod:自适应确定阈值的方法,有均值法(cv.ADAPTIVE_THRESH_MEAN_C)和高斯法(cv.ADAPTIVE_THRESH_GAUSSIAN_C)两种
thresholdType:选择图像二值化的方法,只能是cv.THREASH_BINARY或cv.THREASH_BINARY_INV
blockSize:自适应确定阈值的像素领域大小,一般取值为3、5、7
C:从平均值或加权平均值中减去的常数,可以为正数或负数
示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
if __name__ == '__main__':
# 读取图像并判断是否读取成功
img = cv.imread('./images/lena.jpg')
if img is None:
print('Failed to read lena.jpg.')
sys.exit()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 彩色图像二值化
_, img_B = cv.threshold(img, 125, 255, cv.THRESH_BINARY)
_, img_B_V = cv.threshold(img, 125, 255, cv.THRESH_BINARY_INV)
cv.imshow('img_B', img_B)
cv.imshow('img_B_V', img_B_V)
# 灰度图像二值化
_, gray_B = cv.threshold(gray, 125, 255, cv.THRESH_BINARY)
_, gray_B_V = cv.threshold(gray, 125, 255, cv.THRESH_BINARY_INV)
cv.imshow('gray_B', gray_B)
cv.imshow('gray_B_V', gray_B_V)
# 灰度图像TOZERO变换
_, gray_T = cv.threshold(gray, 125, 255, cv.THRESH_TOZERO)
_, gray_T_V = cv.threshold(gray, 125, 255, cv.THRESH_TOZERO_INV)
cv.imshow('gray_T', gray_T)
cv.imshow('gray_T_V', gray_T_V)
# 灰度图像TRUNC变换
_, gray_TRUNC = cv.threshold(gray, 125, 255, cv.THRESH_TRUNC)
cv.imshow('gray_TRUNC', gray_TRUNC)
# 灰度图像大律法和三角形法二值化
img1 = cv.imread('./images/threshold.png', cv.IMREAD_GRAYSCALE)
_, img1_O = cv.threshold(img1, 100, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
_, img1_T = cv.threshold(img1, 125, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE)
cv.imshow('img1', img1)
cv.imshow('img1_O', img1_O)
cv.imshow('img1_T', img1_T)
# 灰度图像自适应二值化
adaptive_mean = cv.adaptiveThreshold(img1, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 13, 0)
adaptive_gauss = cv.adaptiveThreshold(img1, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 13, 0)
cv.imshow('adaptive_mean', adaptive_mean)
cv.imshow('adaptive_gauss', adaptive_gauss)
cv.waitKey(0)
cv.destroyAllWindows()
部分运行结果如下图所示。
下一篇将会介绍OpenCV中图像的连接与变换等操作。