Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go 如何将gif分割成图像_Go - Fatal编程技术网

Go 如何将gif分割成图像

Go 如何将gif分割成图像,go,Go,如何在go中将gif分割成图像? image/gif的DecodeAll返回gif,它包含一个调色板数组。但不知道如何将每个调色板转换为图像?是一个接口,并实现该接口,因此,例如,如果您想将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_

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

是一个接口,并实现该接口,因此,例如,如果您想将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()
}

考虑以下几点:
帧可以包含透明像素或区域,一个很好的例子是(我猜)每帧有一个全彩块,其余部分是透明的

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

您必须以与浏览器相同的方式处理图像,即将旧帧保留在画布上,并用新帧覆盖但是这并不总是正确的。AFAIK,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
}
导入(
“错误”
“fmt”
“图像”
“图像/绘图”
“图像/gif”
“图像/png”
“io”
“操作系统”
)
//解码以GIF图像的形式读取和分析给定的读取器
func SplitAnimatedGIF(读卡器io.reader)(错误){
延迟函数(){
如果r:=recover();r!=nil{
err=fmt.Errorf(“解码时出错:%s”,r)
}
}()
gif,err:=gif.DecodeAll(读取器)
如果错误!=零{
返回错误
}
imgWidth,imgHeight:=getGifDimensions(gif)
过密:=image.NewRGBA(image.Rect(0,0,imgWidth,imgHeight))
draw.draw(overpaignage,overpaignage.Bounds(),gif.Image[0],Image.ZP,draw.Src)
对于i,srcImg:=范围gif.Image{
draw.draw(overpaignage,overpaignage.Bounds(),srcImg,image.ZP,draw.Over)
//保存当前帧“堆栈”。这将用该名称覆盖现有文件
文件,错误:=os.Create(fmt.Sprintf(“%s%d%s”,“i”,“.png”))
如果错误!=零{
返回错误
}
err=png.Encode(文件,过密)
如果错误!=零{
返回错误
}
file.Close()文件
}
归零
}
func getgif维度(gif*gif.gif)(x,y int){
var lowestX int
var lowestY int
var highestX int
var highestY int
对于u,img:=范围gif.Image{
如果img.Rect.Min.X<最低点X{
lowestX=img.Rect.Min.X
}
如果img.Rect.Min.Y<下限{
lowestY=img.Rect.Min.Y
}
如果img.Rect.Max.X>highestX{
最大高度X=最大垂直高度X
}
如果img.Rect.Max.Y>highestY{
最大高度=最大垂直高度
}
}
返回最高X-最低X,最高Y-最低
}
(未经测试,但应有效)

请注意,
gif.DecodeAll
可以而且会频繁地死机,因为互联网上的许多gif图像都有些破损。您的浏览器尝试解码它们,例如,将用黑色替换缺少的颜色
image/gif
不会这样做,而是会引起恐慌。这就是为什么我们
推迟
恢复
的原因

此外,我使用
getGifDimensions
的原因与上面所述的类似:单帧不一定是您在浏览器中看到的。在这种情况下,帧仅比完整图像小,这就是为什么我们必须迭代所有帧并获得图像的“真实”维度

如果你真的想把它做好,你可能应该阅读GIF规范,类似的东西更容易理解。从这一点上,您应该决定如何处理框架,以及在过度涂漆时如何处理透明度


编辑:如果您在线拆分一些GIF,可以很容易地观察到前面提到的一些效果,例如-通过“忽略优化”和“使用以前帧的详细信息重新绘制每个帧”来了解我的意思。

谢谢。我是新手,仍然不熟悉它表示接口实现的方式。@mrd0ll4r的答案是正确的。这确实会导致错误的画面。哦,是的,这是正确的。我从我不久前写的一个项目中获取了代码,所以我当时可能不知道,will fix,谢谢!draw.draw将当前帧与上一帧混合,对吗?谢谢您的详细回答。@BhoomtawathPlinsut
draw.draw
将当前帧与以前的所有帧混合,而这些帧又与以前的帧混合,因此是:)一个小提示:“for”循环中的
os.Open
调用将返回一个错误,如果文件不存在,它可能不存在。更好的方法可能是
os.OpenFile(fmt.Sprintf(“%s%d%s”,”,i,“.png”)、os.O|RDWR | os.O|u CREATE | os.O|u APPEND,0655)
@greenracoon23,谢谢,我应该知道的!我将它改为使用
os.Create
,这可能更容易理解