go - 如何将gif分割成图片

标签 go

如何在 go 中将 gif 拆分为图像? image/gif 的 DecodeAll 返回 GIF,其中包含一个调色板数组。但不知道如何将每个调色板转换成图像?

最佳答案

请考虑以下几点:
框架可以包含透明像素或区域,一个很好的例子是 this image on wikipedia其中(我猜)每帧都有这些全色 block 之一,而帧的其余部分是透明的。

这给您带来了一个问题:特别是动画 GIF,它不使用多帧来创建真彩色静态图像,DecodeAll 返回的帧不是 例如,如果您在浏览器中打开图片,您实际看到的是什么。

您必须以与浏览器相同的方式处理图像,即将旧框架留在某种 Canvas 上并用新框架覆盖。 但是这并不总是正确的。据我所知,GIF 帧可以包含处理方法,指定您应该如何(或是否?)处理该帧。

无论如何,为了达到您的目的,在大多数情况下也适用的最简单的方法是

import (
    "errors"
    "fmt"
    "image"
    "image/draw"
    "image/gif"
    "image/png"
    "io"
    "os"
)

// Decode reads and analyzes the given reader as a GIF image
func SplitAnimatedGIF(reader io.Reader) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("Error while decoding: %s", r)
        }
    }()

    gif, err := gif.DecodeAll(reader)

    if err != nil {
        return err
    }

    imgWidth, imgHeight := getGifDimensions(gif)

    overpaintImage := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight))
    draw.Draw(overpaintImage, overpaintImage.Bounds(), gif.Image[0], image.ZP, draw.Src)

    for i, srcImg := range gif.Image {
        draw.Draw(overpaintImage, overpaintImage.Bounds(), srcImg, image.ZP, draw.Over)

        // save current frame "stack". This will overwrite an existing file with that name
        file, err := os.Create(fmt.Sprintf("%s%d%s", "<some path>", i, ".png"))
        if err != nil {
            return err
        }

        err = png.Encode(file, overpaintImage)
        if err != nil {
            return err
        }

        file.Close()
    }

    return nil
}

func getGifDimensions(gif *gif.GIF) (x, y int) {
    var lowestX int
    var lowestY int
    var highestX int
    var highestY int

    for _, img := range gif.Image {
        if img.Rect.Min.X < lowestX {
            lowestX = img.Rect.Min.X
        }
        if img.Rect.Min.Y < lowestY {
            lowestY = img.Rect.Min.Y
        }
        if img.Rect.Max.X > highestX {
            highestX = img.Rect.Max.X
        }
        if img.Rect.Max.Y > highestY {
            highestY = img.Rect.Max.Y
        }
    }

    return highestX - lowestX, highestY - lowestY
}

(未经测试,但应该可以)

请注意,gif.DecodeAll 可能并且将会频繁 崩溃,因为互联网上的许多 GIF 图像都有些损坏。您的浏览器会尝试解码它们,例如,将用黑色替换缺失的颜色。 image/gif 不会那样做,而是 panic 。这就是我们延迟恢复的原因。

此外,出于与上述类似的原因,我使用了 getGifDimensions:单帧不必是您在浏览器中看到的那样。在这种情况下,帧只比完整图像小,这就是为什么我们必须遍历所有帧并获得图像的“真实”尺寸。

如果您真的真的想做对,您可能应该阅读 GIF 规范 GIF87a , GIF89a和类似 this article 的东西这更容易理解。由此,您应该决定如何处理框架以及在重绘时如何处理透明度。

编辑:如果在线拆分一些 GIF,可以很容易地观察到前面提到的一些效果,例如 thisthis - 尝试“忽略优化”和“使用先前帧的细节重绘每一帧”来了解我的意思。

关于go - 如何将gif分割成图片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33295023/

相关文章:

go - 同时从两个 channel 消费会导致 goroutine 占用我的 RAM

json - 为什么 JSON 解析不会因传递给 Decode() 的完全不同的类型而失败?

go - 无论平台如何,如何在所有代码上运行go vet

go - golang中的可变参数函数

postgresql - pq : password authentication failed for user "user-name" while accessing postgres in vscode

go - Golang 中的时间布局

docker - 在 docker 容器中调用公共(public)地址时如何修复拨号 tcp i/o 超时?

go - 如何获取指针的地址?

go - Golang 中的安全关闭连接

go - 如何将 golang 1.7 上下文与 http.Request(用于身份验证)一起使用