美文网首页
瑞芯微板子使用探索【RK3588】

瑞芯微板子使用探索【RK3588】

作者: georgeguo | 来源:发表于2023-11-04 19:42 被阅读0次

    瑞芯微板子使用探索【RK3588】

    1 信息查看

    1.1 查看RK系列

    root@firefly:~# lspci
    0002:20:00.0 PCI bridge: Fuzhou Rockchip Electronics Co., Ltd Device 3588 (rev 01)
    0002:21:00.0 Network controller: Broadcom Inc. and subsidiaries Device 449d (rev 02
    

    3588硬件信息:

    • 内嵌的 NPU 支持 INT4/INT8/INT16/FP16 混合运算,算力高达 6TOP;
    • 支持 8K@60fps 的H.265 和 VP9 解码器、8k@30fps 的 H.264 解码器和 4K@60fps 的 AV1 解码器;
    • 支持 8K@30fps 的 H.264和 H.265 编码器,高质量的 JPEG 编码器/解码器,专门的图像预处理器和后处理器。

    NPU加速:

    使用NPU需要下载RKNN SDK,RKNN SDK 为带有 NPU 的 RK3588S/RK3588 芯片平台提供编程接口,能够帮助用户部署使用 RKNN-Toolkit2 导出的 RKNN 模型,加速 AI 应用的落地。

    1.2 查看npu的使用情况

    cat /sys/kernel/debug/rknpu/load
    watch -n 1 cat /sys/kernel/debug/rknpu/load
    

    2 瑞芯微RKNN开发流程

    B站视频教程: https://www.bilibili.com/video/BV1Kj411D78q?p=3&vd_source=9138e2a910cf9bbb083cd42a6750ed10

    RKNN开发流程

    上图中包含了RKNN模型开发中用到的所用项目:

    ① RKNN工具rknn-toolkit2https://github.com/rockchip-linux/rknn-toolkit2, RKNN-Toolkit-Lite2 provides Python programming interfaces for Rockchip NPU platform (RK3566, RK3568, RK3588, RK3588S) to help users deploy RKNN models and accelerate the implementation of AI applications.

    • 提供了Python接口(RKNN, X86架构下)

    • 模型转换功能,将onnx、torch、tensorflow等模型转onnx

    • 模型推理,支持模拟推理和连板推理两种模式,在,若指定了target,即rknn.init_runtime(target='rk3588'),则是连板推理模式,需要通过usb连接到盒子上;若未指定target,即rknn.init_runtime(target=None),默认在rknn-toolkit2的模拟器中进行推理;

    • 性能评估,提供了模型性能分析函数,如:accuracy_analysis、eval_perf、eval_memory等接口,可以查看项目仓库中rknn-toolkit2/doc以及rknn-toolkit2/rknn_toolkit_lite2/docs中的文档。

    • 开发需要的文档基本都在该仓库中,深入开发需仔细阅读对应的开发文档。

    • 目录rknn-toolkit2/examples/onnx/yolov5中包含了yolov5的转换和推理文档,不过Coovally使用的yolov5在后处理阶段需要使用sigmoid,而该文档中缺失

    ② RKNN工具rknn-toolkit lite 2。这个工具不是独立的,包含在rknn-toolkit2中,对应目录为rknn-toolkit2/rknn_toolkit_lite2。提供Python版本的模型推理功能,只能推理,对应RKNNLite类。

    ③ rknpu2:项目地址 https://github.com/rockchip-linux/rknpu2.git, 该提供了RKNN 开发的C SDK,运行在边端。

    ④ rknn_server:盒子中的服务程序,用于接收连板运行的名。盒子启动后,默认启动。若连板推理时,发现版本不匹配,可以从RKNPU2中复制对应的动态库, 如:rknpu2/runtime/RK3588/Linux/librknn_api/aarch64/librknnrt.so。

    3 模型转换

    3.1 pth转ONNX

    3.1.1 yolov5-det转onnx

    yolov5官网提供了export.py脚本,环境配置好后,执行该脚本即可。

    python3 export.py --weights ../models/yolov5s-det/13978/best.pt --include onnx --opset 12 --simplify
    

    注意:RKNN必须使用opset=12

    3.1.2 yolov8-det转onnx

    参考:

    构建yolov8的开发环境,因为涉及修改yolov8的部分代码,所以最好直接将yolov8代码下载下来,单独构建一个yolov8的运行环境;

    yolov8官方封装了YOLO类,直接调用转换即可【当前使用的版本是 Ultralytics YOLOv8.0.200】

    from ultralytics import YOLO
    
    if __name__ == "__main__":
    
        src_pt_model = "../models/yolov8-det/13981/best.pt"
        dst_onnx_model = "../models/yolov8-det/13981/best.onnx"
    
        src_pt_model = "../models/yolov8-seg/13931/best.pt"
        dst_onnx_model = "../models/yolov8-seg/13931/best.onnx"
    
        # Load a model
        model = YOLO(src_pt_model)  # load a custom trained model
    
        # Export the model
        model.export(format='onnx')
    

    3.2 ONNX转RKNN

    3.2.1 yolov5 ONNX转RKNN【服务器端操作】

    RKNN工具rknn-toolkit2:https://github.com/rockchip-linux/rknn-toolkit2, RKNN-Toolkit-Lite2 provides Python programming interfaces for Rockchip NPU platform (RK3566, RK3568, RK3588, RK3588S) to help users deploy RKNN models and accelerate the implementation of AI applications.

    因为使用的是RK3588板子,所以安装rknn-toolkit2

    pip3 install rknn_toolkit2==1.5.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
    

    直接使用pip3安装,提示ERROR: No matching distribution found for rknn_toolkit2==1.5.2,直接从github的release上下载,并安装,下载文件约356M,安装包为whl文件,解压后可以获得。包中仅仅支持36/38/310版本的python。

    tar zxvf rknn-toolkit2-1.5.2.tar.gz
    cd rknn-toolkit2-1.5.2/packages
    pip3 install --no-dependencies rknn_toolkit2-1.5.2+b642f30c-cp38-cp38-linux_x86_64.whl -U -i https://pypi.tuna.tsinghua.edu.cn/simple   # 否则安装很多依赖包且报错,后续需要时再重新安装
    pip3 install onnx onnxruntime onnxoptimizer onnxsim ruamel_yaml -i https://pypi.tuna.tsinghua.edu.cn/simple
    

    如果直接在板子中安装,需要安装rknn-toolkit-lite 2。在rknn-toolkit2-1.5.2\rknn_toolkit_lite2\packages中找到rknn_toolkit_lite2-1.5.2-cp38-cp38-linux_aarch64.whl安装即可

    参考:

    环境搭建好之后,调用RKNN的API转换即可。下面的代码转换为yolov5 onnx转rknn示例。RKNN模型默认精度是fp16

    import os
    from rknn.api import RKNN
    
    if __name__ == '__main__':
    
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        src_onnx_model = "models/yolov5s-det/13978/best.onnx"
        dst_rknn_model = "models/yolov5s-det/13978/best_origin_3_output.rknn"
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(
            mean_values=[[0, 0, 0]],
            std_values=[[255, 255, 255]],
            target_platform='rk3588',
            quantized_method='channel',  # layer
            optimization_level=1  # 0  1  2  3
        )
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        ret = rknn.load_onnx(
            model=src_onnx_model,
            outputs=["/model.24/m.0/Conv_output_0", "/model.24/m.1/Conv_output_0", "/model.24/m.2/Conv_output_0"])
        if ret != 0:
            print('Load yolov5 failed!')
            exit(ret)
        print('done')
    
        # Build model
        print('--> Building model')
        ret = rknn.build(
            do_quantization=False,
            dataset=dataset,
            rknn_batch_size=1
        )
        if ret != 0:
            print('Build yolov5 failed!')
            exit(ret)
        print('done')
    
        # Export RKNN model
        print('--> Export RKNN model')
        ret = rknn.export_rknn(dst_rknn_model)
        if ret != 0:
            print('Export yolov5rknn failed!')
            exit(ret)
        print('done')
    
        ret = rknn.accuracy_analysis(inputs=[img_path])
        if ret != 0:
            print('Accuracy analysis failed!')
        print(ret)
        print('done')
        rknn.release()
    
    • dataset.txt文本文件存放的就是用于量化的图片路径,如下:
    /opt/data/code/yolov5-research/data/quantify_data/0/fa4f0905d4f1f68c07f2e0f3fc26ab39.jpg
    /opt/data/code/yolov5-research/data/quantify_data/0/9dc8c98f67484b5fa921ee392fc68bc3.jpg
    /opt/data/code/yolov5-research/data/quantify_data/0/8a047ffcc0c61a15632a54c73fe7f70e.jpg
    /opt/data/code/yolov5-research/data/quantify_data/0/02f66416341f3224cb0123508ab98f1a.jpg
    /opt/data/code/yolov5-research/data/quantify_data/0/eb6ac6aca0b14e9f766c7122d0d81fd1.jpg
    /opt/data/code/yolov5-research/data/quantify_data/0/e4d231eb6464cd0f103bf72a06806264.jpg
    

    3.2.2 yolov8 ONNX转RKNN【服务器端操作】

    yolov8-det转rknn流程同yolov5。

    4 模型量化

    4.1 yolov5-det量化

    4.1.1 普通量化

    yolov5普通量化的示例代码如下,

    import os
    from rknn.api import RKNN
    
    if __name__ == '__main__':
    
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        src_onnx_model = "models/yolov5s-det/13978/best.onnx"
        dst_rknn_model = "models/yolov5s-det/13978/best_int8_3_output.rknn"
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(
            mean_values=[[0, 0, 0]],
            std_values=[[255, 255, 255]],
            target_platform='rk3588',
            quantized_dtype="asymmetric_quantized-8",
            # quantized_algorithm="mmse",  # normal
            quantized_method='channel',  # layer
            optimization_level=1  # 0  1  2  3
        )
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        ret = rknn.load_onnx(
            model=src_onnx_model,
            outputs=["/model.24/m.0/Conv_output_0", "/model.24/m.1/Conv_output_0", "/model.24/m.2/Conv_output_0"])
        if ret != 0:
            print('Load yolov5 failed!')
            exit(ret)
        print('done')
    
        # Build model
        print('--> Building model')
        ret = rknn.build(do_quantization=True, dataset=dataset, rknn_batch_size=1)
        if ret != 0:
            print('Build yolov5 failed!')
            exit(ret)
        print('done')
    
        # Export RKNN model
        print(f'--> Export RKNN model to {dst_rknn_model}')
        # ret = rknn.export_rknn(dst_rknn_model, cpp_gen_cfg=True, target='rk3588')
        ret = rknn.export_rknn(dst_rknn_model)
        if ret != 0:
            print('Export yolov5rknn failed!')
            exit(ret)
        print('done')
    
        ret = rknn.accuracy_analysis(inputs=[img_path], target='rk3588')
        if ret != 0:
            print('Accuracy analysis failed!')
        print(ret)
        print('done')
    
        rknn.release()
    

    关键点:

    • rknn.config中设置量化的相关参数,可以参考《Rockchip_User_Guide_RKNN_Toolkit2_CN-1.5.2.pdf》
    • rknn.load_onnx,这里要指定outputs,用于截掉网络中无法使用sigmoid的层。如下图所示,红色框去除,解码绿色框部分。
    yolov5_3_output.png
    • rknn.build,编译模型,启用量化,设置batch_size。
    • rknn.export_rknn,导出rknn模型。
    • rknn.accuracy_analysis,对模型进行性能分析。

    4.1.2 混合量化

    混合量化就是对模型中进行int8量化之后精度损失较大的部分,再修改为fp16,不使用int8量化。包含两个步骤,具体可参考文档《Rockchip_User_Guide_RKNN_Toolkit2_CN-1.5.2.pdf》。

    步骤1:hybrid_quantization_step1,yolov5-det示例代码

    import os
    from rknn.api import RKNN
    
    if __name__ == "__main__":
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        src_onnx_model = "models/yolov5s-det/13978/best.onnx"
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(
            mean_values=[[0, 0, 0]],
            std_values=[[255, 255, 255]],
            target_platform='rk3588'
        )
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        ret = rknn.load_onnx(
            model=src_onnx_model,
            outputs=["/model.24/m.0/Conv_output_0", "/model.24/m.1/Conv_output_0", "/model.24/m.2/Conv_output_0"])
        if ret != 0:
            print('Load yolov5 failed!')
            exit(ret)
        print('done')
    
        # Build model
        rknn.hybrid_quantization_step1(
            dataset=dataset,
            rknn_batch_size=1,
            proposal=True,
            proposal_dataset_size=1
        )
    
        rknn.release()
    

    步骤2:hybrid_quantization_step2,使用步骤1的输出,作为步骤2 的输入。步骤1生成的best.quantization.cfg用于说明需要修回fp16的节点。若第一步骤中的proposal=False,这里必须手动修改。若proposal=True,则自动生成需要修改的节点。yolov5-det示例代码。

    from rknn.api import RKNN
    
    if __name__ == "__main__":
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        dst_rknn_model = "models/yolov5s-det/13978/best_hybrid8_3_output.rknn"
    
        # Create RKNN object
        rknn = RKNN()
    
        # hybrid_quantization_step2, 输入均为第1步输出的文件
        rknn.hybrid_quantization_step2(
            model_input="best.model",
            data_input="best.data",
            model_quantization_cfg="best.quantization.cfg"
        )
    
        # Export RKNN model
        print('--> Export RKNN model')
        ret = rknn.export_rknn(dst_rknn_model, target='rk3588')
        if ret != 0:
            print('Export yolov5rknn failed!')
            exit(ret)
        print('done')
    
        rknn.accuracy_analysis(
            inputs=[img_path],
            target='rk3588',
        )
    
    
        rknn.release()
    

    4.1.3 量化前后对比

    注意:rknn模型也可以通过Netron进行查看,不过要使用较新的版本,下面是yolov5未量化、int8量化、混合精度的对比。

    各种量化对比

    4.1.4 量化过程遇到的问题

    问题1:为何yolov5使用int8量化之后,推理速度反而变慢了?

    量化前后,输出的维度不一样了,batch_size 变成了32。这个是因为我在服务器上量化时,把batch size设置为了32,把batch_size设置为1即可。

    ret = rknn.build(do_quantization=True, dataset=dataset, rknn_batch_size=1)
    

    问题2:按照yolov5的前处理进行时,推理结果异常

    由于rknn默认使用的是data_format="nhwc",而正常yolov5使用的顺序是data_format="nchw",因此可以在前处理部分修改,也可以在inference调用是显示指定data_format="nchw"。但是在rk3588上使用data_format="nchw"时,提示不支持nchw,所以建议还是使用默认的“nhwc”顺序。

    rk_result = self._rk_session.inference(inputs=[img], data_format="nhwc")
    

    问题3:模型量化之后,后处理该如何做?

    直接按照未量化前的数据格式输入模式,int8量化模型推理的置信度会全为0;

    参考:

    解决方法:yolov5 3个卷积之后有sigmoid操作,如果这个时候使用int8量化,则输出的结果都是0。解决方法加载onnx模型时提取卷积之后的结果,然后手动写后处理后处理操作。

    # Load ONNX model
    ret = rknn.load_onnx(
        model=src_onnx_model,
        outputs=["/model.24/m.0/Conv_output_0", "/model.24/m.1/Conv_output_0", "/model.24/m.2/Conv_output_0"])
    

    问题4:yolov5 seg直接取output0和output1的输出进行后处理之后,可以获得正确的结果,但是取/model.22/Mul_2_output_0、/model.22/Split_output_1、/model.22/Concat_output_0、/model.22/proto/cv3/conv/Conv_output_0推理的结果存在截断的情况?
    <img src="https://img.haomeiwen.com/i1700062/cc11052edfe858e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="轮廓被截断" style="zoom:50%;" />

    原因分析,绘制出bbox,发现在后处理过程中,bbox缩放错误,导致了分割区域出现被切分的现象。

    问题5:yolov5 seg直接转rknn,使用默认的2个输出,不进行量化,但是推理仍然无结果
    原因分析:yolov5 seg训练时使用的参数是768x768,而使用export.py转onnx时,默认是640x640,虽然可以直接转rknn模型,但是推理无结果。解决方法是在导出脚本上添加模型的输入尺寸。

    python3 export.py --weights ../models/yolov5s-seg/13933/best.pt --include onnx --opset 12 --simplify --img-size 768 768
    

    4.2 yolov8-det量化

    4.2.1 普通量化

    不修改模型,导出默认的输出,由于sigmoid函数的影响,导致输出无结果,因此量化后保证模型可以使用,必须导出2个节点,或者6个节点。

    导出两个节点的RKNN模型(无需修改模型代码直接导出即可):

    import os
    from rknn.api import RKNN
    
    if __name__ == '__main__':
    
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        src_onnx_model = "models/yolov8s-det/13980/best_normal.onnx"
        dst_rknn_model = "models/yolov8s-det/13980/best_2_output_int8.rknn"
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(
            mean_values=[[0, 0, 0]],
            std_values=[[255, 255, 255]],
            target_platform='rk3588',
            quantized_dtype="asymmetric_quantized-8",
            quantized_method='channel',  # layer
            optimization_level=1  # 0  1  2  3
        )
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        ret = rknn.load_onnx(
            model=src_onnx_model,
            outputs=['/model.22/Mul_2_output_0', '/model.22/Split_output_1']
        )
        if ret != 0:
            print('Load yolov8 failed!')
            exit(ret)
        print('done')
    
        # Build model
        print('--> Building model')
        ret = rknn.build(do_quantization=True, dataset=dataset, rknn_batch_size=1)
        if ret != 0:
            print('Build yolov8 failed!')
            exit(ret)
        print('done')
    
        # Export RKNN model
        print(f'--> Export RKNN model {dst_rknn_model}')
        ret = rknn.export_rknn(dst_rknn_model)
        if ret != 0:
            print('Export yolov8rknn failed!')
            exit(ret)
        print('done')
    
        ret = rknn.accuracy_analysis(inputs=[img_path])
        if ret != 0:
            print('Accuracy analysis failed!')
        print(ret)
        print('done')
    

    导出6个输出的模型,需要先修改模型代码,再导出onnx,再转换。

    修改1:ultralytics/nn/modules/head.py,在class Detect新增“导出onnx增加”下的代码。同时把forward中的函数替换成下面的forward。

    class Detect(nn.Module):
        """YOLOv8 Detect head for detection models."""
        dynamic = False  # force grid reconstruction
        export = False  # export mode
        shape = None
        anchors = torch.empty(0)  # init
        strides = torch.empty(0)  # init
    
        # 导出onnx增加
        conv1x1 = nn.Conv2d(16, 1, 1, bias=False).requires_grad_(False)
        x = torch.arange(16, dtype=torch.float)
        conv1x1.weight.data[:] = nn.Parameter(x.view(1, 16, 1, 1))
        
        def forward(self, x):
            y = []
            for i in range(self.nl):
                t1 = self.cv2[i](x[i])
                t2 = self.cv3[i](x[i])
                y.append(self.conv1x1(t1.view(t1.shape[0], 4, 16, -1).transpose(2, 1).softmax(1)))
                # y.append(t2.sigmoid())
                y.append(t2)
            return y
    

    修改2:ultralytics/engine/exporter.py。在函数export_onnx()将output_names替换掉,如下:

    # output_names = ['output0', 'output1'] if isinstance(self.model, SegmentationModel) else ['output0']
    output_names = ['reg1', 'cls1',  'reg2', 'cls2',  'reg3', 'cls3']
    

    导出onnx

    import os
    from ultralytics import YOLO
    
    if __name__ == "__main__":
        input_height, input_width = 640, 640
        src_pt_model = "models/yolov8s-det/13980/best.pt"
    
        # Load a model
        model = YOLO(src_pt_model)  # load a custom trained model
    
        # Export the model
        model.export(
            format='onnx',
            imgsz=[input_height, input_width],
            opset=12,
            verbose=True,
            simplify=True
        )
    
        src_onnx_model = "models/yolov8s-det/13980/best.onnx"
        dst_onnx_model = "models/yolov8s-det/13980/best_6_output.onnx"
        os.rename(src_onnx_model, dst_onnx_model)
        print(f"rename {src_onnx_model} to {dst_onnx_model}")
    

    转换为6个输出的RKNN模型

    import os
    from rknn.api import RKNN
    
    
    if __name__ == '__main__':
    
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        src_onnx_model = "models/yolov8s-det/13980/best_6_output.onnx"
        dst_rknn_model = "models/yolov8s-det/13980/best_6_output_int8.rknn"
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588')
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        ret = rknn.load_onnx(
            model=src_onnx_model,
            outputs=['reg1', 'cls1',  'reg2', 'cls2',  'reg3', 'cls3']
        )
        if ret != 0:
            print('Load yolov8 failed!')
            exit(ret)
        print('done')
    
        # Build model
        print('--> Building model')
        ret = rknn.build(
            do_quantization=True,
            dataset=dataset,
            rknn_batch_size = 1
        )
        if ret != 0:
            print('Build yolov8 failed!')
            exit(ret)
        print('done')
    
        # Export RKNN model
        print(f'--> Export RKNN model to {dst_rknn_model}')
        ret = rknn.export_rknn(dst_rknn_model)
        if ret != 0:
            print('Export yolov8rknn failed!')
            exit(ret)
        print('done')
    
        ret = rknn.accuracy_analysis(inputs=[img_path], target="rk3588")
        if ret != 0:
            print('Accuracy analysis failed!')
        print(ret)
        print('done')
    
    

    4.2.2 混合精度量化

    混合量化步骤1:

    import os
    import shutil
    from rknn.api import RKNN
    
    
    if __name__ == "__main__":
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
    
        output_node_count = 2
        if output_node_count == 2:
            src_onnx_model = "models/yolov8s-det/13980/best_normal.onnx"
            tmp_model_dir = "models/yolov8s-det/13980/best_2_output_hybrid"
            os.makedirs(tmp_model_dir, exist_ok=True)
        elif output_node_count == 6:
            # 需要修改源码,实现best_6_output.onnx的导出
            src_onnx_model = "models/yolov8s-det/13980/best_6_output.onnx"
            tmp_model_dir = "models/yolov8s-det/13980/best_6_output_hybrid"
            os.makedirs(tmp_model_dir, exist_ok=True)
    
        # Create RKNN object
        rknn = RKNN()
    
        if not os.path.exists(src_onnx_model):
            print('model not exist')
            exit(-1)
    
        # pre-process config
        print('--> Config model')
        rknn.config(
            mean_values=[[0, 0, 0]],
            std_values=[[255, 255, 255]],
            target_platform='rk3588'
        )
        print('done')
    
        # Load ONNX model
        print('--> Loading model')
        if output_node_count == 2:
            ret = rknn.load_onnx(
                model=src_onnx_model,
                outputs=['/model.22/Mul_2_output_0', '/model.22/Split_output_1']
            )
            if ret != 0:
                print('Load yolov8 failed!')
                exit(ret)
        elif output_node_count == 6:
            ret = rknn.load_onnx(
                model=src_onnx_model,
                outputs=['reg1', 'cls1',  'reg2', 'cls2',  'reg3', 'cls3']
            )
            if ret != 0:
                print('Load yolov8 failed!')
                exit(ret)
            print('done')
        else:
            raise Exception(f"invalid output_node_count = {output_node_count}")
        print(f'output_node_count = {output_node_count}, done')
    
        # Build model
        rknn.hybrid_quantization_step1(
            dataset=dataset,
            rknn_batch_size=1,
            proposal=True,
            proposal_dataset_size=1
        )
    
        rknn.release()
    
        # move file
        file_prefix = os.path.basename(src_onnx_model)[0:-5]
        shutil.move(file_prefix + ".model", os.path.join(tmp_model_dir, file_prefix + ".model"))
        shutil.move(file_prefix + ".data", os.path.join(tmp_model_dir, file_prefix + ".data"))
        shutil.move(file_prefix + ".quantization.cfg", os.path.join(tmp_model_dir, file_prefix + ".quantization.cfg"))
        print(f"copy files to {tmp_model_dir}")
    

    混合量化步骤2:

    import os
    from rknn.api import RKNN
    
    if __name__ == "__main__":
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
    
        output_node_count = 2
        if output_node_count == 2:
            src_onnx_model = "models/yolov8s-det/13980/best_normal.onnx"
            tmp_model_dir = "models/yolov8s-det/13980/best_2_output_hybrid"
            dst_rknn_model = "models/yolov8s-det/13980/best_2_output_hybrid_custom.rknn"
            os.makedirs(tmp_model_dir, exist_ok=True)
        elif output_node_count == 6:
            # 需要修改源码,实现best_6_output.onnx的导出
            src_onnx_model = "models/yolov8s-det/13980/best_6_output.onnx"
            tmp_model_dir = "models/yolov8s-det/13980/best_6_output_hybrid"
            dst_rknn_model = "models/yolov8s-det/13980/best_6_output_hybrid.rknn"
            os.makedirs(tmp_model_dir, exist_ok=True)
    
        # Create RKNN object
        rknn = RKNN()
    
        # hybrid_quantization_step2, 输入均为第1步输出的文件
        file_prefix = os.path.basename(src_onnx_model)[0:-5]
        rknn.hybrid_quantization_step2(
            model_input=os.path.join(tmp_model_dir, file_prefix + ".model"),
            data_input=os.path.join(tmp_model_dir, file_prefix + ".data"),
            model_quantization_cfg= os.path.join(tmp_model_dir, file_prefix + ".quantization_custom.cfg")
        )
    
        # Export RKNN model
        print('--> Export RKNN model')
        ret = rknn.export_rknn(dst_rknn_model, target='rk3588')
        if ret != 0:
            print('Export yolov8rknn failed!')
            exit(ret)
        print('done')
    
        rknn.accuracy_analysis(
            inputs=[img_path],
            target='rk3588',
        )
    
        rknn.release()
    

    4.2.3 量化过程中遇到的问题

    问题1:yolov8导出onnx后,量化为int8之后,为什么置信度量化后全为0?

    因为sigmoid的值域(0,1),int8量化后就为0了。所以去掉sigmoid。导出下面红色框中的两层即可,'/model.22/Mul_2_output_0', '/model.22/Split_output_1'。

    yolov8去除的output

    参考:

    问题2:yolov8混合量化时报错?

    E hybrid_quantization_step1: Catch exception when building RKNN model!
    E hybrid_quantization_step1: Traceback (most recent call last):
    E hybrid_quantization_step1:   File "rknn/api/rknn_base.py", line 2109, in rknn.api.rknn_base.RKNNBase.hybrid_quantization_step1
    E hybrid_quantization_step1:   File "rknn/api/quantizer.py", line 265, in rknn.api.quantizer.Quantizer.save_hybrid_cfg
    E hybrid_quantization_step1:   File "rknn/api/quantizer.py", line 268, in rknn.api.quantizer.Quantizer.save_hybrid_cfg
    E hybrid_quantization_step1:   File "/opt/data/virtualenvs/yolov8/lib/python3.8/site-packages/ruamel/yaml/main.py", line 1229, in dump
    E hybrid_quantization_step1:     error_deprecation('dump', 'dump', arg="typ='unsafe', pure=True")
    E hybrid_quantization_step1:   File "/opt/data/virtualenvs/yolov8/lib/python3.8/site-packages/ruamel/yaml/main.py", line 1017, in error_deprecation
    E hybrid_quantization_step1:     sys.exit(1)
    E hybrid_quantization_step1: SystemExit: 1
    

    参考:https://github.com/laitathei/YOLOv8-ONNX-RKNN-HORIZON-TensorRT-Segmentation/tree/master

    ruamel_yaml版本的问题,默认安装的版本过高,导致执行失败,重新安装ruamel_yaml即可。

    pip3 install ruamel_yaml==0.17.40 -i https://pypi.tuna.tsinghua.edu.cn/simple
    

    问题3:yolov8 int8或者混合精度量化之后,无论使用1个输出、2个输出、6个输出,都无法检测结果,而fp16正常推理?

    原因分析:出现该问题主要在预处理上,yolov8的预处理默认进行了归一化即除掉了255,导出时使用mean_values=[[0, 0, 0]], std_values=[[1, 1, 1]]若使用fp16时是没问题的,但int8时,先除255归一化之后,输入的值可能已经是空了,所以很难推理出结果。

    解决方法,导出时使用mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]],预处理中把除255去掉。说明,量化之后在前处理时,就不能再Normalize。

    # image_data = np.array(img) / 255.0
    

    问题4:yolov8 int8量化之后,2个输出时,检测框会发生偏移,6个输出时,检测框偏大,但不偏移。

    原因分析:yolov8 2输出时, int8、fp16、hybrid转换之后,使用同一套前后处理代码,int8、hybrid之后的模型均存在检测框偏移的情况,从而可以推断是模型推理的问题。6个输出之所以不存在检测结果偏移的问题,是因为6个输出后都是基于fp32推理的。基于此,如果使用混合精度量化,模仿6输出,将6输出后的层全部设置为float16,应该就可以解决检测框偏移的问题。在best_6_output.quantization.cfg文件中添加一下内容,在生成混合精度的量化时,该问题解决。

    custom_quantize_layers:
        /model.22/cv2.2/cv2.2.0/conv/Conv_output_0: float16
        /model.22/cv2.2/cv2.2.0/act/Mul_output_0: float16
        /model.22/cv2.2/cv2.2.1/conv/Conv_output_0: float16
        /model.22/cv2.2/cv2.2.1/act/Mul_output_0: float16
        /model.22/cv2.2/cv2.2.2/Conv_output_0: float16
        /model.22/cv3.2/cv3.2.0/conv/Conv_output_0: float16
        /model.22/cv3.2/cv3.2.0/act/Mul_output_0: float16
        /model.22/cv3.2/cv3.2.1/conv/Conv_output_0: float16
        /model.22/cv3.2/cv3.2.1/act/Mul_output_0: float16
        /model.22/cv3.2/cv3.2.2/Conv_output_0: float16
        /model.22/Concat_2_output_0: float16
        /model.22/Reshape_2_output_0_shape4_/model.22/Concat_3: float16
        /model.22/cv2.1/cv2.1.0/conv/Conv_output_0: float16
        /model.22/cv2.1/cv2.1.0/act/Mul_output_0: float16
        /model.22/cv2.1/cv2.1.1/conv/Conv_output_0: float16
        /model.22/cv2.1/cv2.1.1/act/Mul_output_0: float16
        /model.22/cv2.1/cv2.1.2/Conv_output_0: float16
        /model.22/cv3.1/cv3.1.0/conv/Conv_output_0: float16
        /model.22/cv3.1/cv3.1.0/act/Mul_output_0: float16
        /model.22/cv3.1/cv3.1.1/conv/Conv_output_0: float16
        /model.22/cv3.1/cv3.1.1/act/Mul_output_0: float16
        /model.22/cv3.1/cv3.1.2/Conv_output_0: float16
        /model.22/Concat_1_output_0: float16
        /model.22/Reshape_1_output_0_shape4_/model.22/Concat_3: float16
        /model.22/cv2.0/cv2.0.0/conv/Conv_output_0: float16
        /model.22/cv2.0/cv2.0.0/act/Mul_output_0: float16
        /model.22/cv2.0/cv2.0.1/conv/Conv_output_0: float16
        /model.22/cv2.0/cv2.0.1/act/Mul_output_0: float16
        /model.22/cv2.0/cv2.0.2/Conv_output_0: float16
        /model.22/cv3.0/cv3.0.0/conv/Conv_output_0: float16
        /model.22/cv3.0/cv3.0.0/act/Mul_output_0: float16
        /model.22/cv3.0/cv3.0.1/conv/Conv_output_0: float16
        /model.22/cv3.0/cv3.0.1/act/Mul_output_0: float16
        /model.22/cv3.0/cv3.0.2/Conv_output_0: float16
        /model.22/Concat_output_0: float16
        /model.22/Reshape_output_0_shape4_/model.22/Concat_3: float16
        /model.22/Concat_3_output_0: float16
        /model.22/Split_output_0_shape4: float16
        /model.22/Split_output_1_shape4: float16
        /model.22/dfl/Reshape_output_0: float16
        /model.22/dfl/Softmax_pre_tp: float16
        /model.22/dfl/Softmax_new: float16
        /model.22/dfl/conv/Conv_output_0: float16
        /model.22/dfl/Reshape_1_output_0_shape4_/model.22/Slice_2split: float16
        /model.22/dfl/Reshape_1_output_0_shape4_/model.22/Slice_2split_conv_/model.22/Slice_2split: float16
        /model.22/Slice_output_0_shape4_before_conv: float16
        /model.22/Slice_1_output_0: float16
        /model.22/Slice_output_0: float16
        /model.22/Sub_output_0: float16
        /model.22/Add_1_output_0: float16
        /model.22/Sub_1_output_0: float16
        /model.22/Concat_4_swap_concat_reshape_i1_out: float16
        /model.22/Add_2_output_0: float16
        /model.22/Div_1_output_0: float16
        /model.22/Concat_4_swap_concat_reshape_i0_out: float16
        /model.22/Concat_4_output_0_shape4: float16
        /model.22/Mul_2_output_0_shape4_before: float16
        /model.22/Mul_2_output_0: float16
        /model.22/Split_output_1: float16
    

    说明量化之后模型的检测性能存在不稳定性,需要仔细分析,慎重量化。

    4.2.4 yolov8-det量化总结

    主要结论如下:

    • 默认输出1个output,output0,int8量化之后无推理结果,无需修改官方源码;
    • 2个output,'/model.22/Mul_2_output_0', '/model.22/Split_output_1',int8量化之后无推理结果,无需修改官方源码;
    • 6个output,'reg1', 'cls1', 'reg2', 'cls2', 'reg3', 'cls3',int8量化之后有推理结果,需要修改官方源码
    • 导出rknn时,mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]]使用这两个值,前处理不进行归一化,否则量化之后无法推理;
    • yolov8导出onnx前需要修改源代码,int8仅在导出2/6个输出的版本时可以正确推理;
    • 量化之后,输出节点为2个时,推理结果的检测框存在向左上偏移的现象;
    • 量化之后,输出节点为6个时,推理结果的检测框偏大,但不会发生偏移;
    • 未量化之前,输出节点为6个或者2时,推理结果的检测框均正常;
    • 建议先使用输出节点为6的量化模型,或者对2个输出节点的模型,定制化量化内容,再量化为混合精度。

    4.3 yolov8-seg量化

    yolov8默认输出两个节点,分别是output0和output1,output0对应的是目标检测结果和部分分割结果,output1是部分分割结果。因此可以从output0中提取/model.22/Mul_2_output_0、/model.22/Split_output_1、/model.22/Concat_output_0,从output1的上一层提取/model.22/proto/cv3/conv/Conv_output_0,构成四个节点的输出,防止量化后,因sigmoid操作导致无检测结果。

    • 输出两节点:'output0', 'output1', 量化之后无推理结果。
    • 输出四节点:'/model.22/Mul_2_output_0', '/model.22/Split_output_1', '/model.22/Concat_output_0','/model.22/proto/cv3/conv/Conv_output_0,量化之后可以推理。

    5 模型部署

    5.1 在板子上部署

    参考:yolov5篇---yolov5训练pt模型并转换为rknn模型,部署在RK3588开发板上——从训练到部署全过程 https://blog.csdn.net/m0_46825740/article/details/128818516

    下载rknpu2:https://github.com/rockchip-linux/rknpu2

    下面的命令是在边端设备上使用官方提供的代码,编译完成后,进行推理的示例。

    git clone https://github.com/rockchip-linux/rknpu2.git
    
    - examples/rknn_yolov5_demo => #define OBJ_CLASS_NUM 3  # 修改为对应类别数
    - coco_80_labels_list.txt   # 修改对应的标签
    
    ./build-linux_RK3588.sh
    ./rknn_yolov5_demo ./model/RK3588/yolov5s-640-640.rknn ./model/bus.jpg
    ./rknn_yolov5_video_demo ./model/RK3588/yolov5s-640-640.rknn 28ab8ba8a51a8e46eabc58575b6c208e.mp4 264
    ./rknn_yolov5_video_demo ./model/RK3588/yolov5s-640-640.rknn vlc-record-2023-09-22-11h14m49s.mp4 264
    

    5.2 问题

    问题1:固件中rknn_server版本较低
    解决方法:更新rknn_server【开发模式】

    cp /root/rknpu2/runtime/RK3588/Linux/rknn_server/aarch64/usr/bin/restart_rknn.sh /usr/bin
    cp /root/rknpu2/runtime/RK3588/Linux/rknn_server/aarch64/usr/bin/start_rknn.sh /usr/bin
    cp /root/rknpu2/runtime/RK3588/Linux/rknn_server/aarch64/usr/bin/rknn_server /usr/bin
    

    问题2:复制更新rknn_server后,执行提示库的版本不对

    root@firefly:/usr/bin# 1090443 RKNN SERVER loadRuntime(110): dlsym rknn_set_input_shapes failed: /lib/librknnrt.so: undefined symbol: rknn_set_input_shapes, reuqired librknnrt.so >= 1.5.2!
    E RKNN: [07:43:34.070] 6, 4
    E RKNN: [07:43:34.070] Invalid RKNN model version 6
    E RKNN: [07:43:34.070] rknn_init, load model failed!
    1090444 RKNN SERVER init(183): rknn_init fail! ret=-6
    1090444 RKNN SERVER process_msg_init(381): Client 1 init model fail!
    

    解决方法:复制对应的库,然后重启rknn_server即可

    cp /root/rknpu2/runtime/RK3588/Linux/librknn_api/aarch64/librknnrt.so /lib/
    restart_rknn.sh
    

    问题3:E RKNN: [12:54:27.331] Mismatch driver version, librknnrt version: 1.5.2 (c6b7b351a@2023-08-23T15:28:22) requires driver version >= 0.7.0, but you have driver version: 0.6.4 which is incompatible!

    原因及解决方法:出现这个错误,说明板子的固件版本较低,在官网下载对应的固件,并重新烧写即可。

    6 模型推理

    6.1 使用python rknn推理【服务器端】

    加载转换生成的rknn模型进行推理。这里有个关键点,即rknn.init_runtime(target='rk3588', core_mask=RKNN.NPU_CORE_AUTO),若target设置为对应的目标设备型号,则进行连板推理,也就是通过rknn_server在边缘设备上推理。若target设置为None,则在模拟器上进行推理。

    import os
    import cv2
    import tqdm
    from rknn.api import RKNN
    import multiprocessing
    
    def process(rknn_model, img_path):
        # Create RKNN object
        rknn = RKNN()
        if not os.path.exists(rknn_model):
            print('model not exist')
            exit(-1)
    
        rknn.load_rknn(rknn_model)
        rknn.init_runtime(target='rk3588', core_mask=RKNN.NPU_CORE_AUTO)
    
        img = cv2.imread(filename=img_path)
        img = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (640, 640))
    
        for item in tqdm.trange(10):
            result = rknn.inference(inputs=[img], data_format="nhwc")
            # print(result[0].shape)
    
        rknn.release()
    
    def main():
        img_path = "data/car.jpg"
        dataset = "data/dataset.txt"
        rknn_model = "models/yolov5s-det/13978/best_hybrid8_3_output.rknn"
        p_count = 1
        p_list = list()
        for _ in range(p_count):
            p = multiprocessing.Process(target=process, args=(rknn_model, img_path))
            p_list.append(p)
        for p in p_list:
            p.start()
        for p in p_list:
            p.join()
        print("process over")
    
    if __name__ == '__main__':
        main()
    

    6.2 使用python rknnlite推理【边缘设备端】

    参考rknn_toolkit_lite2\examples\inference_with_lite\test.py编写测试例子。

    import os
    import cv2
    import tqdm
    import time
    import platform
    import numpy as np
    from rknnlite.api import RKNNLite
    
    def get_host():
        device_compatible_node = '/proc/device-tree/compatible'
        # get platform and device type
        system = platform.system()
        machine = platform.machine()
        os_machine = system + '-' + machine
        if os_machine == 'Linux-aarch64':
            try:
                with open(device_compatible_node) as f:
                    device_compatible_str = f.read()
                    if 'rk3588' in device_compatible_str:
                        host = 'RK3588'
                    else:
                        host = 'RK356x'
            except IOError:
                print('Read device node {} failed.'.format(device_compatible_node))
                exit(-1)
        else:
            host = os_machine
        return host
    
    if __name__ == "__main__":
        host_name = get_host()
        print(host_name)
    
        rknn_file = "model/yolov5s-det/13978/best.rknn"
        rknn_file = "model/yolov5s-det/13978/best_int8.rknn"
    
    
        if not os.path.exists(rknn_file):
            print(f"{rknn_file} not exist")
            exit(0)
    
        rknn = RKNNLite(verbose=False, verbose_file="verbose.log")
        rknn.load_rknn(rknn_file)
        rknn.init_runtime(target=host_name, core_mask=RKNNLite.NPU_CORE_AUTO)
    
        ori_img = cv2.imread('./data/car.jpg')
        img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2RGB)
        resized_image = cv2.resize(img, (640, 640))
    
        start_time = time.time()
        for i in tqdm.tqdm(range(100)):
            outputs = rknn.inference(inputs=[resized_image])
            print(len(outputs), outputs[0].shape)
        print(f"cost: {time.time() - start_time}, fps: {100 / (time.time() - start_time):.4f}")
    
        rknn.release()
    
    

    6.3 问题

    问题1:AttributeError: rknnlite/api/lib/hardware/DOLPHIN/linux-aarch64/librknn_api.so: undefined symbol: rknn_set_core_mask
    解决方法:出现这个问题的原因是因为librknn_api.so的库是有问题的,直接将rknpu2库中的librknnrt.so 拷贝过去即可,执行命令如下:

    # 备份
    cp /opt/virtualenvs/rknn/lib/python3.8/site-packages/rknnlite/api/lib/hardware/DOLPHIN/linux-aarch64/librknn_api.so /opt/virtualenvs/rknn/lib/python3.8/site-packages/rknnlite/api/lib/hardware/DOLPHIN/linux-aarch64/librknn_api.so.bak
    
    # 拷贝    
    cp /root/rknpu2/runtime/RK3588/Linux/librknn_api/aarch64/librknnrt.so /opt/virtualenvs/rknn/lib/python3.8/site-packages/rknnlite/api/lib/hardware/DOLPHIN/linux-aarch64/librknn_api.so
    

    问题2:W RKNN: [01:59:35.391] Output(output0):size_with_stride larger than model origin size, if need run OutputOperator in NPU, please call rknn_create_memory using size_with_stride.

    这个告警暂时可以忽略,可以在代码中屏蔽告警。参考yolov5的前后处理:https://github.com/Applied-Deep-Learning-Lab/Yolov5_RK3588/blob/main/base/rk3588.py

    from hide_warnings import hide_warnings
    @hide_warnings
    def rk_infer(self, img):
        rk_result = self._rk_session.inference(inputs=[img], data_format="nhwc")
        return rk_result
    

    问题3:如何选中使用的NPU,即如何设置core_mask?

    self._rk_session.init_runtime(target=self.host_name, core_mask=RKNNLite.NPU_CORE_AUTO)
    self._rk_session.init_runtime(target=self.host_name, core_mask=RKNNLite.NPU_CORE_0_1_2)
    

    解决方法:通过RKNNLite.NPU_CORE_AUTO/RKNNLite.NPU_CORE_0_1_2/RKNNLite.NPU_CORE_0都可以设置使用那个NPU,测试发现NPU_CORE_AUTO NPU的利用率最高。

    7 其他

    A1 镜像烧录步骤

    参考
    -【ROC-RK3568-PC开发板试用体验】烧录Ubuntu20.04系统:https://dev.t-firefly.com/thread-124315-1-1.html

    步骤1:安装驱动

    • 下载DriverAssitant_v5.0.zip,解压,然后运行里面的DriverInstall.exe。

    注意若通过linux连接到板子时,需要安装驱动adb

    apt install adb
    

    步骤2.连接设备

    • 使用Type-c数据线连接设备USB_OTG接口,USB连接PC端,查看设备管理器(以下为安装驱动成功)

    步骤3:整体固件【在官网下载对应的固件https://www.t-firefly.com/doc/download/183.html

    • 下载网盘上所提供的固件,或者是选择自己编译出来的固件,更新固件分为单个整体固件和分区镜像;

    1)选择整体固件方式:解压RK官方烧录工具RKDevTool_Release_v2.92.zip,打开RKDevTool.exe,会自动识别到ADB设备;
    2)按【切换】按钮,进入loader烧录模式;
    3)按【固件】按钮,选择要升级的固件文件,加载固件之后,然后点击【升级】按钮,等待烧写为完成即可

    步骤4:镜像烧录完之后,安装必要的软件

    apt install git cmake g++ -y 
    apt install lrzsz tree vim htop -y 
    

    A2 RK3588运行Docker

    检测板子中Docker的运行环境

    git clone https://github.com/moby/moby.git
    cd moby/contrib
    ./check-config.sh .config
    

    参考:https://blog.csdn.net/xiaoning132/article/details/130541520

    板子中安装Docker

    ## 卸载旧版本
    sudo apt-get remove docker docker-engine docker.io containerd runc
    
    ## 设置存储库
    sudo apt-get update -y
    sudo apt-get install -y ca-certificates curl gnupg lsb-release
    
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    
    echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    ## 安装 Docker
    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
    ## 验证是否安装成功
    
    sudo docker run hello-world
    
    

    参考:https://github.com/DHDAXCW/Rk3588-Docker

    A3 GStreamer拉流

    参考:

    安装gstreamer

    
    apt-get update
    
    apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio
    
    apt-get install libunwind8-dev
    
    apt-get install libgtk2.0-dev pkg-config
    
    # 查看安装结果
    dpkg -l | grep gstreamer
    
    

    使用命令测试gstreamer

    # hello world
    gst-launch-1.0 videotestsrc ! videoconvert ! autovideosink
    
    # Adding a capability to the pipeline
    gst-launch-1.0 videotestsrc ! video/x-raw, format=BGR ! autovideoconvert ! ximagesink
    
    # Setting width, height and framerate
    gst-launch-1.0 videotestsrc ! video/x-raw, format=BGR ! autovideoconvert ! videoconvert ! video/x-raw, width=640, height=480, framerate=1/2 ! ximagesink
    
    # rtsp
    gst-launch-1.0 rtspsrc location=rtsp://172.18.18.202:5554/T_Perimeter_ball001 latency=10 ! queue ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=480 ! ximagesink
    
    

    使用opencv测试gstreamner

    步骤1:重新编译opencv。opencv默认未开启gstreamer的支持,所以需要先重新编译opencv

    git clone https://github.com/opencv/opencv.git
    cd opencv 
    mkdir build && cd build
    
    
    cmake -D WITH_GSTREAMER=ON \
    -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D BUILD_opencv_python2=OFF \
    -D BUILD_opencv_python3=ON \
    -D PYTHON3_PACKAGES_PATH=/usr/local/lib/python3.8/dist-packages/ \
    -D PYTHON3_LIBRARY=/usr/lib/python3.8/config-3.8-aarch64-linux-gnu/libpython3.8.so \
    -D OPENCV_GENERATE_PKGCONFIG=YES ..
    
    
    make -j6 && make install
    
    cd /etc/ld.so.conf.d/ # 切换目录
    touch opencv.conf # 新建opencv配置文件
    echo /usr/local/lib/ > opencv.conf # 填写opencv编译后库所在的路径
    sudo ldconfig
    
    

    步骤2:使用下面脚本测试gstreamer

    # opencv【需要重新编译opencv并启用WITH_GSTREAMER=ON】
    
    import cv2
    gstreamer_str = "sudo gst-launch-1.0 rtspsrc location=rtsp://172.18.18.202:5554/T_Perimeter_ball001 latency=1000 ! queue ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=480,format=BGR ! appsink drop=1"
    cap = cv2.VideoCapture(gstreamer_str, cv2.CAP_GSTREAMER)
    print(cap.isOpened())
    while(cap.isOpened()):
        ret, frame = cap.read()
        if ret:
            cv2.imshow("Input via Gstreamer", frame)
            if cv2.waitKey(25) & 0xFF == ord('q'):
                break
    
    cap.release()
    cv2.destroyAllWindows()
    
    

    遇到的问题

    问题1:未安装libgtk2.0-dev导致

    Traceback (most recent call last):
      File "opencv_demo.py", line 14, in <module>
        cv2.destroyAllWindows()
    cv2.error: OpenCV(4.8.0-dev) /root/code/opencv/modules/highgui/src/window.cpp:1266: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows'
    

    问题2:无法运行gstream,camke之后GStreamer显示为No

    Video I/O:
      DC1394:                      YES (2.2.5)
      FFMPEG:                      YES
        avcodec:                   YES (58.54.100)
        avformat:                  YES (58.29.100)
        avutil:                    YES (56.31.100)
        swscale:                   YES (5.5.100)
        avresample:                YES (4.0.0)
      GStreamer:                   NO
      v4l/v4l2:                    YES (linux/videodev2.h)
    

    原因:没有安装apt-get install libunwind8-dev,导致的。可以使用下面的代码查看是否支持gstreamer

    import cv2
    print(cv2.getBuildInformation())
    

    问题3:fail to load module gail

    解决方法:出现这个问题的原因是因为librknn_api

    sudo apt-get install libgail-common
    

    GStreamer Python:https://github.com/GStreamer/gst-python
    https://gist.github.com/liviaerxin/9934a5780f5d3fe5402d5986fc32d070

    git clone https://github.com/GStreamer/gst-python.git
    cd gst-python
    
    GSTREAMER_VERSION=$(gst-launch-1.0 --version | grep version | tr -s ' ' '\n' | tail -1)
    git checkout $GSTREAMER_VERSION
    
    PYTHON=/usr/bin/python3.8
    LIBPYTHON=$($PYTHON -c 'from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))')
    LIBPYTHONPATH=$(dirname $(ldconfig -p | grep -w $LIBPYTHON | head -1 | tr ' ' '\n' | grep /))
    PREFIX=$(dirname $(dirname $(which python3))) # in jetson nano, `PREFIX=~/.local` to use local site-packages,
    
    LIBPYTHON=libpython3.8.so
    LIBPYTHONPATH=/lib/aarch64-linux-gnu
    PREFIX=/usr
    
    ./autogen.sh --disable-gtk-doc --noconfigure
    ./configure --with-libpython-dir=$LIBPYTHONPATH --prefix $PREFIX
    
    make
    make install
    

    错误:No package 'pygobject-3.0' found

    apt install -y python-gi-dev

    错误:checking for headers required to compile python extensions... not found
    configure: error: could not find Python headers

    sudo apt-get install python3-dev libpython3-dev

    checking for PYGOBJECT... yes
    checking for libraries required to embed python... no
    configure: error: Python libs not found. Windows requires Python modules to be explicitly linked to libpython
    

    相关文章

      网友评论

          本文标题:瑞芯微板子使用探索【RK3588】

          本文链接:https://www.haomeiwen.com/subject/rogaidtx.html