英文:
Golang image/gif EncodeAll have many black dots
问题
我有许多PNG图像,想要将它们编码为GIF动画。
这些PNG图像没有任何黑点,但是GIF结果中有很多黑点。
g := new(gif.GIF)
frames := len(images)
g.Image = make([]*image.Paletted, frames)
g.Delay = make([]int, frames)
eg := errgroup.Group{}
var cl color.Palette = palette.Plan9
for k, img := range images {
img := img
k := k
eg.Go(func() error {
Paletted := image.NewPaletted(img.Bounds(), cl)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[k] = Paletted
g.Delay[k] = deply
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
var buf bytes.Buffer
err := gif.EncodeAll(&buf, g)
我的PNG图像信息:
- 文件类型:PNG
- 文件扩展名:png
- MIME类型:image/png
- 位深度:8
- 颜色类型:带有Alpha通道的RGB
- 压缩:Deflate/Inflate
- 滤波器:自适应
- 交错:非交错
- SRGB渲染:感知渲染
- Exif字节顺序:大端序(Motorola,MM)
- 色彩空间:sRGB
使用palgen.Generate(img, 256)生成的图像:
英文:
I have many png images and want encode them to a gif animation.
These png images dont have any black dots ,but the gif result have many dots.
g := new(gif.GIF)
frames := len(images)
g.Image = make([]*image.Paletted, frames)
g.Delay = make([]int, frames)
eg := errgroup.Group{}
var cl color.Palette = palette.Plan9
for k, img := range images {
img := img
k := k
eg.Go(func() error {
Paletted := image.NewPaletted(img.Bounds(), cl)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[k] = Paletted
g.Delay[k] = deply
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
var buf bytes.Buffer
err := gif.EncodeAll(&buf, g)
my png info:
> File Type : PNG
File Type Extension : png
MIME Type : image/png
Bit Depth : 8
Color Type : RGB with Alpha
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
SRGB Rendering : Perceptual
Exif Byte Order : Big-endian (Motorola, MM)
Color Space : sRGB
used palgen.Generate(img, 256):
答案1
得分: 2
GIF使用256色调色板,而PNG通常是RGBA格式,每个颜色通道至少有8位(包括透明度)。在你提供的不完整示例代码中,你使用了预定义的palette.Plan9
调色板。从原始PNG中选择至少最显著的颜色,显示为RGB颜色#f08740
。但是在Plan9
调色板中没有匹配的颜色,所以FloydSteinberg
将不得不使用Plan9
调色板中的“最近”颜色进行抖动处理。显然,这并不起作用。
你需要使用适应的调色板来避免抖动,要么完全避免,要么至少将其最小化。由于你提供的示例不完整,我不得不自己编写一个最小示例,根据给定的唯一PNG源图像创建一个自定义调色板的GIF(下次请上传单独的图像,不要将所有内容放在一个图像中,这样非常不方便)。
快速搜索发现Go模块github.com/xyproto/palgen
可以根据输入图像和指定的颜色数量创建自定义的color.Palette
调色板;这个模块似乎在积极维护,并且我在使用它时立即成功:
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
以下是一个完整的示例,根据source.png
源PNG图像生成一个适合的GIF(gif.gif
),并且没有抖动:
package main
import (
"image"
"image/draw"
"image/gif"
_ "image/png"
"os"
"github.com/xyproto/palgen"
)
func main() {
g := new(gif.GIF)
g.Image = make([]*image.Paletted, 1)
g.Delay = make([]int, 1)
f, err := os.Open("source.png")
if err != nil {
panic(err)
}
defer f.Close()
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
Paletted := image.NewPaletted(img.Bounds(), pal)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[0] = Paletted
g.Delay[0] = 100
out, err := os.Create("gif.gif")
if err != nil {
panic(err)
}
defer out.Close()
err = gif.EncodeAll(out, g)
if err != nil {
panic(err)
}
}
源图像source.png
:
最终的GIF gif.gif
:
英文:
GIF uses a 256 color palette, whereas PNG typically is RGBA with at least 8 bits per color channel (and alpha). In your incomplete example code you use the predefined palette.Plan9
color palette. Picking at least the most dominant color in the origin PNG shows it's the RGB color #f08740
. But there is no matching color in the Plan9
palette, so FloydSteinberg
will have to dither using "nearest" colors from the Plan9
palette. This obviously doesn't work well.
You need to use an adapted palette to avoid dithering either at all or at least minimize it. As you are giving a non-minimal and incomplete example, I had to roll a minimal example myself that creates a custom palette for the GIF based on the only PNG source given (and please, do upload separate images next time, don't put everything into a single image, it makes things really inconvenient).
A quick google search reveals the Go module github.com/xyproto/palgen
that does create a custom color.Palette
based on an input image and with the specified numbers of colors; this module seems to be actively maintained and I had immediate success in using it:
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
The full example that produces for me a suitable GIF (gif.gif
) without dithering, given a source PNG in source.png
:
package main
import (
"image"
"image/draw"
"image/gif"
_ "image/png"
"os"
"github.com/xyproto/palgen"
)
func main() {
g := new(gif.GIF)
g.Image = make([]*image.Paletted, 1)
g.Delay = make([]int, 1)
f, err := os.Open("source.png")
if err != nil {
panic(err)
}
defer f.Close()
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
Paletted := image.NewPaletted(img.Bounds(), pal)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[0] = Paletted
g.Delay[0] = 100
out, err := os.Create("gif.gif")
if err != nil {
panic(err)
}
defer out.Close()
err = gif.EncodeAll(out, g)
if err != nil {
panic(err)
}
}
Source image source.png
:
Final GIF gif.gif
:
答案2
得分: 0
使用:
draw.Draw(Paletted,img.Bounds(),img,image.ZP, draw.Src)
替换为:
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
英文:
Use:
draw.Draw(Paletted,img.Bounds(),img,image.ZP, draw.Src)
Replace:
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论