一键抠图Portrait Matting人像抠图 (C++和Android源码)_android 基于modnet人物抠图-程序员宅基地

技术标签: Matting  人像抠图Android  MODNet  人像抠图  深度学习  Matting Android  一健抠图  

一键抠图Portrait Matting人像抠图 (C++和Android源码)


目录

一键抠图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的博客-程序员宅基地1.Trimap和StrokesTrimap和Strokes都是一种静态图像抠图算法,现有静态图像抠图算法均需对给定图像添加手工标记以增加抠图问题的额外约束。Trimap,三元图,是对给定图像的一种粗略划分,即将给定图像划分为前景、背景和待求未知区域Strokes则采用涂鸦的方式在图像上随意标记前景和背景区域,剩余未标记部分则为待求的未知区域Trimap是最常用的先验知识,多数抠图算法采用了Trimap作为先验知识,顾名思义Trimap是一个三元图,每个像素取值为{0,128,...https://panjinquan.blog.csdn.net/article/details/119648686


 更多项目《一键抠图》系列文章请参考:


 可能,有小伙伴搞不清楚分割(segmentation)和抠图(matting)有什么区别,我这里简单说明一下:

  •  分割(segmentation):从深度学习的角度来说,分割本质是像素级别的分类任务,其损失函数最简单的莫过于是交叉熵CrossEntropyLoss(当然也可以是Focal Loss,IOU Loss,Dice Loss等);对于前景和背景分割任务,输出Mask的每个像素要么是0,要么是1。如果拿去直接做图像融合,就很不自然,Mask边界很生硬,这时就需要使用抠图算法了
  •  抠图(matting): 而抠图本质是一种回归任务,其损失函数可以是MSE Loss,L1 Loss,L2 Loss等,对于前景和背景抠图任务,输出Mask的每个像素是0~1之间的连续值,可看作是对图像透明通道(Alpha)的回归预测。可以用公式表示为C = αF + (1-α)B ,其中α(不透明度)、F(前景色)和B(背景色),alpha是[0, 1]之间的连续值,可以理解为像素属于前景的概率。在人像分割任务中,alpha只能取0或1,而抠图任务中,alpha可取[0, 1]之间的连续值,
  • 本质上就是一句话:分割是分类任务,而抠图是回归任务。

1. 项目介绍:

关于《MODNet: Trimap-Free Portrait Matting in Real Time》,请参考:

官方GitHub仅仅放出推理代码,并未提供训练过程和数据处理代码 ;鄙人参考原论文花了几个星期的时间,总算复现了其基本效果,并做了一些轻量化和优化的工作,主要有:

  • 复现Pytorch版本的MODNet训练过程和数据处理
  • 增加了数据增强方法:如多尺度随机裁剪,Mosaic(拼图),随机背景融合等方法,提高模型泛化性
  • 对MODNet骨干网络backbone进行轻量化,减少计算量
  • 目前提供三个版本:高精度人像抠图+快速人像抠图+超快人像抠图
  • 转写模型推理过程,实现C++版本人像抠图算法
  • 实现Android版本人像抠图算法,支持CPU和GPU
  • 提供高精度版本人像抠图,可以达到精细到发丝级别的抠图效果(Android GPU 150ms,  CPU 500ms左右)
  • 提供轻量化快速版人像抠图,满足基本的人像抠图效果,可以在Android达到实时的抠图效果(Android GPU 60ms,  CPU 140ms左右)

最近发现,百度PaddleSeg团队也复现了MODNet算法(基于PaddlePaddle框架,非Pytorch版本),提供了更丰富的backbone模型选择,如MobileNetV2,ResNet50,HRNet_W18,可适用边缘端、服务端等多种任务场景,有兴趣的可以看看:

 PaddlePaddle版本:https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.3/contrib/Matting


2. MODNet抠图算法:

基于深度学习的Matting分为两大类:

  • 一种是基于辅助信息输入。即除了原图和标注图像外,还需要输入其他的信息辅助预测。最常见的辅助信息是Trimap,即将图片划分为前景,背景及过度区域三部分。另外也有以背景或交互点作为辅助信息。

  • 一种是不依赖任何辅助信息,直接对Alpha进行预测。如本博客复现的MODNet

第一种方法,需要加入辅助信息,而辅助信息一般较难获取,这也限制其应用,为了提升Matting的应用性,针对Portrait Matting领域MODNet摒弃了辅助信息,直接实现Alpha预测,实现了实时Matting,极大提升了基于深度学习Matting的应用价值。

MODNet模型学习分为三个部分,分别为:语义部分(S),细节部分(D)和融合部分(F)

  • 在语义估计中,对high-level的特征结果进行监督学习,标签使用的是下采样及高斯模糊后的GT,损失函数用的L2-Loss,用L2loss应该可以学到更soft的语义特征;
  • 在细节预测中,结合了输入图像的信息和语义部分的输出特征,通过encoder-decoder对人像边缘进行单独地约束学习,用的是交叉熵损失函数。为了减小计算量,encoder-decoder结构较为shallow,同时处理的是原图下采样后的尺度。
  • 在融合部分,把语义输出和细节输出结果拼起来后得到最终的alpha结果,这部分约束用的是L1损失函数。

3. Matting数据集

(1) 开源数据集

数据集

说明

matting_human_datasets

  • 本数据集为目前已知最大的人像matting数据集,包含34427张图像和对应的matting结果图。
  • 数据集由北京玩星汇聚科技有限公司高质量标注,使用该数据集所训练的人像软分割模型已商用。
  • 数据集中的原始图片来源于Flickr、百度、淘宝。经过人脸检测和区域裁剪后生成了600*800的半身人像。
  • https://github.com/aisegmentcn/matting_human_datasets
  • PS:Matting比较粗糙,没有达到头发细致抠图;不过数据比较大,可以作为pretrained数据集使用

Deep Image Matting

  • Adobe Research论文《Deep Image Matting》提供的Matting Dataset。大约有455张图片,论文将MSCOCO和PASCAL VOC当做背景图,与455张图片进行合成后,大概有45500张训练图片和1000张测试图片
  • 论文地址:https://sites.google.com/view/deepimagematting
  • 项目地址:https://github.com/Joker316701882/Deep-Image-Matting
  • PS:该数据集发邮箱给作者申请即可,一般作为通用物体Matting数据集,比较精细;如果用于人像抠图,需要自行把含有人的图片挑选出来

PPM-100

  • PPM-100 是论文 MODNet (Github | Arxiv) 中提出的一个人像抠图基准,它包含了100张来自Flickr的人像图片,具有以下特点:

  • 精细标注 - 所有图像都被仔细标注并检查。
  • 丰富多样 - 图像涵盖全身/半身人像和各种姿态。
  • 高分辨率 - 图像的分辨率介于1080P和4K之间。
  • 自然背景 - 所有图像都包含原始无替换的背景。
  • 项目地址:https://github.com/ZHKKKe/PPM

PPM-100下载:https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.3/contrib/Matting

RealWorldPortrait-636

 Compsition-1k

HAttMatting

 AM-2k

BG-20k

VideoMatte240K

PhotoMatte85

其他的:

(2) 训练和测试数据说明

关于训练数据如何生成的问题:

  • 原论文MODNet使用了PPM-100数据集+私有的数据集,并合成了大部分训练数据
  • 鄙人复现时,先使用matting_human_datasets数据集训练base-model当作pretrained模型;然后合并多个数据集(PPM-100 + RealWorldPortrait-636 + Deep Image Matting),采用背景图来自VOC+COCO+BG-20k ,一共合成了5W+的训练数据和500+的测试数据
  • 合成的方法有两种:方法1:利用公式:合成图 = 前景*alpha+背景*(1-alpha) ;方法二:前景+mask+背景通过GAN生成;

(3) 合成代码实现

这是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<float> 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<uchar>(i);
        uchar *dptr = out.ptr<uchar>(i);
        float *mptr = matte.ptr<float>(i);
        uchar *bptr = bg.ptr<uchar>(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);
        }
    }
}

4. Android JNI接口

  • 目前已经实现Android版本人像抠图算法,支持CPU和GPU
  • 提供高精度版本人像抠图,可以达到精细到发丝级别的抠图效果(Android GPU 150ms,  CPU 500ms左右)
  • 提供轻量化快速版人像抠图,满足基本的人像抠图效果,可以在Android达到实时的抠图效果(Android GPU 60ms,  CPU 140ms左右)

目前,提供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);


}
  • 运行APP闪退:dlopen failed: library "libomp.so" not found

参考解决方法:解决dlopen failed: library “libomp.so“ not found_PKing666666的博客-程序员宅基地_dlopen failed


5. Demo测试效果 

实际使用中,建议你:

  • 背景越单一,抠图的效果越好,背景越复杂,抠图效果越差;建议你实际使用中,找一比较单一的背景,如墙面,天空等
  • 上半身抠图的效果越好,下半身或者全身抠图效果较差;本质上这是数据的问题,因为训练数据70%都是只有上半身的
  • 白种人抠图的效果越好,黑人和黄种人抠图效果较差;这也是数据的问题,因为训练数据大部分都是隔壁的老外

下图是高精度版本人像抠图和快速人像构图的测试效果,相对而言,高精度版本人像抠图可以精细到发丝级别的抠图效果;而快速人像构图目前仅能实现基本的抠图效果:

原图  Mask图像  融合图像

抠图

​​ ​​ ​​

抠图

其他测试图片

​​

​​

​​
​​​​

6. Android完整项目代码

整套Android项目源码内容包含:

  1.  Android版本人像抠图算法,支持CPU和GPU
  2. 提供高精度版本人像抠图,可以达到精细到发丝级别的抠图效果(Android GPU 150ms,  CPU 500ms左右)
  3. 提供轻量化快速版人像抠图,满足基本的人像抠图效果,可以在Android达到实时的抠图效果(Android GPU 60ms,  CPU 140ms左右)
  4. Demo支持图片,视频,摄像头测试
  5. 所有依赖库都已经配置好,可直接build运行,若运行出现闪退,请参考dlopen failed: library “libomp.so“ not found 解决。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/guyuealian/article/details/121680939

智能推荐

宝塔Nginx 安装check模块nginx_upstream_check_module_宝塔 安装 nginx_upstream_check_module-程序员宅基地

文章浏览阅读2.1k次。第一步:在宝塔安装nginx,选择编译安装第二步:在这个基础上加新增模块nginx_upstream_check_module,步骤如下1.执行命令获取nginx_upstream_check_module模块: wgethttps://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master2.进入..._宝塔 安装 nginx_upstream_check_module

c/c++易错点收集(持续更新)_c++易错题总结-程序员宅基地

文章浏览阅读1.7k次,点赞7次,收藏27次。收集了c,c++语言在学习和应用的时候一些易错点,加入了个人分析。仅供参考,有错误的地方请多多指出_c++易错题总结

Anaconda安装、创建环境、TensorFlow、CUDA以及cuDNN安装-程序员宅基地

文章浏览阅读1.9k次,点赞5次,收藏17次。利用Anaconda安转TensorFlow、cuda以及cuDNN教程。

POST请求参数_当客户端通过post请求提交的参数为自定义js对象,接收参数需要那个注释-程序员宅基地

文章浏览阅读488次。设置form表单为:新建一个post.js里面内容为:从app.js中把主体部分拷贝过来。然后服务器运营post.js文件。加入data和end事件。在form.js中打开。输入用户名和密码。提交。结果返回ok .证明post请求后服务器响应了。服务器中也有存的内容引入querystring处理请求参数模块。引用querystring:重新输入名和密码,提交。此时用户名和密码以对象的形式来进行存储..._当客户端通过post请求提交的参数为自定义js对象,接收参数需要那个注释

php源码域名授权破解,域名授权系统PHP源码 V2.7.0 支持盗版追踪-程序员宅基地

文章浏览阅读2.9k次。温馨提示:本信息由【会员:老猫】搜集整理发布,版权归原作者及发布者所有,您如有异议请 举报 或者 版权申诉。最新漂亮简洁大气的域名授权系统PHP源码,域名授权系统PHP版,功能强大带有后台,经过版本升级,全新美观大气的UI洁面!支持盗版追踪,与卡密系统对接购买卡密对域名进行授权,支持授权代码、到期时间代码、在线更新代码、PHP批量加密等功能。适应范围php授权系统、PHP域名授权系统查看更多关于 ..._域名管理php源码

第九周项目二 对称矩阵压缩储存的实现与应用(1)_anaconda生成对称矩阵-程序员宅基地

文章浏览阅读169次。/* Copyright (c)2016,烟台大学计算机与控制工程学院 作 者:王晓慧 完成日期:2016年10月27日 版 本 号:v1.0 *问题描述:(1)用压缩形式存储对称矩阵,实现下面的操作并测试 void Init(int *&b);//为N阶对称矩阵初始化存储数据的一维数组B _anaconda生成对称矩阵

随便推点

机器学习(Machine Learning)&深度学习(Deep Learning)学习资料总结_machine learnig和deep learning 求解 math word problem-程序员宅基地

文章浏览阅读564次。机器学习(Machine Learning)&深度学习(Deep Learning)资料总结下面链接是GitHub上一位大神的总结,非常详细的列举了Machine Learning & Deep Learning的很多资料,具有很好的参考价值click here( 机器学习(Machine Learning)&深度学习(Deep Learning)资料总结)_machine learnig和deep learning 求解 math word problem solver

记一次post请求参数为json格式时,HTTPServletRequest拿不到请求参数问题_http 发送post请求拿不到参数-程序员宅基地

文章浏览阅读6.3k次。根据业务需求需要在满足条件的post请求前,做一些处理,因此想到用 HandlerInterceptor 来拦截请求以做进一步处理,便如图所示获取参数但是,这种获取参数的形式可以获取表单形式(header),是拿不到参数为json格式的post请求参数的,于是便又有了一下方式获取json格式的参数这种方法可以再拦截器找那个拿到参数,但是!!!这样在controller中使用..._http 发送post请求拿不到参数

php 脚本 备份数据库表,php数据库备份脚本的方法-程序员宅基地

文章浏览阅读223次。php数据库备份脚本的方法PHP是开源软件,所有PHP的源代码每个人都可以看得到,代码在许多工程师手中进行了检测,同时它与Apache编绎在一起的方式也可以让它具有灵活的安全设定。我们为大家收集整理了关于php数据库备份脚本,以方便大家参考。php数据库备份脚本代码如下:// 备份数据库$host = "localhost";$user = "root"; //数据库账号$password = "..._php 备份数据表

四大Hybrid App移动开发平台对比_目前两大主流移动端开发平台是-程序员宅基地

文章浏览阅读1k次。[值得一用的Apps]四大Hybrid App移动开发平台对比摘要:作为一名Web开发者来说要如何站在移动互联网的浪潮之巅呢?是选择学习原生开发,研究Java、Object-C、C#等语言,还是选择继续使用网页开发,容忍HTML5功能的局限性?就在开发者左右为难的情况下Hybrid App作为一个折中的解决方案诞生了。作者:来源:ZDNet CIO与应用频道 | 2013年04_目前两大主流移动端开发平台是

微信小程序云函数查询云数据库返回指定单列的数据_{"errorcode":1,"errormessage":"user code exception-程序员宅基地

文章浏览阅读1.9w次,点赞12次,收藏30次。问题描述用云函数获取云数据库中的值时,想只取得某一列的值返回,代码写成如下所示let accessToken = db.collection('wxConfig').where({ name: "AccessToken" }).get();accessToken = accessToken.data[0].valuereturn accessToken;发现报错,返回{“..._{"errorcode":1,"errormessage":"user code exception caught","stacktrace":"err

CNSD在这里记录自己成长-程序员宅基地

文章浏览阅读5.7k次。CNSD在这里记录自己成长CNSD在这里记录自己成长CNSD在这里记录自己成长CNSD在这里记录自己成长CNSD在这里记录自己成长CNSD在这里记录自己成长CNSD在这里记录自己成长_cnsd

推荐文章

热门文章

相关标签