Сокращение занимаемой площади в 60 секунд видео AVI

У меня есть программа, которая использует библиотеки AForge, чтобы хранить 60-секундный поток изображений с веб-камеры в буфере памяти. При обнаружении движения он записывает следующие 30 секунд в тот же буфер, который перезаписывает первые 30 секунд. По сути, у вас есть 30 секундное видео с обеих сторон от любого движения, которое еще не записано, что дает вам 60 секунд видео.

Проблема в том, что 60 секунд растровых изображений из AForge COMPRESSED составляют около 3 ГБ в ОЗУ. В довершение всего, результирующий 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(videoDevice.@Source, new Recording(counter, device.Name, videoDevice.@Source, _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);
            }
        }
    }

Таким образом, вопрос на самом деле заключается в том, как я могу еще больше уменьшить объем используемой памяти в буфере, но в то же время удерживать в памяти последние 30 секунд видео?

Я немного сгорел на этой неделе и не вижу, чего не хватает. Любые предложения приветствуются!

1 ответ

Решение

В некоторых быстрых расчетах говорится, что HD-видео с разрешением 1920x1080x24 бит и скоростью 15 кадров в секунду в течение 60 секунд составляет около 5,3 ГБ. Вы получаете некоторое сжатие кадров, чтобы использовать 3 ГБ.

VideoFileWriter (почему эта переменная не является локальной для функции?) использует видеокодек AVI по умолчанию, который также будет сжимать кадры. Поскольку предположительно кадры в основном статичны, это экономит много места.

Я бы предложил найти способ сохранить видео в памяти в виде сжатого видеопотока.

Другие вопросы по тегам