Привет, сообщество 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();
Этот код выполняется в дополнительном потоке и будет остановлен извне. Код работает до сих пор, но иногда он довольно медленный, что означает, что размер моего стека увеличивается, и в моей системе заканчивается оперативная память, и ОС убивает ее.
В настоящее время он работает в моей системе разработки:
В зависимости от кодека (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 Майкл
можно попробовать cudacodec::VideoWriter android.googlesource.com/platform/external/opencv3/+/master/… в общем, кодирование видео дорого, возможно придется пропускать кадры. Какое у вас разрешение и частота кадров?
@ChristophRackwitz спасибо за ваш комментарий, знаете ли вы простой способ написать cv::Mat (или интерфейс) для ffmpeg на C++? Я не могу избавиться от opencv, потому что он мне нужен и для обработки изображений.
@Micka, спасибо за подсказку, я пытаюсь скомпилировать его с помощью cudacodec. Но все же, когда я понял это правильно, мой процессор уже имеет аппаратное ускорение, поэтому в cuda не должно быть необходимости. Еще одна проблема, позже она должна работать на старом компьютере без графического процессора nvidia. Я снимаю 1080p50, я ничего не могу отбросить, результат используется для исследований, и каждое изображение важно.
fmpeg -codecs, чтобы перечислить доступные. некоторые из них имеют аппаратное ускорение.
Я не думаю, что по умолчанию opencv использует какие-либо аппаратные кодировщики.
взгляните на это: forum.up-community.org/discussion/3398/…
Спасибо @Micka. Я не могу использовать Intel Media SDK из-за процессоров AMD, но один из других параметров работает хорошо, спасибо за показ, см. мой ответ ниже.
здорово, молодец! Искал информацию, потому что вы упомянули i7 как свою систему разработки.
@Micka, я бы хотел использовать Intel Media SDK, потому что он включает H265 и H264, так что это был хороший намек на вас, но мне нужно найти решения, которые работают с Intel и AMD, с дополнительным графическим процессором и без него. . Спасибо за правильный путь





-------- старый - ищите ответ внизу --------
Спасибо @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.
Что такое FILE in FILE *pipeout в вашем коде? Есть ли какие-либо библиотеки, которые мне нужно включить при запуске вашего кода?
Это стандартная библиотека C, вы можете найти документы, например, здесь: cplusplus.com/reference/cstdio/FILE Так что #include <stdio.h> должно быть всем, что вам нужно.
Я бы предложил напрямую использовать библиотеки ffmpeg. OpenCV не является медиа-библиотекой.