英文:
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("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)})
}
}
[...]
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>>8), byte(g>>8), byte(b>>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 << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = uint32(c.A)
a |= a << 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论