Go 如何将gif分割成图像
如何在go中将gif分割成图像? image/gif的DecodeAll返回gif,它包含一个调色板数组。但不知道如何将每个调色板转换为图像?是一个接口,并实现该接口,因此,例如,如果您想将GIF的每一帧保存到PNG文件中,您可以对每个图像进行编码: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_
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将当前帧与上一帧混合,对吗?谢谢您的详细回答。@BhoomtawathPlinsutdraw.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
,这可能更容易理解