lite.ai.toolkit
lite.ai.toolkit copied to clipboard
[FAQ]: 作者回答👉关于如何根据抠图模型的alpha合成新背景?
由于问这个问题的人比较多,我在lite里面增加了一些辅助函数来实现背景合成,但还没合并进主分支,具体细节可以参考以下这段逻辑,有需要的可以参考一下(目前不考虑性能的优化问题,有需要的同学可以自己根据这段逻辑做特定的性能优化):
void lite::utils::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)
{
// user-friendly method for background swap.
if (fgr_mat.empty() || pha_mat.empty() || bgr_mat.empty()) return;
const unsigned int fg_h = fgr_mat.rows;
const unsigned int fg_w = fgr_mat.cols;
const unsigned int bg_h = bgr_mat.rows;
const unsigned int bg_w = bgr_mat.cols;
const unsigned int ph_h = pha_mat.rows;
const unsigned int ph_w = pha_mat.cols;
const unsigned int channels = fgr_mat.channels();
if (channels != 3) return; // only support 3 channels.
const unsigned int num_elements = fg_h * fg_w * channels;
cv::Mat bg_mat_copy, ph_mat_copy, fg_mat_copy;
if (bg_h != fg_h || bg_w != fg_w)
cv::resize(bgr_mat, bg_mat_copy, cv::Size(fg_w, fg_h));
else bg_mat_copy = bgr_mat; // ref only.
if (ph_h != fg_h || ph_w != fg_w)
cv::resize(pha_mat, ph_mat_copy, cv::Size(fg_w, fg_h));
else ph_mat_copy = pha_mat; // ref only.
if (ph_mat_copy.channels() == 1)
cv::cvtColor(ph_mat_copy, ph_mat_copy, cv::COLOR_GRAY2BGR); // 0.~1.
// convert mats to float32 points.
if (bg_mat_copy.type() != CV_32FC3) bg_mat_copy.convertTo(bg_mat_copy, CV_32FC3); // 0.~255.
if (ph_mat_copy.type() != CV_32FC3) ph_mat_copy.convertTo(ph_mat_copy, CV_32FC3); // 0.~1.
if (fgr_mat.type() != CV_32FC3) fgr_mat.convertTo(fg_mat_copy, CV_32FC3); // 0.~255.
else fg_mat_copy = fgr_mat; // ref only
// element wise operations.
out_mat = fg_mat_copy.clone();
const float *fg_ptr = (float *) fg_mat_copy.data;
const float *bg_ptr = (float *) bg_mat_copy.data;
const float *ph_ptr = (float *) ph_mat_copy.data;
float *mutable_out_ptr = (float *) out_mat.data;
// TODO: add omp support instead of native loop.
if (!fgr_is_already_mul_pha)
for (unsigned int i = 0; i < num_elements; ++i)
mutable_out_ptr[i] = fg_ptr[i] * ph_ptr[i] + (1.f - ph_ptr[i]) * bg_ptr[i];
else
for (unsigned int i = 0; i < num_elements; ++i)
mutable_out_ptr[i] = fg_ptr[i] + (1.f - ph_ptr[i]) * bg_ptr[i];
if (!out_mat.empty() && out_mat.type() != CV_8UC3)
out_mat.convertTo(out_mat, CV_8UC3);
}
使用案例(MODNet还在开发中,此处仅用作参考示例)
static void test_default()
{
std::string onnx_path = "../../../hub/onnx/cv/modnet_photographic_portrait_matting-512x512.onnx";
std::string test_img_path = "../../../examples/lite/resources/test_lite_matting_input.jpg";
std::string test_bgr_path = "../../../examples/lite/resources/test_lite_matting_bgr.jpg";
std::string save_fgr_path = "../../../logs/test_lite_modnet_fgr.jpg";
std::string save_pha_path = "../../../logs/test_lite_modnet_pha.jpg";
std::string save_merge_path = "../../../logs/test_lite_modnet_merge.jpg";
std::string save_swap_path = "../../../logs/test_lite_modnet_swap.jpg";
lite::cv::matting::MODNet *modnet =
new lite::cv::matting::MODNet(onnx_path, 16); // 16 threads
lite::types::MattingContent content;
cv::Mat img_bgr = cv::imread(test_img_path);
cv::Mat bgr_mat = cv::imread(test_bgr_path);
// 1. image matting.
modnet->detect(img_bgr, content, true);
if (content.flag)
{
if (!content.fgr_mat.empty()) cv::imwrite(save_fgr_path, content.fgr_mat);
if (!content.pha_mat.empty()) cv::imwrite(save_pha_path, content.pha_mat * 255.);
if (!content.merge_mat.empty()) cv::imwrite(save_merge_path, content.merge_mat);
// swap background
cv::Mat out_mat;
lite::utils::swap_background(content.fgr_mat, content.pha_mat, bgr_mat, out_mat, true);
if (!out_mat.empty())
{
cv::imwrite(save_swap_path, out_mat);
std::cout << "Saved Swap Image Done!" << std::endl;
}
std::cout << "Default Version MGMatting Done!" << std::endl;
}
delete modnet;
}
效果示例
- 合成图
- 原图
- 背景图