Почему видеозапись opencv такая медленная?

Привет, сообщество stackoverflow, У меня сложная проблема, и мне нужна ваша помощь, чтобы понять, что здесь происходит. Моя программа захватывает кадры с карты видеозахвата (Blackmagic), которая пока работает нормально, в то же время я отображаю захваченные изображения с помощью opencv (cv::imshow), который также работает хорошо (но довольно сильно тратит процессор). Захваченные изображения также должны храниться на диске, для этого я помещаю захваченные фреймы (cv::Mat) в стек, чтобы наконец записать их асинхронно с opencv:

cv::VideoWriter videoWriter(path, cv::CAP_FFMPEG, fourcc, fps, *size);
videoWriter.set(cv::VIDEOWRITER_PROP_QUALITY, 100);

int id = metaDataWriter.insertNow(path);

while (this->isRunning) {

    while (!this->stackFrames.empty()) {

        cv:Mat m = this->stackFrames.pop();

        videoWriter << m;
    }
    
}

videoWriter.release();

Этот код выполняется в дополнительном потоке и будет остановлен извне. Код работает до сих пор, но иногда он довольно медленный, что означает, что размер моего стека увеличивается, и в моей системе заканчивается оперативная память, и ОС убивает ее.

В настоящее время он работает в моей системе разработки:

  • Убунту 18.04.05
  • OpenCV 4.4.0, скомпилированный с Cuda
  • Intel i7 10-го поколения, 32 ГБ ОЗУ, графический процессор Nvidia p620, твердотельный накопитель M.2

В зависимости от кодека (fourcc) это приводит к высокой нагрузке на ЦП. До сих пор я использовал в основном "MJPG", "x264". Иногда даже MJPG включает одно ядро ​​ЦП на 100% загрузку, и мой стек поднимается до тех пор, пока программы не закончатся. Иногда после перезагрузки эта проблема исправляется, и кажется, что нагрузка распределяется по всем ядрам.

Что касается документа Intel. для моего процессора он имеет встроенное аппаратное кодирование/декодирование для нескольких кодеков. Но я думаю, что opencv их не использует. Opencv даже использует свой собственный ffmpeg, а не один из моей системы. Вот моя команда сборки opencv:

cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D WITH_TBB=ON \
-D WITH_CUDA=ON \
-D BUILD_opencv_cudacodec=OFF \
-D ENABLE_FAST_MATH=1 \
-D CUDA_FAST_MATH=1 \
-D WITH_CUBLAS=1 \
-D WITH_V4L=ON \
-D WITH_QT=OFF \
-D WITH_OPENGL=ON \
-D WITH_GSTREAMER=ON \
-D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_ENABLE_NONFREE=ON \
-D WITH_FFMPEG=1 \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
-D WITH_CUDNN=ON \
-D OPENCV_DNN_CUDA=ON \
-D CUDA_ARCH_BIN=6.1 ..

Я только начал разработку с Linux и C++, прежде чем я работал с Java/Maven, поэтому использование cmake все еще находится в стадии разработки, пожалуйста, будьте со мной полегче.

В основном мой вопрос: как я могу ускорить кодирование / запись видео, в лучшем случае использовать аппаратное ускорение? Или, если вы думаете, что есть что-то еще подозрительное, пожалуйста, дайте мне знать.

BR Майкл

Я бы предложил напрямую использовать библиотеки ffmpeg. OpenCV не является медиа-библиотекой.

Christoph Rackwitz 17.12.2020 17:11

можно попробовать cudacodec::VideoWriter android.googlesource.com/platform/external/opencv3/+/master/‌​… в общем, кодирование видео дорого, возможно придется пропускать кадры. Какое у вас разрешение и частота кадров?

Micka 17.12.2020 21:18

@ChristophRackwitz спасибо за ваш комментарий, знаете ли вы простой способ написать cv::Mat (или интерфейс) для ffmpeg на C++? Я не могу избавиться от opencv, потому что он мне нужен и для обработки изображений.

user2267367 17.12.2020 22:42

@Micka, спасибо за подсказку, я пытаюсь скомпилировать его с помощью cudacodec. Но все же, когда я понял это правильно, мой процессор уже имеет аппаратное ускорение, поэтому в cuda не должно быть необходимости. Еще одна проблема, позже она должна работать на старом компьютере без графического процессора nvidia. Я снимаю 1080p50, я ничего не могу отбросить, результат используется для исследований, и каждое изображение важно.

user2267367 17.12.2020 22:42
Используя Libav* вы захотите узнать, поддерживает ли ваш процессор (intel?) quicksync/QSV. скажите fmpeg -codecs, чтобы перечислить доступные. некоторые из них имеют аппаратное ускорение.
Christoph Rackwitz 17.12.2020 22:52

Я не думаю, что по умолчанию opencv использует какие-либо аппаратные кодировщики.

Micka 17.12.2020 23:54

взгляните на это: forum.up-community.org/discussion/3398/…

Micka 17.12.2020 23:58

Спасибо @Micka. Я не могу использовать Intel Media SDK из-за процессоров AMD, но один из других параметров работает хорошо, спасибо за показ, см. мой ответ ниже.

user2267367 18.12.2020 11:56

здорово, молодец! Искал информацию, потому что вы упомянули i7 как свою систему разработки.

Micka 18.12.2020 20:17

@Micka, я бы хотел использовать Intel Media SDK, потому что он включает H265 и H264, так что это был хороший намек на вас, но мне нужно найти решения, которые работают с Intel и AMD, с дополнительным графическим процессором и без него. . Спасибо за правильный путь

user2267367 21.12.2020 10:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
10
5 753
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

-------- старый - ищите ответ внизу --------

Спасибо @Micka за многочисленные советы, я нашел то, что нужно.

Использование cudacodec::VideoWriter не так просто, после компиляции я не смог его использовать из-за этой ошибки, и даже если я смогу запустить его, на ПК для развертывания нет графического процессора nvidia.

Поскольку я также собираюсь использовать ПК с процессорами AMD, я не могу использовать cv::CAP_INTEL_MFX в качестве параметра api-reference для cv::VideoWriter. Но есть еще cv::CAP_OPENCV_MJPEG, который отлично работает с кодеком MJPG (поддерживаются не все видеоконтейнеры, я использую .avi, к сожалению, .mkv не работал с этой конфигурацией). Если пользователь не использует MJPG в качестве кодека, я использую cv::CAP_ANY, а opencv решает, что использовать.

Так,

cv::VideoWriter videoWriter(path, cv::CAP_OPENCV_MJPEG, fourcc, fps, *size);

работает довольно хорошо, даже на моей старой системе.

К сожалению, я никогда раньше не менял параметр api-reference, только с ffmpeg на gstreamer, я прочитал в документе opencv только последнюю строку «cv::CAP_FFMPEG или cv::CAP_GSTREAMER». и я не видел, что есть "например" до... Спасибо, @Micka, что заставил меня снова читать.

P.S. для моей проблемы с производительностью с cv:: imshow я изменился с

cv::namedWindow(WINDOW_NAME, cv::WINDOW_NORMAL);

к

cv::namedWindow(WINDOW_NAME, cv::WINDOW_OPENGL);

Который, очевидно, использует OpenGL и работает лучше. Также переход с cv::Mat на cv::UMat может повысить производительность, см. здесь

-------------- РЕДАКТИРОВАТЬ лучшее решение ----------------

Поскольку у меня все еще были проблемы с OpenCV VideoWriter для некоторых систем, я искал другое решение. Теперь я пишу кадры с помощью FFMPEG. Для FFMPEG я могу использовать GPU или CPU в зависимости от используемого кодека. Если FFMPEG установлен через snapd (Ubuntu 18.04), по умолчанию cuda включена:

sudo snap install ffmpeg --devmode

(--devmode является необязательным, но у меня были проблемы с записью файлов в определенное место, это был единственный способ исправить это)

И вот мой код:

//this string is automatically created in my program, depending on user input and the parameters of the input frames

string ffmpegCommand = "ffmpeg -y -f rawvideo -vcodec rawvideo -framerate 50 -pix_fmt bgr24 -s 1920x1080 -i - -c:v h264_nvenc -crf 14 -maxrate:v 10M -r 50 myVideoFile.mkv";

FILE *pipeout = popen(ffmpegCommand.data(), "w");

int id = metaDataWriter.insertNow(path);

//loop will be stopped from another thread
while (this->isRunning) {
    //this->frames is a stack with cv::Mat elements in the right order
    //it is filled by another thread
    while (!this->frames.empty()) {


        cv::Mat mat = frames.front();
        frames.pop();
        fwrite(mat.data, 1, s, pipeout);

    }  
}

fflush(pipeout);
pclose(pipeout);

Таким образом, файл (pipeout) используется для записи mat.data в ffmpeg, сам ffmpeg выполняет кодирование и запись файла. К параметрам:

-y = перезаписывать выходные файлы без запроса

-f = формат, в данном случае используется для ввода rawvideo

-vcodec = кодек для ввода, который также является необработанным видео, потому что используемый cv::Mat.data не имеет сжатия/кодека

-framerate = входная частота кадров, которую я получаю от моей карты захвата/OpenCv

-pix_fmt = формат моих необработанных данных, в данном случае bgr24, поэтому 8 бит на каждый канал, потому что я использую обычный OpenCV bgr cv::Mat

-s = размер каждого кадра, в моем случае 1920x1080

-i = ввод, в данном случае мы читаем из stdinput вы можете видеть здесь "-", значит файл (pipeout) захватывается ffmpeg

-c:v = кодек вывода, так что это для кодирования видео, здесь используется h264_nvenc, который является кодеком GPU

-r = скорость вывода кадров, также 50 в данном случае myVideoFile.mkv = это просто имя файла, который создается ffmpeg, вы можете изменить этот файл и путь

Дополнительные параметры для повышения качества: -crf 14 -maxrate:v 10M

Это работает очень хорошо для меня и использует мое аппаратное ускорение графического процессора или с другим кодеком, отвечающим за процессор. Я надеюсь, что это поможет и другим разработчикам.

Большое спасибо. Работает как шарм. Очень хорошая идея передать конвейер ffmpeg с помощью pipeout.

Meto 19.03.2021 10:30

Что такое FILE in FILE *pipeout в вашем коде? Есть ли какие-либо библиотеки, которые мне нужно включить при запуске вашего кода?

Nagarjun Vinukonda 06.09.2022 20:20

Это стандартная библиотека C, вы можете найти документы, например, здесь: cplusplus.com/reference/cstdio/FILE Так что #include <stdio.h> должно быть всем, что вам нужно.

user2267367 07.09.2022 11:19

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