2009-07-28 14 views
45

¿Alguien sabe cómo crear un gif animado usando C#? Lo ideal sería tener cierto control sobre la reducción de color utilizada.cómo crear un gif animado en .net

¿Está utilizando imagemagick (como proceso externo iniciado) la mejor opción?

+0

imagemagick parece tener las mejores opciones (difuminado, reducción de color, etc.). La lib sugerida y otros métodos de creación son de muy mala calidad –

+0

¿Has probado alguna solución final? respuesta no marcada ... – Kiquenet

Respuesta

16

Si llamar imagemagick es la mejor opción es difícil de entender sin conocer los parámetros de calidad que son importantes. Algunas otras opciones serían:

estos tienen la ventaja de que no tiene una dependencia de una tercera parte de la biblioteca, que puede o no estará disponible en todos los sistemas que ejecuten su código.

Este article en MS Support explica cómo guardar un gif con una tabla de colores personalizada (esto requiere una confianza total). Un gif animado es solo un conjunto de gifs para cada imagen con información adicional en el encabezado. Por lo tanto, la combinación de estos dos artículos debería brindarle lo que necesita.

23

Hay una clase .NET incorporada que codificará los archivos GIF. GifBitmapEncode MSDN

System.Windows.Media.Imaging.GifBitmapEncoder gEnc = new GifBitmapEncoder(); 

foreach (System.Drawing.Bitmap bmpImage in images) 
{ 
    var bmp = bmpImage.GetHbitmap(); 
    var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
     bmp, 
     IntPtr.Zero, 
     Int32Rect.Empty, 
     BitmapSizeOptions.FromEmptyOptions()); 
    gEnc.Frames.Add(BitmapFrame.Create(src)); 
    DeleteObject(bmp); // recommended, handle memory leak 
} 
using(FileStream fs = new FileStream(path, FileMode.Create)) 
{ 
    gEnc.Save(fs); 
} 
+3

¿Hay alguna manera de usar este código para tener un cierto retraso entre cada fotograma o establecer una frecuencia de fotogramas? – uSeRnAmEhAhAhAhAhA

+7

Um. Este código falla masivamente. Todo lo que hace es colocar un grupo de imágenes una encima de la otra y las guarda como una sola imagen. – uSeRnAmEhAhAhAhAhA

+0

Algo útil a tener en cuenta en la solución anterior es que necesita manejar la posible pérdida de memoria causada por el objeto GDI Bitmap - este es el 'bmpImage.GetHbitmap()' en el código dado. Como [notas de MSDN, esto debe borrarse manualmente] (https://msdn.microsoft.com/en-us/library/1dz311e4.aspx). [Aquí] (https://msdn.microsoft.com/en-us/library/1dz311e4.aspx#Anchor_3) es un ejemplo de código sobre cómo hacerlo para evitar que el objeto Bitmap GDI ocupe demasiada memoria. – vburca

1

Para utilizar el ejemplo de una aplicación de Windows Forms, agregue referencias a estos conjuntos:

C: \ Archivos de programa \ Referencia Asambleas \ Microsoft \ Framework.NETFramework \ v4.0 \ PresentationCore .dll C: \ Archivos de programa \ Conjuntos de referencia \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xaml.dll C: \ Archivos de programa \ Conjuntos de referencia \ Microsoft \ Framework.NETFramework \ v4.0 \ WindowsBase.dll

Luego

  • Int32Rect está en el espacio de nombres System.Windows

  • BitmapSizeOptions está en el espacio de nombres System.Windows.Media.Imaging

  • BitmapFrame está en el espacio de nombres System.Windows.Media.Imaging

Además, no se olvide de cerrar el flujo de archivos (algo así):

using(FileStream targetFile = new FileStream(path, FileMode.Create)) 
{ 
    gEnc.Save(targetFile); 
} 
17

También podría considerar el uso de la biblioteca ImageMagick.

Hay dos.envolturas netos para la biblioteca que aparece al http://www.imagemagick.org/script/api.php

He aquí un ejemplo de cómo hacerlo utilizando el Magick.net wrapper:

using (MagickImageCollection collection = new MagickImageCollection()) 
{ 
    // Add first image and set the animation delay to 100ms 
    collection.Add("Snakeware.png"); 
    collection[0].AnimationDelay = 100; 

    // Add second image, set the animation delay to 100ms and flip the image 
    collection.Add("Snakeware.png"); 
    collection[1].AnimationDelay = 100; 
    collection[1].Flip(); 

    // Optionally reduce colors 
    QuantizeSettings settings = new QuantizeSettings(); 
    settings.Colors = 256; 
    collection.Quantize(settings); 

    // Optionally optimize the images (images should have the same size). 
    collection.Optimize(); 

    // Save gif 
    collection.Write("Snakeware.Animated.gif"); 
} 
19

Este código GIF animados Creater de https://github.com/DataDink/Bumpkit puede establecer Demora de fotogramas foreach:

Usos .Net estándar Gif Encoding y agrega encabezados de animación.

EDIT: Hizo el código similar a un escritor de archivos típico.

using System; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.IO; 
using System.Threading.Tasks; 

/// <summary> 
/// Creates a GIF using .Net GIF encoding and additional animation headers. 
/// </summary> 
public class GifWriter : IDisposable 
{ 
    #region Fields 
    const long SourceGlobalColorInfoPosition = 10, 
     SourceImageBlockPosition = 789; 

    readonly BinaryWriter _writer; 
    bool _firstFrame = true; 
    readonly object _syncLock = new object(); 
    #endregion 

    /// <summary> 
    /// Creates a new instance of GifWriter. 
    /// </summary> 
    /// <param name="OutStream">The <see cref="Stream"/> to output the Gif to.</param> 
    /// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000/DefaultFrameDelay.</param> 
    /// <param name="Repeat">No of times the Gif should repeat... -1 to repeat indefinitely.</param> 
    public GifWriter(Stream OutStream, int DefaultFrameDelay = 500, int Repeat = -1) 
    { 
     if (OutStream == null) 
      throw new ArgumentNullException(nameof(OutStream)); 

     if (DefaultFrameDelay <= 0) 
      throw new ArgumentOutOfRangeException(nameof(DefaultFrameDelay)); 

     if (Repeat < -1) 
      throw new ArgumentOutOfRangeException(nameof(Repeat)); 

     _writer = new BinaryWriter(OutStream); 
     this.DefaultFrameDelay = DefaultFrameDelay; 
     this.Repeat = Repeat; 
    } 

    /// <summary> 
    /// Creates a new instance of GifWriter. 
    /// </summary> 
    /// <param name="FileName">The path to the file to output the Gif to.</param> 
    /// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000/DefaultFrameDelay.</param> 
    /// <param name="Repeat">No of times the Gif should repeat... -1 to repeat indefinitely.</param> 
    public GifWriter(string FileName, int DefaultFrameDelay = 500, int Repeat = -1) 
     : this(new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), DefaultFrameDelay, Repeat) { } 

    #region Properties 
    /// <summary> 
    /// Gets or Sets the Default Width of a Frame. Used when unspecified. 
    /// </summary> 
    public int DefaultWidth { get; set; } 

    /// <summary> 
    /// Gets or Sets the Default Height of a Frame. Used when unspecified. 
    /// </summary> 
    public int DefaultHeight { get; set; } 

    /// <summary> 
    /// Gets or Sets the Default Delay in Milliseconds. 
    /// </summary> 
    public int DefaultFrameDelay { get; set; } 

    /// <summary> 
    /// The Number of Times the Animation must repeat. 
    /// -1 indicates no repeat. 0 indicates repeat indefinitely 
    /// </summary> 
    public int Repeat { get; } 
    #endregion 

    /// <summary> 
    /// Adds a frame to this animation. 
    /// </summary> 
    /// <param name="Image">The image to add</param> 
    /// <param name="Delay">Delay in Milliseconds between this and last frame... 0 = <see cref="DefaultFrameDelay"/></param> 
    public void WriteFrame(Image Image, int Delay = 0) 
    { 
     lock (_syncLock) 
      using (var gifStream = new MemoryStream()) 
      { 
       Image.Save(gifStream, ImageFormat.Gif); 

       // Steal the global color table info 
       if (_firstFrame) 
        InitHeader(gifStream, _writer, Image.Width, Image.Height); 

       WriteGraphicControlBlock(gifStream, _writer, Delay == 0 ? DefaultFrameDelay : Delay); 
       WriteImageBlock(gifStream, _writer, !_firstFrame, 0, 0, Image.Width, Image.Height); 
      } 

     if (_firstFrame) 
      _firstFrame = false; 
    } 

    #region Write 
    void InitHeader(Stream SourceGif, BinaryWriter Writer, int Width, int Height) 
    { 
     // File Header 
     Writer.Write("GIF".ToCharArray()); // File type 
     Writer.Write("89a".ToCharArray()); // File Version 

     Writer.Write((short)(DefaultWidth == 0 ? Width : DefaultWidth)); // Initial Logical Width 
     Writer.Write((short)(DefaultHeight == 0 ? Height : DefaultHeight)); // Initial Logical Height 

     SourceGif.Position = SourceGlobalColorInfoPosition; 
     Writer.Write((byte)SourceGif.ReadByte()); // Global Color Table Info 
     Writer.Write((byte)0); // Background Color Index 
     Writer.Write((byte)0); // Pixel aspect ratio 
     WriteColorTable(SourceGif, Writer); 

     // App Extension Header for Repeating 
     if (Repeat == -1) 
      return; 

     Writer.Write(unchecked((short)0xff21)); // Application Extension Block Identifier 
     Writer.Write((byte)0x0b); // Application Block Size 
     Writer.Write("NETSCAPE2.0".ToCharArray()); // Application Identifier 
     Writer.Write((byte)3); // Application block length 
     Writer.Write((byte)1); 
     Writer.Write((short)Repeat); // Repeat count for images. 
     Writer.Write((byte)0); // terminator 
    } 

    static void WriteColorTable(Stream SourceGif, BinaryWriter Writer) 
    { 
     SourceGif.Position = 13; // Locating the image color table 
     var colorTable = new byte[768]; 
     SourceGif.Read(colorTable, 0, colorTable.Length); 
     Writer.Write(colorTable, 0, colorTable.Length); 
    } 

    static void WriteGraphicControlBlock(Stream SourceGif, BinaryWriter Writer, int FrameDelay) 
    { 
     SourceGif.Position = 781; // Locating the source GCE 
     var blockhead = new byte[8]; 
     SourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE 

     Writer.Write(unchecked((short)0xf921)); // Identifier 
     Writer.Write((byte)0x04); // Block Size 
     Writer.Write((byte)(blockhead[3] & 0xf7 | 0x08)); // Setting disposal flag 
     Writer.Write((short)(FrameDelay/10)); // Setting frame delay 
     Writer.Write(blockhead[6]); // Transparent color index 
     Writer.Write((byte)0); // Terminator 
    } 

    static void WriteImageBlock(Stream SourceGif, BinaryWriter Writer, bool IncludeColorTable, int X, int Y, int Width, int Height) 
    { 
     SourceGif.Position = SourceImageBlockPosition; // Locating the image block 
     var header = new byte[11]; 
     SourceGif.Read(header, 0, header.Length); 
     Writer.Write(header[0]); // Separator 
     Writer.Write((short)X); // Position X 
     Writer.Write((short)Y); // Position Y 
     Writer.Write((short)Width); // Width 
     Writer.Write((short)Height); // Height 

     if (IncludeColorTable) // If first frame, use global color table - else use local 
     { 
      SourceGif.Position = SourceGlobalColorInfoPosition; 
      Writer.Write((byte)(SourceGif.ReadByte() & 0x3f | 0x80)); // Enabling local color table 
      WriteColorTable(SourceGif, Writer); 
     } 
     else Writer.Write((byte)(header[9] & 0x07 | 0x07)); // Disabling local color table 

     Writer.Write(header[10]); // LZW Min Code Size 

     // Read/Write image data 
     SourceGif.Position = SourceImageBlockPosition + header.Length; 

     var dataLength = SourceGif.ReadByte(); 
     while (dataLength > 0) 
     { 
      var imgData = new byte[dataLength]; 
      SourceGif.Read(imgData, 0, dataLength); 

      Writer.Write((byte)dataLength); 
      Writer.Write(imgData, 0, dataLength); 
      dataLength = SourceGif.ReadByte(); 
     } 

     Writer.Write((byte)0); // Terminator 
    } 
    #endregion 

    /// <summary> 
    /// Frees all resources used by this object. 
    /// </summary> 
    public void Dispose() 
    { 
     // Complete File 
     _writer.Write((byte)0x3b); // File Trailer 

     _writer.BaseStream.Dispose(); 
     _writer.Dispose(); 
    } 
} 
0

me di cuenta de que una más gran alternativa a ImageMagic y NGif no aparece en respuesta.

FFMpeg se puede utilizar para crear GIF animados de:

  • secuencia de imágenes (archivos)
  • vídeo existente (por ejemplo, MP4 o AVI)
  • de objetos C# de mapa de bits, proporcionando datos de entrada como "ravvideo" a través de la entrada estándar (sin el uso de archivos temporales)

puede comenzar ffmpeg.exe directamente desde el código C# (con System.Diagnostics.Process) o utilizar uno de los existin g .NET envoltorios ffmpeg:

var ffmpeg = new NReco.VideoConverter.FFMpegConverter(); 
ffmpeg.ConvertMedia("your_clip.mp4", null, "result.gif", null, new ConvertSettings()); 

(este ejemplo de código se utiliza libre NReco VideoConverter - Soy autor de este componente, no dude en hacer cualquier pregunta sobre su uso).

El tamaño GIF puede reducirse fácilmente disminuyendo la velocidad de fotogramas y/o el tamaño de fotograma. También es posible obtener GIF animados de aspecto fino con un enfoque de 2 pasos que genera la paleta GIF óptima.

+0

Oye, estaba buscando un código para convertir varias imágenes a un formato MP4 (h.264) con NReco. No pude encontrar ningún ejemplo de trabajo por ahí. ¿Podrías por favor ayudarme con esto? ¡Gracias! – Moji

+0

Hola, ¿sabes cómo puedo combinar imágenes con un video? La fusión de videos funciona bien. Pero cuando intento fusionar e imagen entre dos videos, no funciona con nreco. – Kanishka

+0

@Kanishka si sabe cómo hacerlo desde la línea de comandos con ffmpeg.exe, también se puede hacer con el envoltorio de VideoConverter. –