【问题标题】:opencv blend (overlay) two image with different channelsopencv混合(叠加)具有不同通道的两个图像
【发布时间】:2018-08-12 16:06:08
【问题描述】:

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

【问题讨论】:

  • 对于每个 y 坐标:对于每个 x 坐标:按照您喜欢的方式手动混合;
  • 感谢您的回复。但是,两张图片可能有不同的通道数,那么如何手动混合不同的通道数呢?
  • 取决于你想如何融合...没有“对”或“错”,这真的取决于你想要达到什么...
  • 再次感谢您的回复。我想要一个简单的混合结果 = 背景 * alpha + inputimage * (1-alpha)。由于在加载它们之前我不知道背景和输入图像通道的数量,但我希望我的函数能够自动处理这些情况。我最初的想法是将两者都转换为 4 个通道,但 cvColor 需要您知道特定的输入和输出才能完成这项工作。您对此有什么建议吗?
  • 可以询问频道数。如果为 1,则为灰度,3 为 BGR,4 为 BGRA,在加载 opencv 的 imread 后,请在 cvtColor 中使用 GRAY2BGRA 或 BGR2BGRA 标志

标签: java c++ opencv opencv3.0 opencv4android


【解决方案1】:

否,OpenCV 中没有原生提供此功能的 API。另一方面,没有什么能阻止你编写自己的代码来做到这一点。

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

  • 传递两个输入图像:背景为 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);

结果如下:

此实现对于生产目的来说不是万无一失的,因此使用它需要您自担风险。

【讨论】:

    【解决方案2】:

    感谢所有试图回答我问题的人。最后,我采取了枚举背景图像条件的路径并将其转换为相同的通道数,然后使用加权和。不确定这是否是正确的处理方法,但它似乎对我有用,除了透明背景 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-12
      • 2017-10-22
      • 1970-01-01
      • 2012-06-22
      • 2021-01-14
      • 1970-01-01
      • 2018-09-19
      相关资源
      最近更新 更多