Мое приложение — настольное приложение Winforms, использующее OpenCV для записи и хранения видеофайлов. В настоящее время я использую
VideoCapture videoCapture = VideoCapture.FromCamera(index: cameraIndex, apiPreference: VideoCaptureAPIs.DSHOW);
И по большей части все работало нормально. Но примерно несколько дней назад мой клиент сообщил, что иногда после загрузки приложения и завершения процесса инициализации камеры FPS падал до 1–5 кадров в секунду и выглядел очень нестабильно, если не перезапустить приложение. Я могу воспроизвести с помощью многократно переинициализированной VideoCapture, и это произошло случайно. Так что я потратил два полных дня, методом проб и ошибок, пытаясь их выследить. Но не повезло.
Хотя я обнаружил, что если я изменю свои apiPreferences на VideoCaptureAPIs.MSMF, проблема в основном будет решена. Плюс прирост производительности очень заметен. Но это приводит к другой проблеме, похожей на эту проблему. Так что теперь у меня действительно закончились варианты. Я также не совсем уверен, что проблема связана с API, и, возможно, с моей стороны это небрежный код. Но здесь я, по сути, захожу в настоящий тупик.
Попробую воспроизвести на примере. А пока, если можете, взгляните на мой фрагмент. Возможно, вы знаете что-то, чего я не знаю. Спасибо!
Создайте VideoCapture в OpenCVHelper. Я использую DsDevice для получения индекса камеры
public VideoCapture CreateVideoCaptureFromCamera(int cameraIndex, int width, int height, double inputFPS, int IsAutoFocus)
{
try
{
VideoCapture videoCapture = VideoCapture.FromCamera(index: cameraIndex, apiPreference: VideoCaptureAPIs.DSHOW);
videoCapture.FrameHeight = height;
videoCapture.FrameWidth = width;
videoCapture.Fps = inputFPS;
if (IsAutoFocus == (int)AutoFocusStatus.On)
{
videoCapture.AutoFocus = true;
}
else
{
videoCapture.AutoFocus = false;
videoCapture.Focus = 100;
}
videoCapture.Zoom = 0;
videoCapture.FourCC = "MJPG";
return videoCapture;
}
catch
{
throw;
}
}
Теперь на уровне Presenter. Создайте захват видео и используйте многопоточность.
videoCapturePresenter = recorderMainAppManager.CreateVideoCapture(
index: camera.Index,
resolution: resolution, //resolutions[resolutions.Count - 1],
fps: ProfilePresenter.CameraFps,
focus: ProfilePresenter.IsAutoFocus ?? (int)AutoFocusStatus.Off
);
captureTokenSource = new();
_captureThread = new Thread(() => CaptureFrame(videoCapturePresenter, captureTokenSource))
{
Name = "Capture Thread",
Priority = ThreadPriority.AboveNormal, ///This is not the cause because i added after i can reproduced the issues.
};
_captureThread.Start();
Это CaptureFrame. Я использовал Cv2.Resize, чтобы изменить размер кадра до размера PictureBox, прежде чем перенести его на уровень пользовательского интерфейса. Без этого PictureBox попытается изменить размер каждого кадра самостоятельно и занять половину доступной оперативной памяти.
private void CaptureFrame(VideoCapture capture, CancellationTokenSource captureToken)
{
string newOverlayText = string.Empty;
try
{
ShipmentOverlay shipmentOverlay = recorderMainAppManager.GetShipmentOverlay
(
resolution: $"{capture.FrameWidth}x{capture.FrameHeight}"
);
string username = $"Employee Name : {UserPresenter.Username}";
while (!captureToken.IsCancellationRequested)
{
string datetime = DateTimeOffset.Now.ToString("dd/MM/yyyy HH:mm:ss");
string overlayText = createNewOverlayString();
using (Mat rawFrame = new())
{
newOverlayText = $"Date : {datetime} | {username} | {overlayText}";
if (!capture.Read(rawFrame))
{
Console.WriteLine("Error: Failed to read frame.");
break;
}
if (rawFrame.Empty())
{
Console.WriteLine("Error: Empty frame.");
break;
}
using (Mat frameWithOverlay = recorderMainAppManager.DrawOverlay(frame: rawFrame, shipmentOverlay: shipmentOverlay, overlayText: newOverlayText))
{
Mat clonedFrame = frameWithOverlay.Clone();
{
lock (clonedFrame)
{
var currentPictureBox = recorderMainAppView.GetPicRecordResolution();
var targetSize = new OpenCvSharp.Size() { Width = currentPictureBox.Width, Height = currentPictureBox.Height };
if (targetSize.Width > 0 && targetSize.Height > 0)
{
Mat captureMat = new();
Cv2.Resize(clonedFrame, captureMat, targetSize, interpolation: InterpolationFlags.Area);
using (Bitmap _bitmap = BitmapConverter.ToBitmap(captureMat))
{
Bitmap clonedResizeFrame = (Bitmap)_bitmap.Clone();
recorderMainAppView.UpdateCameraView(clonedResizeFrame);
}
if (_isRecording == RecordStatus.RECORD)
{
_frameQueue.Enqueue(clonedFrame);
}
else
{
captureMat.Dispose();
}
}
}
}
}
}
_threadStopEvent.Wait(1);
if (captureToken.IsCancellationRequested) break;
}
}
catch
{
throw;
}
}
Это уровень пользовательского интерфейса. Когда моя камера начнет создавать кадр, он будет отправлен в PictureBox в качестве предварительного просмотра видео для моего пользователя.
public void UpdateCameraView(Bitmap img)
{
try
{
if (picRecord.InvokeRequired &&
WindowState != FormWindowState.Minimized)
{
using (picRecord.Image)
{
Invoke(() => picRecord.Image = img);
}
}
}
catch (ObjectDisposedException)
{
// This function throw everytime in debug mode, apparently because some video frame aren't being disposed yet
// But frmMain is already disposed. Happened only when i exit the app.
// For now, do nothing. But if you know how to solve this. Feel free.
return;
}
}
Изменить: здесь находится _frameQueue, как и было запрошено. Я не думал, что это действительно связано с вопросом, поскольку он находится в отдельном потоке и не запускается до тех пор, пока пользователь не введет его. Но проблема возникла еще до начала этого процесса.
Сначала создается другой поток. Это предназначено для преобразования каждого кадра в видеофайлы при вводе пользователем записи.
videoWriterPresenter = recorderMainAppManager.CreateVideoWriter(
capture: videoCapturePresenter,
savedDir: outputDirectory,
BaseLabel: data.FirstOrDefault(t => !string.IsNullOrEmpty(t)));
_threadStopEvent.Reset();
writerTokenSource = new();
_writerThread = new Thread(() => WriteFrame(writerTokenSource))
{
Name = "Writer Thread",
IsBackground = true
};
_writerThread.Start();
private void WriteFrame(CancellationTokenSource writerToken)
{
Stopwatch stopwatch = Stopwatch.StartNew();
double _delay = 1_000 / videoCapturePresenter!.Fps;
try
{
while (!_threadStopEvent.Wait(0) && !writerToken.IsCancellationRequested)
{
if (stopwatch.Elapsed.TotalMilliseconds
Подробнее здесь: https://stackoverflow.com/questions/791 ... pencvsharp
Случайное падение FPS каждый раз, когда камера завершает инициализацию в OpenCVSharp ⇐ C#
Место общения программистов C#
1730173901
Anonymous
Мое приложение — настольное приложение Winforms, использующее OpenCV для записи и хранения видеофайлов. В настоящее время я использую
VideoCapture videoCapture = VideoCapture.FromCamera(index: cameraIndex, apiPreference: VideoCaptureAPIs.DSHOW);
И по большей части все работало нормально. Но примерно несколько дней назад мой клиент сообщил, что иногда после загрузки приложения и завершения процесса инициализации камеры FPS падал до 1–5 кадров в секунду и выглядел очень нестабильно, если не перезапустить приложение. Я могу воспроизвести с помощью многократно переинициализированной VideoCapture, и это произошло случайно. Так что я потратил два полных дня, методом проб и ошибок, пытаясь их выследить. Но не повезло.
Хотя я обнаружил, что если я изменю свои apiPreferences на VideoCaptureAPIs.MSMF, проблема в основном будет решена. Плюс прирост производительности очень заметен. Но это приводит к другой проблеме, похожей на эту проблему. Так что теперь у меня действительно закончились варианты. Я также не совсем уверен, что проблема связана с API, и, возможно, с моей стороны это небрежный код. Но здесь я, по сути, захожу в настоящий тупик.
Попробую воспроизвести на примере. А пока, если можете, взгляните на мой фрагмент. Возможно, вы знаете что-то, чего я не знаю. Спасибо!
Создайте VideoCapture в OpenCVHelper. Я использую DsDevice для получения индекса камеры
public VideoCapture CreateVideoCaptureFromCamera(int cameraIndex, int width, int height, double inputFPS, int IsAutoFocus)
{
try
{
VideoCapture videoCapture = VideoCapture.FromCamera(index: cameraIndex, apiPreference: VideoCaptureAPIs.DSHOW);
videoCapture.FrameHeight = height;
videoCapture.FrameWidth = width;
videoCapture.Fps = inputFPS;
if (IsAutoFocus == (int)AutoFocusStatus.On)
{
videoCapture.AutoFocus = true;
}
else
{
videoCapture.AutoFocus = false;
videoCapture.Focus = 100;
}
videoCapture.Zoom = 0;
videoCapture.FourCC = "MJPG";
return videoCapture;
}
catch
{
throw;
}
}
Теперь на уровне Presenter. Создайте захват видео и используйте многопоточность.
videoCapturePresenter = recorderMainAppManager.CreateVideoCapture(
index: camera.Index,
resolution: resolution, //resolutions[resolutions.Count - 1],
fps: ProfilePresenter.CameraFps,
focus: ProfilePresenter.IsAutoFocus ?? (int)AutoFocusStatus.Off
);
captureTokenSource = new();
_captureThread = new Thread(() => CaptureFrame(videoCapturePresenter, captureTokenSource))
{
Name = "Capture Thread",
Priority = ThreadPriority.AboveNormal, ///This is not the cause because i added after i can reproduced the issues.
};
_captureThread.Start();
Это CaptureFrame. Я использовал Cv2.Resize, чтобы изменить размер кадра до размера PictureBox, прежде чем перенести его на уровень пользовательского интерфейса. Без этого PictureBox попытается изменить размер каждого кадра самостоятельно и занять половину доступной оперативной памяти.
private void CaptureFrame(VideoCapture capture, CancellationTokenSource captureToken)
{
string newOverlayText = string.Empty;
try
{
ShipmentOverlay shipmentOverlay = recorderMainAppManager.GetShipmentOverlay
(
resolution: $"{capture.FrameWidth}x{capture.FrameHeight}"
);
string username = $"Employee Name : {UserPresenter.Username}";
while (!captureToken.IsCancellationRequested)
{
string datetime = DateTimeOffset.Now.ToString("dd/MM/yyyy HH:mm:ss");
string overlayText = createNewOverlayString();
using (Mat rawFrame = new())
{
newOverlayText = $"Date : {datetime} | {username} | {overlayText}";
if (!capture.Read(rawFrame))
{
Console.WriteLine("Error: Failed to read frame.");
break;
}
if (rawFrame.Empty())
{
Console.WriteLine("Error: Empty frame.");
break;
}
using (Mat frameWithOverlay = recorderMainAppManager.DrawOverlay(frame: rawFrame, shipmentOverlay: shipmentOverlay, overlayText: newOverlayText))
{
Mat clonedFrame = frameWithOverlay.Clone();
{
lock (clonedFrame)
{
var currentPictureBox = recorderMainAppView.GetPicRecordResolution();
var targetSize = new OpenCvSharp.Size() { Width = currentPictureBox.Width, Height = currentPictureBox.Height };
if (targetSize.Width > 0 && targetSize.Height > 0)
{
Mat captureMat = new();
Cv2.Resize(clonedFrame, captureMat, targetSize, interpolation: InterpolationFlags.Area);
using (Bitmap _bitmap = BitmapConverter.ToBitmap(captureMat))
{
Bitmap clonedResizeFrame = (Bitmap)_bitmap.Clone();
recorderMainAppView.UpdateCameraView(clonedResizeFrame);
}
if (_isRecording == RecordStatus.RECORD)
{
_frameQueue.Enqueue(clonedFrame);
}
else
{
captureMat.Dispose();
}
}
}
}
}
}
_threadStopEvent.Wait(1);
if (captureToken.IsCancellationRequested) break;
}
}
catch
{
throw;
}
}
Это уровень пользовательского интерфейса. Когда моя камера начнет создавать кадр, он будет отправлен в PictureBox в качестве предварительного просмотра видео для моего пользователя.
public void UpdateCameraView(Bitmap img)
{
try
{
if (picRecord.InvokeRequired &&
WindowState != FormWindowState.Minimized)
{
using (picRecord.Image)
{
Invoke(() => picRecord.Image = img);
}
}
}
catch (ObjectDisposedException)
{
// This function throw everytime in debug mode, apparently because some video frame aren't being disposed yet
// But frmMain is already disposed. Happened only when i exit the app.
// For now, do nothing. But if you know how to solve this. Feel free.
return;
}
}
Изменить: здесь находится _frameQueue, как и было запрошено. Я не думал, что это действительно связано с вопросом, поскольку он находится в отдельном потоке и не запускается до тех пор, пока пользователь не введет его. Но проблема возникла еще до начала этого процесса.
Сначала создается другой поток. Это предназначено для преобразования каждого кадра в видеофайлы при вводе пользователем записи.
videoWriterPresenter = recorderMainAppManager.CreateVideoWriter(
capture: videoCapturePresenter,
savedDir: outputDirectory,
BaseLabel: data.FirstOrDefault(t => !string.IsNullOrEmpty(t)));
_threadStopEvent.Reset();
writerTokenSource = new();
_writerThread = new Thread(() => WriteFrame(writerTokenSource))
{
Name = "Writer Thread",
IsBackground = true
};
_writerThread.Start();
private void WriteFrame(CancellationTokenSource writerToken)
{
Stopwatch stopwatch = Stopwatch.StartNew();
double _delay = 1_000 / videoCapturePresenter!.Fps;
try
{
while (!_threadStopEvent.Wait(0) && !writerToken.IsCancellationRequested)
{
if (stopwatch.Elapsed.TotalMilliseconds
Подробнее здесь: [url]https://stackoverflow.com/questions/79125599/random-fps-drop-every-time-camera-finished-initialized-in-opencvsharp[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия