2009-07-28 6 views
45

誰でもC#を使ってアニメーションGIFを作成する方法を知っていますか? 理想的には、使用されている色の減色を何らかの形で制御します。.netでアニメーションgifを作成する方法

imagemagick(外部開始プロセスとして)を使用するのが最適ですか?

+0

imagemagickにはまだ最適なオプション(ディザリング、カラーリダクションなど)があるようです。提案されたlibやその他の作成方法はかなり劣っています –

+0

あなたは最終的な解決策を試しましたか?答えが表示されていません... – Kiquenet

答えて

16

imagemagickを呼び出すことが最善の選択であるかどうかは、重要な品質パラメータを知らなくても、一見難しいことです。いくつかの他のオプションは次のようになります。これらはあなたがかもしれないまたはかもしれない第三、一部のライブラリに依存していないという利点がある

をCodePlexの上

  • Rick van den Bosch's code archive.org mirror
  • NGif記事コードを実行しているすべてのシステムで利用できるわけではありません。

    MSサポートのarticleでは、カスタムカラーテーブルを使用してgifを保存する方法について説明しています(完全信頼が必要です)。アニメーションGIFは、ヘッダーにいくつかの追加情報を加えた各画像の単なるGIFのセットです。したがって、これら2つの記事を組み合わせることで、必要なものを得ることができます。

23

GIFファイルをエンコードする.NETクラスが組み込まれています。

します。C:\ Program Files \リファレンスアセンブリ\マイクロソフト\ Framework.NETFramework \ v4.0の\ PresentationCore Windowsからのサンプルを使用するには 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

このコードを使用して各フレーム間に一定の遅延を持たせる方法や、フレームレートを設定する方法はありますか? – uSeRnAmEhAhAhAhAhA

+7

Um。このコードは大量に失敗します。それは、画像の束を互いに重ね合わせて一つの画像として保存することだけです。 – uSeRnAmEhAhAhAhAhA

+0

上記のソリューションでは、GDI Bitmapオブジェクトによって引き起こされる可能性のあるメモリリークを処理する必要があります。これは、指定されたコードの 'bmpImage.GetHbitmap()'です。 [MSDNのメモとして、これは手動で削除する必要があります](https://msdn.microsoft.com/en-us/library/1dz311e4.aspx)。 [Here](https://msdn.microsoft.com/en-us/library/1dz311e4.aspx#Anchor_3)は、GDI Bitmapオブジェクトがあまりにも多くのメモリを占有しないようにするためのコード例です。 – vburca

1

は、これらのアセンブリへの参照を追加し、アプリをフォーム.dll C:¥Program Files¥Reference Assemblies¥Microsoft¥Framework.NETFramework¥v4.0¥System.Xaml.dll C:¥Program Files¥Referenceアセンブリ言語¥Microsoft¥Framework.NETFramework¥v4.0¥WindowsBase.dll

次に

  • Int32Rectは

  • BitmapSizeOptionsが

  • BitmapFrameがSystem.Windows.Media.Imaging名前空間

    にあるSystem.Windows.Media.Imaging名前空間にあるSystem.Windows名前空間であります

また、ファイルストリームをクローズすることを忘れないでください(このようなもの):

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

また、ImageMagickライブラリの使用を検討することもできます。

2つあります。ここでhttp://www.imagemagick.org/script/api.php

に掲載されているライブラリの純ラッパーは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

https://github.com/DataDink/BumpkitからこれのGIFアニメーションクリエーターコードが遅延foreachのフレームを設定することができます。

使用方法を.Net標準GIFエンコーディングとアニメーションヘッダーを追加します。

EDIT:一般的なファイルライターに似たコードを作成しました。

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

私は、ImageMagicとNGifのもう1つの偉大な選択肢はまだ答えに載っていないことに気付きました。

FFMpegは、アニメーションGIFを作成するために使用することができるから:

  • 一連の画像(ファイル)
  • 既存のビデオクリップ(例えば、MP4またはAVI)
  • C#ビットマップオブジェクトから入力データを提供することによって(任意の一時ファイルを使用せずに)標準入力から「ravvideo」

としてあなたは(System.Diagnostics.Process付き)C#コードからffmpeg.exe直接起動したり、existinのいずれかを使用することができますグラム.NETのffmpegのラッパー:

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

(このコード例では、フリーNReco VideoConverterを使用しています - 私は、その使用に関するご質問をお気軽に、このコンポーネントの作者です)。

GIFサイズは、フレームレートおよび/またはフレームサイズを減らすことによって簡単に減らすことができます。 また、最適なGIFパレットを生成する2パスアプローチで、細かいアニメーションGIFを取得することも可能です。

+0

ねえ、複数の画像をNRecoでMP4(h.264)形式に変換するコードを探していました。そこに実用的な例は見つけられませんでした。これで私を助けてくれますか?ありがとう! – Moji

+0

こんにちは、私はビデオと画像をマージする方法を知っていますか?動画をマージするとうまくいきます。しかし、2つのビデオの間でマージしてイメージを作成しようとすると、nrecoとは連携しません。 – Kanishka

+0

@Kanishka ffmpeg.exeでコマンドラインからそれを行う方法を知っていれば、VideoConverterラッパーでも同じことができます。 –

関連する問題