¿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?
¿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?
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.
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);
}
¿Hay alguna manera de usar este código para tener un cierto retraso entre cada fotograma o establecer una frecuencia de fotogramas? – uSeRnAmEhAhAhAhAhA
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
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
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);
}
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");
}
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();
}
}
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:
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.
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
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
@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. –
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 –
¿Has probado alguna solución final? respuesta no marcada ... – Kiquenet