计算机眼中的图像
图像的存储形式
import cv2
img = cv2.imread('img/test.jpg')
print(img)
可见在计算机中是以矩阵的形式存储图像的每一个像素点。
不过在 OpenCV 中是以 BGR 的顺序存储的,参见 官方文档:
imread
还有第 2 个参数:
cv2.IMREAD_COLOR
:彩色图像,就是一般的 BGR 形式cv2.IMREAD_GRAYSCALE
:灰度图像- 把白色与黑色之间按对数关系分为若干等级,称为 灰度
- 灰度图与黑白图不同,黑白图只有黑与白两种颜色
使用 img.shape
可以查看图像的 (h, w, c) ,即 (高,宽,通道数),3 表示 RGB 形式。
img1 = cv2.imread('img/test.jpg')
img2 = cv2.imread('img/test.jpg', cv2.IMREAD_COLOR)
img3 = cv2.imread('img/test.jpg', cv2.IMREAD_GRAYSCALE)
print(img1.shape)
print(img2.shape)
print(img3.shape)
输出:
查看图像
进行一些图像操作之后,想查看一下图片目前是什么样子的,可以通过 cv2.imshow()
函数实现。
cv2.imshow('window name', img)
# 等待时间,以毫秒为单位,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
这 3 行代码很常用,可以封装成一个方法简化调用:
def cv_show(window_name, temp):
cv2.imshow(window_name, temp)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv_show('window1', img2)
cv_show('window2', img3)
保存图像:
cv2.imwrite('文件名.png', img)
视频的读取与处理
其实视频就是很多张连续的图像。
vc = cv2.VideoCapture('media/test.mp4')
if vc.isOpened():
flag = vc.read()
else:
flag = False
while flag:
ret, frame = vc.read()
if frame is None:
break
if ret:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('result', gray)
if cv2.waitKey(100) & 0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
ROI 区域
Rgion Of Interest,感兴趣区域。
即在图像中以方框等方式勾勒出需要处理的区域。
img = cv2.imread('test.jpg')
sect = img[200:500, 300:500]
cv_show('sect', sect)
颜色通道提取
split 函数可以将多通道图像分割为单通道图像。
容易知道 B、G、R 分别对应的下标为:
B | G | R |
---|---|---|
0 | 1 | 2 |
所以就可以分别绘制出只包含其中一个通道的图像。
b, g, r = cv2.split(img)
# 只保留R
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 1] = 0
cv_show('R', cur_img)
# 只保留G
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 2] = 0
cv_show('G', cur_img)
# 只保留B
cur_img = img.copy()
cur_img[:, :, 1] = 0
cur_img[:, :, 2] = 0
cv_show('B', cur_img)
边界填充
- BORDER_REPLICATE:复制法,也就是复制最边缘像素
- BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制
- BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴对称
- BORDER_WRAP:外包装法
- BORDER_CONSTANT:常量法,常数值填充
import matplotlib.pyplot as plt
img = cv2.imread('media/yuki.jpg') top_size, bottom_size, left_size, right_size = (100, 100, 100, 100) replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE) reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT) reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101) wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP) constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT, value=0) plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL') plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE') plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT') plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101') plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP') plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT') plt.show()
不知道为啥这里的结果有些泛蓝,原图是这样的:
加法运算
直接加上一个常数相当于每一个像素点都加。
sky = cv2.imread('media/sky.jpg')
sky1 = sky + 10
print(sky[0, 0, :], "\n")
print(sky1[0, 0, :])
两张图像宽高相同时,也可以进行相加。如果宽高不相同,可以用 cv2.resize()
方法来调整大小,如
img_fix = cv2.resize(img, (400, 300))
加出的结果如果越界(0~255),会对 256 取模。
OpenCV 也提供了 cv2.add
用来进行加法运算,不过这个方法在越界时会取最大值 255。
图像融合
图像相加和图像融合是不同的,图像融合有专门的函数,其公式为:
$result = αx_1 + βx_2 + b$
yuki = cv2.imread('media/yuki.jpg')
sky = cv2.imread('media/sky.jpg')
result1 = yuki + sky
result2 = cv2.addWeighted(yuki, 0.4, sky, 0.6, 0)
cv_show("result1", result1)
cv_show("result2", result2)
这里的 0.4
相当于 α
,0.6
相当于 β
,0
相当于 b
。