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)
origin png:

with num:

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
gif with black dots:

used palgen.Generate(img, 256):

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:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With