如何在OpenCV中重现Photoshop的多重混合?

时间:2015-09-19 01:30:13

标签: opencv photoshop core-image gimp cifilter

我正在尝试在OpenCV中重现Photoshop的多重混合模式。与此相同的是您在GIMP中找到的,或者在Apple的CoreImage框架中使用CIMultiplyBlendMode时。

我在网上阅读的所有内容都表明,只需将两个输入图像的通道相乘(即Blend = AxB)即可实现多次混合。并且,这是有效的,除了α是<的情况。 1.0。

您可以在GIMP / PhotoShop / CoreImage中非常简单地测试,方法是创建两个图层/图像,每个图层填充不同的纯色,然后修改第一层的不透明度。 (顺便说一句,当您修改alpha时,由于某种原因,GIMP中的操作不再是可交换的。)

一个简单的例子:如果A =(0,0,0,0)且B =(0.4,0,0,1.0),而C = AxB,那么我希望C为(0,0,0) ,0)。这是简单的乘法。但这并不是这种混合在实践中的实施方式。在实践中,C =(0.4,0,0,1.0),或C = B.

底线是这样的:我需要找出乘法混合模式的公式(显然比AxB更多),然后在OpenCV中实现它(一旦我有公式,这应该是微不足道的。)

非常感谢任何见解。

另外,作为参考,这里有一些链接显示多个混合只是AxB:

How does photoshop blend two images together

Wikipedia - Blend Modes

Photoshop Blend Modes

2 个答案:

答案 0 :(得分:2)

我设法解决了这个问题。如果有任何建议的改进,请随时发表评论。

首先,我发现了如何在这篇文章中实现乘法函数的线索:

multiply blending

这是C ++中一个快速的OpenCV实现。

{{1}}

答案 1 :(得分:2)

这是一个基于GIMP源代码的OpenCV解决方案,特别是函数gimp_operation_multiply_mode_process_pixels

注意

  • 不是循环所有像素,而是可以进行矢量化,但我遵循GIMP的步骤。
  • 输入图像的类型必须为CV_8UC3或CV_8UC4。
  • 它还支持opacity值,必须在[0,255]
  • 在最初的GIMP实现中,还有对掩码的支持。它最终可以简单地添加到代码中。
  • 此实现实际上不是对称的,并且会重现您的奇怪的行为。

代码:

#include <opencv2\opencv.hpp>
using namespace cv;

Mat blend_multiply(const Mat& level1, const Mat& level2, uchar opacity)
{
    CV_Assert(level1.size() == level2.size());
    CV_Assert(level1.type() == level2.type());
    CV_Assert(level1.channels() == level2.channels());

    // Get 4 channel float images
    Mat4f src1, src2;

    if (level1.channels() == 3)
    {
        Mat4b tmp1, tmp2;
        cvtColor(level1, tmp1, COLOR_BGR2BGRA);
        cvtColor(level2, tmp2, COLOR_BGR2BGRA);
        tmp1.convertTo(src1, CV_32F, 1. / 255.);
        tmp2.convertTo(src2, CV_32F, 1. / 255.);
    }
    else
    {
        level1.convertTo(src1, CV_32F, 1. / 255.);
        level2.convertTo(src2, CV_32F, 1. / 255.);
    }

    Mat4f dst(src1.rows, src1.cols, Vec4f(0., 0., 0., 0.));

    // Loop on every pixel

    float fopacity = opacity / 255.f;
    float comp_alpha, new_alpha;

    for (int r = 0; r < src1.rows; ++r)
    {
        for (int c = 0; c < src2.cols; ++c)
        {
            const Vec4f& v1 = src1(r, c);
            const Vec4f& v2 = src2(r, c);
            Vec4f& out = dst(r, c);

            comp_alpha = min(v1[3], v2[3]) * fopacity;
            new_alpha = v1[3] + (1.f - v1[3]) * comp_alpha;

            if ((comp_alpha > 0.) && (new_alpha > 0.))
            {
                float ratio = comp_alpha / new_alpha;

                out[0] = max(0.f, min(v1[0] * v2[0], 1.f)) * ratio + (v1[0] * (1.f - ratio));
                out[1] = max(0.f, min(v1[1] * v2[1], 1.f)) * ratio + (v1[1] * (1.f - ratio));
                out[2] = max(0.f, min(v1[2] * v2[2], 1.f)) * ratio + (v1[2] * (1.f - ratio));
            }
            else
            {
                out[0] = v1[0];
                out[1] = v1[1];
                out[2] = v1[2];
            }

            out[3] = v1[3];

        }
    }

    Mat3b dst3b;
    Mat4b dst4b;
    dst.convertTo(dst4b, CV_8U, 255.);
    cvtColor(dst4b, dst3b, COLOR_BGRA2BGR);

    return dst3b;
}

int main()
{
    Mat3b layer1 = imread("path_to_image_1");
    Mat3b layer2 = imread("path_to_image_2");

    Mat blend = blend_multiply(layer1, layer2, 255);

    return 0;
}