opencv用不同通道混合(覆盖)两个图像

8wigbo56  于 2023-03-03  发布在  其他
关注(0)|答案(2)|浏览(154)

我想用opencv覆盖两张图片(可能是不同格式(通道))。最初我使用addWeighted,但是,它在两张不同通道的图片上失败了。在opencv中有什么方法可以处理这种情况吗?谢谢

au9on6nz

au9on6nz1#

    • 不**,OpenCV中没有API可以原生地提供这个特性。另一方面,没有什么可以阻止你编写自己的代码来实现这个特性。

几周前,A对我在互联网上其他地方看到的一个函数进行了一些更改,以便能够:

  • 通过两个输入图像:* * 背景为BGR**,前景为BGRA;
  • 根据简单的透明度规则将它们混合在一起;
  • (我不记得这些代码大部分来自哪里,抱歉...但谢谢你,不管你是谁!)*
void overlayImage(const cv::Mat &background, const cv::Mat &foreground, cv::Mat &output, cv::Point2i location, double opacity = 1.0)
{
    background.copyTo(output);

    // start at the row indicated by location, or at row 0 if location.y is negative.
    for (int y = std::max(location.y , 0); y < background.rows; ++y) {
        int fY = y - location.y; // because of the translation

        // we are done of we have processed all rows of the foreground image.
        if (fY >= foreground.rows)
            break;

        // start at the column indicated by location, or at column 0 if location.x is negative.
        for (int x = std::max(location.x, 0); x < background.cols; ++x) {
            int fX = x - location.x; // because of the translation.

            // we are done with this row if the column is outside of the foreground image.
            if (fX >= foreground.cols)
                break;

            // determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
            double opacity_level = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
            if (opacity >= 0.0 && opacity < 1.0)
                opacity_level *= opacity;

            // and now combine the background and foreground pixel, using the opacity, but only if opacity > 0.
            for (int c = 0; opacity_level > 0 && c < output.channels(); ++c) {
                unsigned char foregroundPx = foreground.data[fY * foreground.step + fX * foreground.channels() + c];
                unsigned char backgroundPx = background.data[y * background.step + x * background.channels() + c];
                output.data[y*output.step + output.channels()*x + c] = backgroundPx * (1.-opacity_level) + foregroundPx * opacity_level;
            }
        }
    }
}

以下是用于测试的输入图像:* 左 * 是 * 背景 *,而 * 右 * 上的图像是 * 前景 *。

要将前景完全复制到背景上,只需执行以下操作:

cv::Mat background = cv::imread("road.png");         // 3-chan BGR
cv::Mat foreground= cv::imread("tulip.png", -1);     // 4-chan BGRA
cv::Point location(0, 0);  

cv::Mat output;
overlayImage(input_bkg, input_target, output, location, 1.0);
cv::imwrite("output_alpha1.0.png", output);

并以50%的透明度执行复制:

overlayImage(input_bkg, input_target, output, location, 0.5);
cv::imwrite("output_alpha0.5.png", output);

以下是结果:

此实现对于生产用途来说并不是无懈可击的,因此使用它要自担风险。

nlejzf6q

nlejzf6q2#

感谢大家尝试回答我的问题。最后,我采取了枚举的背景图像的条件,并将其转换为相同的通道数,然后使用加权求和的路径。不知道这是否是正确的方式来处理它,但它似乎为我工作,除了透明背景PNG。有什么建议吗?

private boolean convertToSameChannels() {
  if (_overlayImage.channels() != _image.channels()) {
    if (_image.channels() == 1) {
      if (_overlayImage.channels() == 3) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_BGR2GRAY);
      } else if (_overlayImage.channels() == 4) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_BGRA2GRAY);
      }
    } else if (_image.channels() == 3) {
      if (_overlayImage.channels() == 1) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_GRAY2BGR);
      } else if (_overlayImage.channels() == 4) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_BGRA2BGR);
      }
    } else if (_image.channels() == 4) {
      if (_overlayImage.channels() == 3) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_BGR2BGRA);
      } else if (_overlayImage.channels() == 1) {
        Imgproc.cvtColor(_overlayImage, _overlayImage, Imgproc.COLOR_GRAY2BGRA);
      }
    } else {
      System.out.println("overlay image not suppoerted error");
      return false;
    }
  }
  return true;
}

相关问题