视频人像分割算法—C++推理(视频抠图 图片抠图)

本文章记录对RobustVideoMatting模型进行C++推理的过程。


相关参考

本文参考模型代码:
[1]RobustVideoMatting.
本文参考C++推理工具:
[2]lite.ai.toolkit-RVM.


提示:该案例运行环境为Windows+CPU环境

一、基于lite.ai.toolkit的RVM推理编译

1.源码编译

从参考【2】处下载源码后,需要使用cmake进行编译。从官网下载 cmake的GUI版本,安装成功后对RVM源码编译:

Step 1:将源码中lite.ai.toolkit\lib文件夹下的所有依赖库替换(源码中dylib是Mac系统的动态依赖库格式,Windows下是dll格式);将cmakelist.txt中不需要的推理格式(我这里仅保留onnxruntime)注释掉。

Step 2:在cmake中选择好生成路径,点击Configure编译,成功后点击Generate生成。
camke界面示例
请添加图片描述
成功后会提示Configuring done 及 Generating done

Step 3:上述步骤完成后会发现在对应生成路径下有了sln文件。点击进入:
在这里插入图片描述
在这里切换版本为Release,右击ALL_BUILD生成:
在这里插入图片描述
成功生成后显示如下:
在这里插入图片描述
Step 4:下面运行一下看一看吧~
打开test_lite_rvm.cpp,如下图配置好路径后,将lite.ai.toolkit\lib文件夹下所有的dll文件拷贝到RobustVideoMatting-v1.0\examples\build\Release文件夹下
在这里插入图片描述
双击lite_rvm.exe,运行结果:
在这里插入图片描述
在logs文件夹下即可查看推理结果
在这里插入图片描述
以上,一个在Windows+CPU环境下的视频人像分割的C++推理demo就搞定啦~ 接下来我们就在作者原版demo上自己修改玩一下吧!

二、推理功能改进与完善

1.修改背景颜色

lite.ai.toolkit/sourceFile/rvm.cpp更改以下代码(改为白色):

  cv::Mat mbmat = bmat.mul(pmat) + rest * 255.;
  cv::Mat mgmat = gmat.mul(pmat) + rest * 255.;
  cv::Mat mrmat = rmat.mul(pmat) + rest * 255.;

重新生成lite.ai.toolkit.dll,将其copy至与exe相同目录下,得到结果:
在这里插入图片描述


2.灵活输入输出

按照原版本,在推理前要把待推理的视频放到resource文件夹下,即路径是写死的。现在简单改动一下,能够让用户在命令行里通过拖拽文件进行推理。

在RobustVideoMatting.lite.ai.toolkit/test_lite_rvm.cpp中,增加获取文件名函数:

//get the file's name that you input
string get_file_name(string file_path)
{
    int location = file_path.rfind("\\") + 1;//记录最后一次出现"\"的位置
    string file_name = file_path.substr(location);//提取"\"后字符串,作为文件名
    return file_name;
}
为test_video()和test_image()增加string类型参数,主函数调用如下:
 cout << "请输入待处理视频路径:" << endl;
 cin >> VideoPath;
 test_video(VideoPath);
 
 cout << "请输入待处理图片路径:(若无输入图片退出即可)" << endl;
 cin >> ImagePath;
 test_image(ImagePath);

重新编译后,将文件拖拽到命令行中,即可get到文件的路径,进行推理啦:
在这里插入图片描述


3.同时输出视频mask和result

为了方便对MIOU评价指标的计算,同时能够更好地呈现结果,现在试试同时输出mask和result吧

在lite.ai.toolkit/rvm.cpp中,修改函数detect_video,增加一输出路径,函数更改后如下:

 void RobustVideoMatting::detect_video(const std::string &video_path,
                                      const std::string &output_path,
                                      //
                                     // const std::string &output_path_mask,
                                      //
                                      std::vector<types::MattingContent> &contents,
                                      bool save_contents, float downsample_ratio,
                                      unsigned int writer_fps)
{
    int location = output_path.rfind("\\") + 1;
    const std::string fileName = output_path.substr(location);
    const std::string fileNameMask = "mask_" + fileName;
    const std::string output_path_mask = output_path.substr(0,location)+fileNameMask;
  // 0. init video capture
  cv::VideoCapture video_capture(video_path);
  const unsigned int width = video_capture.get(cv::CAP_PROP_FRAME_WIDTH);
  const unsigned int height = video_capture.get(cv::CAP_PROP_FRAME_HEIGHT);
  const unsigned int frame_count = video_capture.get(cv::CAP_PROP_FRAME_COUNT);
  if (!video_capture.isOpened())
  {
    std::cout << "Can not open video: " << video_path << "\n";
    return;
  }
  // 1. init video writer
  cv::VideoWriter video_writer(output_path, cv::VideoWriter::fourcc('m', 'p', '4', 'v'),
                               writer_fps, cv::Size(width, height));
  //
  cv::VideoWriter video_writer_mask(output_path_mask, cv::VideoWriter::fourcc('m', 'p', '4', 'v'),
      writer_fps, cv::Size(width, height));
  //
  if (!video_writer.isOpened())
  {
    std::cout << "Can not open writer: " << output_path << "\n";
    return;
  }
  //
  if (!video_writer_mask.isOpened())
  {
      std::cout << "Can not open writer mask: " << output_path_mask << "\n";
      return;
  }
  //

  // 2. matting loop
  cv::Mat mat;
  unsigned int i = 0;
  while (video_capture.read(mat))
  {
    i += 1;
    types::MattingContent content;
    this->detect(mat, content, downsample_ratio);
    // 3. save contents and writing out.
    if (content.flag)
    {
      if (save_contents) contents.push_back(content);
      if (!content.merge_mat.empty()) video_writer.write(content.merge_mat);
      if(!content.pha_mat.empty())video_writer_mask.write(content.pha_mat * 255.);
    }
    // 4. check context states.
    if (!context_is_update) break;
#ifdef LITEORT_DEBUG
    std::cout << i << "/" << frame_count << " done!" << "\n";
#endif
  }

  // 5. release
  video_capture.release();
  video_writer.release();
  //
  video_writer_mask.release();
  //
}

接下来推一个demo试试看:
在这里插入图片描述
结果输出如下:
在这里插入图片描述
OK,实现mask和result同时输出啦!

嗷对了,记得将新生成的lite.ai.toolkit.dll复制到exe同路径下!


4.可在控制台选择模型 | 下采样比 | 推理线程数

由于不同模型及下采样比适用的效果不同,同时不同机器的CPU型号不一样,最大的推理线程数也不同。为了方便使用,我这里将这些功能集成到控制台里能够自由选择,使用时的效果如下:
在这里插入图片描述
在这里插入图片描述


5.视频抠图可实现背景替换

之前将视频中的人像分割出来以后我们仅将背景颜色做了一定修改,现在能否给视频换个背景呢?
lite.ai.toolkit作者最近将背景替换功能加入了工具箱中,现在我们将这部分功能添加到这个独立的子工程里。

重新编译lite.ai.toolkit,将最新生成的lite.ai.toolkit.dll、lite.ai.toolkit.exp、lite.ai.toolkit.lib拷贝至lite_rvm相同的目录下:在这里插入图片描述

修改rvm.h中detect_video()和detect()接口:

void detect(const cv::Mat& mat, types::MattingContent& content,
            float downsample_ratio = 0.25f, bool video_mode = false,
            bool remove_noise = false, bool minimum_post_process = false);
            
void detect_video(const std::string& video_path,
            const std::string& output_path,
            std::vector<types::MattingContent>& contents,
            bool save_contents = false,
            float downsample_ratio = 0.25f,
            unsigned int writer_fps = 20,
            bool remove_noise = false,
            bool minimum_post_process = false,
            const cv::Mat& background = cv::Mat());

在utils.h中加入新增功能函数定义:
//Matting&Segmentation Utils
    LITE_EXPORTS void swap_background(const cv::Mat& fgr_mat, const cv::Mat& pha_mat, const cv::Mat& bgr_mat, cv::Mat& out_mat, bool fgr_is_already_mul_pha = false);
    LITE_EXPORTS void remove_small_connected_area(cv::Mat& alpha_pred, float threshold = 0.05f);

现在我们来推一个视频看看吧:
在这里插入图片描述
输出结果:
原视频
更换完背景

总结

本文最终经过改进的应用程序可以直接下载使用:
(背景替换功能暂未添加)
下载地址: 视频人像分割系统
提取码:o9jl

另外需要源码学习的小伙伴可在下面链接中找到Windows端能够运行的lite.ai.toolkit :下载地址: 视频人像分割系统源码
提取码:nuxp

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值