基于RK3588 板子实现yolov5 模型转化推理—-实测python 版

1. yolov5 模型训练

模型训练可以基于ubuntu  or windowns

   1.1 下载yolov5模型
YOLOV5https://github.com/ultralytics/yolov5

  1.2 模型训练

      可以参考官方文档,这里就不详细介绍了。将训练好的权重文件备用

     

2. Pt —>ONNX 模型转化

    2.1在训练好的pt 模型后,找到yolov5 文件下export.py 文件

   

     2.2 修改export 下模型文件,配置文件等数据

 修改 “opset”   修改onnx  的版本,要改为11or 12 

 如果没有安装onnx  则执行

pip  install  onnx==1.12

 最后修改转化文件的类型,onnx

在找到 models/yolo.py 文件

将原本的 forward(self,x )  函数用一下代码替代:注意,仅在模型转化时才替换一下该函数,若执行模型训练和推理时,记得要修改会原来代码。

    def forward(self, x):

        z = []  # inference output

        for i in range(self.nl):
            z.append(torch.sigmoid(self.m[i](x[i])))  # conv output

        return z

最后执行export.py 文件,生成onnx 模型结果如下:

3.ONNX—>RKNN 模型转化

    onnx 模型转为rknn 需要在ubuntu 环境下

   3.1 rknn 官方代码下载

      RKNN_YOLOV5_DEMOhttps://github.com/rockchip-linux/rknn-toolkit2/tree/master/rknpu2/examples/rknn_yolov5_demo

     1.1安装环境

        创建虚拟环境,创建一个新的 conda 环境,python版本设置为3.6、3.8或者3.10

conda create -n rknn python=3.8

        激活环境 

conda activate rknn

       安装rknn 工具包:下载rknn 工具包

$ pip install rknn_toolkit2-1.5.0+1fa95b5c-cp38-cp38-linux_x86_64.whl

     确认安装成功 检查 RKNN-Toolkit2 是否安装成功

(rknn)$ python
(rknn)$ from rknn.api import RKNN

    如未报错说明安装成功

   3.2 修改onnx2rknn.py 

 platform = 'rk3588'
    exp = 'face_best'
    Width = 640
    Height = 640
    # Model from https://github.com/airockchip/rknn_model_zoo
    MODEL_PATH = '/rk3588/rknpu2/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/face-1best.onnx' 
    NEED_BUILD_MODEL = True
    # NEED_BUILD_MODEL = False
    im_file = '/ydemo_ai/rknpu2/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/frame_1.jpg'

3.3 执行onnx2rknn.py 

4.RKNN 模型推理

 4.1将生成的rknn 模型移到板子中,板子中的同样安装rknn 工具包

rknn_toolkit_lite2https://github.com/rockchip-linux/rknn-toolkit2/tree/master/rknn_toolkit_lite2找到 rknn_toollkit_lite2/packages ,里面有所需环境安装包, 上文在ubuntu下安装的是 rknn-toolkit 注意区分,一个是ubuntu 工具环境,  lite2 为 rknn 板端环境。

创建虚拟环境,记得使用miconda   安装教程可自行搜索

在虚拟环境下安装;

pip  install rknn_toolkit_lite2-2.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

4.2 推理代码 demo

修改模型地址和测试文件即可,支持图像,视频.

from copy import copy
import time
import numpy as np
import cv2
from rknnlite.api import RKNNLite

RKNN_MODEL = '/home/linaro/rknn-toolkit-lite2/onnx2rknn/face_best-640-640_rk3588.rknn'
# IMG_PATH = './input/240513_00000741.jpg'
OBJ_THRESH = 0.25
NMS_THRESH = 0.45
IMG_SIZE = (640, 640)
OUTPUT_VIDEO_PATH = 'output_1.mp4'
BOX = (0, 0, 640, 640)

# CLASSES = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
#            'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
#            'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
#            'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
#            'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
#            'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
#            'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
#            'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
#            'hair drier', 'toothbrush']
# CLASSES =  ['tv', 'phone', 'battery', 'clothes', 'seat', 'people', 'Keyboard']

CLASSES =  ['PERSON']

anchors = [[[10, 13], [16, 30], [33, 23]],
           [[30, 61], [62, 45], [59, 119]],
           [[116, 90], [156, 198], [373, 326]]]

class Letter_Box_Info():
    def __init__(self, shape, new_shape, w_ratio, h_ratio, dw, dh, pad_color) -> None:
        self.origin_shape = shape
        self.new_shape = new_shape
        self.w_ratio = w_ratio
        self.h_ratio = h_ratio
        self.dw = dw
        self.dh = dh
        self.pad_color = pad_color

def box_process(position, anchors):
    grid_h, grid_w = position.shape[2:4]
    col, row = np.meshgrid(np.arange(0, grid_w), np.arange(0, grid_h))  # (80, 80) (80, 80)
    col = col.reshape(1, 1, grid_h, grid_w)    # (1, 1, 80, 80)
    row = row.reshape(1, 1, grid_h, grid_w)
    grid = np.concatenate((col, row), axis=1)  # (1, 2, 80, 80)
    stride = np.array([IMG_SIZE[1]//grid_h, IMG_SIZE[0]//grid_w]).reshape(1,2,1,1)  # 8 8

    col = col.repeat(len(anchors), axis=0)
    row = row.repeat(len(anchors), axis=0)
    anchors = np.array(anchors)
    anchors = anchors.reshape(*anchors.shape, 1, 1)  # (3, 2, 1, 1)

    box_xy = position[:,:2,:,:]*2 - 0.5
    box_wh = pow(position[:,2:4,:,:]*2, 2) * anchors

    box_xy += grid
    box_xy *= stride
    box = np.concatenate((box_xy, box_wh), axis=1)   # (3, 4, 80, 80)

    # Convert [c_x, c_y, w, h] to [x1, y1, x2, y2]
    xyxy = np.copy(box)
    xyxy[:, 0, :, :] = box[:, 0, :, :] - box[:, 2, :, :]/ 2  # top left x
    xyxy[:, 1, :, :] = box[:, 1, :, :] - box[:, 3, :, :]/ 2  # top left y
    xyxy[:, 2, :, :] = box[:, 0, :, :] + box[:, 2, :, :]/ 2  # bottom right x
    xyxy[:, 3, :, :] = box[:, 1, :, :] + box[:, 3, :, :]/ 2  # bottom right y

    return xyxy
#
def filter_boxes(boxes, box_confidences, box_class_probs):
    """Filter boxes with object threshold.
    """
    box_confidences = box_confidences.reshape(-1)
    class_max_score = np.max(box_class_probs, axis=-1)
    classes = np.argmax(box_class_probs, axis=-1)


    _class_pos = np.where(class_max_score* box_confidences >= OBJ_THRESH)
    scores = (class_max_score* box_confidences)[_class_pos]

    boxes = boxes[_class_pos]
    classes = classes[_class_pos]

    return boxes, classes, scores

# def filter_boxes(boxes, box_confidences, box_class_probs):
#     """Filter boxes with object threshold.
#     """
#     boxes = boxes.reshape(-1, 4)
#     box_confidences = box_confidences.reshape(-1)
#     box_class_probs = box_class_probs.reshape(-1, box_class_probs.shape[-1])
#
#     _box_pos = np.where(box_confidences >= OBJ_THRESH)
#     boxes = boxes[_box_pos]
#     box_confidences = box_confidences[_box_pos]
#     box_class_probs = box_class_probs[_box_pos]
#
#     class_max_score = np.max(box_class_probs, axis=-1)
#     classes = np.argmax(box_class_probs, axis=-1)
#     _class_pos = np.where(class_max_score >= OBJ_THRESH)
#
#     boxes = boxes[_class_pos]
#     classes = classes[_class_pos]
#     scores = (class_max_score * box_confidences)[_class_pos]
#
#     return boxes, classes, scores

def nms_boxes(boxes, scores):
    """Suppress non-maximal boxes.
    # Returns
        keep: ndarray, index of effective boxes.
    """
    x = boxes[:, 0]
    y = boxes[:, 1]
    w = boxes[:, 2] - boxes[:, 0]
    h = boxes[:, 3] - boxes[:, 1]

    areas = w * h
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)

        xx1 = np.maximum(x[i], x[order[1:]])
        yy1 = np.maximum(y[i], y[order[1:]])
        xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
        yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])

        w1 = np.maximum(0.0, xx2 - xx1 + 0.00001)
        h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
        inter = w1 * h1

        ovr = inter / (areas[i] + areas[order[1:]] - inter)
        inds = np.where(ovr <= NMS_THRESH)[0]
        order = order[inds + 1]
    keep = np.array(keep)
    return keep

def post_process(input_data, anchors):
    boxes, scores, classes_conf = [], [], []
    # 1*255*h*w -> 3*85*h*w
    input_data = [_in.reshape([len(anchors[0]),-1]+list(_in.shape[-2:])) for _in in input_data]
    for i in range(len(input_data)):                                    # (3, 85, 80, 80)
        boxes.append(box_process(input_data[i][:,:4,:,:], anchors[i]))  # (3, 4, 80, 80)
        scores.append(input_data[i][:,4:5,:,:])                         # (3, 1, 80, 80)
        classes_conf.append(input_data[i][:,5:,:,:])                    # (3, 80, 80, 80)

    def sp_flatten(_in):
        ch = _in.shape[1]
        _in = _in.transpose(0,2,3,1)
        return _in.reshape(-1, ch)

    boxes = [sp_flatten(_v) for _v in boxes]                  # (3, 19200, 4)
    classes_conf = [sp_flatten(_v) for _v in classes_conf]    # (3, 19200, 80)
    scores = [sp_flatten(_v) for _v in scores]                # (3, 19200, 1)

    boxes = np.concatenate(boxes)                  # (25200, 4)
    classes_conf = np.concatenate(classes_conf)    # (25200, 80)
    scores = np.concatenate(scores)                # (25200, 1)

    # filter according to threshold
    boxes, classes, scores = filter_boxes(boxes, scores, classes_conf)
    # (12, 4)  12  12

    # nms
    nboxes, nclasses, nscores = [], [], []

    for c in set(classes):
        inds = np.where(classes == c)
        b = boxes[inds]
        c = classes[inds]
        s = scores[inds]
        keep = nms_boxes(b, s)

        if len(keep) != 0:
            nboxes.append(b[keep])
            nclasses.append(c[keep])
            nscores.append(s[keep])

    if not nclasses and not nscores:
        return None, None, None

    boxes = np.concatenate(nboxes)
    classes = np.concatenate(nclasses)
    scores = np.concatenate(nscores)

    return boxes, classes, scores

def draw(image, boxes, scores, classes):
    for box, score, cl in zip(boxes, scores, classes):
        print("cl:",cl)
        top, left, right, bottom = [int(_b) for _b in box]
        print("%s @ (%d %d %d %d) %.3f" % (CLASSES[cl], top, left, right, bottom, score))
        cv2.rectangle(image, (top, left), (right, bottom), (255, 0, 0), 2)
        cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
                    (top, left - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

def letterbox(im, new_shape=(640, 640), color=(0, 0, 0), letter_box_info_list=[]):
    shape = im.shape[:2]  # current shape [height, width]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])

    ratio = r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
    # dw, dh = np.mod(dw, 32), np.mod(dh, 32)

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    letter_box_info_list.append(Letter_Box_Info(shape, new_shape, ratio, ratio, dw, dh, color))
    return im, letter_box_info_list

def get_real_box(box, in_format='xyxy', letter_box_info_list=[]):
    bbox = copy(box)
    # unletter_box result
    if in_format=='xyxy':
        bbox[:,0] -= letter_box_info_list[-1].dw
        bbox[:,0] /= letter_box_info_list[-1].w_ratio
        bbox[:,0] = np.clip(bbox[:,0], 0, letter_box_info_list[-1].origin_shape[1])

        bbox[:,1] -= letter_box_info_list[-1].dh
        bbox[:,1] /= letter_box_info_list[-1].h_ratio
        bbox[:,1] = np.clip(bbox[:,1], 0, letter_box_info_list[-1].origin_shape[0])

        bbox[:,2] -= letter_box_info_list[-1].dw
        bbox[:,2] /= letter_box_info_list[-1].w_ratio
        bbox[:,2] = np.clip(bbox[:,2], 0, letter_box_info_list[-1].origin_shape[1])

        bbox[:,3] -= letter_box_info_list[-1].dh
        bbox[:,3] /= letter_box_info_list[-1].h_ratio
        bbox[:,3] = np.clip(bbox[:,3], 0, letter_box_info_list[-1].origin_shape[0])
    return bbox


if __name__ == '__main__':

    def sigmoid(x):
        return 1 / (1 + np.exp(-x))

 

    rknn = RKNNLite()

    print('--> Load RKNN model')
    ret = rknn.load_rknn(RKNN_MODEL)
    if ret != 0:
        print('Load RKNN model failed')
        exit(ret)
    print('done')
    ret = rknn.init_runtime()
    if ret != 0:
        print('Init runtime environment failed!')
        exit(ret)
    print('done')

    cap = cv2.VideoCapture(0)

    # # 获取视频的一些属性
    # fps = cap.get(cv2.CAP_PROP_FPS)
    # width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    # height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # 创建 VideoWriter 对象
    # x, y, w, h = BOX
    # fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # 或者使用 'XVID'
    # out = cv2.VideoWriter(OUTPUT_VIDEO_PATH, fourcc, fps, (w, h))

    # fps = 0.0
    # while True:
    t1 = time.time()
    # 读取一帧
    # ret, frame1 = cap.read()
    # if not ret:
    #     break
    # img0 = frame1
    img0 = cv2.imread('/home/linaro/rknn-toolkit-lite2/predict_img/office_data.jpg')
    # img0 = img0 / 255.0 
    # img0 = img0.astype(np.int8)
    # 加载帧
    # img0 = frame
    img, letter_box_info_list = letterbox(im= img0.copy(), new_shape=(IMG_SIZE[1], IMG_SIZE[0]))  # padded resize

    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # HWC to CHW, BGR to RGB
    # img = np.expand_dims(img, axis=0)
    # img = img.astype(np.int8)
    if len(img.shape) == 3:
        img = img[None]  # expand for batch dim

    outputs = rknn.inference(inputs=[img])  # Inference

    boxes, classes, scores = post_process(outputs, anchors)
    print("classes",classes)
    print("scores",scores)
    boxes_filter, scores_filter, classes_filter = [0, 0, 0, 0], [], []
    # scores_filter = np.array(scores)
    # classes_filter = np.array(classes)
    # processed_scores = sigmoid(scores_filter)
    # processed_classes = sigmoid(classes_filter)
    # print("scores_filter",processed_scores)
    # print("classes_filter",processed_classes)
    mask = scores > 0.4

    scores_filter = scores[mask]
    boxes_filter = boxes[mask]
    classes_filter = classes[mask]

    # 筛选 scores 和对应的 boxes
    # scores_filter = processed_scores[mask]
    # boxes_filter = boxes[mask]
    # classes_filter = processed_classes[mask]
    # max_box = [0, 0, 0, 0]
    # for box, score, cl in zip(boxes, scores, classes):
    #     print("cl",cl)
    #     # if cl == 0:
    #         # if (box[2]-box[0])*(box[3]-box[1]) > (max_box[2]-max_box[0])*(max_box[3]-max_box[1]):
    #     max_box = box
    #     boxes_filter = np.expand_dims(max_box, axis=0)
    #     scores_filter = np.expand_dims(score, axis=0)
    #     classes_filter = np.expand_dims(cl, axis=0)
    # img_p = img0.copy()

    # print("boxes_filter",boxes_filter)
    draw(img0, get_real_box(boxes_filter, 'xyxy', letter_box_info_list), scores_filter, classes_filter)
    print("run time:",time.time()-t1)
    # cv2.namedWindow('Window', cv2.WINDOW_NORMAL) 
    # cv2.imshow("Window",img0)
    # cv2.waitKey(1)
    cv2.imwrite(f"./res/{time.time()}.jpg", img0)
    print("run time:",time.time()-t1)

        # out.write(img_p)

    rknn.release()

推理结果:

作者:码农的日常搅屎棍

物联沃分享整理
物联沃-IOTWORD物联网 » 基于RK3588 板子实现yolov5 模型转化推理—-实测python 版

发表回复