2017-12-01 16 views
-2

AForge 라이브러리를 사용하여 메모리 버퍼의 웹캠에서 60 초 분량의 이미지를 실행중인 프로그램을 유지하는 프로그램이 있습니다. 움직임이 감지되면 다음 30 초를 동일한 버퍼에 기록하고 처음 30 초를 덮어 씁니다. 사실상, 이미 기록되지 않은 움직임의 양면에서 30 초 분량의 동영상을 롤업하여 총 60 초의 비디오를 제공합니다.AVI 비디오 60 초의 발자국 축소

AFURA COMPRESSED의 비트 맵 이미지는 RAM에 3GB 정도입니다. 그 위에, 결과 avi 파일은 약 3 메가 바이트입니다. 그것은 꽤 큰 차이입니다!

어디에서 잘못 될지 누가 알 수 있습니까? 이 속도로 매 시간마다 디스크에 직접 비디오를 녹화하고 모든 이벤트에 대해 수동으로 주기적으로 비디오를 재생하는 것이 더 유용 할 것입니다!

CameraController.cs - 각 연결된 웹캠에 대한 초기화를 정렬 :

시스템은 다음과 같은 세 가지 구성 요소로 구성되어 있습니다. 이전에 사용 된 설정에 대한 아이디어를 제공하기 위해 주석 처리 된 구성 요소에 남겨 두었습니다.

public class CameraController : ServiceBase 
    { 

     public virtual void OnStart() 
     { 
      Start(60, 0.4f); 
     } 


     private FilterInfoCollection _VideoCaptureDevices; 
     private MotionDetector _MotionDetector; 
     private Dictionary<string, Recording> _Streams = new Dictionary<string, Recording>(); 
     private Dictionary<int, VideoCaptureDevice> _Devices = new Dictionary<int, VideoCaptureDevice>(); 
     private int _Framerate; 


     private int _MaxVideoLength; 
     private float _MotionSensitivity; 


     public void Start(int maxVideoLength, float motionSensitivity){ 

      _MaxVideoLength = maxVideoLength; 
      _MotionSensitivity = motionSensitivity; 

      Init(); 

     } 

     public void Init() 
     { 
      try 
      { 
       _MotionDetector = GetDefaultMotionDetector(); 

       _VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); 

       int counter = 0; 
       foreach (FilterInfo device in _VideoCaptureDevices) 
       { 
        var videoDevice = new VideoCaptureDevice(device.MonikerString); 

        //_Framerate = videoDevice.VideoCapabilities[0].AverageFrameRate == 0 
        // ? 25 
        // : videoDevice.VideoCapabilities[0].AverageFrameRate; 

        _Framerate = 15; 

        _Streams.Add([email protected], new Recording(counter, device.Name, [email protected], _MaxVideoLength, _Framerate)); 

        videoDevice.NewFrame += new NewFrameEventHandler(NewFrame); 
        videoDevice.Start(); 

        _Devices.Add(counter++, videoDevice); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 


     public void NewFrame(object sender, NewFrameEventArgs eventArgs) 
     { 
      try 
      { 
       var device = (VideoCaptureDevice) sender; 
       _Streams[@device.Source].AddBitmap((Bitmap) eventArgs.Frame.Clone()); 

       if (_Streams[@device.Source].IsRecording) 
       { 
        _Streams[@device.Source].CheckRecording(); 

        if (_Streams[@device.Source].SaveRequired) 
         _Streams[@device.Source].WriteToFile(); 
       } 
       else 
       { 
        var motion = _MotionDetector.ProcessFrame(_Streams[@device.Source].Bitmap); 
        if (motion > _MotionSensitivity) 
         _Streams[@device.Source].StartRecording(); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 

     } 



     public void StopVideo(bool stopWebcams = false) 
     { 

      foreach (var device in _Devices) 
      { 
       var stream = _Streams[device.Value.Source]; 

       if(stream.IsRecording) 
        stream.FileWriter.Close(); 

       if(device.Value.IsRunning && stopWebcams) 
        device.Value.SignalToStop(); 
      } 

     } 





     public static AForge.Vision.Motion.MotionDetector GetDefaultMotionDetector() 
     { 
      AForge.Vision.Motion.IMotionDetector detector = null; 
      AForge.Vision.Motion.IMotionProcessing processor = null; 
      AForge.Vision.Motion.MotionDetector motionDetector = null; 

      //detector = new AForge.Vision.Motion.TwoFramesDifferenceDetector() 
      //{ 
      // DifferenceThreshold = 15, 
      // SuppressNoise = true 
      //}; 

      //detector = new AForge.Vision.Motion.CustomFrameDifferenceDetector() 
      //{ 
      // DifferenceThreshold = 15, 
      // KeepObjectsEdges = true, 
      // SuppressNoise = true 
      //}; 

      detector = new AForge.Vision.Motion.SimpleBackgroundModelingDetector() 
      { 
       DifferenceThreshold = 10, 
       FramesPerBackgroundUpdate = 10, 
       KeepObjectsEdges = true, 
       MillisecondsPerBackgroundUpdate = 10, 
       SuppressNoise = true 
      }; 

      //processor = new AForge.Vision.Motion.GridMotionAreaProcessing() 
      //{ 
      // HighlightColor = System.Drawing.Color.Red, 
      // HighlightMotionGrid = true, 
      // GridWidth = 100, 
      // GridHeight = 100, 
      // MotionAmountToHighlight = 100F 
      //}; 

      processor = new AForge.Vision.Motion.BlobCountingObjectsProcessing() 
      { 
       //HighlightColor = System.Drawing.Color.Red, 
       //HighlightMotionRegions = true, 
       MinObjectsHeight = 10, 
       MinObjectsWidth = 10 
      }; 

      motionDetector = new AForge.Vision.Motion.MotionDetector(detector, processor); 

      return motionDetector; 
     } 
    } 

그런 다음 Recording.cs있다 - 컨트롤 할 때 녹음

public class Recording 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Source { get; set; } 
    public Bitmap Bitmap { get; set; } 
    public bool IsRecording { get; set; } 
    public bool SaveRequired { get; set; } 
    public int TimeLimitSec { get; set; } 
    public int FrameRate { get; set; } 


    public string DirString = ConfigurationManager.AppSettings["DesinationFolder"].ToString(); 

    public Stopwatch Timer = new Stopwatch(); 

    public VideoFileWriter FileWriter = new VideoFileWriter(); 

    public VideoBuffer VideoBuffer; 
    public int BufferPosition { get; set; } 

    public Recording(int id, string name, string source, int timeLimit, int framerate) 
    { 
     Id = id; 
     Name = name; 
     Source = @source; 
     IsRecording = false; 
     SaveRequired = false; 
     TimeLimitSec = timeLimit; 
     FrameRate = framerate; 
     VideoBuffer = new VideoBuffer(timeLimit, framerate); 
    } 

    public string FileName { get; set; } 

    public void StartRecording() 
    { 
     IsRecording = true; 
     Timer.Reset(); 
     Timer.Start(); 
    } 

    public void StopRecording() 
    { 
     IsRecording = false; 
     SaveRequired = true; 
     Timer.Reset(); 
     Timer.Stop(); 
    } 

    public void WriteToFile() 
    { 
     try 
     { 
      if (!Directory.Exists(@DirString)) 
       Directory.CreateDirectory(@DirString); 

      FileName = @DirString + @"\Video_" + Id + "_" + Name + "_" + DateTime.Now.ToFileTime() + ".avi"; 

      FileWriter.Open(FileName, Bitmap.Width, Bitmap.Height, FrameRate, VideoCodec.Default); 

      for (int frame = 0; frame < VideoBuffer.BufferPosition; frame++) 
      { 
       FileWriter.WriteVideoFrame(Compression.Decompress<Bitmap>(VideoBuffer.Buffer[frame])); 
      } 

      FileWriter.Close(); 

      SaveRequired = false; 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 


    public void AddBitmap(Bitmap bitmap) 
    { 
     try 
     { 
      this.Bitmap = bitmap; 

      this.VideoBuffer.AddBitmap(bitmap); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    public void CheckRecording() 
    { 
     try 
     { 
      if (IsRecording && Timer.Elapsed.TotalSeconds > TimeLimitSec) 
       StopRecording(); 
     } 
     catch (Exception ex) 
     { 
      var msg = ex.Message; 
      Console.WriteLine(ex.Message); 
     } 
    } 

    private void SaveImage() 
    { 
     Bitmap.Save(@"D:\Storage\IMG_"+ Id + "_" + Name + "_" + DateTime.Now.ToFileTime() + ".jpg"); 
    } 
} 

그리고 마지막으로 VideoBuffer.cs 쓰기/시작/중지 - 비트 맵의 ​​실행 버퍼를 제어 할 수 있습니다. 비트 맵은 byte []로 압축되어 있습니다.

public class VideoBuffer 
    { 
     public int BufferLengthSeconds { get; set; } 

     public byte[][] Buffer { get; set; } 

     public int BufferPosition { get; set; } 

     public int MaxPosition { get; set; } 

     public bool Recorded { get; set; } 


     public VideoBuffer(int secondsToBuffer, int framerate) 
     { 
      MaxPosition = secondsToBuffer * framerate * 2; // Have our buffer before an event is started, as well as the length of time for the next 

      //Buffer = new Bitmap[MaxPosition + 1]; // Plus one allows us to add the latest bitmap and then clone everything but the first index 
      Buffer = new byte[MaxPosition + 1][]; 


      BufferPosition = 0; 
     } 


     public void AddBitmap(Bitmap bitmap) 
     { 
      try 
      { 
       // If we haven't reached the maximum buffer size, keep adding it as normal 
       if (BufferPosition < MaxPosition) 
       { 
        Buffer[BufferPosition++] = Compression.Compress(bitmap); 
       } 
       else 
       { 
        // Otherwise, shuffle everything down one. 
        Buffer[MaxPosition] = Compression.Compress(bitmap); 
        var tempBuffer = new byte[MaxPosition + 1][]; 

        Array.Copy(Buffer, 1, tempBuffer, 0, Buffer.Length - 1); 

        tempBuffer.CopyTo(Buffer, 0); 

       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 
    } 

그래서 정말 IS 질문은, 어떻게 더 버퍼의 메모리 풋 프린트를 줄이고,하지만 여전히 한 번에 메모리에 영상의 마지막 30 초 유지?

나는 이번 주에 불타 버려서 무엇이 빠졌는지를 볼 수 없다. 모든 제안을 환영합니다!

+0

메모리 매핑 된 파일로 원하는 작업을 수행 할 수 있습니까? – mjwills

+0

그런데 왜 다시 프레임 형태로 60 초를 저장합니까? – Evk

+0

메모리 매핑 된 파일로 인해 디스크에 너무 많은 부하가 걸릴 수 있습니다. 하지만 요즘에는 6GB RAM이 많지 않습니다.이 작업을 수행하기 위해 몇 개의 RAM이 있습니까? 분명히 당신은 충분한 RAm을 가지고 있지 않으므로 저장을 위해 프레임을 압축해야하고 작업을 위해 다시 압축을 풀 필요가 있습니다. https://stackoverflow.com/questions/3517965/convert-bmp-to-png-in-memory-for-clipboard-pasting-in-net 또는 50 달러에 RAM을 더 구입하십시오. – Harry

답변

1

일부 빠른 수학에서는 1920x1080x24 비트 색상의 HD 비디오가 15fps에서 60 초 동안 약 5.3GB라고합니다. 3GB를 사용하려면 일부 프레임 압축이 필요합니다.

VideoFileWriter (함수에 대해 로컬 변수가 아닌 이유는 무엇입니까?)는 프레임 사이에서 압축되는 기본 AVI 비디오 코덱을 사용하고 있습니다. 아마도 프레임은 대부분 고정되어 있기 때문에 많은 공간을 절약 할 수 있습니다.

메모리 비디오를 압축 된 비디오 스트림으로 저장하는 방법을 제안합니다.

+0

'VideoFileWriter'는 이전 버전 리팩터링에서 숙취 :) 나는 압축 된 비디오 스트림 제안에 맞다고 생각합니다. 또한 이미지를 압축하기 전에 먼저 이미지를 .png으로 변환하도록 비트 맵 압축을 전환했습니다. 압축을 많이 향상시키지는 않았지만 확실하게 비디오 품질을 향상 시켰습니다. – Dezzamondo