在实际项目中处理人物图片时,经常会遇到需要将人体各部位精确分割的需求。比如需要将脸部标记为蓝色、脖颈部位标记为绿色、其他区域标记为红色等。这种细粒度的分割任务在虚拟试衣、动作捕捉、医疗影像分析等领域都有广泛应用。
我最初尝试使用PaddleSeg中的BiSeNet-v2和PP_LiteSeg模型,它们基于CelebAMask-HQ数据集训练,对于简单的人脸分割效果尚可。但当遇到复杂发型、头饰或特殊服装时,分割效果就大打折扣了。特别是对于颈部区域的识别,这些模型表现很不稳定,经常出现断裂或缺失的情况。
经过多次测试和比较,最终锁定了两个表现更优的模型:百度的ACE2P和阿里的M2FP。ACE2P在CVPR2019的LIP挑战赛中获得了三项第一,其优势在于:
但ACE2P有个明显的缺陷——它竟然没有脖子!模型把下巴以下的皮肤都归为脸部范畴,但在实际分割时这部分信息又丢失了。这对于需要精确区分面部和颈部区域的应用场景来说是个硬伤。
相比之下,M2FP基于Mask2Former架构改进而来,在人体解析任务中表现更全面。它能准确识别包括颈部在内的20多个身体部位,特别适合多人场景下的复杂分割需求。不过M2FP的计算开销较大,处理速度明显慢于ACE2P。
要使用ACE2P模型,首先需要搭建运行环境。推荐使用Python 3.7+版本,并安装必要的依赖库:
bash复制pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
pip install paddlehub -i https://mirror.baidu.com/pypi/simple
pip install matplotlib Pillow
如果在Colab环境中遇到libssl报错,可以执行以下命令修复:
bash复制wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
基础使用代码非常简单:
python复制import paddlehub as hub
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# 加载模型
module = hub.Module(name="ace2p")
# 读取并显示原图
test_img_path = "test.jpg"
img = mpimg.imread(test_img_path)
plt.imshow(img)
plt.axis('off')
plt.show()
# 执行分割
results = module.segmentation(
images=[img],
output_dir='./output',
visualization=True
)
# 显示结果
result_img = mpimg.imread(results[0]['path'])
plt.imshow(result_img)
plt.axis('off')
plt.show()
ACE2P默认会输出19个类别的分割结果,包括背景、帽子、头发、手套、太阳镜、上衣、裙子等。每个类别都有预设的颜色编码,但这些默认颜色往往不符合实际项目需求。
ACE2P的颜色渲染是通过palette参数控制的,这个参数定义在PaddleHub/modules/image/semantic_segmentation/ace2p/processor.py文件的get_palette方法中。要修改颜色方案,我们需要了解其工作机制。
原始的颜色生成算法相当有趣:它通过位操作来生成独特的颜色值,确保每个类别都有明显区别。但这种自动生成的颜色可能不符合我们的设计需求。比如我们可能需要:
实现自定义颜色的完整代码如下:
python复制import paddlehub as hub
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
# 加载模型
module = hub.Module(name="ace2p")
# 定义新的颜色映射
new_palette_dic = {
"background": [255,255,255], # 白色
"Hat": [0,0,255], # 蓝色
"Hair": [0,0,255],
"Glove": [255,0,0], # 红色
"Sunglasses": [0,0,255],
"UpperClothes": [255,0,0],
"Dress": [255,0,0],
"Coat": [255,0,0],
"Socks": [255,0,0],
"Pants": [255,0,0],
"Jumpsuits": [255,0,0],
"Scarf": [0,255,0], # 绿色
"Skirt": [255,0,0],
"Face": [0,0,255],
"Left-arm": [255,0,0],
"Right-arm": [255,0,0],
"Left-leg": [255,0,0],
"Right-leg": [255,0,0],
"Left-shoe": [255,0,0],
"Right-shoe": [255,0,0],
}
# 将颜色字典转换为ACE2P需要的格式
new_palette = []
for v in new_palette_dic.values():
new_palette.extend(v) # 将RGB值平铺
# 应用自定义颜色
module.palette = new_palette
# 执行分割并可视化
image = mpimg.imread("test.jpg")
results = module.segmentation(
images=[image],
output_dir='./output',
visualization=True
)
# 显示结果
plt.imshow(mpimg.imread(results[0]['path']))
plt.axis('off')
plt.show()
这种方法虽然解决了颜色定制的问题,但依然无法处理ACE2P缺失颈部区域的根本缺陷。为此,我们需要引入M2FP模型来补充这部分功能。
M2FP是基于Mask2Former架构改进的人体解析模型,由阿里达摩院开发。它支持多人场景下的精细分割,能识别包括"Torso-skin"(颈部皮肤)在内的20多个身体部位。安装过程比ACE2P稍复杂:
bash复制# 安装基础依赖
pip install -U openmim
mim install mmcv
# 安装ModelScope和相关组件
pip install antlr4-python3-runtime
pip install modelscope
pip install "modelscope[cv]" -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html
M2FP的基础使用代码如下:
python复制from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
# 初始化管道
segmentation_pipeline = pipeline(
Tasks.image_segmentation,
'damo/cv_resnet101_image-multiple-human-parsing'
)
# 执行分割
input_img = 'test.jpg'
result = segmentation_pipeline(input_img)
# 获取各个部位的mask
labels = result[OutputKeys.LABELS]
masks = result['masks']
# 可视化特定部位
face_mask = masks[labels.index('Face')]
plt.imshow(face_mask, cmap='gray')
plt.title('Face Mask')
plt.axis('off')
plt.show()
M2FP的输出包含多个mask,每个mask对应一个身体部位。我们可以组合这些mask来创建符合需求的分割图。比如要生成脸部蓝色、颈部绿色、衣服红色的效果:
python复制from PIL import Image
import numpy as np
# 获取各部位mask
face = masks[labels.index('Face')] * 255
hair = masks[labels.index('Hair')] * 255
neck = masks[labels.index('Torso-skin')] * 255
clothes = masks[labels.index('UpperClothes')] * 255
# 创建空白画布 (height, width, 3)
h, w = face.shape
result_img = np.full((h, w, 3), 255, dtype=np.uint8) # 白色背景
# 填充各部位颜色
result_img[face > 0] = [0, 0, 255] # 蓝色脸部
result_img[hair > 0] = [0, 0, 255] # 蓝色头发
result_img[neck > 0] = [0, 255, 0] # 绿色颈部
result_img[clothes > 0] = [255, 0, 0] # 红色衣服
# 保存结果
Image.fromarray(result_img).save('result.jpg')
在实际项目中,直接使用M2FP的原生实现会遇到性能瓶颈。特别是当需要处理大量图片时,逐像素操作的速度会成为主要瓶颈。以下是几种有效的优化策略:
1. 向量化操作替代循环
原始的实现使用双重循环遍历每个像素,这在Python中非常低效。我们可以改用NumPy的向量化操作:
python复制# 优化后的颜色填充逻辑
def apply_mask(color, mask, target):
target[mask > 0] = color
return target
result_img = np.full((h, w, 3), 255, dtype=np.uint8)
result_img = apply_mask([0, 0, 255], face, result_img)
result_img = apply_mask([0, 0, 255], hair, result_img)
result_img = apply_mask([0, 255, 0], neck, result_img)
result_img = apply_mask([255, 0, 0], clothes, result_img)
2. 批量处理与并行计算
对于大批量图片,可以使用Python的multiprocessing模块实现并行处理:
python复制from multiprocessing import Pool
import os
def process_image(img_path):
# 处理单张图片的逻辑
pass
if __name__ == '__main__':
img_dir = 'input_images'
img_paths = [os.path.join(img_dir, f) for f in os.listdir(img_dir)]
with Pool(processes=4) as pool: # 使用4个进程
pool.map(process_image, img_paths)
3. 模型推理优化
M2FP模型本身也支持一些优化手段:
python复制# 启用fp16推理
segmentation_pipeline = pipeline(
Tasks.image_segmentation,
'damo/cv_resnet101_image-multiple-human-parsing',
device='cuda',
model_revision='v1.0.1',
pipeline_kwargs={'fp16': True}
)
4. 缓存与预处理
对于静态内容,可以预先生成分割结果并缓存。对于视频流,可以利用帧间连续性,只在关键帧执行完整分割,中间帧使用光流等方法估计。
经过这些优化后,处理速度通常能有5-10倍的提升。在我的实际项目中,优化后的方案能够在保持精度的同时,满足实时处理的需求。特别是在处理视频流时,配合适当的缓存策略,可以实现流畅的实时人体部件分割与渲染效果。