import cv2
import numpy as np
import matplotlib.pyplot as plt
import imageio
def cv_show_image(name, img): # 显示图像,这个函数在下面会一直用到
cv2.imshow(name, img) # img 通道顺序为BGR
# 等待的时间以毫秒ms为单位,0表示无限等待,按任意键退出
cv2.waitKey(0)
cv2.destroyAllWindows() # 销毁所有窗口
filename = './tools/shiyuan.jpeg'
img = cv2.imread(filename) # opencv读取的图片格式为 BGR
print('img.shape: ', img.shape)
cv_show_image('color_image', img)
# 设定imread()函数的参数,直接返回灰度图
img2 = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
print('img2.shape: ', img2.shape)
cv_show_image('gray_image', img2)
# 已读取彩色图像,需要将彩色图转换为灰度图
# 标准转换公式如下: gray = 0.299 * Red + 0.587 * Green + 0.114 * Blue (加权平均)
# 还有: 完全均值、最大值法、最小值法、photoshop转灰度图法,此处略去
img3 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print('img3.shape: ', img3.shape)
cv_show_image('color2gray', img3)
opencv读取的图像通道顺序为BGR,和常用的RGB通道顺序不一致,这在图像的显示上存在差异,小编习惯于使用matplotlib显示图像,故matplotlib显示图像前需要进行通道转换 (面对灰度图就不需要转换了,反正只有一个通道,只是plt显示灰度图时, plt.imshow(img, cmap='gray'))
,记得设置cmap参数为gray。
# 图像转换 三种由 BGR 转变为 RGB 通道的方法
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # 返回一个新的修改后的图像
plt.axis('off')
plt.show()
# split 分别获得图像的 Blue、Green、Red 通道,之后重新merge组合
b, g, r = cv2.split(img)
plt.imshow(cv2.merge([r, g, b]))
plt.axis('off')
plt.show()
new_img = img[:, :, ::-1]
# [::-1] 表示顺序相反的意思,图像shape是[H, W, C] 其中C表示通道,开始为BGR顺序,将C维度顺序逆转,就变成了RGB
plt.imshow(new_img) # 返回一个新的修改后的图像
plt.axis('off')
plt.show()
啰嗦一句,plt显示图像,要求图像的通道顺序为RGB,所以面对opencv读取的图像,需要将图像的通道顺序由BGR转换为RGB;反过来,若使用opencv中的cv2.imshow() 函数显示图像,由于该函数要求图像的通道顺序为BGR,所以面对plt.imread()、imageio.imread(), PIL.imread()等函数读取的图像,也需要将图像的通道顺序从RGB转换为BGR。转换方法类似如下:cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
。cv2.cvtColor()函数中第二个参数有多种,涉及到BGR、RGB、GRAY、HSV等多种类型,此处不赘述。
图像的ROI区域看起来高大上,其实就是Region of Interest 图像中感兴趣的部分,一般先将图像中感兴趣的部分提取出来,然后进行操作。例如若想要截取图像中某方形区域,可以使用np.array的切片操作,演示代码如下。
import numpy as np
import cv2
import matplotlib.pyplot as plt
def cv_show_image(name, image):
cv2.imshow(name, image)
cv2.waitKey(0)
cv2.destroyAllWindows()
filename = './tools/shiyuan.jpeg'
img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
cv_show_image('gray_img', img)
img2 = img[100: 400, 200: 600] # 切片操作
cv_show_image('img_roi', img2)
将两张图像进行融合,函数为cv2.addWeighted(img1, alpha, img2, beta, bias)
,例如cv2.addWeighted(img1, 0.6, img2, 0.4, 0)
其中第一个图像的权重为0.6,第二张图像的权重为0.4,bias偏置项为0,bias主要对图像亮度上的调整。进行图像融合需要保证融合的两个图像有相同的shape,通常通过cv2.resize()进行调整。
filename1 = './tools/shiyuan.jpeg'
img1 = cv2.imread(filename1)
filename2 = './tools/shiyuan2.jpeg'
img2 = cv2.imread(filename2)
img2 = cv2.resize(img2, img1.shape[:2])
img3 = cv2.addWeighted(img1, 0.6, img2, 0.4, 0)
cv_show_image('shiyuan', img3)
融合结果如下:
补充一句,cv2.resize()常用的方法有两种:cv2.resize(img, (500, 400))
和cv2.resize(img, (0, 0), fx=3, fy=2)
,前者将图像img变换为指定大小(500, 400),后者没有规定变换后的图像大小,只是要求将图像宽度变为原来的3倍(fx=3),高度变为原来的2倍(fy=2)。
还有一个小知识点,将两个shape相同的图像相加,有两种常用方法:(1) 使用+号直接相加。(2) 使用cv2.add()函数相加;前者对应像素点像素值相加,若超过了255,则对255取余,后者若超过255,直接截断,取值为255。
视频本质上就是一张一张的图像,称为帧。单位时间内显示的帧的数量,我们称之为帧率,帧率越高,视频越流畅,若帧率低于某一范围,就会视频出现卡顿现象。读取视频的代码如下(想运行代码但手上没视频文件的小伙伴,可以使用电脑录屏功能自己生成):
# 捕获摄像头
vc = cv2.VideoCapture('./tools/test.mp4')
# 若视频正确打开,则isOpened()函数返回True,反之False
while vc.isOpened():
is_open, frame = vc.read() # read()读视频,is_open 为布尔值,frame为图像帧
if frame is None: # frame == None 表示视频读完了,break退出
break
if is_open:
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('test.mp4', gray_img)
# cv2.waitKey(50)表示帧持续的时间,时间越短,视频播放的速度越快
if cv2.waitKey(50) & 0xFF == 27: # 27是Esc键的ASCII码,也可以换为q键,如: oxFF == ord('q')
break
vc.release() # 释放摄像头
cv2.destroyAllWindows()
VideoCapture对象常用的还有get()方法,可以获得诸如:帧的高度、帧的宽度、帧率、视频总帧数、视频时长等属性。代码如下:
# 宽度
width = vc.get(cv2.CAP_PROP_FRAME_WIDTH)
# 高度
height = vc.get(cv2.CAP_PROP_FRAME_HEIGHT)
# 帧率
fps = vc.get(cv2.CAP_PROP_FPS)
# 总帧数
frame_count = vc.get(cv2.CAP_PROP_FRAME_COUNT)
# 总秒数
time_count = int(frame_count / fps) '
print('width: ', width)
print('height: ', height)
print('fps: ', fps)
print('frame_count: ', frame_count)
print('time_count: ', time_count)
参考文献: