使用image.Decode解码JPEG并写入PDF时颜色不正确?

huangapple go评论66阅读模式
英文:

Incorrect colors when decoding JPEG using image.Decode and writing to PDF?

问题

我正在尝试使用image.Decode()方法解码图像文件(PNG、JPG、GIF和BMP格式),以获取image.Image,然后将像素数据写入PDF流,并进行压缩。我遇到的问题是,当我解码JPEG时,生成的PDF中的颜色不正确。其他所有图像格式都按预期工作。我附上了问题的屏幕截图。

屏幕截图:
<https://i.stack.imgur.com/e3Hc8.png>

有人知道可能是什么原因导致这个问题吗?在使用image.Decode()时,处理JPEG时是否需要特殊处理?非常感谢任何关于如何解决此问题的建议!

编辑:

代码:

var iData image.Image
iFile, err := os.Open(path)
if err != nil {
  [...]
} else {
  iData, _, err = image.Decode(iFile)
}
[...]
x.Dictionary.Set("ColorSpace", "/DeviceRGB")
x.Dictionary.Set("BitsPerComponent", 8)
for j := 0; j < iData.Bounds().Dy()/pixelMul; j++ {
	for k := 0; k < iData.Bounds().Dx()/pixelMul; k++ {
		r, g, b, _ := iData.At(k*pixelMul, j*pixelMul).RGBA()
		x.Write([]byte{byte(r), byte(g), byte(b)})
	}
}
[...]

在PDF中生成的图像在直接使用jpeg.Decode时看起来是一样的。

我期望在生成的PDF中的图像看起来与原始PNG图像相似,可能会有一些质量损失。

原始PNG:<https://i.stack.imgur.com/RNkGq.png>

转换后的JPG:<https://i.stack.imgur.com/yJ69y.jpg>

其他JPEG也存在相同的问题,例如来自w3c的第一个测试JPEG <https://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/A_2_1-BF-01.htm>。

英文:

I'm trying to create a PDF by decoding image files (in PNG, JPG, GIF, and BMP format) using the image.Decode() method to get the image.Image. Then, I write the pixel data into a PDF stream, which is later compressed. The problem I'm encountering is that when I decode a JPEG, the colors are incorrect in the resulting PDF. All other image formats are working as expected. I've attached a screenshot of the issue.

Screenshot:
<https://i.stack.imgur.com/e3Hc8.png>

Does anyone know what could be causing this problem? Is there a specific way that JPEGs need to be handled differently when using image.Decode()? Any suggestions on how to fix this issue would be greatly appreciated!

Edit:

Code:

var iData image.Image
iFile, err := os.Open(path)
if err != nil {
  [...]
} else {
  iData, _, err = image.Decode(iFile)
}
[...]
x.Dictionary.Set(&quot;ColorSpace&quot;, &quot;/DeviceRGB&quot;)
x.Dictionary.Set(&quot;BitsPerComponent&quot;, 8)
for j := 0; j &lt; iData.Bounds().Dy()/pixelMul; j++ {
	for k := 0; k &lt; iData.Bounds().Dx()/pixelMul; k++ {
		r, g, b, _ := iData.At(k*pixelMul, j*pixelMul).RGBA()
		x.Write([]byte{byte(r), byte(g), byte(b)})
	}
}
[...]

The resulting image in the pdf looks the same when using the jpeg.Decode directly.

I exptect the image in the resulting pdf to look just like the original png with possibly a bit of degredation.

Original PNG: <https://i.stack.imgur.com/RNkGq.png>

Converted JPG: <https://i.stack.imgur.com/yJ69y.jpg>

Other JPEGs also have the same issue, e.g. the first test JPEG from w3c <https://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/A_2_1-BF-01.htm>

答案1

得分: 1

Color.RGBA() 返回范围在 0..0xffff 的预乘 alpha 值的颜色分量。

将这样的值转换为 byte 类型,如 byte(r),将保留其最低的 8 位,与原始值相比似乎是随机的。如果你需要一个 8 位的颜色分量,不要将其转换为 byte,而是使用高 8 位,即向右移动 8 位(或除以 256):

x.Write([]byte{byte(r>>8), byte(g>>8), byte(b>>8)})

解释为什么对于 PNG 和 GIF 仍然有效,但对于 JPEG 不起作用:

解码 PNG 和 GIF 图像可能使用的是使用 color.RGBA 颜色模型的图像模型,该模型使用 8 位值存储分量。但是它的 RGBA.RGBA() 方法通过复制原始的 8 位值将这些值转换为 16 位值:

func (c RGBA) RGBA() (r, g, b, a uint32) {
    r = uint32(c.R)
    r |= r << 8
    g = uint32(c.G)
    g |= g << 8
    b = uint32(c.B)
    b |= b << 8
    a = uint32(c.A)
    a |= a << 8
    return
}

这意味着如果你取低 8 位,你将得到与取高 8 位相同的原始值。解码 JPEG 图像可能使用的是不会复制这种 "实现行为" 的 color.YCbCr 颜色类型。

不要依赖这一点。当你需要从 16 位分量获取 8 位分量时,始终使用高 8 位。

英文:

Color.RGBA() returns the alpha-premultiplied color components in the range of 0..0xffff.

Converting such a value to byte like byte(r) will keep its lowest 8 bits which will seemingly be just random compared to the original value. You need an 8-bit color component, do not convert it to byte but use the higher 8 bits, which means shift right by 8 (or divide by 256):

x.Write([]byte{byte(r&gt;&gt;8), byte(g&gt;&gt;8), byte(b&gt;&gt;8)})

Explanation why it still worked for PNG and GIF, but not for JPEG:

Decoding PNG and GIF images likely uses an image model that uses the color.RGBA color model, which stores components using 8-bit values. But its RGBA.RGBA() method converts these values to 16-bit values by duplicating the original 8-bit values:

func (c RGBA) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R)
	r |= r &lt;&lt; 8
	g = uint32(c.G)
	g |= g &lt;&lt; 8
	b = uint32(c.B)
	b |= b &lt;&lt; 8
	a = uint32(c.A)
	a |= a &lt;&lt; 8
	return
}

Which means if you take the lower 8 bits, you get the same original value just as if you take the 8 higher bits. Decoding JPEG images will likely use the color.YCbCr color type which does not reproduce this "implementation behavior".

Do not depend on this. When you need an 8-bit component from a 16-bit component, always use the higher 8 bits.

huangapple
  • 本文由 发表于 2023年1月11日 18:08:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75081329.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定