Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang image/gif EncodeAll have many black dots

Tags:

image

go

gif

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

with num: enter image description here

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

used palgen.Generate(img, 256):

palgen.Generate(img, 256)

like image 995
Jishi Avatar asked Nov 03 '25 22:11

Jishi


1 Answers

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:

source.png

Final GIF gif.gif:

gif.gif

like image 99
TheDiveO Avatar answered Nov 06 '25 19:11

TheDiveO



Donate For Us

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