【问题标题】:OpenCV java alpha BlendingOpenCV java alpha 混合
【发布时间】:2020-04-13 21:36:44
【问题描述】:

我正在尝试移植以下代码(从 C++ 到 Java)以在我的图像之间进行良好的 alpha 混合,但它不起作用:

#include opencv2/opencv.hpp

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

    // Read the images
    Mat foreground = imread("puppets.png");
    Mat background = imread("ocean.png");
    Mat alpha = imread("puppets_alpha.png");

    // Convert Mat to float data type
    foreground.convertTo(foreground, CV_32FC3);
    background.convertTo(background, CV_32FC3);

    // Normalize the alpha mask to keep intensity between 0 and 1
    alpha.convertTo(alpha, CV_32FC3, 1.0/255); // 

    // Storage for output image
    Mat ouImage = Mat::zeros(foreground.size(), foreground.type());

    // Multiply the foreground with the alpha matte
    multiply(alpha, foreground, foreground); 

    // Multiply the background with ( 1 - alpha )
    multiply(Scalar::all(1.0)-alpha, background, background); 

    // Add the masked foreground and background.
    add(foreground, background, ouImage); 

    // Display image
    imshow("alpha blended image", ouImage/255);
    waitKey(0);

    return 0;
}

代码可以在这里找到:https://www.learnopencv.com/alpha-blending-using-opencv-cpp-python/ 还有我的 Java 版本:

public static Mat alphaBlend(Mat background, Mat foreground) {
        Vector<Mat> rgba = new Vector<Mat>();
        // split RBGA image for separate channels
        Core.split(background, rgba);
        // get alpha channel
        Mat alpha = rgba.get(3);

        // Convert Mat to float data type
        foreground.convertTo(foreground, CvType.CV_32FC3);
        background.convertTo(background, CvType.CV_32FC3);

        // Normalize the alpha mask to keep intensity between 0 and 1
        alpha.convertTo(alpha, CvType.CV_32FC3, 1.0/255); //
        Imgproc.cvtColor(alpha,alpha, Imgproc.COLOR_GRAY2BGRA,4);

        Mat outImage = Mat.zeros(foreground.size(),foreground.type());

        // Multiply the foreground with the alpha matte
        Core.multiply(alpha, foreground, foreground);

        Mat kernel = new MatOfDouble(1.0);

        Core.subtract(kernel, alpha, alpha);

        // Multiply the background with ( 1 - alpha )
        Core.multiply(alpha, background, background);

        // Add the masked foreground and background.
        Core.add(foreground, background, outImage);

        Core.divide(new MatOfDouble(255), outImage,outImage);
        return outImage;
    }

我认为我的问题是在 java 中移植这个:

multiply(Scalar::all(1.0)-alpha, background, background); 

任何帮助将不胜感激!

谢谢

编辑: 这是一个工作版本,但边界不混合透明像素,导致图像交集边界处出现空白像素/线:

    private static Mat merge(Mat background, Mat foreground) {
        Vector<Mat> rgba = new Vector<Mat>();
        // split RBGA image for separate channels
        Core.split(background, rgba);
        // get alpha channel
        Mat alpha = rgba.get(3);
        // Convert Mat to float data type
        // Normalize the alpha mask to keep intensity between 0 and 1
        alpha.convertTo(alpha, CvType.CV_32FC3, 1.0/255); //
        Mat dst = new Mat(256,256,CvType.CV_8UC4);
        alpha.convertTo(alpha,CvType.CV_8UC1);
        Mat alphaInv = new Mat();
        // invert the mask
        Core.absdiff(new MatOfDouble(1.0), alpha, alphaInv);
        foreground.copyTo(dst, alphaInv);
        // case where foreground is full JPEG in BGR
        if(dst.type() != CvType.CV_8UC4) {
            Imgproc.cvtColor(dst, dst, Imgproc.COLOR_BGR2BGRA, 4);
        }
        background.copyTo(dst, alpha);
        return dst;
    }

【问题讨论】:

    标签: java c++ opencv alphablending blending


    【解决方案1】:

    根据您分享的链接,看起来目标只是使用图像遮罩,我认为有一种更简单的方法可以做到这一点,使用 copyTo 函数。试着把它放在你的 main 中,当它工作时转移到你的函数:

    // Read the images
    Mat foreground = Imgcodecs.imread("puppets.png");
    Mat background = Imgcodecs.imread("ocean.png");
    Mat mask = Imgcodecs.imread("puppets_alpha.png");
    Mat notMask = new Mat();
    Core.bitwise_not(mask, notMask);
    Mat result = new Mat();
    Core.copyTo(background, result, mask);
    Core.copyTo(foreground, result, notMask);
    Imgcodecs.imwrite("result.png", result);
    

    【讨论】:

    • 感谢您的帮助。上面的代码没有提供好的结果,但我的第一次尝试是朝那个方向。使用 alpha 波段作为蒙版并使用此蒙版复制像素。它工作正常,但问题是边界中 0...1 之间的透明像素。因为对于那个像素,它是全白像素。这是因为我试图混合这些像素以获得平滑的交叉点
    • 您可以查看我的编辑以查看基本的工作功能,但仍不完美。需要混合边框透明像素
    【解决方案2】:

    我建议使用 Core 的 addWeighted 方法进行基本的 alpha 混合。

    看起来你想要一个基于掩码的可变 alpha 混合,你需要按照 api 指定的顺序排列你的参数,比如addition/subtraction/multiplication。对于您的乘法,这意味着标量应该是第二个参数。对于您的减法,这意味着标量应该再次成为第二个参数(我发现自己将矩阵乘以负 1,然后添加到标量值)。

    【讨论】:

    • addWeighted 函数混合了两个图像,即使相交像素也能提供透明度。我想要的是把一个图像放到另一个没有透明度(没有混合颜色)的相交部分。本教程给出了很好的结果(我在 python 中尝试过,这正是我想要的结果)。
    • 您是否尝试按照第二段中链接的 API 重新排列参数顺序? java通常要求标量值是秒参数,看起来你将它设置为第一个
    • 是的,我做了,我测试了很多组合都没有成功。我不知道我的代码有什么问题
    • 我添加了另一个答案,我认为这可能会简化您正在做的事情,尝试一下,让我知道它是如何进行的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 2010-11-20
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多