web-dev-qa-db-fra.com

Séquence d'image en flux vidéo?

Comme beaucoup de gens semblent l'avoir déjà (il y a plusieurs discussions à ce sujet ici), je cherche des moyens de créer une vidéo à partir d'une séquence d'images.

Je veux implémenter mes fonctionnalités en C #!

Voici ce que je ne veux pas faire:

/*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
}

Je sais qu'il existe un projet appelé Splicer ( http://splicer.codeplex.com/ ), mais je ne trouve pas de documentation appropriée ni d'exemples clairs que je peux suivre ( ceux-ci sont les exemples que j'ai trouvés).

Le plus proche que je veux faire, ce que je trouve ici sur CodePlex est la suivante: Comment puis-je créer une vidéo à partir d’un répertoire d’images en C #?

J'ai aussi lu quelques discussions sur ffmpeg (par exemple ceci: C # et FFmpeg de préférence sans commandes Shell? et cela: convertir une séquence d'images en utilisant ffmpeg ) mais je ne trouve personne pour vous aider moi avec mon problème et je ne pense pas que ffmpeg -command-line-style est la meilleure solution pour moi (en raison de la quantité d’images).

Je crois que je peux utiliser le projet Splicer de quelque manière que ce soit (?). 

Dans mon cas, il s’agit d’environ 30 000 images, où chaque image doit être affichée pendant environ 200 ms (dans le flux vidéo que je veux créer). 

(De quoi parle la vidéo? Des plantes en croissance ...)

Quelqu'un peut-il m'aider à compléter ma fonction?

52
Hauns TM

Eh bien, cette réponse arrive un peu tard, mais depuis que j'ai remarqué une activité récente avec ma question initiale (et le fait qu’il n’y ait pas eu de solution de travail), je voudrais vous donner ce qui a finalement fonctionné pour moi.

Je vais scinder ma réponse en trois parties:

  • Contexte
  • Problème
  • Solution

Contexte

(cette section n'est pas importante pour la solution)

Mon problème initial était que j'avais beaucoup d'images (c'est-à-dire une quantité énorme), des images qui étaient stockées individuellement dans une base de données sous forme de tableaux d'octets. Je voulais faire une séquence vidéo avec toutes ces images.

La configuration de mon équipement ressemblait à ce dessin général: enter image description here

Les images représentaient des plants de tomates en croissance dans différents États. Toutes les images ont été prises toutes les minutes.

/*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
}

J'avais une base de données très simple pour stocker des images, il n'y avait qu'une table (la table ImageSet): enter image description here


Problème

J'avais lu de nombreux articles sur ffmpeg (voir ma question initiale), mais je ne trouvais aucun moyen de passer d'une collection d'images à une vidéo.


Solution

Enfin, j'ai une solution de travail! La majeure partie provient du projet open source AForge.NET . En bref, vous pourriez dire que AForge.NET est une bibliothèque de vision artificielle et d'intelligence artificielle en C # . (Si vous voulez une copie du framework, récupérez-le simplement de http://www.aforgenet.com/ )

Dans AForge.NET, il existe cette classe VideoFileWriter (une classe permettant d’écrire des fichiers vidéo à l’aide de ffmpeg). Cela a fait presque tout le travail. (Il y a aussi un très bon exemple ici )

C’est la classe finale (réduite) que j’ai utilisée pour extraire et convertir des données d’image en vidéo de ma base de données d’images:

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();
            }
        }

    }
}

Mise à jour 2013-11-29 (comment faire) (espérons que c'est ce que vous avez demandé pour @Kiquenet?)

  1. Téléchargez AForge.NET Framework à partir de la page téléchargements (téléchargez l’archive Zip complète et retrouvez de nombreuses solutions Visual Studio intéressantes avec des projets, tels que Vidéo, dans le AForge.NET Framework-2.2.5\Samples folder...)
  2. Namespace: AForge.Video.FFMPEG (de la documentation )
  3. Assemblage: AForge.Video.FFMPEG (dans AForge.Video.FFMPEG.dll) (à partir de la documentation ) (vous pouvez trouver ce AForge.Video.FFMPEG.dll dans le dossier AForge.NET Framework-2.2.5\Release)

Si vous souhaitez créer votre propre solution , assurez-vous de disposer d'une référence à AForge.Video.FFMPEG.dll dans votre projet. Ensuite, il devrait être facile d'utiliser la classe VideoFileWriter . Si vous suivez le lien vers la classe, vous en trouverez un très bon exemple. Dans le code, ils alimentent VideoFileWriter avec Bitmap image dans une boucle for-


58
Hauns TM

J'ai trouvé ce code dans le slicer samples , semble assez proche de ce que vous voulez:

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();
  }
}
10
Adam

Je n'ai pas réussi à faire fonctionner l'exemple ci-dessus. Cependant, j'ai trouvé une autre bibliothèque qui fonctionne incroyablement bien une fois. Essayez via NuGet "accord.extensions.imaging.io", puis j’ai écrit la petite fonction suivante:

    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();
    }

Il lit toutes les images d'un dossier et en fait une vidéo.

Si vous voulez le rendre plus agréable, vous pouvez probablement lire les dimensions de l’image au lieu de coder en dur, mais vous comprenez le point.

8
Sebastian

Cette fonction est basée sur la bibliothèque Splicer.Net.Il m'a fallu un bon bout de temps pour comprendre comment cette bibliothèque fonctionne. Assurez-vous que votre image par seconde est correcte. En passant standard 24 f/s.

Dans mon cas, j’ai 15 images et j’ai maintenant besoin de 7 secondes de vidéo -> donc fps = 2 . 

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;
        }

J'espère que cela t'aides.

1
Jevgenij Kononov

Ceci est une solution pour créer une vidéo à partir d'une séquence d'images en utilisant Visual Studio en C #.

Mon point de départ était la réponse de "Hauns TM" ci-dessous, mais mes exigences étant plus fondamentales que les leurs, cette solution serait peut-être mieux adaptée aux utilisateurs moins avancés (comme moi)

Bibliothèques:

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

Vous pouvez obtenir la bibliothèque FFMPEG en cherchant FFMPEG dans "Outils -> Gestionnaire de paquets NuGet -> Gérer les paquets NuGet pour une solution ...".

Les variables que j'ai passées à la fonction sont: 

  • 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"]

Une fonction:

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