nvidia xavier nx平台OpenCV对接硬件编码器

free-jdx 2021-05-27 16:16:56 7372
1. 前言

尝试在xavier nx平台做图像处理应用
应用摘要如下

1.捕捉摄像头设备
2.颜色转换格式
3.图像处理
4.编码与HW编码器

1~3使用OpenCV实现
我想实现的是通过图像处理的结果(例如。覆盖图像与边框)到HW编码器。
我引用了jetson_multimedia_api_reference,但是混淆了

HW: JetsonNX开发工具包,SW: Jetpack4.4

2. 参考python示例 & patch
import sys
import cv2

def read_cam():
    cap = cv2.VideoCapture("filesrc location=/home/nvidia/sample_1080p_h264.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! nvvidconv ! video/x-raw, format=BGRx ! videoconvert ! video/x-raw,format=BGR ! appsink  ")

    w = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    h = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print('Src opened, %dx%d @ %d fps' % (w, h, fps))

    gst_out = "appsrc ! video/x-raw, format=BGR ! queue ! videoconvert ! video/x-raw,format=BGRx ! nvvidconv ! nvv4l2h264enc ! h264parse ! matroskamux ! filesink location=test.mkv "
    out = cv2.VideoWriter(gst_out, cv2.CAP_GSTREAMER, 0, float(fps), (int(w), int(h)))
    if not out.isOpened():
        print("Failed to open output")
        exit()

    if cap.isOpened():
        while True:
            ret_val, img = cap.read();
            if not ret_val:
                break;
            out.write(img);
            cv2.waitKey(1)
    else:
     print "pipeline open failed"

    print("successfully exit")
    cap.release()
    out.release()

if __name__ == '__main__':
    read_cam()

但现在需要拿编码器输出的数据进行后期处理
考虑使用多媒体api

参考以下patch

diff --git a/multimedia_api/ll_samples/samples/13_multi_camera/main.cpp b/multimedia_api/ll_samples/samples/13_multi_camera/main.cpp
index 49a9ab8..0613f0b 100644
--- a/multimedia_api/ll_samples/samples/13_multi_camera/main.cpp
+++ b/multimedia_api/ll_samples/samples/13_multi_camera/main.cpp
@@ -39,6 +39,8 @@
 #include <stdio.h>
 #include <stdlib.h>

+#include <opencv2/opencv.hpp>
+
 using namespace Argus;
 using namespace EGLStream;

@@ -259,7 +261,7 @@ bool ConsumerThread::threadInitialize()
     input_params.width = STREAM_SIZE.width();
     input_params.height = STREAM_SIZE.height();
     input_params.layout = NvBufferLayout_Pitch;
-    input_params.colorFormat = NvBufferColorFormat_NV12;
+    input_params.colorFormat = NvBufferColorFormat_ABGR32;
     input_params.nvbuf_tag = NvBufferTag_VIDEO_CONVERT;

     NvBufferCreateEx (&m_compositedFrame, &input_params);
@@ -333,8 +335,8 @@ bool ConsumerThread::threadExecute()
             if (!m_dmabufs[i])
             {
                 m_dmabufs[i] = iNativeBuffer->createNvBuffer(iEglOutputStreams[i]->getResolution(),
-                                                          NvBufferColorFormat_YUV420,
-                                                          NvBufferLayout_BlockLinear);
+                                                          NvBufferColorFormat_ABGR32,
+                                                          NvBufferLayout_Pitch);
                 if (!m_dmabufs[i])
                     CONSUMER_PRINT("\tFailed to create NvBuffer\n");
             }
@@ -349,7 +351,21 @@ bool ConsumerThread::threadExecute()
         {
             // Composite multiple input to one frame
             NvBufferComposite(m_dmabufs, m_compositedFrame, &m_compositeParam);
-            g_renderer->render(m_compositedFrame);
+            void *pdata = NULL;
+
+            NvBufferMemMap(m_compositedFrame, 0, NvBufferMem_Read, &pdata);
+            NvBufferMemSyncForCpu(m_compositedFrame, 0, &pdata);
+
+            cv::Mat imgbuf = cv::Mat(STREAM_SIZE.height(),
+                                     STREAM_SIZE.width(),
+                                     CV_8UC4, pdata);
+            cv::Mat display_img;
+            cvtColor(imgbuf, display_img, CV_RGBA2BGR);
+
+            NvBufferMemUnMap(m_compositedFrame, 0, &pdata);
+
+            cv::imshow("img", display_img);
+            cv::waitKey(1);
         }
         else
             g_renderer->render(m_dmabufs[0]);

Makefile:

+CPPFLAGS+=`pkg-config --cflags opencv`
+LDFLAGS+=`pkg-config --libs opencv`
3. OpenCV安装方法
1不要通过Jetpack安装OpenCV 3.3.1。默认安装。请取消勾选OpenCV 3.3.1
2 Jetpack将禁止安装样例包,如果你不选中OpenCV,请从
https://developer.nvidia.com/embedded/dlc/multimedia-api-r2821 2
3获取脚本https://github.com/AastaNV/JEP/blob/master/script/install_opencv3.4.0_TX2.sh
4执行脚本
mkdir OpenCV
$ ./install_opencv3.4.0_TX2.sh OpenCV

5应用补丁并重建09_camera_jpeg_capture
6运行
$ export DISPLAY=:0
09_camera_jpeg_capture$ ./camera_jpeg_capture --disable-jpg --cap-time 10
4. V4L2架构参考sample

参考12_camera_v4l2_cuda。
该示例用于v4l2采集

5. gstreamer示例

在opencv中,可以像VideoWriter一样使用gstreamer管道

/ Get resolution and framerate from capture
unsigned int width = cap.get (cv::CAP_PROP_FRAME_WIDTH);
unsigned int height = cap.get (cv::CAP_PROP_FRAME_HEIGHT);
unsigned int fps = cap.get (cv::CAP_PROP_FPS);

// Create the writer with gstreamer pipeline encoding into H264, muxing into mkv container and saving to file 
cv::VideoWriter gst_nvh264_writer("appsrc ! queue ! videoconvert ! video/x-raw,format=BGRx ! nvvidconv ! nvv4l2h264enc ! video/x-h264,format=byte-stream ! h264parse ! matroskamux ! filesink location=test-nvh264-writer.mkv ", 0, fps, cv::Size (width, height));
  if (!gst_nvh264_writer.isOpened ()) {
    std::cout << "Failed to open gst_nvh264 writer." << std::endl;
    return (-6);
  }

在循环中,使用以下命令推送处理过的帧(每个捕获帧一个):

gst_nvh264_writer.write(frame);
6. NvVideoEncoder

考虑在MMAPI中使用NvVideoEncoder
需要创建NvBuffer和复制cv::Mat数据到它。

方法如下:

1. 创建导入cv::Mat(ARGB32)的NvBuffer
2. 为YUV420M创建匹配编码器 输入格式的NvBuffer。
3. 创建NvVideoEncoder
4. 编码器输出平面缓冲区的出队列
5. 将ARGB转换为YUV420M

尝试将cv::Mat转换成NvBuffer
如果par.num_planes = 1,这是正确的
但这种方法似乎是无效的

cv::Mat src = cv::Mat::zeros(height, width, CV_8UC4);
ret = NvBufferGetParams(fd, par);    //fd is V4L2_PIX_FMT_ABGR32
for( unsigned int plane = 0; plane < par.num_planes; plane++){
    ret = NvBufferMemMap(fd, plane, NvBufferMem_Write, &vaddr);  // vaddr is void*
    if(ret == 0){
        for( unsigned int i = 0; i < par.height[plane]; i++){
            memcpy((uint8_t *)vaddr + i *par.pitch[plane], src.data[ i * src.step], src.width * sizeof((uint8_t)) * 4);
        }
    }
    NvBufferMemSyncForDevice (fd, plane, &vaddr);
}
7. CUDA过滤器

因为OpenCV的主要格式是BGR,在Jetson平台上并不支持BGR。
在处理之后,需要将其转换为RGBA,复制到NvBuffer,转换到YUV420并进行编码。
它可能不会带来好的性能。

有CUDA过滤器可以应用于RGBA缓冲区。
如果能在用例中使用CUDA过滤器,性能会更好

函数调用如下:

  //CUDA postprocess
    {
        EGLImageKHR egl_image;
        egl_image = NvEGLImageFromFd(egl_display, dmabuf_fd);
        CUresult status;
        CUeglFrame eglFrame;
        CUgraphicsResource pResource = NULL;
        cudaFree(0);
        status = cuGraphicsEGLRegisterImage(&pResource,
                    egl_image,
                    CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
        if (status != CUDA_SUCCESS)
        {
            printf("cuGraphicsEGLRegisterImage failed: %d \n",status);
        }
        status = cuGraphicsResourceGetMappedEglFrame(&eglFrame, pResource, 0, 0);
        status = cuCtxSynchronize();
        if (create_filter) {
            filter = cv::cuda::createSobelFilter(CV_8UC4, CV_8UC4, 1, 0, 3, 1, cv::BORDER_DEFAULT);
            //filter = cv::cuda::createGaussianFilter(CV_8UC4, CV_8UC4, cv::Size(31,31), 0, 0, cv::BORDER_DEFAULT);
            create_filter = false;
        }
        cv::cuda::GpuMat d_mat(h, w, CV_8UC4, eglFrame.frame.pPitch[0]);
        filter->apply (d_mat, d_mat);

        status = cuCtxSynchronize();
        status = cuGraphicsUnregisterResource(pResource);
        NvDestroyEGLImage(egl_display, egl_image);
    }

dmabuf_fd是RGBA格式。
经过处理后,可以将其转换回NV12并发送给硬件编码器。

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
free-jdx
红包 92 5 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
free-jdx
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区