【问题标题】:libpng returning invalid data for 16b/channel truecolour ImageMagick imageslibpng 返回 16b/通道真彩色 ImageMagick 图像的无效数据
【发布时间】:2016-04-23 22:21:11
【问题描述】:

我有一个每通道 16 位的真彩色图像(由 ImageMagick 创建),我想将其读入单独的 R、G、B 平面。问题是生成的 16 位像素值只是复制了结果中的低字节和高字节。例如,如果红色分量的近似值为 0x42,那么我的 libpng 代码实际上返回的值是 0x4242。

我的第一个问题是我不太相信输入实际上有 16 位像素。这是identify -verbose 返回的内容:

   Type: TrueColor    
   Endianess: Undefined    
   Colorspace: RGB
   Depth: 16/8-bit    
   Channel depth:
      red: 8-bit
      green: 8-bit
      blue: 8-bit    
   Channel statistics:
      Red:
       min: 0 (0)
       max: 65535 (1)
   Properties:
      PNG:IHDR.bit_depth       : 16
      PNG:IHDR.color_type      : 2

这张图片是否真的有我可以提取的 16 位数据?如果是这样,怎么做?我的libpng读取代码基本上是这样的(确认png_get_color_type返回2,png_get_bit_depth返回16后):

   png_uint_32 rowbytes = png_get_rowbytes(png, info);
   image        = (png_byte *) xmalloc(height * rowbytes);
   row_pointers = (png_bytep*) xmalloc(sizeof(png_bytep) * height);

   for(int i = 0; i < (int)height; i++)
      row_pointers[i] = image + (i*rowbytes);

   png_read_image(png, row_pointers);

我的代码将image 拆分为 RGB 平面是这样做的:

   uint16_t *src = (uint16_t*) image;   // libpng composite image
   uint16_t *rplane, *gplane, *bplane;  // separate planar channels
   ...
   for(size_t i=0; i<nrows; i++) {
      for(size_t j=0; j<ncols; j++) {
         *rplane++ = *src++;
         *gplane++ = *src++;
         *bplane++ = *src++;
      }
   }

所以这里假设 libpng 返回的 48 位像素数据是 16b R,然后是 16b B,然后是 16b G。

如果我将图像读入 Matlab,它会报告由于 ImageMagick 的限制,它只能读取 8-b 像素。 如果 Matlab 报告像素 (x,y) 的红色分量为 0x42,例如,上面的 libpng 代码报告它的值为 0x4242。然而,Matlab 和上面的 libpng 代码同意所有像素的 RGB 值,除了 Matlab 将它们全部报告为单个 8 位分量,并且 libpng 代码在每个 16 位中复制该值两次组件。

有什么想法吗?谢谢。

编辑

非详细identify 输出:

> identify in.png
in.png PNG 2776x2776 2776x2776+0+0 16-bit DirectClass 14.35MB 0.000u 0:00.000

【问题讨论】:

    标签: imagemagick libpng


    【解决方案1】:

    “识别”报告中的“深度:16/8 位”行表示图像以 16 位样本存储,但所有像素都可以使用 8 位无损失地表示。也就是说,每个像素的每个分量都有一个值,其高字节等于它的低字节(即,具有一个可以被 257 整除的值)。

    例如,对于这个 PPM 图像

    P3 2 2 65535
        0     0     0
    32896 32896 32896
    32896 32896 32896
    65535 65535 65535
    

    样本都可以被 257 整除,并且 识别 -verbose file.ppm 报告

    Depth: 16/8-bit
      Channel depth:
        gray: 8-bit
    

    但是如果你把最后一行改成“65535 65535 65534”, 然后识别 -verbose file.ppm 报告

    Depth: 16-bit
      Channel depth:
        red: 8-bit
        green: 8-bit
        blue: 16-bit
    

    要了解图像实际上是如何存储在 PNG 文件中的,您必须查看“识别”显示的 PNG:IHDR 属性。或者您可以使用“pngcheck”来获取有关 PNG 文件内容的真实报告。

    PNG specification。当从 8 位样本缩放到 16 位样本时,ImageMagick 通过简单地乘以或除以 257.0 来实现这一点,反之亦然。请参阅 ImageMagick 源代码中的“ScaleQuantumToShort()”和“ScaleShortToQuantum()”内联函数。

    【讨论】:

    • 谢谢。从那以后,我发现图像实际上是创建为 8 位,然后按照您描述的方式扩展到 16 位,所以我看到的是真实数据。这显然是将 8 位扩展到 16 位的标准方法。不过,仍在尝试找到有关为什么这是一件好事的参考。
    【解决方案2】:

    如果我制作一张 1000x1000 的图像,其中充满了难以压缩的随机数据,那么它的大小为 5.7MB(正如您对 100 万像素的 16 位红色、16 位绿色和 16 位蓝色所期望的那样)并根据identify 显示为 16 位:

    convert -size 1000x1000! xc:gray +noise random image.png
    
    ls -lhrt
    -rw-r--r--      1 mark  staff   5.7M 18 Jan 16:59 image.png
    
    identify image.png
    image.png PNG 1000x1000 1000x1000+0+0 16-bit sRGB 6.011MB 0.000u 0:00.000
    

    如果我现在做同样的事情,但使用 8 位像素:

    convert -size 1000x1000! xc:gray +noise random -depth 8 image.png
    

    它在identify 中显示为 8 位,占用空间的一半:

    identify image.png
    image.png PNG 1000x1000 1000x1000+0+0 8-bit sRGB 3.006MB 0.000u 0:00.000
    
    ls -lhrt
    -rw-r--r--      1 mark  staff   2.9M 18 Jan 17:01 image.png
    

    所以我推断identify 说的是实话,我很确定你的图像实际上不是 16 位的。您是如何创建它的?

    根据 Glenn 的回答,我想一种解决方法可能是将图像的左上角或右下角像素设置为所有三个通道中的素数,例如 65,521,这应该有效地防止任何图像存储在单字节!并且在后续处理中忽略该像素,或者如果您迫切需要它的值,请添加额外的一行虚拟数据。

    【讨论】:

    • 很遗憾,我没有创建它 - 我刚刚获得了一张测试图像,而作者已经不在了。 identify 确实说它来自 ImageMagick。我已将单行标识输出添加到我的 Q 中。
    • 如何复制我的第一个 convert 命令并制作一个图像并查看它使用您的程序创建的正确的 16 位图像?
    • 或者使用convert -size 1000x1000 gradient:red-blue image.png制作一个漂亮的平滑红蓝16位渐变
    • 好奇 - 渐变确实显示了有效的 16 位像素值,没有绿色。所以,基本上,您认为我的代码正在运行,而我的原始输入图像确实包含重复数据?
    • 很难说。尝试将图像转储到未压缩的二进制文件中,您可以使用xxd 或任何十六进制编辑器轻松查看。使用convert yourImage.png RGB:image.bin。然后就可以看到未压缩的大小和所有的像素值了。
    猜你喜欢
    • 2016-07-24
    • 2016-08-17
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    相关资源
    最近更新 更多