Secuencia de imágenes a transmisión de video

Como muchas personas ya parecen tener (aquí hay varios temas sobre este tema), estoy buscando formas de crear videos a partir de una secuencia de imágenes.

¡Quiero implementar mi funcionalidad en C #!

Esto es lo que quiero hacer:

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

Sé que hay un proyecto llamado Splicer ( http://splicer.codeplex.com/ ) pero no puedo encontrar documentación adecuada o ejemplos claros que pueda seguir ( estos son los ejemplos que encontré).

Lo más cercano que quiero hacer, que encuentro aquí en CodePlex es este: ¿cómo puedo crear un video desde un directorio de imágenes en C #?

También he leído algunos hilos sobre ffmpeg (por ejemplo: C # y FFmpeg preferiblemente sin comandos de shell? Y esto: convertir secuencia de imágenes usando ffmpeg ) pero no encuentro a nadie que me ayude con mi problema y no creo ffmpeg – estilo de línea de comando es la mejor solución para mí (debido a la cantidad de imágenes).

Creo que puedo usar el proyecto Splicer de alguna forma (?).

En mi caso, se trata de aproximadamente> 30 000 imágenes donde cada imagen debe mostrarse durante unos 200 ms (en la videostream que quiero crear).

(¿De qué se trata el video? Plantas creciendo …)

¿Alguien puede ayudarme a completar mi función?

Bueno, esta respuesta llega un poco tarde, pero dado que he notado algo de actividad con mi pregunta original últimamente (y el hecho de que no se proporcionó una solución de trabajo) me gustaría darte lo que finalmente funcionó para mí.

Dividiré mi respuesta en tres partes:

  • Fondo
  • Problema
  • Solución

Fondo

(esta sección no es importante para la solución)

Mi problema original era que tenía muchas imágenes (es decir, una gran cantidad), imágenes que se almacenaban individualmente en una base de datos como matrices de bytes. Quería hacer una secuencia de video con todas estas imágenes.

La configuración de mi equipo era algo así como este dibujo general: enter image description here

Las imágenes representan el cultivo de plantas de tomate en diferentes estados. Todas las imágenes fueron tomadas cada 1 minuto durante el día.

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

Tenía una base de datos muy simple para almacenar imágenes, solo había una tabla (la tabla ImageSet) en ella: enter image description here


Problema

Había leído muchos artículos sobre ffmpeg (vea mi pregunta original) pero no pude encontrar ninguno sobre cómo pasar de una colección de imágenes a un video.


Solución

¡Finalmente, conseguí una solución de trabajo! La parte principal proviene del proyecto de código abierto AForge.NET . En resumen, podría decirse que AForge.NET es una biblioteca de visión artificial e inteligencia artificial en C # . (Si desea una copia del marco, simplemente descárguelo de http://www.aforgenet.com/ )

En AForge.NET, existe esta clase VideoFileWriter (una clase para escribir archivos de video con la ayuda de ffmpeg). Esto hizo casi todo el trabajo. (También hay un muy buen ejemplo aquí )

Esta es la clase final (reducida) que utilicé para buscar y convertir datos de imagen en un video de mi base de datos de imágenes:

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

Actualización 2013-11-29 (cómo hacerlo) (Espero que esto sea lo que pediste @Kiquenet?)

  1. Descargue AForge.NET Framework desde la página de descargas (descargue el archivo ZIP completo y encontrará muchas soluciones interesantes de Visual Studio con proyectos, como Video, en la AForge.NET Framework-2.2.5\Samples folder ...)
  2. Espacio de nombres: AForge.Video.FFMPEG (de la documentación )
  3. Assembly: AForge.Video.FFMPEG (en AForge.Video.FFMPEG.dll ) (de la documentación ) (puede encontrar este AForge.Video.FFMPEG.dll en la AForge.NET Framework-2.2.5\Release )

Si desea crear su propia solución , asegúrese de tener una referencia a AForge.Video.FFMPEG.dll en su proyecto. Entonces debería ser fácil usar la clase VideoFileWriter . Si sigues el enlace a la clase, encontrarás un muy buen ejemplo (y simple). En el código, están alimentando el VideoFileWriter con una Bitmap image en un bucle


Encontré este código en las muestras de segmentación, se parece bastante a lo que desea:

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

No pude lograr que funcione el ejemplo anterior. Sin embargo, encontré otra biblioteca que funciona increíblemente bien una vez. Pruebe a través de NuGet “accord.extensions.imaging.io”, luego escribí la siguiente pequeña función:

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

Lee todas las imágenes de una carpeta y crea un video a partir de ellas.

Si quieres hacerlo mejor, probablemente puedas leer las dimensiones de la imagen en lugar de la encoding, pero entiendes el punto.

Esta función se basa en la biblioteca Splicer.Net. Tócame años para entender cómo funciona esa biblioteca. Asegúrese de que su fps (cuadro por segundo) es correcto. Por cierto, estándar 24 f / s.

En mi caso tengo 15 imágenes y ahora necesito 7 segundos de video-> fps = 2. Los Fps pueden variar según la plataforma … o el uso del desarrollador.

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

Espero que esto ayude.