Pytorch应用实战(1)- 基于YOLO的视频人脸马赛克处理
本文介绍
YoloV11(Github)提供了非常方便的API帮助用户实现目标检测(detect)、语义分割(segement)、肢体识别(Pose)等功能。
本文将基于YoloV11的目标检测来实现一个视频人脸马赛克的处理,帮助大家入门YoloV11的目标检测使用。
本文并不包含模型训练部分
给图片的人脸打码
在进行视频处理前,先对图片进行处理,熟悉下YoloAPI的使用。
首先安装Yolo官方的依赖库:ultralytics。该库封装了Yolo训练、推理等各种方法,用户可以很简单方便的使用Yolo。
```python !pip install ultralytics ```
接下来导入要使用的包:
```python import cv2 # 用于处理图片 from ultralytics import YOLO # 用于调用YOLO模型 ```
接下来加载YOLO模型。这里我使用的是由 yolov8-face项目 训练的,专门用于人脸检测的Yolo模型。你需要先下载 yolov8n-face.pt 模型,然后放在当前目录下。
```python
model = YOLO("yolov8n-face.pt")
```
接下来,下载我们要用的测试图片。
```python !wget https://ultralytics.com/images/bus.jpg ```
读取并展示该图片:
```python
image = cv2.imread("bus.jpg")
cv2.imshow(image)
```

获取到图片后,用上面加载的模型对该图片进行推理:
```python # 由于YOLO一次可以推理多张图片,因此返回结果为list results = model(image) ```
对于目标检测任务,结果被存在result的boxes属性中:
```python results[0].boxes # 因为只有一张图片,所以使用results[0]获取 ```
输出:
```
ultralytics.engine.results.Boxes object with attributes:
# 由于模型检测到了两张人脸,因此下面的数据都是有两个。
cls: tensor([0., 0.]) # 物体的类别。由于使用的是专门的人脸检测,因此只有人脸(0)一个类别。
conf: tensor([0.7887, 0.7506]) # 置信度(0~1)
...
xyxy: tensor([[114., 417., 154., 467.],
[270., 421., 307., 470.]]) # 两张人脸在图片中的位置
...
```
拿到人脸坐标位置后,就可以对人脸进行处理了。处理代码如下:
```python
mosaic_scale = 10 # 定义马赛克的程度。即将图片模糊多少倍
boxes = results[0].boxes.xyxy
for i in range(len(boxes)): # 遍历人脸
x1, y1, x2, y2 = boxes[i].int()
# 获取当前人脸数据(region of interest)
# 假设人脸的大小为49x37,则roi.shape为(49, 37, 3)
roi = image[y1:y2, x1:x2]
# 获取人脸区域的height和width
h, w = roi.shape[:2]
# 将人脸缩放{mosaic_scale}倍。人脸大小变为4x3
small_roi = cv2.resize(roi, (w // mosaic_scale, h // mosaic_scale), interpolation=cv2.INTER_LINEAR)
# 再将缩放后的人脸变回原来的大小,即49x37,这样就实现了马赛克效果。
mosaic_roi = cv2.resize(small_roi, (w, h), interpolation=cv2.INTER_NEAREST)
# 将原图中的人脸替换为马赛克后的人脸
image[y1:y2, x1:x2] = mosaic_roi
```
完成人脸马赛克处理后,重新展示下图片:
```python cv2.imshow(image) ```

给视频的人脸打码
给视频打码就是一帧帧的处理图片,然后再将图片合成回视频即可。
给视频打码需要经过如下步骤:
- 使用
ffmpeg提取音频。 - 逐帧读取视频图片,将其进行马赛克处理。
- 将处理好的帧合成视频,此时视频是没有声音的。
- 将第1步提取的音频和处理好的无声视频合成起来。
接下来开始进行代码演示。
首先,导入要使用的依赖
```python import ffmpeg # 需要安装ffmpeg-python import cv2 from numpy import ndarray from ultralytics import YOLO from tqdm import tqdm ```
接下俩,我们将上一节给图片打马赛克的代码封装成一个方法,以便后续调用:
```python
def mosaic_image(model, image:ndarray, mosaic_scale = 10) -> ndarray:
results = model(image, verbose=False)
results[0].boxes
boxes = results[0].boxes.xyxy
for i in range(len(boxes)):
x1, y1, x2, y2 = boxes[i].int()
roi = image[y1:y2, x1:x2]
h, w = roi.shape[:2]
small_roi = cv2.resize(roi, (w // mosaic_scale, h // mosaic_scale), interpolation=cv2.INTER_LINEAR)
mosaic_roi = cv2.resize(small_roi, (w, h), interpolation=cv2.INTER_NEAREST)
image[y1:y2, x1:x2] = mosaic_roi
return image
```
之后就可以正式开始进行视频马赛克的处理了。
首先,定义一下输入视频路径、输出视频路径和中间文件的路径,并加载模型文件。
```python
# You can download the video from the link:
# https://github.com/iioSnail/pytorch_deep_learning_examples/tree/main/asserts/mp4
input_video = "kunkun.mp4"
tmp_audio = "tmp.wav"
tmp_video = "tmp_kunkun.mp4"
output_video = "mosaic_kunkun.mp4"
model = YOLO("yolov8n-face.pt")
```
之后,使用ffmpeg提取视频中的音频:
```python ffmpeg.input(input_video).output(tmp_audio, format='wav').run(overwrite_output=True) ```
接下来开始逐帧处理视频视频,并将其存储为新的视频:
```python
# 打开视频
cap = cv2.VideoCapture(input_video)
if not cap.isOpened():
print("Error: Could not open video file.")
exit(0)
# 获取视频的宽高和帧率
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
# 获取总帧率,用于打印进度条
n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# 配置视频输出
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(tmp_video, fourcc, fps, (width, height))
# 逐帧处理视频图片
pro_bar = tqdm(total=n_frames)
while True:
# 读取一帧视频。
# ret为bool,表示是否读取到帧。
# frame为该帧的图片数据,类型为ndarray。例如:ndarray的shape为(360, 640, 3)
ret, frame = cap.read()
if not ret: # 所有的帧都处理完成,退出循环
break
# 对当前帧进行马赛克处理
frame = mosaic_image(model, frame)
# 将处理好的图片写入out
out.write(frame)
# 更新进度条
pro_bar.update(1)
# 释放资源
cap.release()
out.release()
pro_bar.close()
```
最后,将生成的无声的视频和一开始提取的音频文件合成为最终的视频即可。
```python video_stream = ffmpeg.input(tmp_video) audio_stream = ffmpeg.input(tmp_audio) ffmpeg.output(video_stream, audio_stream, output_video, vcodec="copy", acodec='aac').run(overwrite_output=True) ```
最终效果:
