我在西湖1 2015-10-23 04:43
浏览 175
已采纳

如何将gif分割成图片

How can I split gif into images in go? image/gif's DecodeAll return GIF, which contains an array of palette. But don't know how to convert each palette into an image?

  • 写回答

2条回答 默认 最新

  • drq22639 2015-10-23 07:04
    关注

    Consider the following:
    Frames can contain transparent pixels or areas, a good example is this image on wikipedia which (I guess) has one of these full-color blocks per frame and the rest of the frame transparent.

    This introduces a problem for you: Especially with animated GIFs, that do not use multiple frames to create a true-colored static image, the frames that DecodeAll returns are not what you actually see if you, for example, open the image in your browser.

    You'll have to process the image in the same way your browser would, i.e. leave the old frames on a kind of canvas and overpaint with the new frame. BUT this is not always true. GIF frames can, AFAIK, contain a disposal method, specifying how (or if?) you should dispose of the frame.

    Anyways, to get to your point, the most simple approach that will also work in most cases is something like

    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
    }
    

    (untested, but should work)

    Note that gif.DecodeAll can and will panic frequently, because a lot of the GIF images on the internet are somewhat broken. Your browser tries to decode them and will, for example, replace missing colors with black. image/gif will not do that, but panic instead. That's why we defer the recover.

    Also, I used the getGifDimensions for a similar reason as stated above: single frames need not be what you see in your browser. In this case, the frames are just smaller than the complete image, that's why we have to iterate over all frames and get the "true" dimensions of the image.

    If you really really want to do it right, you should probably read the GIF spec GIF87a, GIF89a and something like this article which is a lot easier to understand. From that, you should decide how to dispose of the frames and what to do with transparency while overpainting.

    EDIT: Some of the effects mentioned earlier can be observed easily if you split some GIFs online, for example this or this - play around with "Ignore optimizations" and "Redraw every frame with details from previous frames" to see what I mean.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘