【问题标题】:OpenCV - create color maskOpenCV - 创建颜色蒙版
【发布时间】:2019-07-23 06:02:54
【问题描述】:

朋友们!有两个输入图像。一个背景图像,另一个蒙版图像。我需要得到面具的彩色部分。

我需要得到什么: backgroundmaskresult image

但我得到了完全不同的东西:backgroundmaskresult image

我的代码在 C# 中:

 //Read files
Mat img1 = CvInvoke.Imread(Environment.CurrentDirectory + "\\Test\\All1.jpg");
Mat img = CvInvoke.Imread(Environment.CurrentDirectory + "\\Test\\OriginalMask.jpg");

// Threshold and MedianBlur mask
CvInvoke.Threshold(img, img, 0, 255, Emgu.CV.CvEnum.ThresholdType.BinaryInv);
CvInvoke.MedianBlur(img, img, 13);

// without this conversion, an error appears: (mtype == CV_8U || mtype == CV_8S) && _mask.sameSize(*psrc1)
CvInvoke.CvtColor(img, img, Emgu.CV.CvEnum.ColorConversion.Rgb2Gray);

CvInvoke.BitwiseNot(img1, img1, img);

//Save file
img1.Save(Environment.CurrentDirectory + "\\Test\\Result.jpg");

第一个问题:怎样才能达到图中的效果?

第二个问题:不转换掩码为什么会报错:(mtype == CV_8U || mtype == CV_8S) && _mask.sameSize(*psrc1)

第三个问题:如何在最终的图像中制作透明背景而不是白色背景?

解决方案不必在 C# 中。该解决方案适用于任何编程语言,因为语法 OpenCV 大致相同。提前谢谢你。

【问题讨论】:

  • 关于你的第一个和第三个问题,请看下面我的回答。对于第二个问题:我认为,img(也)作为三通道(彩色)图像加载,因为没有设置说明符。 thresholdmedianBlur 也适用于彩色图像,而 bitwise_not 仅适用于单通道(灰度)图像。因此,如果事先没有转换,则会出现错误消息。

标签: c# python c++ windows opencv


【解决方案1】:

我将在我的回答中使用 C++,因为我最熟悉它。

这是我的建议:

// Load background as color image.
cv::Mat background = cv::imread("background.jpg", cv::IMREAD_COLOR);

// Load mask image as grayscale image.
cv::Mat mask = cv::imread("mask.jpg", cv::IMREAD_GRAYSCALE);

// Start time measurement.
auto start = std::chrono::system_clock::now();

// There are some artifacts in the JPG...
cv::threshold(mask, mask, 128, 255, cv::THRESH_BINARY);

// Initialize result image.
cv::Mat result = background.clone().setTo(cv::Scalar(255, 255, 255));

// Copy pixels from background to result image, where pixel in mask is 0.
for (int x = 0; x < background.size().width; x++)
    for (int y = 0; y < background.size().height; y++)
        if (mask.at<uint8_t>(y, x) == 0)
            result.at<cv::Vec3b>(y, x) = background.at<cv::Vec3b>(y, x);

// End time measurement.
auto end = std::chrono::system_clock::now();

// Output duration duration.
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << elapsed_seconds.count() << "\n";

// Write result.
cv::imwrite("result.png", result);

// Start time measurement.
start = std::chrono::system_clock::now();

// Generate new image with alpha channel.
cv::Mat resultTransparent = cv::Mat(result.size(), CV_8UC4);

// Copy pixels in BGR channels from result to transparent result image.
// Where pixel in mask is not 0, set alpha to 0.
for (int x = 0; x < background.size().width; x++)
{
    for (int y = 0; y < background.size().height; y++)
    {
        resultTransparent.at<cv::Vec4b>(y, x)[0] = result.at<cv::Vec3b>(y, x)[0];
        resultTransparent.at<cv::Vec4b>(y, x)[1] = result.at<cv::Vec3b>(y, x)[1];
        resultTransparent.at<cv::Vec4b>(y, x)[2] = result.at<cv::Vec3b>(y, x)[2];

        if (mask.at<uint8_t>(y, x) != 0)
            resultTransparent.at<cv::Vec4b>(y, x)[3] = 0;
        else
            resultTransparent.at<cv::Vec4b>(y, x)[3] = 255;
    }
}

// End time measurement.
end = std::chrono::system_clock::now();

// Output duration duration.
elapsed_seconds = end - start;
std::cout << elapsed_seconds.count() << "\n";

// Write transparent result.
cv::imwrite("resultTransparent.png", resultTransparent);

这两个输出的结果(您不会在白色 StackOverflow 背景上看到第二张图像的透明度):

【讨论】:

  • 感谢您提出的解决方案。告诉我,整个操作执行了多少时间,以毫秒为单位,不算读写文件的时间?
  • 我编辑了答案并使用 chrono 添加了两个时间测量值,即您需要添加 #include &lt;chrono&gt;。在释放模式下,第一次操作大约需要 12 毫秒,第二次大约需要 39 毫秒。但我非常怀疑这些数字是否有意义——没有使用优化,运行代码的系统是什么,......
  • 很有趣。在我的 Intel Core i5-8250U 处理器上,第一次操作需要 0.47 秒,第二次操作需要 1.4 秒
  • 这就是我在调试模式下(大致)得到的——你检查了吗?
  • 我在控制台 std::cout 中查看信息
【解决方案2】:

只是为HanseHirse的回答添加一个小细节:

如果您向蒙版添加高斯模糊(就像您在CvInvoke.MedianBlur(img, img, 13); 的问题中所做的那样),蒙版的边缘会更平滑,并且输出图像在放置在另一个图像之上时会看起来更好。

您只需将输出图像的第四个通道直接设置为模糊蒙版即可。

所以不是

if (mask.at<uint8_t>(y, x) != 0)
            resultTransparent.at<cv::Vec4b>(y, x)[3] = 0;
        else
            resultTransparent.at<cv::Vec4b>(y, x)[3] = 255;

你可以试试

resultTransparent.at<cv::Vec4b>(y, x)[3] = mask.at<uint8_t>(y, x);

【讨论】:

  • 你完全正确。我将“美化”部分留给了问题作者。感谢您的加入。
【解决方案3】:

Python 中的结果相同,如果它可以激发您的灵感:

import cv2
import numpy as np
# Read files
img1 = cv2.imread("All1.jpg",cv2.IMREAD_COLOR);
img = cv2.imread("OriginalMask.jpg",cv2.IMREAD_GRAYSCALE)  # loading in grayscale

# Threshold and MedianBlur mask
_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)  # corrected to 127 instead of 0
img = cv2.medianBlur(img, 13)

# fill with white
dest= np.full(np.shape(img1),255,np.uint8)

# Assuming dst and src are of same sizes
# only copy values where the mask has color > 0
dest[img>0] = img1[img>0]  # after @T.Kau's suggestion


cv2.imshow('dest',dest)
cv2.waitKey(0)
cv2.destroyAllWindows()

【讨论】:

  • 在python中你可以通过dest[img&gt;0] = img1[img&gt;0]来保存一些行
猜你喜欢
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-25
  • 2013-04-15
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
相关资源
最近更新 更多