【问题标题】:OpenCV and Unsharp Masking Like Adobe PhotoshopOpenCV 和像 Adob​​e Photoshop 一样的 Unsharp Masking
【发布时间】:2012-10-29 02:21:37
【问题描述】:

我正在尝试像在 Adob​​e Photoshop 中那样实现非锐化蒙版。我在互联网上收集了很多信息,但我不确定我是否遗漏了什么。代码如下:

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

目前我正在使用灰度图像进行测试。如果我在 Photoshop 中尝试完全相同的参数,我会得到更好的结果。我自己的代码会导致图像嘈杂。我做错了什么。

第二个问题是,如何在 RGB 图像上应用非锐化遮罩?我是否必须对 3 个通道中的每一个通道进行锐化遮罩,还是在另一个颜色空间中会更好?这些事情是如何在 Photoshop 中完成的?

感谢您的帮助!

【问题讨论】:

    标签: c++ opencv image-processing reverse-engineering photoshop


    【解决方案1】:

    我也在尝试复制 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 * (数量 / 100) * (O - GB))
    其中 GB 是 O 的高斯模糊版本。

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

    【讨论】:

    • 你是如何在这个公式中找到神奇的“2 *”乘数的?我在 pixastic 和其他来源中看到了相同的数学,但没有 2x 乘法(尽管它们没有假装等于 Photoshop)
    • 通过了解 Photoshop 的功能。
    • 你能看看这个演示nodeca.github.io/pica/demo 吗?我已经实现了你的公式。它应用于亮度 (0.299*R + 0.587*G + 0.114*B)。然后使用找到的亮度差异来缩放 RBG 通道。可以吗,那么大的“锐化量”会导致颜色漂移?
    • @Vitaly,我不确定我得到你了。无论如何,Photoshop 将其应用于所有 RGB 通道。如果你只想要亮度,你需要通过转换来做到这一点。
    • @Vitaly,正如我所说,默认情况下,它可以独立地在所有频道上运行。如果您只想将它​​应用到亮度上,您可以使用混合模式或转到 LAB,选择 L 通道,应用并转换回来。同样,它在输入图像的所有通道上运行。
    【解决方案2】:

    根据文档:

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

    第四个参数不是“半径”而是“sigma”——高斯核标准差。半径相当“ksize”。无论如何 Photoshop 不是开源的,因此我们不能确定它们使用与 OpenCV 相同的方式从 sigma 计算半径。

    频道

    是的,您应该对任何或所有通道应用锐利,这取决于您的目的。当然,您可以使用任何空间:如果您只想要锐利的亮度分量并且不想增加色彩噪点,您可以将其转换为 HSL 或实验室空间和仅锐利的 L 通道(Photoshop 也有所有这些选项)。

    【讨论】:

    • 我认为您的意思是转换为 HSV,而不是 HSL(如果您只想提高亮度)。 V是亮度。 L是亮度。锐化 V 可避免颜色偏移。
    【解决方案3】:

    作为对@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

    【讨论】:

      【解决方案4】:

      这是我所做的代码。 我正在使用此代码来实现 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);
              }
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-12
        • 2014-01-07
        • 1970-01-01
        • 1970-01-01
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多