【问题标题】:OpenCV keep background transparent during warpAffineOpenCV在warpAffine期间保持背景透明
【发布时间】:2015-08-26 15:05:51
【问题描述】:

我使用 warpPerspective() 函数创建一个鸟视图图像,如下所示:

warpPerspective(frame, result, H, result.size(), CV_WARP_INVERSE_MAP, BORDER_TRANSPARENT);

结果看起来非常好,而且边框是透明的: Bird-View-Image

现在我想把这张图片放在另一张“out”的图片之上。我尝试使用这样的函数 warpAffine 执行此操作:

warpAffine(result, out, M, out.size(), CV_INTER_LINEAR, BORDER_TRANSPARENT);

我还根据 stackoverflow 上已经提出的问题将“输出”转换为具有 alpha 通道的四通道图像: Convert Image

这是代码:cvtColor(out, out, CV_BGR2BGRA);

我希望看到棋盘,但看不到灰色背景。但实际上我的结果是这样的:

Result Image

我做错了什么?我是否忘记了要做的事情?还有其他方法可以解决我的问题吗?任何帮助表示赞赏:)

谢谢!

最好的问候 DamBedEi

【问题讨论】:

  • afaik,openCV 不处理 cv::imshow 中的透明度,您可以尝试将图像另存为 .png 文件并检查那里是否应用了透明度?
  • 我做到了。似乎应用了透明度。但是我没有看到背景,而是看到了典型的透明度模式,这并不是更好:D
  • 可能没有背景...您希望看到什么?如果您想“合并”两个具有透明度的图像(例如,具有透明前景的纯色背景),您必须在保存到文件之前(手动)执行此操作。
  • 是的,这就是我想做的。你知道如何手动操作吗?

标签: c++ opencv


【解决方案1】:

我希望有更好的方法,但你可以这样做:

  1. 正常做warpaffine(没有透明的东西)
  2. 找到包围扭曲图像的轮廓
  3. 使用此轮廓创建蒙版(图像内的白色值扭曲,边框中的黑色值)
  4. 使用此蒙版将变形的图像复制到另一张图像中


示例代码:

// load images
cv::Mat image2 = cv::imread("lena.png");
cv::Mat image = cv::imread("IKnowOpencv.jpg");
cv::resize(image, image, image2.size());

// perform warp perspective
std::vector<cv::Point2f> prev;
prev.push_back(cv::Point2f(-30,-60));
prev.push_back(cv::Point2f(image.cols+50,-50));
prev.push_back(cv::Point2f(image.cols+100,image.rows+50));
prev.push_back(cv::Point2f(-50,image.rows+50 ));
std::vector<cv::Point2f> post;
post.push_back(cv::Point2f(0,0));
post.push_back(cv::Point2f(image.cols-1,0));
post.push_back(cv::Point2f(image.cols-1,image.rows-1));
post.push_back(cv::Point2f(0,image.rows-1));
cv::Mat homography = cv::findHomography(prev, post);
cv::Mat imageWarped;
cv::warpPerspective(image, imageWarped, homography, image.size());

// find external contour and create mask
std::vector<std::vector<cv::Point> > contours;
cv::Mat imageWarpedCloned = imageWarped.clone(); // clone the image because findContours will modify it
cv::cvtColor(imageWarpedCloned, imageWarpedCloned, CV_BGR2GRAY); //only if the image is BGR
cv::findContours (imageWarpedCloned, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

// create mask
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8U); 
cv::drawContours(mask, contours, 0, cv::Scalar(255), -1);

// copy warped image into image2 using the mask
cv::erode(mask, mask, cv::Mat()); // for avoid artefacts
imageWarped.copyTo(image2, mask); // copy the image using the mask

//show images
cv::imshow("imageWarpedCloned", imageWarpedCloned);    
cv::imshow("warped", imageWarped);
cv::imshow("image2", image2);    
cv::waitKey();

【讨论】:

  • 我找到了一个similar solution,但是扭曲了遮罩而不是找到轮廓。它看起来更优雅,但在我的示例中,性能略低(3 毫秒扭曲蒙版,2 毫秒寻找轮廓)
【解决方案2】:

解决此问题的最简单方法之一(不一定是最有效的)是将图像扭曲两次,但每次将 OpenCV 常量边界值设置为不同的值(即第一次为零,第二次为 255)。这些常数值应选择图像中的最小值和最大值。

那么很容易找到两个warp值接近相等的二进制掩码。

更重要的是,您还可以通过简单的代数来创建透明效果,如下所示:

new_image = np.float32((warp_const_255 - warp_const_0) *
                preferred_bkg_img) / 255.0 + np.float32(warp_const_0)

我更喜欢这种方法的主要原因是 openCV 似乎可以平滑地向下(或向上)插值到图像边缘的恒定值。一个完全二元的蒙版会将这些深色或浅色边缘区域作为伪影拾取。上述方法更像是真正的透明,并与首选背景正确混合。

【讨论】:

    【解决方案3】:

    这是一个用透明“边框”扭曲的小测试程序,然后将扭曲的图像复制到纯色背景。

    int main()
    {
        cv::Mat input = cv::imread("../inputData/Lenna.png");
    
        cv::Mat transparentInput, transparentWarped;
    
        cv::cvtColor(input, transparentInput, CV_BGR2BGRA);
        //transparentInput = input.clone();
    
        // create sample transformation mat
        cv::Mat M = cv::Mat::eye(2,3, CV_64FC1);
        // as a sample, just scale down and translate a little:
        M.at<double>(0,0) = 0.3;
        M.at<double>(0,2) = 100;
        M.at<double>(1,1) = 0.3;
        M.at<double>(1,2) = 100;
    
        // warp to same size with transparent border:
        cv::warpAffine(transparentInput, transparentWarped, M, transparentInput.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
    
    
        // NOW: merge image with background, here I use the original image as background:
        cv::Mat background = input;
    
        // create output buffer with same size as input
        cv::Mat outputImage = input.clone();
    
        for(int j=0; j<transparentWarped.rows; ++j)
            for(int i=0; i<transparentWarped.cols; ++i)
            {
                cv::Scalar pixWarped = transparentWarped.at<cv::Vec4b>(j,i);
                cv::Scalar pixBackground = background.at<cv::Vec3b>(j,i);
                float transparency = pixWarped[3] / 255.0f; // pixel value: 0 (0.0f) = fully transparent, 255 (1.0f) = fully solid
    
                outputImage.at<cv::Vec3b>(j,i)[0] = transparency * pixWarped[0] + (1.0f-transparency)*pixBackground[0];
                outputImage.at<cv::Vec3b>(j,i)[1] = transparency * pixWarped[1] + (1.0f-transparency)*pixBackground[1];
                outputImage.at<cv::Vec3b>(j,i)[2] = transparency * pixWarped[2] + (1.0f-transparency)*pixBackground[2];
            }
    
        cv::imshow("warped", outputImage);
    
    
    
        cv::imshow("input", input);
        cv::imwrite("../outputData/TransparentWarped.png", outputImage);
        cv::waitKey(0);
        return 0;
    }
    

    我用这个作为输入:

    并得到这个输出:

    看起来 ALPHA 通道不是由 warpAffine 设置为零,而是设置为 205...

    但总的来说,我会这样做(未优化)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-29
      • 2011-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多