libpng 为 16b/通道 truecolour ImageMagick 图像返回无效数据
libpng returning invalid data for 16b/channel truecolour ImageMagick images
我有一个每通道 16 位的真彩色图像(由 ImageMagick 创建),我想将其读入单独的 R、G、B 平面。问题在于生成的 16 位像素值简单地复制了结果中的低字节和高字节。如果红色分量的近似值为0x42,例如我的libpng代码实际上returns一个值为0x4242.
我的第一个问题是我不太相信输入实际上有 16 位像素。这就是 identify -verbose
returns:
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
returns 2, and png_get_bit_depth
returns 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
如果我制作一张 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 位,占 space 的一半:
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 的回答,我想一个解决方法可能是在所有三个通道中将图像的 top-left 或右下角像素设置为质数,例如 65,521,这应该可以有效防止任何图像可以存储在一个字节中!并在后续处理中忽略该像素,或者如果您迫切需要它的值,请添加一行额外的虚拟数据。
"identify" 报告中的 "Depth: 16/8-bit" 行表示图像以 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 文件中的,您必须查看 "identify" 显示的 PNG:IHDR 属性。或者您可以使用 "pngcheck" 获取有关 PNG 文件内容的真实报告。
样本缩放的解释在
PNG specification。当从 8 位样本缩放到 16 位样本时,ImageMagick 通过简单地乘以或除以 257.0 来实现这一点,反之亦然。请参阅 ImageMagick 源代码中的 "ScaleQuantumToShort()" 和 "ScaleShortToQuantum()" 内联函数。
我有一个每通道 16 位的真彩色图像(由 ImageMagick 创建),我想将其读入单独的 R、G、B 平面。问题在于生成的 16 位像素值简单地复制了结果中的低字节和高字节。如果红色分量的近似值为0x42,例如我的libpng代码实际上returns一个值为0x4242.
我的第一个问题是我不太相信输入实际上有 16 位像素。这就是 identify -verbose
returns:
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
returns 2, and png_get_bit_depth
returns 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
如果我制作一张 1000x1000 的图像,其中充满难以压缩的随机数据,它的大小为 5.7MB(正如您对 100 万像素的 16 位红色、16 位绿色和 16 位蓝色)并根据 identify
:
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 位,占 space 的一半:
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 的回答,我想一个解决方法可能是在所有三个通道中将图像的 top-left 或右下角像素设置为质数,例如 65,521,这应该可以有效防止任何图像可以存储在一个字节中!并在后续处理中忽略该像素,或者如果您迫切需要它的值,请添加一行额外的虚拟数据。
"identify" 报告中的 "Depth: 16/8-bit" 行表示图像以 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 文件中的,您必须查看 "identify" 显示的 PNG:IHDR 属性。或者您可以使用 "pngcheck" 获取有关 PNG 文件内容的真实报告。
样本缩放的解释在 PNG specification。当从 8 位样本缩放到 16 位样本时,ImageMagick 通过简单地乘以或除以 257.0 来实现这一点,反之亦然。请参阅 ImageMagick 源代码中的 "ScaleQuantumToShort()" 和 "ScaleShortToQuantum()" 内联函数。