将透明图像放置在不透明图像上 - OpenCV

4

我有这两张图片:

first
enter image description here
我想把第一张图片放在第二张上面。在那个论坛搜索后,我按照以下方式尝试做到这一点:

cv::Mat dst(std::max(background.rows, small->rows),std::max(background.cols, small->cols), CV_8UC4);
background.copyTo(dst);
small->copyTo(dst);
cv::imwrite(path_to_save.str(), dst);

很不幸,我只得到了第二张图片。我认为透明像素被视为白色像素,并覆盖了第一张图片的像素。

如何只复制非透明像素?


昨天我尝试使用Python将人物的背景变为透明,但没有成功。作为替代方案,我使用位运算,并用绿色背景代替透明背景来创建遮罩。这可以通过以下代码实现:OpenCV: setting all pixels of specific BGR value to another BGR value。在最后一步中,你也可以尝试使用dst = cv.addWeighted(img1,0.7,img2,0.3,0)代替dst = cv.add(img1_bg,img2_fg) - Santhosh Dhaipule Chandrakanth
copyTo函数总是覆盖原图像。如果您只想复制/覆盖图像的某些部分,请使用掩模。如果您想混合图像,请使用两个像素的线性组合,例如alpha x transparent-pixel + (1-alpha) x background-pixel,其中alpha可以是您的alpha通道值缩放到0..1的值。 - Micka
OpenCV在Alpha通道方面的功能不是很强大,因为这更多涉及图像编辑或计算机图形而非计算机视觉。可能有更好的库适合此类需求,甚至Qt也可能更适合。 - Micka
1个回答

2

我不知道有任何来自opencv的直接函数,但我编写了显式方法来执行该操作。

假设:

  • 您的背景是CV_8UC4。 在使用该样本时,您可以轻松地将该方法扩展到其他版本。

代码

void merge_images(cv::Mat* background, cv::Mat* upcoming, int x, int y)
{
    auto handle_cv_8uc4 = [=](int i, int j)
            {

                if(upcoming->at<cv::Vec4b>(j, i)[3] > 10)//10 is only epsilon for trash hold, you can put also 0 or anything else.
                {
                    background->at<cv::Vec4b>(y+j, x+i) = upcoming->at<cv::Vec4b>(j, i);
                }
            };

    auto handle_cv_8uc3 = [=](int i, int j)
    {
        background->at<cv::Vec4b>(y+j, x+i)[0] = upcoming->at<cv::Vec3b>(j, i)[0];
        background->at<cv::Vec4b>(y+j, x+i)[1] = upcoming->at<cv::Vec3b>(j, i)[1];
        background->at<cv::Vec4b>(y+j, x+i)[2] = upcoming->at<cv::Vec3b>(j, i)[2];
        background->at<cv::Vec4b>(y+j, x+i)[3] = 255;
    };

    for(int i = 0; i < upcoming->cols; i++)
    {
        for(int j = 0; j < upcoming->rows; j++)
        {
            if(j + y >= background->rows)
            {
                break;
            }

            if(x + i >= background->cols)
            {
                return;
            }

            switch(upcoming->channels())
            {
                case 3:
                {
                    handle_cv_8uc3(i, j);
                    break;
                }

                case 4:
                {
                    handle_cv_8uc4(i, j);
                    break;
                }

                default:
                {
                    //maybe error?
                }
            }

        }
    }
}

我认为这段代码可以自我解释。如果有不清楚的地方,请随时提问。 现在,如果您想使用该代码,假设您有backgroundspr,并且您想将spr放置到background中:

cv::Mat dst(background.rows, background.cols, CV_8UC4);
merge_images(&dst, &background, 0, 0);
merge_images(&dst, &spr, 50, 50);

说明

cv::Mat dst(background.rows, background.cols, CV_8UC4);

准备一个与背景大小相同的矩阵(以避免修改原始背景)。

merge_images(&dst, &background, 0, 0);

为您准备的副本添加背景

merge_images(&dst, &spr, 50, 50);

添加图片,使其左上角位于(50,50)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接