图像序列到视频流?

Hau*_* TM 56 c# video ffmpeg image aforge

像许多人似乎已经拥有(这里有几个主题)我正在寻找从一系列图像创建视频的方法.

我想在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
}
Run Code Online (Sandbox Code Playgroud)

我知道有一个名为Splicer的项目(http://splicer.codeplex.com/)但我找不到合适的文档或明确的例子我可以遵循(这些是我发现的例子).

我想在CodePlex上找到的最接近的是: 如何在C#中的图像目录中创建视频?

我还阅读了一些关于ffmpeg的线程(例如:C#和FFmpeg最好没有shell命令?这个:使用ffmpeg转换图像序列)但我发现没有人帮我解决我的问题而且我不认为ffmpeg -命令行样式对我来说是最好的解决方案(因为图像数量).

我相信我可以用某种方式使用Splicer -project(?).

在我的情况下,它大约是> 30 000张图像,其中每张图像应显示约200毫秒(在我想要创建的视频流中).

(视频是关于什么的?植物生长......)

谁能帮我完成我的功能?

Hau*_* TM 60

好吧,这个答案有点晚了,但是因为我最近注意到了一些关于原始问题的活动(事实上没有提供可行的解决方案),我想告诉你最终为我工作的内容.

我将把答案分成三部分:

  • 背景
  • 问题

背景

(此部分对解决方案并不重要)

我最初的问题是我有很多图像(即数量很大),图像作为字节数组单独存储在数据库中.我想制作一个包含所有这些图像的视频序列.

我的设备设置类似于这个总图: 在此输入图像描述

图像描绘了生长在不同状态的番茄植物.所有图像均在白天每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
}
Run Code Online (Sandbox Code Playgroud)

我有一个非常简单的数据库用于存储图像,其中只有一个表(表ImageSet): 在此输入图像描述


问题

我读过很多关于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();
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

更新2013-11-29(如何)(希望这是你对@Kiquenet的要求?)

  1. 下载页面下载AForge.NET Framework (下载完整的ZIP存档,您将在...中找到许多有趣的Visual Studio解决方案,包括视频等项目AForge.NET Framework-2.2.5\Samples folder)
  2. 命名空间:( AForge.Video.FFMPEG来自文档)
  3. 汇编: AForge.Video.FFMPEG(in AForge.Video.FFMPEG.dll)(来自文档)(你可以AForge.Video.FFMPEG.dllAForge.NET Framework-2.2.5\Release文件夹中找到)

如果要创建自己的解决方案,请确保AForge.Video.FFMPEG.dll在项目中引用.那么应该很容易使用VideoFileWriter类.如果你按照课程的链接,你会发现一个非常好的(和简单的例子).在代码中,他们Bitmap imagefor-loop中提供VideoFileWriter


  • 如果有人感兴趣,可以在[Youtube](http://www.youtube.com/watch?v=oMZMghjuNT8)上找到带有“声音效果”的最终视频。请将此评论仅作为解决方案的演示。:o) (2认同)

小智 11

我在切片器示例中找到了这段代码,看起来非常接近你想要的:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)


Seb*_*ian 9

我无法让上面的例子工作.但是我确实发现了另一个工作得非常好的库.尝试通过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();
    }
Run Code Online (Sandbox Code Playgroud)

它从文件夹中读取所有图像并从中制作视频.

如果你想让它更好,你可能会阅读图像尺寸而不是硬编码,但你明白了.

  • 该库现在称为DotImaging (3认同)
  • 我已经通过 NuGet 安装了 Accord,但我无法让这段代码工作。它无法识别 VideoWriter 类。 (2认同)

小智 5

这是使用 C# 使用 Visual Studio 从图像序列创建视频的解决方案。

我的出发点是下面“Hauns TM”的答案,但我的要求比他们的更基本,所以这个解决方案可能更适合不太高级的用户(比如我自己)

图书馆:

using System;
using System.IO;
using System.Drawing;
using Accord.Video.FFMPEG;
Run Code Online (Sandbox Code Playgroud)

您可以通过在“工具 -> NuGet 包管理器 -> 管理解决方案的 NuGet 包...”中搜索 FFMPEG 来获取 FFMPEG 库。

我传递给函数的变量是:

  • 输出文件名 ="C://outputFolder//outputMovie.avi"
  • 输入图像序列 = ["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();
  }
}
Run Code Online (Sandbox Code Playgroud)


Bar*_*jen 5

FFMediaToolkit是在2020年一个很好的解决方案,与.NET的核心支持。

https://github.com/radek-k/FFMediaToolkit

FFMediaToolkit 是一个跨平台的 .NET Standard 库,用于创建和读取视频文件。它通过 FFmpeg.Autogen 绑定使用本机 FFmpeg 库。

图书馆的自述文件有一个很好的例子来解决所问的问题。

// You can set there codec, bitrate, frame rate and many other options.
var settings = new VideoEncoderSettings(width: 1920, height: 1080, framerate: 30, codec: VideoCodec.H264);
settings.EncoderPreset = EncoderPreset.Fast;
settings.CRF = 17;
var file = MediaBuilder.CreateContainer(@"C:\videos\example.mp4").WithVideo(settings).Create();
while(file.Video.FramesCount < 300)
{
    file.Video.AddFrame(/*Your code*/);
}
file.Dispose(); // MediaOutput ("file" variable) must be disposed when encoding is completed. You can use `using() { }` block instead.
Run Code Online (Sandbox Code Playgroud)

  • 重要免责声明 - 该库不应用于生产用途 (3认同)