欢迎访问我的博客首页。
1. 读取图像并保存为 MP4
逐帧读取图像,把该帧旋转 180°,再保存为 mp4。读取视频时,可以设置起始帧序号或起始毫秒。代码除旋转帧外,还可以裁剪帧。由于写视频前需要指定每帧宽高,所以裁剪后的宽高要和指定的宽高一致。
import os
import cv2
from tqdm import tqdm
def video_reader(video_input, video_output):
# 1. 获取输入视频的句柄和属性。
cap = cv2.VideoCapture(video_input)
fps = round(cap.get(cv2.CAP_PROP_FPS))
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
wh = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = int(cap.get(cv2.CAP_PROP_FOURCC))
# 2. 设置起始帧或起始毫秒时间。
# cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
# cap.set(cv2.CAP_PROP_POS_MSEC, 0)
# 3. 设置输出视频的属性。
# fourcc = cv2.VideoWriter_fourcc(*'mp4v')
writer = cv2.VideoWriter()
# 4. 逐帧处理。
idx = 0
pbar = tqdm(total=frame_count)
while cap.isOpened():
rec, im = cap.read()
if not rec:
break
im = cv2.rotate(im, cv2.ROTATE_180)
# im = im[ymin:ymax, xmin:xmax]
# 每 4 秒保存成一个视频。
if idx % (fps * 4) == 0:
writer.release()
path_video = os.path.join(video_output, str(int(idx // (fps * 4))) + '.mp4')
writer.open(path_video, fourcc, fps, wh)
writer.write(im)
idx += 1
pbar.update(1)
pbar.close()
writer.release()
cap.release()
if __name__ == '__main__':
path2input = r"assets/1.mp4"
path2output = r"assets"
video_reader(path2input, path2output)
fourcc 全称 Four-Character Codes,使用四个字符表示视频格式。fourcc 要和 cv2.VideoWriter 保存的视频格式对应。具体如下表:
fourcc | 格式 |
---|---|
PIMI | 较旧的MPEG-1编码,文件名后缀为.avi |
MP42 | MPEG-2编码,产生的文件不会特别大,文件名后缀为.avi |
DIV3 | MPEG-3编码,产生的文件不会特别大,文件名后缀为.avi |
XVID | 较旧的MPEG-4编码,产生的文件不会特别大,文件名后缀为.avi |
MP4V | 较旧的MPEG-4编码,产生的文件不会特别大,文件扩展名应为.m4v |
DIVX | MPEG-4编码,产生的文件不会特别大,文件名后缀为.avi |
X264 | 较新的MPEG-4编码,产生的文件较小,文件扩展名应为.mp4 |
THEO | Ogg Vorbis,产生的文件相对较大,文件名后缀为.ogv |
FLV1 | Flash视频,产生的文件相对较大,文件名后缀为.flv |
MJPG | motion-jpeg编码,产生的文件较大,文件名后缀为.avi |
I420 | 未压缩的YUV编码,4:2:0色度子采样,这种编码广泛兼容,但会产生特别大的文件,文件扩展名应为.avi |
2. 读取图像
2.1 读取与格式
opencv-python 读取图形的函数如下。
img = cv2.imread(filename, flags)
- filename:指定图像的路径。
- flags:用来控制读取文件的类型,flages的参数值如下表。表中第一列参数与第三列是等价的。在设置参数时,既可以使用第一列的参数值,也可以使用第三列的参数值。
该函数可以读取多种格式的图像,具体如下。
下面是例子。
if __name__ == '__main__':
im = cv2.imread(filename=r'assets/woman.jpg', flags=cv2.IMREAD_UNCHANGED)
print(im.shape)
im = cv2.resize(im, (im.shape[1] // 2, im.shape[0] // 2), interpolation=cv2.INTER_AREA)
print(im.shape)
cv2.putText(img=im,
text='DEFGH',
org=(100, 200),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=1,
color=(255, 0, 0),
thickness=3,
lineType=cv2.LINE_4,
bottomLeftOrigin=True)
cv2.imwrite(r'assets/woman2.jpg', im)
2.2 中文路径
opencv 对中文的支持不够好,读取或保存中文路径的图像可以出现异常。这时可以使用下面的方法:
def imread(filename, flags=cv2.IMREAD_UNCHANGED):
return cv2.imdecode(np.fromfile(filename, dtype=np.uint8), flags)
def imwrite(path, im):
ext = os.path.splitext(path)[1]
cv2.imencode(ext, im)[1].tofile(path)
2.3 添加文本
2.1 节已经展示了怎么添加字体,现在我们说一下参数的作用。
- image:要在其上绘制文本的图像。
- text:要绘制的文本字符串。
- org:它是图像中文本字符串左下角的坐标。坐标表示为两个值的元组,即(X坐标值,Y坐标值)。
- font:它表示字体类型。一些字体类型是FONT_HERSHEY_SIMPLEX,FONT_HERSHEY_PLAIN等。
- fontScale:字体比例因子乘以font-specific基本大小。
- color:它是要绘制的文本字符串的颜色。对于BGR,我们通过一个元组。例如:(255,0,0)为蓝色。
- thickness:它是线的粗细像素。
- lineType:这是一个可选参数,它给出了要使用的行的类型。
- bottomLeftOrigin:这是一个可选参数。如果为true,则图像数据原点位于左下角。否则,它位于左上角。
可选字体如下:
字体 | 含义 |
---|---|
cv.FONT_HERSHEY_SIMPLEX | normal size sans-serif font |
cv.FONT_HERSHEY_PLAIN | small size sans-serif font |
cv.FONT_HERSHEY_DUPLEX | normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX) |
cv.FONT_HERSHEY_COMPLEX | normal size serif font |
cv.FONT_HERSHEY_TRIPLEX | normal size serif font (more complex than FONT_HERSHEY_COMPLEX) |
cv.FONT_HERSHEY_COMPLEX_SMALL | smaller version of FONT_HERSHEY_COMPLEX |
cv.FONT_HERSHEY_SCRIPT_SIMPLEX | hand-writing style font |
cv.FONT_HERSHEY_SCRIPT_COMPLEX | more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX |
cv.FONT_ITALIC | flag for italic font |
2.4 几何图形
画圆、线、矩形、四边形、椭圆。
# im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.circle(img=im, center=(100, 100), radius=9, color=(255, 0, 0), thickness=-1, lineType=8)
cv2.line(img=im, pt1=(100, 100), pt2=(500, 400), color=(0, 255, 0), thickness=2, lineType=8)
cv2.rectangle(img=im, pt1=(100, 100), pt2=(500, 400), color=(0, 0, 255), thickness=2, lineType=8)
polygon_1 = np.array([[300, 100], [500, 400], [100, 400]])
polygon_2 = np.array([[300, 100], [500, 250], [300, 400], [100, 250]])
cv2.polylines(img=im, pts=[polygon_1, polygon_2], isClosed=True, color=(0, 255, 255), thickness=2, lineType=8)
cv2.ellipse(img=im, center=(300, 250), axes=(200, 150), angle=0, startAngle=-45, endAngle=225, color=(255, 0, 255), thickness=2, lineType=8)
3. 图像切片
下面的代码使用参数 n_yx 把一副图像切割成大小相等的 n_yx[0] 行,n_yx[1] 列。
def slice_image(image, n_yx: list = None):
if not n_yx:
n_yx = [4, 4]
h = image.shape[0] // n_yx[0]
w = image.shape[1] // n_yx[1]
for i in range(n_yx[0]):
for j in range(n_yx[1]):
image_ij = image[i * h:(i + 1) * h, j * w:(j + 1) * w]
cv2.imwrite(str(i) + str(j) + '.jpg', image_ij)
4. 绘制直方图
绘制三个通道和灰度图的直方图,并显示 rgb 图像。
import cv2
import numpy as np
from matplotlib import pyplot as plt
def show_hist(path_im: str):
# 1.获取图像。
im_bgr = cv2.imread(path_im, 1)
im_gra = cv2.cvtColor(im_bgr, cv2.COLOR_BGR2GRAY)
count_pixel = im_bgr.shape[0] * im_bgr.shape[1]
# 2.统计直方图。
hist_b = cv2.calcHist([im_bgr], [0], None, [256], [0, 255]) * 100 / count_pixel
hist_g = cv2.calcHist([im_bgr], [1], None, [256], [0, 255]) * 100 / count_pixel
hist_r = cv2.calcHist([im_bgr], [2], None, [256], [0, 255]) * 100 / count_pixel
hist_a = cv2.calcHist([im_gra], [0], None, [256], [0, 255]) * 100 / count_pixel
# 3.绘图。
fig, ax = plt.subplots(2, 1, figsize=(10.24, 7.68), dpi=100)
# 3.1 绘制直方图。
ax[0].plot(hist_b, color='b')
ax[0].plot(hist_g, color='g')
ax[0].plot(hist_r, color='r')
ax[0].plot(hist_a, color='gray')
# 3.2 绘制 OpenCV 图像。
im_rgb = cv2.cvtColor(im_bgr, cv2.COLOR_BGR2RGB)
ax[1].imshow(im_rgb)
# 4.显示与保存。
plt.grid()
# plt.show()
plt.savefig('1.jpg')
if __name__ == '__main__':
path_input = r'assets/example.jpg'
show_hist(path_input)
效果如下。
5. 参考
- VideoCapture 属性,OpenCV document。
- fourcc,OpenCV document。
- fourcc,CSDN,2021。