欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

智能人像自动抠图——C++ 实现LFM 模型推理

发布时间:2024/3/26 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 智能人像自动抠图——C++ 实现LFM 模型推理 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

前言

  • 关于抠图,首先想到的是PS的抠图,要美工手动一点点的把细节抠出来,抠图的好坏取决一个美工对PS的熟悉程度,在人像抠图方面,对头发的处理更是耗时耗力的一件事,在拍证件照的照像馆都有固定的绿幕来挡掉复杂的背景,以免增加后期的工作量,那么有没有一种完全自动的抠图办法呢? [A Late Fusion CNN for Digital Matting] CVPR 2019 上的一编论文, 作者提出了全自动抠图这个算法,不需要绿幕,也不需要输入trimap图(有了解传统抠图算法的应该知道这个图是干吗用的),背景图也不需要。在只需要输入一张原图的情况下,就能很好的抠图。我用Pytorch复现了他的这个算法,并且效果还不错。
  • 关于人像抠图的应用场景可谓无所不在,在影视剪辑、直播娱乐、线上教学、视频会议等场景中都有人像分割的身影,它可以帮助用户实时、精准地将人物和背景精准识别出来,实现更精细化的人物美颜、背景虚化替换、弹幕穿人等,我那时想复现这编论文也是因为公司想做证件照相关的项目。
  • 我的编译环境是Windows 10 64位,IDE是VS2019,配置了OpenCV 4.5,实现语言是C++,用OpenCV的dnn来进行模型推理。
  • 模型演示

    1.实现代码

    void LFMatting(const cv::Mat& cv_src,cv::Mat &cv_matting, cv::Mat& cv_dst, cv::dnn::Net& net, int target_w = 640, int target_h = 960); void imshow(std::string name, const cv::Mat& img) {cv::namedWindow(name, 0);int max_rows = 500;int max_cols = 600;if (img.rows >= img.cols && img.rows > max_rows) {cv::resizeWindow(name, cv::Size(img.cols * max_rows / img.rows, max_rows));}else if (img.cols >= img.rows && img.cols > max_cols){cv::resizeWindow(name, cv::Size(max_cols, img.rows * max_cols / img.cols));}cv::imshow(name, img); }void mergeImage(std::vector<cv::Mat>& src_vor, cv::Mat& cv_dst, int channel) {cv::Mat img_merge;cv::Size size(src_vor.at(0).cols * src_vor.size(), src_vor.at(0).rows);if (channel == 1){img_merge.create(size, CV_8UC1);}else if (channel == 3){img_merge.create(size, CV_8UC3);}for (int i = 0; i < src_vor.size(); i++){cv::Mat cv_temp = img_merge(cv::Rect(src_vor.at(i).cols * i, 0, src_vor.at(i).cols, src_vor.at(i).rows));src_vor.at(i).copyTo(cv_temp);}cv_dst = img_merge.clone(); }cv::Mat channelSwitching(const cv::Mat& cv_src) {cv::Mat three_channel = cv::Mat::zeros(cv_src.rows, cv_src.cols, CV_8UC3);std::vector<cv::Mat> channels;if (cv_src.channels() == 1){for (int i = 0; i < 3; i++){channels.push_back(cv_src);}merge(&channels[0], channels.size(), three_channel);}return three_channel; }int main(int argc, char* argv[]) {cv::dnn::Net net = cv::dnn::readNet("model/graph_final_960_640.pb", "model/graph_final_960_640.pbtxt");std::string path = "images";std::vector<std::string> filenames;cv::glob(path, filenames, false);for (auto v : filenames){cv::Mat cv_src = cv::imread(v,1);std::vector<cv::Mat> cv_dsts(3);cv::Mat cv_matting, cv_dst;cv_dsts[0] = cv_src.clone();LFMatting(cv_src,cv_matting, cv_dsts[2], net);cv_dsts[1] = channelSwitching(cv_matting);cv_dsts[1].convertTo(cv_dsts[1], CV_8UC3, 255);mergeImage(cv_dsts, cv_dst, 3);cv::imwrite("dst.jpg", cv_dst);//cv::waitKey();}return 0; }void LFMatting(const cv::Mat& cv_src,cv::Mat &cv_matting, cv::Mat& cv_dst, cv::dnn::Net& net, int target_w,int target_h) {cv::Size reso(target_h, target_w);cv::Mat blob = cv::dnn::blobFromImage(cv_src, 1.0, reso,cv::Scalar(127.156207, 115.917443, 106.031127), true, false);net.setInput(blob);std::vector<cv::Mat> outputs;std::vector<std::string> names = {"deFG_side_0_out/ResizeNearestNeighbor","deBG_side_0_out/ResizeNearestNeighbor","fusion_sigmoid_output/Sigmoid"};net.forward(outputs, names);auto t1 = cv::getTickCount();for (size_t i = 0; i < outputs.size(); ++i){outputs[i] = outputs[i].reshape(0, { outputs[i].size[2], outputs[i].size[3] });cv::resize(outputs[i], outputs[i], cv_src.size(), 0.0, 0.0, cv::INTER_LINEAR);}cv::Mat fg = outputs[0];cv::Mat bg = outputs[1];cv::Mat alpha = outputs[2];cv_matting = fg.mul(alpha) + (1.0 - bg).mul(1.0 - alpha);cv_dst = cv::Mat::zeros(cv::Size(cv_src.cols, cv_src.rows), CV_8UC3);const int bg_color[3] = { 219,142,67 };float* alpha_data = (float*)alpha.data;for (int i = 0; i < cv_matting.rows; i++){for (int j = 0; j < cv_matting.cols; j++){float alpha_ = alpha_data[i * cv_matting.cols + j];cv_dst.at < cv::Vec3b>(i, j)[0] = cv_src.at < cv::Vec3b>(i, j)[0] * alpha_ + (1 - alpha_) * bg_color[0];cv_dst.at < cv::Vec3b>(i, j)[1] = cv_src.at < cv::Vec3b>(i, j)[1] * alpha_ + (1 - alpha_) * bg_color[1];cv_dst.at < cv::Vec3b>(i, j)[2] = cv_src.at < cv::Vec3b>(i, j)[2] * alpha_ + (1 - alpha_) * bg_color[2];}} }

    2.一些不错的效果:




    3.还有一些是场景处理不是很好。



    4. 我现在训练的样本在10000张左右,如果加大样本量或者优化下算法,是可以解决掉这些问题点。
    5. 模型和源码都上传到CSDN,感兴趣的可以下载试玩:https://download.csdn.net/download/matt45m/51419768

    总结

    以上是生活随笔为你收集整理的智能人像自动抠图——C++ 实现LFM 模型推理的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。