it-swarm.dev

비디오 스트림에 이미지 시퀀스?

많은 사람들이 이미 가지고있는 것처럼 (이 주제에는 몇 가지 스레드가 있습니다) 나는 일련의 이미지에서 비디오를 만드는 방법을 찾고 있습니다.

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 -명령 줄 스타일은 이미지 양 때문에 가장 적합한 솔루션입니다.

나는 Splicer 프로젝트를 어떤 방식으로 사용할 수 있다고 생각합니다 (?).

필자의 경우 각 이미지가 약 200ms (생성하려는 비디오 스트림) 동안 표시되어야하는 약> 30 000 개의 이미지입니다.

(동영상은 무엇입니까? 식물 재배 중 ...)

누구든지 내 기능을 완성하도록 도울 수 있습니까?

55
Hauns TM

글쎄,이 답변은 조금 늦었지만 최근에 원래의 질문에 대한 활동을 발견 한 이후 (및 효과적인 해결책이 제공되지 않았다는 사실) 마지막으로 나를 위해 일한 것을 알려 드리고자합니다.

답변을 세 부분으로 나눕니다.

  • 배경
  • 문제
  • 해결책

배경

(이 섹션은 솔루션에 중요하지 않습니다)

내 원래 문제는 데이터베이스에 바이트 배열로 개별적으로 저장된 이미지 (많은 양)가 많았다는 것입니다. 이 모든 이미지로 비디오 시퀀스를 만들고 싶었습니다.

장비 설정은 다음과 같은 일반적인 그림이었습니다. 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
}

이미지를 저장하는 데 매우 간단한 db가 있었고 그 안에 하나의 테이블 (테이블 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 아카이브를 다운로드하면 AForge.NET Framework-2.2.5\Samples folder에서 비디오와 같은 프로젝트를 통해 흥미로운 Visual Studio 솔루션을 찾을 수 있습니다. ...)
  2. 네임 스페이스 : AForge.Video.FFMPEG ( documentation )
  3. 어셈블리 : AForge.Video.FFMPEG (AForge.Video.FFMPEG.dll) ( documentation 에서) (이 AForge.Video.FFMPEG.dllAForge.NET Framework-2.2.5\Release 폴더)

자신의 솔루션을 만들려면 프로젝트에서 AForge.Video.FFMPEG.dll에 대한 참조가 있는지 확인하십시오. 그런 다음 VideoFileWriter 클래스를 사용하는 것이 쉬워야합니다. 클래스에 링크 를 따라 가면 아주 좋은 (그리고 간단한 예)를 찾을 수 있습니다. 코드에서 그들은 for- loop에 Bitmap image을 VideoFileWriter에 공급하고 있습니다.


59
Hauns TM

슬라이서에서이 코드를 찾았습니다 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();
  }
}
11
Adam

위의 예를 작동시킬 수 없었습니다. 그러나 한 번 놀랍도록 잘 작동하는 다른 라이브러리를 찾았습니다. 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();
    }

폴더에서 모든 이미지를 읽고 비디오를 만듭니다.

더 좋게 만들고 싶다면 하드 코딩 대신 이미지 크기를 읽을 수는 있지만 요점을 알 수 있습니다.

9
Sebastian

이 기능은 Splicer.Net 라이브러리를 기반으로합니다. fps (초당 프레임)가 올바른지 확인하십시오. 그런데 표준 24 f/s.

내 경우에는 15 개의 이미지가 있으며 이제 7 초 비디오가 필요하므로 fps = 2입니다. Fps는 플랫폼 또는 개발자 사용에 따라 달라질 수 있습니다.

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

도움이 되었기를 바랍니다.

1
Jevgenij Kononov

C #을 사용하여 Visual Studio를 사용하여 이미지 시퀀스에서 비디오를 만드는 솔루션입니다.

저의 출발점은 아래 "Hauns TM"의 답변이지만 저의 요구 사항은 그들의 요구 사항보다 기본이므로이 솔루션은 저급 사용자 (나 같은)에게 더 적합 할 수 있습니다.

라이브러리 :

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

"도구-> NuGet 패키지 관리자-> 솔루션의 NuGet 패키지 관리 ..."에서 FFMPEG를 검색하여 FFMPEG 라이브러리를 얻을 수 있습니다.

함수에 전달한 변수는 다음과 같습니다.

  • 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();
  }
}
0
Chris Carter