Сокращение занимаемой площади в 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 по умолчанию, который также будет сжимать кадры. Поскольку предположительно кадры в основном статичны, это экономит много места.
Я бы предложил найти способ сохранить видео в памяти в виде сжатого видеопотока.