英文:
Encoding with image/jpeg cause image saturation / wrong pixels
问题
我已经有一段时间遇到这个问题了:我正在创建一个处理图像的模块,其中一个函数是遍历图像的每个像素并反转颜色。当对.png图像进行编码时,该函数返回预期的结果,但是对于.jpeg/jpg图像,它会"饱和"。
处理.png图像的示例(正确):https://i.stack.imgur.com/0r0Go.png
处理.jpeg图像的示例(错误):https://i.stack.imgur.com/I7hNM.jpg
我进行了一些研究,找到了与我的问题最接近的一个问题,尽管它不是一个答案,这是来自Go存储库的问题:https://github.com/golang/go/issues/23936
// 反转颜色函数
func InvertColors(img image.Image) image.Image {
bounds := img.Bounds()
width := bounds.Max.X
height := bounds.Max.Y
inverted := image.NewRGBA(bounds)
for y := bounds.Min.Y; y < height; y++ {
for x := bounds.Min.X; x < width; x++ {
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8(255 - r), uint8(255 - g), uint8(255 - b), uint8(a)}
inverted.SetRGBA(x, y, c)
}
}
return inverted
}
// 主函数示例
func main() {
link := "https://i.imgur.com/n5hsdl4.jpg"
img, err := GetImageFromURL(link)
if err != nil {
panic(err)
}
buf := new(bytes.Buffer)
Encode(buf, img, "jpg")
ioutil.WriteFile("temp.jpg", buf.Bytes(), 0666)
invImg := InvertColors(img)
buf = new(bytes.Buffer)
Encode(buf, invImg, "jpg")
ioutil.WriteFile("temp2.jpg", buf.Bytes(), 0666)
}
// 从URL获取图像
func GetImageFromURL(link string) (image.Image, error) {
_, format, err := ParseURL(link)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", link, nil)
if err != nil {
return nil, err
}
// 必须进行请求。
req.Close = true
req.Header.Set("Content-Type", "image/"+format)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
img, err := Decode(bytes.NewReader(b), format)
if err != nil {
return nil, err
}
return img, nil
}
func ParseURL(link string) (u *url.URL, format string, err error) {
u, err = url.Parse(link)
if err != nil {
return u, "", err
}
format = u.Path[len(u.Path)-4:]
if strings.Contains(format, ".") {
format = strings.Split(format, ".")[1]
}
if format != "png" && format != "jpg" && format != "jpeg" {
return u, "", fmt.Errorf("不支持的格式:%s", format)
}
return u, format, nil
}
func Decode(r io.Reader, format string) (img image.Image, err error) {
if format == "png" {
img, err = png.Decode(r)
if err != nil {
return nil, err
}
} else if format == "jpg" || format == "jpeg" {
img, err = jpeg.Decode(r)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("不支持的格式:%s", format)
}
return img, nil
}
英文:
I have been having this problem for some time: I'm creating a module to process images, one of my functions is to go through each pixel of an image and invert colors. The function returns the expected results when encoding .png images, but it "saturates" .jpeg/jpg images.
Example when processing a .png image (correct): https://i.stack.imgur.com/0r0Go.png
Example when processing a .jpeg image (error): https://i.stack.imgur.com/I7hNM.jpg
I was researching and the closest I found to my problem, although it's not an answer, is this issue from the Go repository: https://github.com/golang/go/issues/23936
// InvertColors function
func InvertColors(img image.Image) image.Image {
bounds := img.Bounds()
width := bounds.Max.X
height := bounds.Max.Y
inverted := image.NewRGBA(bounds)
for y := bounds.Min.Y; y < height; y++ {
for x := bounds.Min.X; x < width; x++ {
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8(255 - r), uint8(255 - g), uint8(255 - b), uint8(a)}
inverted.SetRGBA(x, y, c)
}
}
return inverted
}
// main example
func main() {
link := "https://i.imgur.com/n5hsdl4.jpg"
img, err := GetImageFromURL(link)
if err != nil {
panic(err)
}
buf := new(bytes.Buffer)
Encode(buf, img, "jpg")
ioutil.WriteFile("temp.jpg", buf.Bytes(), 0666)
invImg := InvertColors(img)
buf = new(bytes.Buffer)
Encode(buf, invImg, "jpg")
ioutil.WriteFile("temp2.jpg", buf.Bytes(), 0666)
}
// GetImageFromURL
func GetImageFromURL(link string) (image.Image, error) {
_, format, err := ParseURL(link)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", link, nil)
if err != nil {
return nil, err
}
// Required to make a request.
req.Close = true
req.Header.Set("Content-Type", "image/"+format)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
img, err := Decode(bytes.NewReader(b), format)
if err != nil {
return nil, err
}
return img, nil
}
func ParseURL(link string) (u *url.URL, format string, err error) {
u, err = url.Parse(link)
if err != nil {
return u, "", err
}
format = u.Path[len(u.Path)-4:]
if strings.Contains(format, ".") {
format = strings.Split(format, ".")[1]
}
if format != "png" && format != "jpg" && format != "jpeg" {
return u, "", fmt.Errorf("Unsupported format: %s", format)
}
return u, format, nil
}
func Decode(r io.Reader, format string) (img image.Image, err error) {
if format == "png" {
img, err = png.Decode(r)
if err != nil {
return nil, err
}
} else if format == "jpg" || format == "jpeg" {
img, err = jpeg.Decode(r)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("Unsupported format: %s", format)
}
return img, nil
}
答案1
得分: 4
你的代码中有两个与颜色处理相关的错误(第二个可能与此无关)。
首先,RGBA()
方法返回的是 16 位的 R、G、B、A 值,但你将它们当作了 8 位的值处理。
其次,color.RGBA
值是经过 alpha 预乘的,所以 (R, G, B, A)
的反色应该是 (A-R, A-G, A-B, A)
而不是 (MAX-R, MAX-G, MAX-B, A)
。这可能与你的图片没有明显的 alpha 通道有关。
修复代码的一种方法是将这段代码替换为:
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8((a - r)>>8), uint8((a - g)>>8), uint8((a - b)>>8), uint8(a>>8)}
(注意,你可能会发现,先将图像转换为 image.NRGBA
(如果它还不是)然后迭代存储图像的(非 alpha 预乘)RGBA 通道的底层字节切片,比使用 image
和 color
包提供的更抽象的接口要快得多。)
英文:
You have two bugs in your code related to color-handling (the second of which is probably not relevant).
First, the RGBA()
method returns 16-bit R, G, B, A, but you're treating them like 8-bit values.
Second, color.RGBA
values are alpha-premultiplied, so the inverse of (R, G, B, A)
is (A-R, A-G, A-B, A
) and not (MAX-R, MAX-G, MAX-B, A)
. This is probably not relevant, because it looks like your picture does not have any significant alpha.
One way to fix to your code is to replace this:
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8(255 - r), uint8(255 - g), uint8(255 - b), uint8(a)}
with this:
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8((a - r)>>8), uint8((a - g)>>8), uint8((a - b)>>8), uint8(a>>8)}
(Note, that you may find that first converting your image to image.NRGBA
(if it's not already) and then iterating over underlying byte slice that stores the (non-alpha-premultiplied) RGBA channels for the image is much faster than using the more abstract interfaces provided by the image
and color
packages.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论