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

瑞芯微板子使用探索【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開發(fā)流程

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

RKNN開發(fā)流程

上圖中包含了RKNN模型開發(fā)中用到的所用項目:

① 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的模擬器中進行推理;

  • 性能評估,提供了模型性能分析函數(shù),如:accuracy_analysis、eval_perf、eval_memory等接口,可以查看項目倉庫中rknn-toolkit2/doc以及rknn-toolkit2/rknn_toolkit_lite2/docs中的文檔。

  • 開發(fā)需要的文檔基本都在該倉庫中,深入開發(fā)需仔細閱讀對應的開發(fā)文檔。

  • 目錄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 開發(fā)的C SDK,運行在邊端。

④ rknn_server:盒子中的服務程序,用于接收連板運行的名。盒子啟動后,默認啟動。若連板推理時,發(fā)現(xiàn)版本不匹配,可以從RKNPU2中復制對應的動態(tài)庫, 如:rknpu2/runtime/RK3588/Linux/librknn_api/aarch64/librknnrt.so。

3 模型轉換

3.1 pth轉ONNX

3.1.1 yolov5-det轉onnx

yolov5官網(wǎng)提供了export.py腳本,環(huán)境配置好后,執(zhí)行該腳本即可。

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的開發(fā)環(huán)境,因為涉及修改yolov8的部分代碼,所以最好直接將yolov8代碼下載下來,單獨構建一個yolov8的運行環(huán)境;

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   # 否則安裝很多依賴包且報錯,后續(xù)需要時再重新安裝
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安裝即可

參考:

環(huán)境搭建好之后,調用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中設置量化的相關參數(shù),可以參考《Rockchip_User_Guide_RKNN_Toolkit2_CN-1.5.2.pdf》
  • rknn.load_onnx,這里要指定outputs,用于截掉網(wǎng)絡中無法使用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的節(jié)點。若第一步驟中的proposal=False,這里必須手動修改。若proposal=True,則自動生成需要修改的節(jié)點。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:模型量化之后,后處理該如何做?

直接按照未量化前的數(shù)據(jù)格式輸入模式,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://upload-images.jianshu.io/upload_images/1700062-cc11052edfe858e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="輪廓被截斷" style="zoom:50%;" />

原因分析,繪制出bbox,發(fā)現(xiàn)在后處理過程中,bbox縮放錯誤,導致了分割區(qū)域出現(xiàn)被切分的現(xiàn)象。

問題5:yolov5 seg直接轉rknn,使用默認的2個輸出,不進行量化,但是推理仍然無結果
原因分析:yolov5 seg訓練時使用的參數(shù)是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函數(shù)的影響,導致輸出無結果,因此量化后保證模型可以使用,必須導出2個節(jié)點,或者6個節(jié)點。

導出兩個節(jié)點的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中的函數(shù)替換成下面的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。在函數(shù)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:
        # 需要修改源碼,實現(xiàn)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:
        # 需要修改源碼,實現(xiàn)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版本的問題,默認安裝的版本過高,導致執(zhí)行失敗,重新安裝ruamel_yaml即可。

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

問題3:yolov8 int8或者混合精度量化之后,無論使用1個輸出、2個輸出、6個輸出,都無法檢測結果,而fp16正常推理?

原因分析:出現(xiàn)該問題主要在預處理上,yolov8的預處理默認進行了歸一化即除掉了255,導出時使用mean_values=[[0, 0, 0]], std_values=[[1, 1, 1]]若使用fp16時是沒問題的,但int8時,先除255歸一化之后,輸入的值可能已經(jīng)是空了,所以很難推理出結果。

解決方法,導出時使用mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]],預處理中把除255去掉。說明,量化之后在前處理時,就不能再Normalize。

# image_data = np.array(img) / 255.0

問題4:yolov8 int8量化之后,2個輸出時,檢測框會發(fā)生偏移,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

說明量化之后模型的檢測性能存在不穩(wěn)定性,需要仔細分析,慎重量化。

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個輸出的版本時可以正確推理;
  • 量化之后,輸出節(jié)點為2個時,推理結果的檢測框存在向左上偏移的現(xiàn)象;
  • 量化之后,輸出節(jié)點為6個時,推理結果的檢測框偏大,但不會發(fā)生偏移;
  • 未量化之前,輸出節(jié)點為6個或者2時,推理結果的檢測框均正常;
  • 建議先使用輸出節(jié)點為6的量化模型,或者對2個輸出節(jié)點的模型,定制化量化內容,再量化為混合精度。

4.3 yolov8-seg量化

yolov8默認輸出兩個節(jié)點,分別是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,構成四個節(jié)點的輸出,防止量化后,因sigmoid操作導致無檢測結果。

  • 輸出兩節(jié)點:'output0', 'output1', 量化之后無推理結果。
  • 輸出四節(jié)點:'/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開發(fā)板上——從訓練到部署全過程 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  # 修改為對應類別數(shù)
- 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【開發(fā)模式】

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后,執(zhí)行提示庫的版本不對

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!

原因及解決方法:出現(xiàn)這個錯誤,說明板子的固件版本較低,在官網(wǎng)下載對應的固件,并重新燒寫即可。

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
解決方法:出現(xiàn)這個問題的原因是因為librknn_api.so的庫是有問題的,直接將rknpu2庫中的librknnrt.so 拷貝過去即可,執(zhí)行命令如下:

# 備份
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,測試發(fā)現(xiàn)NPU_CORE_AUTO NPU的利用率最高。

7 其他

A1 鏡像燒錄步驟

參考
-【ROC-RK3568-PC開發(fā)板試用體驗】燒錄Ubuntu20.04系統(tǒng):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數(shù)據(jù)線連接設備USB_OTG接口,USB連接PC端,查看設備管理器(以下為安裝驅動成功)

步驟3:整體固件【在官網(wǎng)下載對應的固件https://www.t-firefly.com/doc/download/183.html

  • 下載網(wǎng)盤上所提供的固件,或者是選擇自己編譯出來的固件,更新固件分為單個整體固件和分區(qū)鏡像;

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的運行環(huán)境

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

解決方法:出現(xiàn)這個問題的原因是因為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
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容