PDF 图像软蒙版伪影

PDF image soft mask artifacts

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

在过程中,我遇到了图像软掩模的大问题。

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

在我成功将图像正确放置之后。我需要找到一种方法来删除图像的黑色背景。 (a.k.a alpha 处理)

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

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

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

但是,当我放大结果时,我发现有一些瑕疵。

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

熊的轮廓周围有黑色噪点。(单击图像可显示大图)当我放置矩形图像时,我发现边缘周围有黑线。

好像RGB通道和A通道不完全匹配(好像A通道大了1~2px)

我是这样实现的。

  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

我就是这么做的。我需要做更多的事情吗?

我花了一周的时间来修复它。但是,即使我在 google.

上搜索,我仍然没有任何想法

我附上结果文件。

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

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


**** 更新 ****

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

我制作了位图。然后我在图像的左侧绘制了一个白色矩形,如上所示。右边是空的。

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

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

我附上 PDF 结果。

Before soft masking

After soft masking

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

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

我对 image-smoothing/anti-aliasing 算法了解不多,但我 将每个完全透明像素的 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--;
  }