OpenCV-图片与视频的基本操作

注意:opencv_python版本选择3.4.1.15,过高可能会有专利问题无法使用。目前通用的教程中版本基本都是opencv2、3.

图片入门

一张图片由海量的像素点构成,而一个像素点由BGR三个颜色通道组成的,也就是三原色光。BGR的数值表示亮度,使用整数表示,从 0 ~ 255 ,一共256级,0 为黑色,255 为白色,因此数值越大图片越亮。一张彩色照片通常就是BGR图片。

对于一张图片来说,它的数值就是一个矩阵,行列就是像素点的数量。程序读取图片时,就是在读取这个图片像素点所映射的矩阵。

图片读取

1
2
3
4
5
6
7
8
jz=cv2.imread("D:\\z-bank\\bank\\PY\\book.png")
print(jz)

out:
[[[230 226 232]
[227 223 229]
[227 222 231]
...

这里的中括号有三层,表示三个维度,用[h,w,c]表示。补充:hwc维度表示一个图像,H表示图像高度,w表示宽度,c表示通道数。
如果使用jz=cv2.imread("book.png",cv2.IMREAD_GRAYSCALE)读取灰度图片,那么将没有c,只有h、w。

imread()函数的第二个参数是读取图片的方式,有以下几种:

  • cv2.IMREAD_COLOR:读取彩色图片,透明度忽略,这是默认方式,用1表示
  • cv2.IMREAD_GRAYSCALE:读取灰度图片。 用0表示
  • cv2.IMREAD_UNCHANGED:读取图片的alpha通道。用-1表示

图片显示

1
2
3
4
jz=cv2.imread("D:\\z-bank\\bank\\PY\\book.png")
cv2.imshow("jz",jz)
cv2.waitKey(0)
cv2.destroyAllWindows()

使用imshow()函数来显示图像,窗口自适应图像尺寸。

waitKey()用于设置窗口开启时间,单位毫秒。设为 0 代表按下任意键即可关闭图片窗口。一般来说就是填 0

destroyAllWindows()用于关闭所有窗口,如果想关闭指定窗口,可以使用destroyWindow()函数。

图片裁剪

jz是一个数组,左边参数为y轴裁剪范围,右边为x轴。单位是像素。具体数值可以在ps->信息窗口中查看。
图像裁剪截取图像的特定区域,在识别图像中的特定目标时,可以使用图像裁剪来提高识别的准确率。

1
2
3
jz=cv2.imread("D:\\z-bank\\bank\\PY\\book.png")
jz=jz[80:183,134:236]
cv_show('jz',jz)

图片保存

cv2.imwrite('book.png',jz)即可保存图片

图片通道分离与合并

图片shape值的最后一位表示通道数,分别是BGR,用0、1、2表示

1
2
3
# b:0:蓝色通道,g:1:绿色通道,r:2:红色通道
b,g,r=cv2.split(jz) # split()操作耗时,尽量使用numpy的索引操作
cv_show('b',b)

图像允许将任意通道置为0,并合并其他通道,从而达到去除某个通道的效果。

1
2
3
4
cpimg=cv2.merge([b,g,r])
cpimg[:,:,0]=0 # 将绿色通道置为0
cpimg[:,:,2]=0 # 将红色通道置为0
cv_show('cpimg',cpimg)

matplotlib显示图片

1
2
3
4
5
6
jz=cv2.imread("D:\\z-bank\\bank\\PY\\book.png")

plt.imshow(jz,cmap='gray',interpolation='bicubic')
plt.xticks([]),plt.yticks([]) # 隐藏x,y轴
plt.show()

其他图像信息

1
2
3
print(type(jz))  # 图片的底层是numpy.ndarray格式
print(jz.size) # 图片大小
print(jz.dtype) # 图片类型

图像基本操作

图片填充

图像填充可以在周边创建边框,例如相框。都使用copyMakeBorder()函数,用法及参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
img=cv2.imread("D:\\z-bank\\bank\\PY\\book.png")
replicate=cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_CONSTANT) # 常量法,常数值填充
reflect =cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_REFLECT) # 反射法,以边界为轴对称填充,如fedcba|abcdefgh|hgfedcb
reflect101 = cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_REFLECT_101) # 反射法,以边缘像素为轴对称填充,如gfedcb|abcdefgh|bagfedc
wrap = cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_WRAP) # 外包装法,如cdefgh|abcdefgh|abcdefg
constant= cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_CONSTANT,value=[255,0,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()

图像加法

对一张图片做加法时,会对每个像素点的RGB值进行加法运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tx=cv2.imread("D:\\z-bank\\bank\\PY\\tx.jpg")
print(tx[:3,:,0])
tx2=tx+10
print(tx2[:3,:,0])

# out:
[[137 137 137 ... 144 144 144]
[137 137 137 ... 144 144 144]
[137 137 137 ... 144 144 144]]

[[147 147 147 ... 154 154 154]
[147 147 147 ... 154 154 154]
[147 147 147 ... 154 154 154]]

如上代码,对 tx 图片每个像素点的BGR值+10,此时图片就会变亮。而如果将+10后的图片与原图相加,将得到一个(137+147)%256=28的结果,即图片变暗。因此,图像加法并不是简单的加法运算,而是对每个像素点的RGB值进行加法运算。

1
2
3
4
5
6
7
tx3=tx+tx2
print(tx3[:3,:,0])

# out:这里的结果是(137+147)%256=28
[[28 28 28 ... 42 42 42]
[28 28 28 ... 42 42 42]
[28 28 28 ... 42 42 42]]

上面的加法运算使用的是Numpy的模运算,也可以使用OpenCV的饱和运算,即超过255的值就截断为255,低于0的值就截断为0。

1
2
3
4
5
6
7
tx3=cv2.add(tx,tx2)
print(tx3[:3,:,0])

#out:
[[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]]

图像融合

图像融合的前提是两张图片的shape必须相同,所以需要先使用cv2.resize()函数调整图片大小。

1
2
3
4
5
6
7
8
9
10
11
tx=cv2.imread("D:\\z-bank\\bank\\PY\\tx.jpg")
xx=cv2.imread("D:\\z-bank\\bank\\PY\\xx.jpg")
print("tx:",tx.shape)
print("xx:",xx.shape)
# resize第二个参数是列数,第一个参数是行数,与shape相反
newxx=cv2.resize(xx,(tx.shape[1],tx.shape[0]))

# out:
tx: (960, 960, 3)
xx: (700, 700, 3)
newxx: (960, 960, 3)

cv2.resize()函数还可以将第二个参数设置为(0,0),第三个参数设置为缩放比例,即可按比例缩放图片。如下,x,y轴缩放为原来的一半,图像大小变为原来的四分之一。

1
2
tx=cv2.imread("D:\\z-bank\\bank\\PY\\tx.jpg")
cv2.resize(tx,(0,0),fx=0.5,fy=0.5)

然后使用cv2.addWeighted()函数进行融合,用法如下。对图像进行加权融合,公式为:dst=α⋅img1+β⋅img2+γ。其中α、β、γ是缩放系数,α+β=1。
最后的效果类似于Photoshop中的图层融合,正片叠底。

1
2
res=cv2.addWeighted(tx,0.5,xx,0.5,5)
cv_show("res",res)

视频入门

视频读取

读取视频时,首先创建一个VideoCapture 对象。参数是设备索引或视频文件的名称。设备索引是指定摄像头,一般用0、1、2等数字表示。也可以填写视频文件的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
videos=cv2.VideoCapture("D:\\z-bank\\bank\\PY\\wg.flv")
if videos.isOpened():
open,frame=videos.read()
else:
open=False

while open:
ret,frame=videos.read() # 读取每一帧的图片,ret为布尔值,frame为图片
if frame is None:
break

if ret==True: # 开始处理视频
gray=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换为灰度图
cv2.imshow('result',gray)
if cv2.waitKey(25) & 0xFF ==27: # 25即帧率,表示每25帧显示一次,0xFF ==27表示按下ESC退出
break

videos.release() # 完成所有操作后,释放捕获器
cv2.destroyAllWindows()

视频保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 视频读取
vd=cv2.VideoCapture("D:\\z-bank\\bank\\PY\\wg.flv")

# 设置视频的输出路径、编码器、帧率、大小
out=cv2.VideoWriter('D:\\z-bank\\bank\\PY\\out.avi',cv2.VideoWriter_fourcc(*'XVID'),25.0,(640,480))

if vd.isOpened():
open,frame=vd.read()
else:
open=False
while open:
ret,frame=vd.read() # 读取每一帧的图片,ret为布尔值,frame为图片
if frame is None:
break

if ret==True:
frame=cv2.flip(frame,0) # 翻转图片
out.write(frame) # 写入视频
cv2.imshow('frame',frame) # 显示图片
if cv2.waitKey(1) & 0xFF==ord('q'):
break

vd.release()
cv2.destroyAllWindows()

OpenCV-图片与视频的基本操作
https://zhouyinglin.cn/post/7ffca621.html
作者
小周
发布于
2023年4月25日
更新于
2023年4月25日
许可协议