3

I am trying to implement unsharp masking like it's done in Adobe Photoshop. I gathered a lot of information on the interent but I'm not sure if I'm missing something. Here's the code:

void unsharpMask( cv::Mat* img, double amount, double radius, double threshold ) {

// create blurred img
cv::Mat img32F, imgBlur32F, imgHighContrast32F, imgDiff32F, unsharpMas32F, colDelta32F, compRes, compRes32F, prod;
double r = 1.5;
img->convertTo( img32F, CV_32F );
cv::GaussianBlur( img32F, imgBlur32F, cv::Size(0,0), radius );
cv::subtract( img32F, imgBlur32F, unsharpMas32F );
// increase contrast( original, amount percent ) 
imgHighContrast32F = img32F * amount / 100.0f;
cv::subtract( imgHighContrast32F, img32F, imgDiff32F );
unsharpMas32F /= 255.0f;
cv::multiply( unsharpMas32F, imgDiff32F, colDelta32F );
cv::compare( cv::abs( colDelta32F ), threshold, compRes, cv::CMP_GT );
compRes.convertTo( compRes32F, CV_32F );

cv::multiply( compRes32F, colDelta32F, prod );
cv::add( img32F, prod, img32F );

img32F.convertTo( *img, CV_8U );
}

At the moment I am testing with a grayscale image. If i try the exact same parameters in Photoshop I get much better result. My own code leads to noisy images. What am I doing wrong.

The 2nd question is, how i can apply unsharp masking on RGB images? Do I have to unsharp mask each of the 3 channels or would it be better in another color space? How are these things done in Photoshop?

Thanks for your help!

4

4 回答 4

3

我也在尝试复制 Photoshop 的 Unsharp Mask。让我们暂时忽略阈值。

我将向您展示如何使用其高斯模糊来复制 Photoshop 的 Unsharp Mask。

假设 O 是原始图像层。

创建一个新层 GB,它是应用在 O 上的高斯模糊。
创建一个新层,它是 O - GB(使用应用图像)。
通过反转 GB - invGB 创建一个新层。
使用 Image Apply 创建一个 O + invGB 的新层。
创建一个新层,它是前一层的反转,即 inv(O + invGB)。
创建一个新层,即 O + (O - GB) - inv(O + invGB)。

当您在 Photoshop 中执行此操作时,您将获得 Unsharp Mask 的完美再现。

如果您回忆一下 inv(L) = 1 - L 进行数学运算,您会得到 Unsharp Mask 是 USM(O) = 3O - 2B。

然而,当我直接在 MATLAB 中执行此操作时,我没有得到 Photoshop 的结果。

希望有人会知道确切的数学。

更新

好的,
我想通了。
在 Photoshop 中 USM(O) = O + (2 * (Amount / 100) * (O - GB))
其中 GB 是 O 的高斯模糊版本。

然而,为了复制 Photoshop 的结果,您必须执行上述步骤并将每个步骤的结果剪辑到 [0, 1] 中,就像在 Photoshop 中所做的那样。

于 2014-04-27T11:28:36.023 回答
1

According to docs:

C++: void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )

4th parameter is not "radius" it is "sigma" - gaussian kernel standard deviation. Radius is rather "ksize". Anyway Photoshop is not open source, hence we can not be sure they use the same way as OpenCV to calculate radius from sigma.

Channels

Yes you should apply sharp to any or to all channels, it depends on your purpose. Sure you can use any space: if you want sharp only brightness-component and don't want to increase color noise you can covert it to HSL or Lab-space and sharp L-channel only (Photoshop has all this options too).

于 2014-04-27T13:13:09.817 回答
0

作为对@Royi 的回应,2x 乘数是在此公式中假设没有钳位的结果:

USM(Original) = Original + Amount / 100 * ((Original - GB) - (1 - (Original + (1 - GB))))

不正确地忽略钳位会减少到:

USM(Original) = Original + 2 * Amount / 100 * (Original - GB) 

但是,正如您还指出的那样,(Original - GB)并且(Original + inv(GB))被限制为 [0, 1]:

USM(Original) = Original + Amount / 100 *
   (Max(0, Min(1, Original - GB)) - (1 - (Max(0, Min(1, Original + (1 - GB))))))

这正确地减少为:

USM(Original) = Original + Amount / 100 * (Original - GB)

以下是说明原因的示例:

https://legacy.imagemagick.org/discourse-server/viewtopic.php?p=133597#p133597

于 2021-05-08T21:12:27.660 回答
-1

这是我所做的代码。我正在使用此代码来实现 Unsharp Mask,它对我来说效果很好。希望它对你有用。

void USM(cv::Mat &O, int d, int amp, int threshold)
{
    cv::Mat GB;
    cv::Mat O_GB;
    cv::subtract(O, GB, O_GB);

    cv::Mat invGB = cv::Scalar(255) - GB;

    cv::add(O, invGB, invGB);

    invGB = cv::Scalar(255) - invGB;

    for (int i = 0; i < O.rows; i++)
    {
        for (int j = 0; j < O.cols; j++)
        {
            unsigned char o_rgb = O.at<unsigned char>(i, j);
            unsigned char d_rgb = O_GB.at<unsigned char>(i, j);
            unsigned char inv_rgb = invGB.at<unsigned char>(i, j);

            int newVal = o_rgb;
            if (d_rgb >= threshold)
            {
                newVal = o_rgb + (d_rgb - inv_rgb) * amp;
                if (newVal < 0)      newVal = 0;
                if (newVal > 255)    newVal = 255;
            }

            O.at<unsigned char>(i, j) = unsigned char(newVal);
        }
    }

}
于 2016-07-04T02:55:20.483 回答