Sequenza di immagini al stream video?

Come molte persone sembrano avere (ci sono diversi thread su questo argomento qui), sto cercando modi per creare video da una sequenza di immagini.

Voglio implementare la mia funzionalità in C #!

Ecco cosa non dovrei fare:

/*Pseudo code*/ void CreateVideo(List 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 } 

So che c’è un progetto chiamato Splicer ( http://splicer.codeplex.com/ ) ma non riesco a trovare documentazione adeguata o esempi chiari che posso seguire ( questi sono gli esempi che ho trovato).

Il più vicino che voglio fare, che trovo qui su CodePlex è questo: come posso creare un video da una directory di immagini in C #?

Ho anche letto alcuni thread su ffmpeg (per esempio questo: C # e FFmpeg preferibilmente senza comandi di shell? E questo: converti la sequenza di immagini usando ffmpeg ) ma non trovo nessuno che mi aiuti con il mio problema e non penso che ffmpeg – lo stile della riga di comando è la soluzione migliore per me (a causa della quantità di immagini).

Credo di poter usare il progetto Splicer in qualche modo (?).

Nel mio caso, si tratta di circa> 30.000 immagini in cui ogni immagine dovrebbe essere visualizzata per circa 200 ms (nel videostream che voglio creare).

(Di cosa parla il video? Le piante crescono …)

Qualcuno può aiutarmi a completare la mia funzione?

Bene, questa risposta arriva un po ‘in ritardo, ma poiché ho notato qualche attività con la mia domanda originale ultimamente (e il fatto che non è stata fornita una soluzione funzionante) mi piacerebbe darti quello che alla fine ha funzionato per me.

Dividerò la mia risposta in tre parti:

  • sfondo
  • Problema
  • Soluzione

sfondo

(questa sezione non è importante per la soluzione)

Il mio problema originale era che avevo un sacco di immagini (cioè una quantità enorme), immagini che erano memorizzate individualmente in un database come array di byte. Volevo fare una sequenza video con tutte queste immagini.

Il mio equipaggiamento era qualcosa come questo disegno generale: inserisci la descrizione dell'immagine qui

Le immagini raffiguravano piante di pomodori in crescita in diversi stati. Tutte le immagini sono state scattate ogni 1 minuto durante il giorno.

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

Ho avuto un db molto semplice per la memorizzazione delle immagini, c’era solo una tabella (la tabella ImageSet) in essa: inserisci la descrizione dell'immagine qui


Problema

Ho letto molti articoli su ffmpeg (vedi la mia domanda originale) ma non ho trovato nulla su come passare da una raccolta di immagini a un video.


Soluzione

Finalmente ho trovato una soluzione funzionante! La parte principale di questo viene dal progetto open source AForge.NET . In breve, si potrebbe dire che AForge.NET è una libreria di computer vision e intelligenza artificiale in C # . (Se vuoi una copia della struttura, prendila da http://www.aforgenet.com/ )

In AForge.NET, c’è questa class VideoFileWriter (una class per scrivere video con l’aiuto di ffmpeg). Questo ha fatto quasi tutto il lavoro. (C’è anche un ottimo esempio qui )

Questa è la class finale (ridotta) che ho usato per recuperare e convertire i dati di immagine in un video dal mio database di immagini:

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

Aggiornamento 2013-11-29 (come fare) (Spero che questo sia quello che hai chiesto a @Kiquenet?)

  1. Scarica AForge.NET Framework dalla pagina dei download (scarica l'archivio completo ZIP e troverai molte interessanti soluzioni di Visual Studio con progetti, come Video, nella AForge.NET Framework-2.2.5\Samples folder ...)
  2. Namespace: AForge.Video.FFMPEG (dalla documentazione )
  3. Assemblaggio: AForge.Video.FFMPEG (in AForge.Video.FFMPEG.dll ) (dalla documentazione ) (puoi trovare questo AForge.Video.FFMPEG.dll nella AForge.Video.FFMPEG.dll AForge.NET Framework-2.2.5\Release )

Se vuoi creare la tua soluzione , assicurati di avere un riferimento a AForge.Video.FFMPEG.dll nel tuo progetto. Quindi dovrebbe essere facile usare la class VideoFileWriter . Se segui il link alla class troverai un ottimo esempio (e semplice). Nel codice, stanno alimentando VideoFileWriter con l' Bitmap image in un for -loop


Ho trovato questo codice nei campioni di slicer, sembra abbastanza vicino a quello che vuoi:

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

Non sono riuscito a ottenere l’esempio sopra riportato per funzionare. Comunque ho trovato un’altra libreria che funziona incredibilmente bene una volta. Prova tramite NuGet “accord.extensions.imaging.io”, quindi ho scritto la seguente piccola funzione:

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

Legge tutte le immagini da una cartella e ne fa un video.

Se vuoi renderlo più bello, potresti probabilmente leggere le dimensioni dell'immagine invece del codice fisso, ma hai capito il punto.

Questa funzione è basata sulla libreria Splicer.Net. Ci vogliono anni per capire come funziona quella libreria. Assicurati che il tuo fps (frame per secondo) sia corretto. Tra l’altro standard 24 f / s.

Nel mio caso ho 15 immagini e ora ho bisogno di 7 secondi di video-> così fps = 2. Fps può variare in base alla piattaforma … o all’utilizzo dello sviluppatore.

 public bool CreateVideo(List 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; } 

Spero che questo ti aiuti.