如何将gif分割成图像

时间:2015-10-23 04:43:40

标签: go

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

2 个答案:

答案 0 :(得分:3)

请考虑以下事项:
帧可以包含透明像素或区域,一个很好的例子是this image on wikipedia(我猜)每帧有一个这样的全彩色块,而帧的其余部分是透明的。

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

您必须以与浏览器相同的方式处理图像,即将旧帧留在某种画布上并用新帧覆盖。 这并非总是如此。 GIF帧可以,AFAIK,包含一种处理方法,指定你应该如何(或如果?)处理帧。

无论如何,为了达到你的观点,在大多数情况下也会起作用的最简单方法就像是

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不会这样做,而是恐慌。这就是我们defer recover

的原因

此外,我使用getGifDimensions的原因与上述类似:单帧不一定是您在浏览器中看到的。在这种情况下,帧只比整个图像小,这就是为什么我们必须遍历所有帧并获得&#34; true&#34;图像的尺寸。

如果您确实 想要做得对,那么您应该阅读GIF规范GIF87aGIF89a以及类似this article的内容更容易理解。从那以后,您应该决定如何处理框架以及在重复绘制时如何处理透明度。

编辑:如果您在线拆分某些GIF,可以轻松观察前面提到的一些效果,例如thisthis - 使用&#34;忽略优化&#34;和#34;使用前一帧中的详细信息重绘每一帧&#34;看看我的意思。

答案 1 :(得分:1)

image.Image是一个接口,*image.Paletted实现了接口,因此,例如,如果您想将GIF的每一帧保存到PNG文件中,您只需对每个图像进行编码:

for i, frame := range img.Image {
    frameFile, err := os.OpenFile(fmt.Sprintf("%d.png", i+1), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }
    err = png.Encode(frameFile, frame)
    if err != nil {
        log.Fatal(err)
    }
    // Not using defer here because we're in a loop, not a function.
    frameFile.Close()
}