在opencv中将2个图像与透明蒙版组合在一起

时间:2016-03-25 08:42:23

标签: c++ opencv image-processing

我基本上要做的是模糊图像,然后将其与原始图像组合在一起,这样原始图像中的某些区域才会模糊(脸部应该模糊不清)。

我的一般想法是掩盖原始Iwant中的部分模糊,然后将原件模糊为副本并且"合并"他们再次在一起。

在某种程度上,这也有效。

我的图片:

(1)原创 Original

(2)原件的部件应该模糊 Original with parts that should be blurred

(3)模糊 Blurred

创建这些图像的我的C ++代码:

int main(void) {
    cv::Mat srcImage = cv::imread(path);
    srcImage.convertTo(srcImage, CV_32FC3, 1.0/255.0);

    Mat _mask;
    Mat img_gray;

    cv::Scalar white = cv::Scalar(255, 255, 255);
    cv::Scalar black = cv::Scalar(0, 0, 0);

    cv::cvtColor(srcImage, img_gray, cv::COLOR_BGR2GRAY);
    img_gray.convertTo(_mask, CV_32FC1);

    // face
    cv::circle(_mask, cv::Point(430, 350), 200, black, -1, 8, 0);

    // eyes
    cv::circle(_mask, cv::Point(502, 260), 27, white, -1, 8, 0);
    cv::circle(_mask, cv::Point(390, 260), 27, white, -1, 8, 0);

    // mouth
    cv::ellipse(_mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);
    cv::threshold(1.0-_mask, _mask, 0.9, 1.0, cv::THRESH_BINARY_INV);

    cv::GaussianBlur(_mask,_mask,Size(21,21),11.0);


    cv::Mat res;
    cv::Mat bg = Mat(srcImage.size(), CV_32FC3);
    bg = cv::Scalar(1.0, 1.0 ,1.0);

    vector<Mat> ch_img(3);
    vector<Mat> ch_bg(3);
    cv::split(srcImage, ch_img);
    cv::split(bg, ch_bg);

    ch_img[0] = ch_img[0].mul(_mask) + ch_bg[0].mul(1.0 - _mask);
    ch_img[1] = ch_img[1].mul(_mask) + ch_bg[1].mul(1.0 - _mask);
    ch_img[2] = ch_img[2].mul(_mask) + ch_bg[2].mul(1.0 - _mask);

    cv::merge(ch_img, res);
    cv::merge(ch_bg, bg);

    // original but with white mask
    res.convertTo(res, CV_8UC3, 255.0);
    imwrite("original_with_mask.jpg", res);


    // blur original image
    cv::Mat blurredImage;
    bilateralFilter(srcImage, blurredImage, 10, 20, 5);
    GaussianBlur(srcImage, blurredImage, Size(19, 19), 0, 0);

    blurredImage.convertTo(blurredImage, CV_8UC3, 255.0);
    imwrite("blurred.jpg", blurredImage);

    cv::Mat maskedImage;
    maskedImage = Mat(srcImage.size(), CV_32FC3);

    // now combine blurred image and original using mask
    // this fails
    cv::bitwise_and(blurredImage, _mask, maskedImage);
    cv::imwrite("masked.jpg", maskedImage);
}

我的问题是cv::bitwise_and(blurredImage, _mask, maskedImage);失败并带有

OpenCV Error: Sizes of input arguments do not match (The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array') in binary_op

可能是因为_mask是单个频道图片,而blurredImagemaskedImage是3频道图片。

如何组合我得到的图像,使图像(2)中的当前白色区域使用透明蒙版模糊,使用&#34; soft&#34;边缘?

3 个答案:

答案 0 :(得分:5)

您可以使用字节通道值的线性组合,而不是浮点转换。参见

int main(int argc, char* argv[])
{
    cv::Mat srcImage = cv::imread("C:/StackOverflow/Input/transparentMaskInput.jpg");

    //  blur whole image
    cv::Mat blurredImage;
    //cv::bilateralFilter(srcImage, blurredImage, 10, 20, 5); // use EITHER bilateral OR Gaússian filter
    cv::GaussianBlur(srcImage, blurredImage, cv::Size(19, 19), 0, 0);

    // create mask
    cv::Scalar white = cv::Scalar(255, 255, 255);
    cv::Scalar black = cv::Scalar(0, 0, 0);

    cv::Mat mask = cv::Mat::zeros(srcImage.size(), CV_8UC1);

    // face
    cv::circle(mask, cv::Point(430, 350), 200, black, -1, 8, 0);

    // eyes
    cv::circle(mask, cv::Point(502, 260), 27, white, -1, 8, 0);
    cv::circle(mask, cv::Point(390, 260), 27, white, -1, 8, 0);

    // mouth
    cv::ellipse(mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);

    cv::GaussianBlur(mask, mask, cv::Size(21, 21), 11.0);

    // byte inversion:
    cv::Mat invertedMask = 255 - mask; // instead of inversion you could just draw the "face" black on a white background!


    cv::Mat outputImage = cv::Mat(srcImage.size(), srcImage.type());
    // for each pixel, merge blurred and original image regarding the blur-mask

    for (int y = 0; y < outputImage.rows; ++y)
    for (int x = 0; x < outputImage.cols; ++x)
    {
        cv::Vec3b pixelOrig = srcImage.at<cv::Vec3b>(y, x);
        cv::Vec3b pixelBlur = blurredImage.at<cv::Vec3b>(y, x);
        float blurVal = invertedMask.at<unsigned char>(y, x)/255.0f; // value between 0 and 1: zero means 100% orig image, one means 100% blurry image
        cv::Vec3b pixelOut = blurVal * pixelBlur + (1.0f - blurVal)* pixelOrig;

        outputImage.at<cv::Vec3b>(y, x) = pixelOut;
    }   

    cv::imshow("input", srcImage);
    cv::imshow("blurred", blurredImage);
    cv::imshow("mask", mask);
    cv::imshow("inverted mask", invertedMask);
    cv::imshow("output", outputImage);

    return 0;
}

使用此输入图像:

enter image description here

计算这个模糊和掩码:

enter image description here

enter image description here

通过计算(mask/255) * blur + (1-mask/255)*blur(线性组合)得到此输出:

enter image description here

答案 1 :(得分:1)

我在OpenCV中为CV_8UC3的两个CV_8UC1图像定义了一个函数来做alphaBlend:

//! 2018.01.16 13:54:39 CST
//! 2018.01.16 14:43:26 CST
void alphaBlend(Mat& img1, Mat&img2, Mat& mask, Mat& blended){
    // Blend img1 and img2 (of CV_8UC3) with mask (CV_8UC1)
    assert(img1.size() == img2.size() && img1.size() == mask.size());
    blended = cv::Mat(img1.size(), img1.type());
    for (int y = 0; y < blended.rows; ++y){
        for (int x = 0; x < blended.cols; ++x){
            float alpha = mask.at<unsigned char>(y, x)/255.0f;
            blended.at<cv::Vec3b>(y,x) = alpha*img1.at<cv::Vec3b>(y,x) + (1-alpha)*img2.at<cv::Vec3b>(y,x);
        }
    }
}

然后,很容易对图像进行alpha弯曲,只需调用alphaBlend(...)即可。这是一个例子:

#include <opencv2/opencv.hpp>
using namespace cv;

//! 2018.01.16 13:54:39 CST
//! 2018.01.16 14:43:26 CST
void alphaBlend(Mat& img1, Mat&img2, Mat& mask, Mat& blended){
    // Blend img1 and img2 (of CV_8UC3) with mask (CV_8UC1)
    assert(img1.size() == img2.size() && img1.size() == mask.size());
    blended = cv::Mat(img1.size(), img1.type());
    for (int y = 0; y < blended.rows; ++y){
        for (int x = 0; x < blended.cols; ++x){
            float alpha = mask.at<unsigned char>(y, x)/255.0f;
            blended.at<cv::Vec3b>(y,x) = alpha*img1.at<cv::Vec3b>(y,x) + (1-alpha)*img2.at<cv::Vec3b>(y,x);
        }
    }
}

Mat createMask(Size sz){
    // create mask
    cv::Mat mask = cv::Mat::zeros(sz, CV_8UC1);
    // white and black
    cv::Scalar white = cv::Scalar(255, 255, 255);
    cv::Scalar black = cv::Scalar(0, 0, 0);
    // face
    cv::circle(mask, cv::Point(430, 350), 200, black, -1, 8, 0);
    // eyes
    cv::circle(mask, cv::Point(502, 260), 27, white, -1, 8, 0);
    cv::circle(mask, cv::Point(390, 260), 27, white, -1, 8, 0);

    // mouth
    cv::ellipse(mask, cv::Point(440, 390), cv::Point(60, 25), 0, 0, 360, white, -1, 8, 0);

    // Blur
    cv::GaussianBlur(mask, mask, cv::Size(21, 21), 11.0);
    return mask;
}

int main(){
    cv::Mat img = cv::imread("img04.jpg");
    //  blur whole image
    cv::Mat blured;
    //cv::bilateralFilter(img, blured, 10, 20, 5); // use EITHER bilateral OR Gaússian filter
    cv::GaussianBlur(img, blured, cv::Size(19, 19), 0, 0);

    // Create the mask
    Mat mask = createMask(img.size());
    Mat mask_inv = 255 - mask;

    // Alpha blend
    Mat blended1, blended2;
    alphaBlend(img, blured, mask, blended1);
    alphaBlend(img, blured, mask_inv, blended2);

    // Display
    cv::imshow("source", img);
    cv::imshow("blured", blured);
    cv::imshow("mask", mask);
    cv::imshow("mask_inv", mask_inv);
    cv::imshow("blended1", blended1);
    cv::imshow("blended2", blended2);
    cv::waitKey();
    return 0;
}

来源:

enter image description here

所迷离:

enter image description here

MASK1:

enter image description here

AlphaBlend 1:

enter image description here

面具2:

enter image description here

AlphaBlend 2:

enter image description here

一些有用的链接:

  1. OpenCV C ++中的Alpha混合:Combining 2 images with transparent mask in opencv

  2. OpenCV Python中的Alpha混合: Gradient mask blending in opencv python

答案 2 :(得分:0)

  

可能是因为_mask是单通道图像和blurImage和   maskedImage是3通道图像。

在调用cv::bitwise_and

之前将其放入

P.S如果你不想改变你的面具,因为你想在另一个地方使用它,只需在临时变量中进行:

cv::Mat _mask_temp;
cv::cvtColor(_mask,_mask_temp,cv::COLOR_GRAY2BGR);
cv::bitwise_and(blurredImage, _mask_temp, maskedImage);
_mask_temp.release(); // just in case you do not want it anymore to be in your memory(optional)

编辑(另一个问题):

屏幕为32F,而图片为8U。所以,你需要这个:

cv::cvtColor(_mask,_mask,cv::COLOR_GRAY2BGR);
_mask.convertTo(_mask, CV_8UC3);