OpenCV and Unsharp Masking Like Adobe Photoshop

为君一笑 提交于 2019-12-03 05:06:08

I'm trying to replicate Photoshop's Unsharp Mask as well. Let's ignore the Threshold for a second.

I will show you how to replicate Photoshop's Unsharp Mask using its Gaussian Blur.

Assuming O is the original image layer.

Create a new layer GB which is a Gaussian Blur applied on O.
Create a new layer which is O - GB (Using Apply Image).
Create a new layer by inverting GB - invGB.
Create a new layer which is O + invGB using Image Apply.
Create a new layer which is inversion of the previous layer, namely inv(O + invGB).
Create a new layer which is O + (O - GB) - inv(O + invGB).

When you do that in Photoshop you'll get a perfect reproduction of the Unsharp Mask.

If you do the math recalling that inv(L) = 1 - L you will get that the Unsharp Mask is USM(O) = 3O - 2B.

Yet when I do that directly in MATLAB I don't get Photoshop's results.

Hopefully someone will know the exact math.

Update

OK,
I figured it out.
In Photoshop USM(O) = O + (2 * (Amount / 100) * (O - GB))
Where GB is a Gaussian Blurred version of O.

Yet, in order to replicate Photoshop's results you must do the steps above and clip the result of each step into [0, 1] as done in Photoshop.

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).

Here's the code what I have done. I am using this code to implement Unsharp Mask and it is working well for me. Hope it is useful for you.

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);
        }
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!