web-dev-qa-db-ja.com

ビデオストリームへの画像シーケンス?

多くの人がすでに持っているように見えます(このテーマについてはいくつかのスレッドがあります)。一連の画像からビデオを作成する方法を探しています。

C#で機能を実装したい!

これは私がしたくないことです:

/*Pseudo code*/
void CreateVideo(List<Image> imageSequence, long durationOfEachImageMs, string outputVideoFileName, string outputFormat)
{
    // Info: imageSequence.Count will be > 30 000 images
    // Info: durationOfEachImageMs will be < 300 ms

    if (outputFormat = "mpeg")
    {
    }
    else if (outputFormat = "avi")
    {      
    }
    else
    {
    }

    //Save video file do disk
}

Splicerhttp://splicer.codeplex.com/ )というプロジェクトがあることは知っていますが、見つかりません適切なドキュメントまたは私が従うことができる明確な例( これら は私が見つけた例です)。

CodePlexでここで見つけた最も近い方法は次のとおりです。 C#で画像のディレクトリからビデオを作成するにはどうすればよいですか?

また、ffmpegについていくつかのスレッドを読みました(たとえば、これ: C#およびFFmpegはシェルコマンドなしで? およびこれ: ffmpegを使用してイメージシーケンスを変換する )が、問題を解決できる人がいないため、ffmpeg-とは思わないコマンドラインスタイルは、(画像の量のため)私にとって最適なソリューションです。

私は何らかの方法でSplicer-projectを使用できると信じています(?)。

私の場合、それは約30000以上の画像であり、各画像は約200ミリ秒(作成したいビデオストリームで)表示されるはずです。

(ビデオの内容は?成長している植物...)

誰かが私の機能を完了するのを手伝ってくれますか?

55
Hauns TM

まあ、この答えは少し遅れていますが、最近私の元の質問でいくつかの活動に気づいたので(そして実用的な解決策が提供されていなかったという事実)、私は最終的に私のために働いたものを提供したいと思います。

回答を3つの部分に分けます。

  • バックグラウンド
  • 問題
  • 解決

バックグラウンド

(このセクションはソリューションにとって重要ではありません)

私の元々の問題は、大量の画像(つまり、膨大な量)があり、その画像がバイト配列としてデータベースに個別に保存されていたことでした。 これらすべての画像を使用してビデオシーケンスを作成したかった

私の機器のセットアップは、この一般的な図面のようなものでした: enter image description here

画像は、さまざまな状態のトマト植物の成長を表しています。すべての画像は、昼間1分ごとに撮影されました。

/*pseudo code for taking and storing images*/
while (true)
{
    if (daylight)
    {
        //get an image from the camera
        //store the image as byte array to db
    }
    //wait 1 min
}

画像を保存するための非常に単純なdbがあり、その中に1つのテーブル(テーブルImageSet)だけがありました enter image description here


問題

私はffmpegに関する多くの記事を読んでいました(元の質問をご覧ください)が、画像のコレクションからビデオに移行する方法が見つかりませんでした。


解決

最後に、実用的なソリューションを得ました!その主な部分は、オープンソースプロジェクト AForge.NET から来ています。要するに、 AForge.NETはC# のコンピュータービジョンおよび人工知能ライブラリであると言えます。 (フレームワークのコピーが必要な場合は、 http://www.aforgenet.com/ から入手してください)

AForge.NETには、このVideoFileWriterクラス(ffmpegを使用してビデオファイルを書き込むためのクラス)があります。これでほぼすべての作業が完了しました。 (非常に良い例もあります here

これは、画像データベースから画像データを取得してビデオに変換するために使用した最終クラス(縮小版)です。

public class MovieMaker
{

    public void Start()
    {
        var startDate = DateTime.Parse("12 Mar 2012");
        var endDate = DateTime.Parse("13 Aug 2012");

        CreateMovie(startDate, endDate);
    }    


    /*THIS CODE BLOCK IS COPIED*/

    public Bitmap ToBitmap(byte[] byteArrayIn)
    {
        var ms = new System.IO.MemoryStream(byteArrayIn);
        var returnImage = System.Drawing.Image.FromStream(ms);
        var bitmap = new System.Drawing.Bitmap(returnImage);

        return bitmap;
    }

    public Bitmap ReduceBitmap(Bitmap original, int reducedWidth, int reducedHeight)
    {
        var reduced = new Bitmap(reducedWidth, reducedHeight);
        using (var dc = Graphics.FromImage(reduced))
        {
            // you might want to change properties like
            dc.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            dc.DrawImage(original, new Rectangle(0, 0, reducedWidth, reducedHeight), new Rectangle(0, 0, original.Width, original.Height), GraphicsUnit.Pixel);
        }

        return reduced;
    }

    /*END OF COPIED CODE BLOCK*/


    private void CreateMovie(DateTime startDate, DateTime endDate)
    {
        int width = 320;
        int height = 240;
        var framRate = 200;

        using (var container = new ImageEntitiesContainer())
        {
            //a LINQ-query for getting the desired images
            var query = from d in container.ImageSet
                        where d.Date >= startDate && d.Date <= endDate
                        select d;

            // create instance of video writer
            using (var vFWriter = new VideoFileWriter())
            {
                // create new video file
                vFWriter.Open("nameOfMyVideoFile.avi", width, height, framRate, VideoCodec.Raw);

                var imageEntities = query.ToList();

                //loop throught all images in the collection
                foreach (var imageEntity in imageEntities)
                {
                    //what's the current image data?
                    var imageByteArray = imageEntity.Data;
                    var bmp = ToBitmap(imageByteArray);
                    var bmpReduced = ReduceBitmap(bmp, width, height);

                    vFWriter.WriteVideoFrame(bmpReduced);
                }
                vFWriter.Close();
            }
        }

    }
}

2013-11-29を更新(手順)(これが@Kiquenetに求めたことだと思いますか?)

  1. ダウンロードページからAForge.NET Frameworkをダウンロードします (完全なZipアーカイブをダウンロードすると、AForge.NET Framework-2.2.5\Samples folder...にビデオなどのプロジェクトを含む多くの興味深いVisual Studioソリューションが見つかります。)
  2. 名前空間:AForge.Video.FFMPEGdocumentation から)
  3. アセンブリ:AForge.Video.FFMPEG(in AForge.Video.FFMPEG.dll)( documentation から)(AForge.Video.FFMPEG.dllフォルダーにこのAForge.NET Framework-2.2.5\Releaseがあります)

独自のソリューションを作成する場合は、プロジェクトにAForge.Video.FFMPEG.dllへの参照があることを確認してください。そうすれば、 VideoFileWriter クラスを簡単に使用できるはずです。クラスへの link に従うと、非常に優れた(そして簡単な例)が見つかります。コードでは、for-ループでBitmap imageをVideoFileWriterに供給しています。


59
Hauns TM

スライサーでこのコードを見つけました samples 、あなたが望むものにかなり近いように見えます:

string outputFile = "FadeBetweenImages.wmv";
using (ITimeline timeline = new DefaultTimeline())
{
    IGroup group = timeline.AddVideoGroup(32, 160, 100);
    ITrack videoTrack = group.AddTrack();
    IClip clip1 = videoTrack.AddImage("image1.jpg", 0, 2); // play first image for a little while
    IClip clip2 = videoTrack.AddImage("image2.jpg", 0, 2); // and the next
    IClip clip3 = videoTrack.AddImage("image3.jpg", 0, 2); // and finally the last
    IClip clip4 = videoTrack.AddImage("image4.jpg", 0, 2); // and finally the last
}

  double halfDuration = 0.5;

  // fade out and back in
  group.AddTransition(clip2.Offset - halfDuration, halfDuration, StandardTransitions.CreateFade(), true);
  group.AddTransition(clip2.Offset, halfDuration, StandardTransitions.CreateFade(), false);

  // again
  group.AddTransition(clip3.Offset - halfDuration, halfDuration, StandardTransitions.CreateFade(), true);
  group.AddTransition(clip3.Offset, halfDuration, StandardTransitions.CreateFade(), false);

  // and again
  group.AddTransition(clip4.Offset - halfDuration, halfDuration, StandardTransitions.CreateFade(), true);
  group.AddTransition(clip4.Offset, halfDuration, StandardTransitions.CreateFade(), false);

  // add some audio
  ITrack audioTrack = timeline.AddAudioGroup().AddTrack();

  IClip audio =
     audioTrack.AddAudio("testinput.wav", 0, videoTrack.Duration);

  // create an audio envelope effect, this will:
  // fade the audio from 0% to 100% in 1 second.
  // play at full volume until 1 second before the end of the track
  // fade back out to 0% volume
  audioTrack.AddEffect(0, audio.Duration,
                 StandardEffects.CreateAudioEnvelope(1.0, 1.0, 1.0, audio.Duration));

  // render our slideshow out to a windows media file
  using (
     IRenderer renderer =
        new WindowsMediaRenderer(timeline, outputFile, WindowsMediaProfiles.HighQualityVideo))
  {
     renderer.Render();
  }
}
11
Adam

上記の例を機能させることができませんでした。しかし、一度驚くほどうまく機能する別のライブラリを見つけました。 NuGet "accord.extensions.imaging.io"を使用してみて、次の小さな関数を作成しました。

    private void makeAvi(string imageInputfolderName, string outVideoFileName, float fps = 12.0f, string imgSearchPattern = "*.png")
    {   // reads all images in folder 
        VideoWriter w = new VideoWriter(outVideoFileName, 
            new Accord.Extensions.Size(480, 640), fps, true);
        Accord.Extensions.Imaging.ImageDirectoryReader ir = 
            new ImageDirectoryReader(imageInputfolderName, imgSearchPattern);
        while (ir.Position < ir.Length)
        {
            IImage i = ir.Read();
            w.Write(i);
        }
        w.Close();
    }

フォルダーからすべての画像を読み取り、それらからビデオを作成します。

より良いものにしたい場合は、おそらくハードコーディングの代わりに画像の寸法を読み取ることができますが、ポイントは得られました。

9
Sebastian

この関数はSplicer.Netライブラリに基づいています。このライブラリがどのように機能するかを理解するために年齢を見てください。 fps(frame per second)が正しいことを確認してください。ちなみに標準の24 f/s。

私の場合、15枚の画像があり、7秒のビデオが必要になったため、fps = 2です。 Fpsは、プラットフォームまたは開発者の使用状況によって異なる場合があります。

public bool CreateVideo(List<Bitmap> bitmaps, string outputFile, double fps)
        {
            int width = 640;
            int height = 480;
            if (bitmaps == null || bitmaps.Count == 0) return false;
            try
            {
                using (ITimeline timeline = new DefaultTimeline(fps))
                {
                    IGroup group = timeline.AddVideoGroup(32, width, height);
                    ITrack videoTrack = group.AddTrack();

                    int i = 0;
                    double miniDuration = 1.0 / fps;
                    foreach (var bmp in bitmaps)
                    {
                        IClip clip = videoTrack.AddImage(bmp, 0, i * miniDuration, (i + 1) * miniDuration);
                        System.Diagnostics.Debug.WriteLine(++i);

                    }
                    timeline.AddAudioGroup();
                    IRenderer renderer = new WindowsMediaRenderer(timeline, outputFile, WindowsMediaProfiles.HighQualityVideo);
                    renderer.Render();
                }
            }
            catch { return false; }
            return true;
        }

お役に立てれば。

1

これは、C#を使用してVisual Studioを使用して画像シーケンスからビデオを作成するためのソリューションです。

私の出発点は以下の「Hauns TM」の答えでしたが、私の要件は要件よりも基本的であったため、このソリューションはあまり経験のないユーザー(私のような)により適しているかもしれません

図書館:

using System;
using System.IO;
using System.Drawing;
using Accord.Video.FFMPEG;

「ツール-> NuGetパッケージマネージャー->ソリューションのNuGetパッケージの管理...」でFFMPEGを検索すると、FFMPEGライブラリを取得できます。

関数に渡した変数は次のとおりです。

  • outputFileName = "C://outputFolder//outputMovie.avi"
  • inputImageSequence = ["C://inputFolder//image_001.avi", "C://inputFolder//image_002.avi", "C://inputFolder//image_003.avi", "C://inputFolder//image_004.avi"]

関数:

private void videoMaker( string outputFileName , string[] inputImageSequence)
{
  int width = 1920;
  int height = 1080;
  var framRate = 25;

  using (var vFWriter = new VideoFileWriter())
  {
    // create new video file
    vFWriter.Open(outputFileName, width, height, framRate, VideoCodec.Raw);

    foreach (var imageLocation in inputImageSequence)
    {
      Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
      vFWriter.WriteVideoFrame(imageFrame);
    }
    vFWriter.Close();
  }
}
0
Chris Carter