目录
一键抠图Portrait Matting人像抠图 (C++和Android源码)
1. 项目介绍:
2. MODNet抠图算法:
3. Matting数据集
(1) 开源数据集
(2) 训练和测试数据说明
(3) 合成代码实现
4. Android JNI接口
5. Demo测试效果
6. Android完整项目代码
抠图算法中(英文中,一般称为Matting),一种是基于辅助信息输入的,加入一些先验信息(如Trimap,背景图,用户交互信息,深度等信息)提供抠图效果,如比较经典的Deep Image Matting和Semantic Image Matting这些算法加入Trimap; Background Matting算法需要提供背景图等;另一种是无需辅助信息,输入RGB图像,直接预测matte的方法,其效果相对第一种方法,会差很多。而对Portrait Matting(人像抠图),现在有很多方案在无需Trimap条件下,也可以获得不错的抠图效果,比如MODNet,Fast Deep Matting等算法,真正实现健抠图的效果。
本篇博客将介绍MODNet人像抠图算法,一个效果相当不错的Matting算法,可以达到头发细致级别的人像抠图效果,是一健抠图哦,先展示一下Android测试效果:
【尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/121680939
模型选择 | 原图 | 高精度人像抠图 | 视频抠图 |
更多抠图算法(Matting),请参考我的一篇博客《图像抠图Image Matting算法调研》:
图像抠图Image Matting算法调研_pan_jinquan的博客-CSDN博客1.Trimap和StrokesTrimap和Strokes都是一种静态图像抠图算法,现有静态图像抠图算法均需对给定图像添加手工标记以增加抠图问题的额外约束。Trimap,三元图,是对给定图像的一种粗略划分,即将给定图像划分为前景、背景和待求未知区域Strokes则采用涂鸦的方式在图像上随意标记前景和背景区域,剩余未标记部分则为待求的未知区域Trimap是最常用的先验知识,多数抠图算法采用了Trimap作为先验知识,顾名思义Trimap是一个三元图,每个像素取值为{0,128,...https://panjinquan.blog.csdn.net/article/details/119648686
可能,有小伙伴搞不清楚分割(segmentation)和抠图(matting)有什么区别,我这里简单说明一下:
关于《MODNet: Trimap-Free Portrait Matting in Real Time》,请参考:
官方GitHub仅仅放出推理代码,并未提供训练过程和数据处理代码 ;鄙人参考原论文花了几个星期的时间,总算复现了其基本效果,并做了一些轻量化和优化的工作,主要有:
最近发现,百度PaddleSeg团队也复现了MODNet算法(基于PaddlePaddle框架,非Pytorch版本),提供了更丰富的backbone模型选择,如MobileNetV2,ResNet50,HRNet_W18,可适用边缘端、服务端等多种任务场景,有兴趣的可以看看:
PaddlePaddle版本:https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.3/contrib/Matting
基于深度学习的Matting分为两大类:
一种是基于辅助信息输入。即除了原图和标注图像外,还需要输入其他的信息辅助预测。最常见的辅助信息是Trimap,即将图片划分为前景,背景及过度区域三部分。另外也有以背景或交互点作为辅助信息。
一种是不依赖任何辅助信息,直接对Alpha进行预测。如本博客复现的MODNet
第一种方法,需要加入辅助信息,而辅助信息一般较难获取,这也限制其应用,为了提升Matting的应用性,针对Portrait Matting领域MODNet摒弃了辅助信息,直接实现Alpha预测,实现了实时Matting,极大提升了基于深度学习Matting的应用价值。
MODNet模型学习分为三个部分,分别为:语义部分(S),细节部分(D)和融合部分(F)。
数据集 |
说明 |
matting_human_datasets |
|
Deep Image Matting |
|
PPM-100 |
PPM-100下载:https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.3/contrib/Matting |
RealWorldPortrait-636 |
|
Compsition-1k |
|
HAttMatting |
|
AM-2k |
|
BG-20k |
|
VideoMatte240K |
|
PhotoMatte85 |
|
其他的:
关于训练数据如何生成的问题:
这是Python实现的背景合成,需要提供原始图像image,以及image的前景图像alpha,和需要合成的背景图像bg_img:
def image_fusion(image: np.ndarray, alpha: np.ndarray, bg_img=(219, 142, 67)):
"""
图像融合:合成图 = 前景*alpha+背景*(1-alpha)
:param image: RGB图像(uint8)
:param alpha: 单通道的alpha图像(uint8)
:param bg_img: 背景图像,可以是任意的分辨率图像,也可以指定指定纯色的背景
:return: 返回与背景合成的图像
"""
if isinstance(bg_img, tuple) or isinstance(bg_img, list):
bg = np.zeros_like(image, dtype=np.uint8)
bg_img = np.asarray(bg[:, :, 0:3] + bg_img, dtype=np.uint8)
if len(alpha.shape) == 2:
# alpha = cv2.cvtColor(alpha, cv2.COLOR_GRAY2BGR)
alpha = alpha[:, :, np.newaxis]
if alpha.dtype == np.uint8:
alpha = np.asarray(alpha / 255.0, dtype=np.float32)
sh, sw, d = image.shape
bh, bw, d = bg_img.shape
ratio = [sw / bw, sh / bh]
ratio = max(ratio)
if ratio > 1:
bg_img = cv2.resize(bg_img, dsize=(math.ceil(bw * ratio), math.ceil(bh * ratio)))
bg_img = bg_img[0: sh, 0: sw]
image = image * alpha + bg_img * (1 - alpha)
image = np.asarray(np.clip(image, 0, 255), dtype=np.uint8)
return image
当然,为了方便JNI调用,我这里还实现C++版本图像合成算法,这部分图像处理的基本工具,都放在我的base-utils中
/***
* 实现图像融合:out = imgBGR * matte + bg * (1 - matte)
* Fix a Bug: 1-alpha实质上仅有B通道参与计算,多通道时(B,G,R),需改Scalar(1.0, 1.0, 1.0)-alpha
* @param imgBGR 输入原始图像
* @param matte 输入原始图像的Mask,或者alpha,matte
* @param out 输出融合图像
* @param bg 输入背景图像Mat(可任意大小),也可以通过Scalar指定纯色的背景
*/
void image_fusion(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {
assert(matte.channels() == 1);
out.create(imgBGR.size(), CV_8UC3);
vector ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};
float max_ratio = *max_element(ratio.begin(), ratio.end());
if (max_ratio > 1.0) {
cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));
}
bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);
int n = imgBGR.channels();
int h = imgBGR.rows;
int w = imgBGR.cols * n;
// 循环体外进行乘法和除法运算
matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);
for (int i = 0; i < h; ++i) {
uchar *sptr = imgBGR.ptr(i);
uchar *dptr = out.ptr(i);
float *mptr = matte.ptr(i);
uchar *bptr = bg.ptr(i);
for (int j = 0; j < w; j += n) {
//float alpha = mptr[j] / 255; //循环体尽量减少乘法和除法运算
float alpha = mptr[j / 3];
float _alpha = 1.f - alpha;
dptr[j] = uchar(sptr[j] * alpha + bptr[j] * _alpha);
dptr[j + 1] = uchar(sptr[j + 1] * alpha + bptr[j + 1] * _alpha);
dptr[j + 2] = uchar(sptr[j + 2] * alpha + bptr[j + 2] * _alpha);
}
}
}
目前,提供Demo源码提供三个JNI接口,可实现一健抠图效果,当然你可以在我C++基础上修改源码,实现更多功能;
matting接口:实现基本的人像构图Matting功能 fusion接口:实现人像构图Matting,并与背景图进行融合 mattingFusion接口:人像构图Matting,并与背景图进行融合(会返回mask)
package com.cv.tnn.model;
import android.graphics.Bitmap;
public class Detector {
static {
System.loadLibrary("tnn_wrapper");
}
/***
* 初始化检测模型
* @param proto: TNN *.tnnproto文件文件名(含后缀名)
* @param model: TNN *.tnnmodel文件文件名(含后缀名)
* @param root:模型文件的根目录,放在assets文件夹下
* @param model_type:模型类型
* @param num_thread:开启线程数
* @param useGPU:是否使用GPU
*/
public static native void init(String proto, String model, String root, int model_type, int num_thread, boolean useGPU);
/***
* 缩放图片
* @param bitmap
* @param resize_width
* @param resize_height
* @return
*/
public static Bitmap resizeBitmap(Bitmap bitmap, int resize_width, int resize_height) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (resize_width <= 0 && resize_height <= 0) {
return bitmap;
} else if (resize_height <= 0) {
resize_height = height * resize_width / width;
} else if (resize_width <= 0) {
resize_width = width * resize_height / height;
}
Bitmap dst = Bitmap.createScaledBitmap(bitmap, resize_width, resize_height, false);
return dst;
}
/***
* 人像构图Matting
* @param bitmap 输入图像(bitmap),ARGB_8888格式
* @param mask 输出Mask图像(bitmap),ARGB_8888格式,调用前需要createBitmap初始化大小,如
* Bitmap.createBitmap(Width, Height, Bitmap.Config.ARGB_8888);
* @return
*/
public static native void matting(Bitmap bitmap, Bitmap mask);
/***
* 人像构图Matting,并与背景图进行融合
* @param bitmap 输入图像(bitmap),ARGB_8888格式
* @param bgmap 输入背景图像(bitmap),ARGB_8888格式,可任意大小的图像
* @param fusion 输出与背景融合后图像,调用前需要createBitmap初始化大小,ARGB_8888格式
*/
public static native void fusion(Bitmap bitmap, Bitmap bgmap, Bitmap fusion);
/***
* 人像构图Matting,并与背景图进行融合
* @param bitmap 输入图像(bitmap),ARGB_8888格式
* @param bgmap 输入背景图像(bitmap),ARGB_8888格式,可任意大小的图像
* @param fusion 输出与背景融合后图像,调用前需要createBitmap初始化大小,ARGB_8888格式
* @param mask 输出Mask图像(bitmap),调用前需要createBitmap初始化大小,ARGB_8888格式
* @return
*/
public static native void mattingFusion(Bitmap bitmap, Bitmap bgmap, Bitmap fusion, Bitmap mask);
}
实际使用中,建议你:
下图是高精度版本人像抠图和快速人像构图的测试效果,相对而言,高精度版本人像抠图可以精细到发丝级别的抠图效果;而快速人像构图目前仅能实现基本的抠图效果:
原图 | Mask图像 | 融合图像 | |
高 精 度 人 像 抠图 |
| | |
超 快 人 像 抠图 |
其他测试图片
|
|
|
|