首页 > 解决方案 > PDF 图像软掩模伪影

问题描述

我正在使用 c++ 开发 PDF 生成器。

在此过程中,我遇到了图像软蒙版的一个大问题。

在此处输入图像描述

像上面一样,我在 PDF 页面的右侧放置了一个图像。粉红熊是位图图像,具有 Alpha 通道。但是,它还没有 PDF 掩码。

在我成功以正确的方式放置图像之后。我需要找到一种方法来删除图像的黑色背景。(又名阿尔法处理)

我发现有几个选项可以将其存档,如下所示。

我的应用程序有渐变图像。因此,软蒙版方法对我来说是最好的。所以,我如下实现它。

在此处输入图像描述

图像的黑色背景消失了。似乎一切都很好。

但是,当我放大结果时,我意识到存在一些伪影。

在此处输入图像描述

左侧是带有 alpha 的正确图像,右侧是 PDF 结果。

熊的轮廓周围有黑噪声。(单击图像可放大) 当我放置一个矩形图像时,我可以在边缘周围找到黑线。

似乎 RGB 通道和 A 通道不完全匹配。(似乎 A 通道大了 1~2 px)

我如下实现它。

  1. 我制作了一个 XObject (SMask)
8 0 obj
<<
/Type /XObject
/Subtype /Image
/Width 693
/Height 972
/BitsPerComponent 8
/Filter /FlateDecode
/ColorSpace /DeviceGray
/Length 137856
>>
stream
  1. 将 XObject 链接到原始图像。
7 0 obj
<<
/Type /XObject
/Subtype /Image
/Width 693
/Height 972
/BitsPerComponent 8
/Filter /FlateDecode
/ColorSpace /DeviceRGB
/SMask 8 0 R
/Length 261436
>>
stream

这就是我所做的一切。我需要做更多的事情吗?

我花了一个星期来修复它。但是,即使我在谷歌上搜索,我仍然没有任何想法。

我附上结果文件。

https://www.dropbox.com/s/09ggj28bhzi8f6e/Output.pdf?dl=0

拜托,有人给我一点建议。


**** 更新 ****

我制作了更简单的版本进行测试。

在此处输入图像描述

我做了一个位图。我在图像的左侧画了一个白色矩形,如上所示。右边是空的。

在此处输入图像描述

将位图放在 PDF 上并通过其 alpha 值应用软蒙版后,白色矩形的边缘有一条黑线。(左侧是白色矩形。右侧是透明的。)

位图的 Alpha 通道与上图完全相同。我已经在 Photoshop 上检查过了。Alpha 通道中没有任何灰色,并且每个像素都与 RGB 通道完全匹配。

在此处输入图像描述

我附上 PDF 结果。

软掩蔽之前

软掩膜后

标签: pdfartifacts

解决方案


这个问题似乎很老了,但仍然有人偶然发现它,我遇到了类似的问题,即透明图像边缘周围出现细灰线,发现问题是由于PDF 查看器的“图像平滑”造成的。要确认,您可以在 Adob​​e Acrobat Reader 中打开文档并从首选项中关闭图像平滑,线条会消失 - 但由于它依赖于查看器,因此不能依赖此解决方案。

在多次阅读 pdf 规范以确保正确拆分 alpha 并进行大量在线搜索之后,我认为问题在于透明像素实际上是“透明黑色”或 rgba(0,0,0, 0),因此观看者自然会尝试混合边缘上的颜色,从而创建一条灰线。我使用的是从浏览器生成的图像,浏览器将任何 alpha=0 的像素视为“透明黑色”。这些 GitHub 问题中提供了更多详细信息:#issue1 # issue2但提到的解决方案是将像素转换为“透明白色”rgba(255,255,255,0),如果背景和图像更改为黑色,则会导致相同的问题。

我对图像平滑/抗锯齿算法了解不多,但我将每个完全透明像素的 RGB 颜色更改为周围像素的平均值,并且伪影消失了!(将 alpha 保持在 0)运行平均功能几次会产生更平滑的结果,因为它们是透明像素,所以只要没有产生硬边缘,平滑的准确性也不那么重要。

这只是一个测试运行,可能有更好更快的图像平滑选项。imgData 是您的 RGB 像素流,而 alphaChannel 是软掩码

let iterations = 10;
  
  while (iterations > 0) {
    let p = 0,
        a = 0;
    
    for (a = 0; a < pixelCount; a++) {
      if (alphaChannel[a] !== 0) {
        p += colorCount;
        continue;
      }
      const colorSum = {r: 0, g: 0, b: 0};

      let count = 0;
      for (i = -2; i < 3; i++) {
        for (let j = -2; j < 3; j++) {
          const index = (a + i + j * this.width) * colorCount;
          if (index < 0 || index >= this.width * this.height * colorCount) {
            continue
          }
          colorSum.r += imgData[index];
          colorSum.g += imgData[index + 1];
          colorSum.b += imgData[index + 2];
          count++;
        }
      }
      imgData[p++] = colorSum.r / count;
      imgData[p++] = colorSum.g / count;
      imgData[p++] = colorSum.b / count;

    }
    iterations--;
  }

推荐阅读