猿问

如何将gif分割成图像

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


ibeautiful
浏览 187回答 2
2回答

慕无忌1623718

考虑以下事项:帧可以包含透明像素或区域,一个很好的例子是维基百科上的这张图像(我猜)每帧都有这些全色块之一,其余的帧是透明的。这给您带来了一个问题:特别是对于不使用多个帧来创建真彩色静态图像的动画 GIF,DecodeAll返回的帧不是您实际看到的帧,例如,如果您在浏览器中打开图像。您必须以与浏览器相同的方式处理图像,即将旧框架保留在某种画布上并用新框架覆盖。但此并非总是如此。GIF 帧可以,AFAIK,包含处理方法,指定您应该如何(或如果?)处理帧。无论如何,为了达到您的目的,在大多数情况下也适用的最简单方法是import (&nbsp; &nbsp; "errors"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "image"&nbsp; &nbsp; "image/draw"&nbsp; &nbsp; "image/gif"&nbsp; &nbsp; "image/png"&nbsp; &nbsp; "io"&nbsp; &nbsp; "os")// Decode reads and analyzes the given reader as a GIF imagefunc SplitAnimatedGIF(reader io.Reader) (err error) {&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; if r := recover(); r != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err = fmt.Errorf("Error while decoding: %s", r)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; gif, err := gif.DecodeAll(reader)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; imgWidth, imgHeight := getGifDimensions(gif)&nbsp; &nbsp; overpaintImage := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight))&nbsp; &nbsp; draw.Draw(overpaintImage, overpaintImage.Bounds(), gif.Image[0], image.ZP, draw.Src)&nbsp; &nbsp; for i, srcImg := range gif.Image {&nbsp; &nbsp; &nbsp; &nbsp; draw.Draw(overpaintImage, overpaintImage.Bounds(), srcImg, image.ZP, draw.Over)&nbsp; &nbsp; &nbsp; &nbsp; // save current frame "stack". This will overwrite an existing file with that name&nbsp; &nbsp; &nbsp; &nbsp; file, err := os.Create(fmt.Sprintf("%s%d%s", "<some path>", i, ".png"))&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; err = png.Encode(file, overpaintImage)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; file.Close()&nbsp; &nbsp; }&nbsp; &nbsp; return nil}func getGifDimensions(gif *gif.GIF) (x, y int) {&nbsp; &nbsp; var lowestX int&nbsp; &nbsp; var lowestY int&nbsp; &nbsp; var highestX int&nbsp; &nbsp; var highestY int&nbsp; &nbsp; for _, img := range gif.Image {&nbsp; &nbsp; &nbsp; &nbsp; if img.Rect.Min.X < lowestX {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lowestX = img.Rect.Min.X&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if img.Rect.Min.Y < lowestY {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lowestY = img.Rect.Min.Y&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if img.Rect.Max.X > highestX {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; highestX = img.Rect.Max.X&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if img.Rect.Max.Y > highestY {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; highestY = img.Rect.Max.Y&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return highestX - lowestX, highestY - lowestY}(未经测试,但应该可以工作)请注意,gif.DecodeAll可能并且会经常发生恐慌,因为互联网上的许多 GIF 图像都有些损坏。您的浏览器会尝试解码它们,例如,将用黑色替换缺失的颜色。image/gif不会那样做,而是恐慌。这就是为什么我们defer将recover.此外,我使用 的getGifDimensions原因与上述类似:单帧不必是您在浏览器中看到的内容。在这种情况下,帧仅比完整图像小,这就是为什么我们必须迭代所有帧并获得图像的“真实”尺寸。如果你真的真的想这样做是正确的,你应该阅读GIF规范GIF87a,GIF89a的让人觉得这篇文章是一个更容易理解。从中,您应该决定如何处理框架以及在重绘时如何处理透明度。编辑:如果您在线拆分一些 GIF,则可以轻松观察到前面提到的一些效果,例如这个或这个- 玩弄“忽略优化”和“使用前一帧的细节重新绘制每一帧”以了解我的意思。

喵喵时光机

image.Image是一个接口,并*image.Paletted实现了该接口,例如,如果您想将 GIF 的每一帧保存到 PNG 文件中,您只需对每个图像进行编码:for i, frame := range img.Image {&nbsp; &nbsp; frameFile, err := os.OpenFile(fmt.Sprintf("%d.png", i+1), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; err = png.Encode(frameFile, frame)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; // Not using defer here because we're in a loop, not a function.&nbsp; &nbsp; frameFile.Close()}
随时随地看视频慕课网APP

相关分类

Go
我要回答