c# - Secuencia de imágenes a transmisión de video
image ffmpeg (4)
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<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
}
Sé que hay un proyecto llamado Splicer ( http://splicer.codeplex.com/ ) pero no puedo encontrar documentación adecuada o ejemplos claros que pueda seguir ( these 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:
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:
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 AForge.NET )
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 here )
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?)
- 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
...) - Espacio de nombres:
AForge.Video.FFMPEG
(de la here ) - Assembly:
AForge.Video.FFMPEG
(enAForge.Video.FFMPEG.dll
) (de la here ) (puede encontrar esteAForge.Video.FFMPEG.dll
en laAForge.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 here . Si sigues el here 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 samples 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();
}
}
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<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;
}
Espero que esto ayude.
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 codificación, pero entiendes el punto.