Последовательность изображения в видео поток? - PullRequest
55 голосов
/ 17 марта 2012

Как и многие люди, похоже, уже (здесь есть несколько тем на эту тему) Я ищу способы создания видео из последовательности изображений.

Я хочу реализовать свою функциональность в 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
}

Я знаю, что есть проект под названием Splicer (http://splicer.codeplex.com/), но я не могу найти подходящую документацию или четкие примеры, которым могу следовать ( эти являются примерами, которые Я нашел).

Самое близкое, что я хочу сделать, что я нахожу здесь на CodePlex, это: Как я могу создать видео из каталога изображений в C #?

Я также прочитал несколько веток о ffmpeg (например, это: C # и FFmpeg предпочтительно без команд оболочки? и это: преобразование последовательности изображений с использованием ffmpeg ) но я не нашел никого, кто помог бы мне с моей проблемой, и я не думаю, что ffmpeg -command-line-style - лучшее решение для меня (из-за количества изображений).

Я считаю, что могу каким-то образом использовать Splicer -проект (?).

В моем случае это примерно> 30 000 изображений, где каждое изображение должно отображаться в течение 200 мс (в видеопотоке, который я хочу создать).

(О чем видео? Растения растут ...)

Может кто-нибудь помочь мне выполнить мою функцию?

Ответы [ 5 ]

59 голосов
/ 11 сентября 2012

Что ж, этот ответ приходит с некоторым опозданием, но, поскольку в последнее время я заметил некоторую активность с моим первоначальным вопросом (и тот факт, что не было предоставлено работающего решения), я хотел бы рассказать вам, что в конечном итоге мне помогло. 1001 *

Я разделю свой ответ на три части:

  • Фон
  • Проблема
  • Решение

Фон

(этот раздел не важен для решения)

Моя первоначальная проблема заключалась в том, что у меня было много изображений (то есть огромное количество), изображений, которые индивидуально сохранялись в базе данных в виде байтовых массивов. Я хотел сделать видеопоследовательность со всеми этими изображениями.

Мои настройки оборудования были примерно такими: 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
}

У меня была очень простая база данных для хранения изображений, в ней была только одна таблица (таблица ImageSet): enter image description here


Проблема

Я прочитал много статей о ffmpeg (см. Мой оригинальный вопрос), но не смог найти ни одного способа перехода от коллекции изображений к видео.


Решение

Наконец-то я получил рабочее решение! Основная его часть взята из проекта с открытым исходным кодом AForge.NET . Короче говоря, можно сказать, что AForge.NET - это библиотека компьютерного зрения и искусственного интеллекта в C # . (Если вам нужна копия фреймворка, просто возьмите ее с http://www.aforgenet.com/)

В AForge.NET есть этот класс VideoFileWriter (класс для записи видеофайлов с помощью ffmpeg). Это сделало почти всю работу. (Здесь также есть очень хороший пример )

Это последний класс (сокращенный), который я использовал для извлечения и преобразования данных изображения в видео из моей базы данных изображений:

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-архив, и вы найдете множество интересных решений Visual Studio с проектами, такими как Video, в AForge.NET Framework-2.2.5\Samples folder ...)
  2. Пространство имен: AForge.Video.FFMPEG (из документации )
  3. Сборка: AForge.Video.FFMPEGAForge.Video.FFMPEG.dll) (из документации ) (вы можете найти этот AForge.Video.FFMPEG.dll в папке AForge.NET Framework-2.2.5\Release)

Если вы хотите создать собственное решение , убедитесь, что в вашем проекте есть ссылка на AForge.Video.FFMPEG.dll. Тогда должно быть легко использовать класс VideoFileWriter . Если вы перейдете по ссылке к классу, вы найдете очень хороший (и простой пример). В коде они подают VideoFileWriter с Bitmap image в for -loop


11 голосов
/ 17 марта 2012

Я нашел этот код в слайсере 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();
  }
}
8 голосов
/ 29 мая 2015

Мне не удалось заставить работать приведенный выше пример. Однако однажды я нашел другую библиотеку, которая работает удивительно хорошо. Попробуйте через 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();
    }

Читает все изображения из папки и делает из них видео.

Если вы хотите сделать его лучше, вы, вероятно, можете прочитать размеры изображения вместо жесткого кодирования, но вы поняли.

1 голос
/ 05 апреля 2018

Эта функция основана на библиотеке Splicer.Net. Потребовалось много времени, чтобы понять, как работает эта библиотека.Убедитесь, что ваш fps (кадр в секунду) правильный.Кстати, стандартная 24 к / с.

В моем случае у меня 15 изображений, и теперь мне нужно 7 секунд видео-> так что 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;
        }

Надеюсь, это поможет.

0 голосов
/ 26 октября 2018

Это решение для создания видео из последовательности изображений с использованием Visual Studio с использованием C #.

Моей отправной точкой был ответ "Hauns TM" ниже, но мои требования были более простыми, чем их, поэтому это решениеможет быть более подходящим для менее продвинутых пользователей (таких как я)

Библиотеки:

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

Вы можете получить библиотеку FFMPEG, выполнив поиск FFMPEG в «Инструменты -> Диспетчер пакетов NuGet -> Управление»Пакеты NuGet для решения ... "

Перечисленные в функцию переменные:

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