java - 如何展平2个不同的图像层?

标签 java android opencv

我有 2 个 Mat 对象,覆盖背景

如何将我的 overlay Mat 放在我的 background Mat 之上,以便只有 overlay Mat 的非透明像素完全显示遮挡背景垫?

我尝试过 addWeighted() ,它结合了 2 个 Mat,但两个“层”仍然可见。

  • 覆盖 Mat 具有透明 channel ,而背景 Mat 则没有。
  • overlay Mat 中的像素要么完全透明,要么完全模糊。
  • 两个垫子尺寸相同。

最佳答案

函数 addWeighted 将不起作用,因为它会对所有像素使用相同的 alpha 值。为了准确地执行您所说的操作,仅替换后台中的非透明值,您可以为此创建一个小函数,如下所示:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
    //must have same size for this to work
    assert(overlay.cols == background.cols && overlay.rows == background.rows);
    cv::Mat result = background.clone();
    for (int i = 0; i < result.rows; i++){
        for (int j = 0; j < result.cols; j++){
            cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j);
            if (pix[3] == 0){
                result.at<cv::Vec3b>(i,j) = cv::Vec3b(pix[0], pix[1], pix[2]);
            }
        }
    }
    return result;
}

我不确定opencv中的透明值是0还是255,所以相应地改变它......我认为0表示不透明,255表示完全透明。

如果您想使用 Alpha channel 的值作为混合速率,请将其稍微更改为:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
    //must have same size for this to work
    assert(overlay.cols == background.cols && overlay.rows == background.rows);
    cv::Mat result = background.clone();
    for (int i = 0; i < result.rows; i++){
        for (int j = 0; j < result.cols; j++){
            cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j);
            double alphaRate = 1.0 - pix[3]/255.0;
            result.at<cv::Vec3b>(i,j) = (1.0 - alphaRate) * cv::Vec3b(pix[0], pix[1], pix[2]) + result.at<cv::Vec3b>(i,j) * alphaRate;
        }
    }
    return result;
}

抱歉,代码是用 C++ 编写的,而不是用 JAVA 编写的,但我认为您可以了解一下。基本上只是像素中的一个循环,如果背景副本中的像素不透明,则将其更改为覆盖层的像素。

* 编辑*

我将通过此编辑来回答您的评论,因为它可能会占用空间。问题是 OpenCV 矩阵如何工作。对于带有 alpha 的图像,数据被组织为一个数组,如 BGRA BGRA .... BGRA,并且像加法、乘法等基本操作在具有相同维度的矩阵中工作...... ..您始终可以尝试使用 split 来分离矩阵(这将重新写入矩阵,因此可能会很慢),然后将 alpha channel 更改为双倍(再次重写),然后进行矩阵的乘法和加法。它应该更快,因为 OpenCV 优化了这些函数....您也可以在 GPU 中执行此操作...

类似这样的事情:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
    std::vector<cv::Mat> channels;
    cv::split(overlay, channels);
    channels[3].convertTo(channels[3], CV_64F, 1.0/255.0);
    cv::Mat newOverlay, result;
    cv::merge(channels, newOverlay);
    result = newOverlay * channels[3] + ((1 - channels[3]) * background);
    return result;
}

不确定 OpenCV 是否允许 CV_8U 乘以 CV_64F,或者这是否会更快......但可能会。

此外,带有循环的线程在线程中没有问题,因此可以对其进行优化...在 Release模式下运行它也会大大提高速度,因为 OpenCV 的 .at 函数做了几个断言....在 Release模式下尚未完成。不确定这是否可以在 JAVA 中更改...

关于java - 如何展平2个不同的图像层?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37116687/

相关文章:

java - Spring-MVC PathVariable匹配不以word开头的正则表达式

java - 如何删除 spring-data-jpa 中多边的项目?

java - 无法在 JPanel 上设置 JLabel 的位置

java - JPA 一对多与多对多 - 书籍和作者

c++ - 使用 rtsp、visual studio OpenCv 2.4.5 访问 IP 摄像机?

android - 字符串图像base64避免JSON对象中包含其他数据(android java)

android - 使用 Fused location Geofences 时是否需要请求位置更新?

android - 显示新 fragment 时添加操作栏项目

c++ - 用于矩阵运算的 OpenCV GPU 库有多好?

python - Python选择ROI OpenCV