YOLOv8 Model Converted Successfully, but Incorrect Inference Results on VIM3 (KSNN 1.4 / KSNN 1.3)

Which system do you use? Android, Ubuntu, OOWOW or others?

VIM3_Ubuntu-gnome-focal_Linux-4.9_arm64_SD-USB_V1.0.7-210625

NPU驱动版本如下

Which version of system do you use? Please provide the version of the system here:

Ubuntu20.04.2

Please describe your issue below:

我使用 aml_npu_sdk_ddk-6.4.8.7-250427 工具包,成功将自定义的 YOLOv8 ONNX 模型转换成了 .nb 格式模型。整个转换过程未出现报错,对应的 .so 库文件也已正常生成。
但我将 .nb 模型和 .so 库文件一并拷贝到 VIM3 开发板后,使用 KSNN 1.4 执行推理时,检测结果出现明显异常:
输入图片里实际是马匹,模型却错误识别为人;
画面中生成了大量错误的检测框;
模型输出结果杂乱、噪点严重。

随后我切换到 KSNN 1.3 环境运行该模型,推理操作直接彻底失败,报错信息如下:

* resize input pictures error
* Set nn inputs error

最终程序完全没有输出任何检测结果。
除此之外,我还测试了 Khadas 官方提供的 YOLOv8 标准 .nb 模型及其配套的 .so 库文件,出现了完全相同的问题,检测结果也依旧异常。


Post a console log of your issue below:

这是我的yolov8n-picture.py,目前没有进行任何修改

import numpy as np
import os
import urllib.request
import argparse
import sys
import math
from ksnn.api import KSNN
from ksnn.types import *
import cv2 as cv
import time

GRID0 = 20
GRID1 = 40
GRID2 = 80
LISTSIZE = 144
SPAN = 1
NUM_CLS = 80
MAX_BOXES = 500
OBJ_THRESH = 0.4
NMS_THRESH = 0.5
mean = [0, 0, 0]
var = [255]

os.environ["QT_QPA_PLATFORM"] = "xcb"

constant_martix = np.array([[0,  1,  2,  3,
			     4,  5,  6,  7,
			     8,  9,  10, 11,
			     12, 13, 14, 15]]).T

CLASSES = ("person", "bicycle", "car","motorbike ","aeroplane ","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","sofa",
           "pottedplant","bed","diningtable","toilet ","tvmonitor","laptop    ","mouse    ","remote ","keyboard ","cell phone","microwave ",
           "oven ","toaster","sink","refrigerator ","book","clock","vase","scissors ","teddy bear ","hair drier", "toothbrush ")

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

def softmax(x, axis=0):
	x = np.exp(x)
	return x / x.sum(axis=axis, keepdims=True)

def process(input):

    grid_h, grid_w = map(int, input.shape[0:2])

    box_class_probs = sigmoid(input[..., :NUM_CLS])
    
    box_0 = softmax(input[..., NUM_CLS: NUM_CLS + 16], -1)
    box_1 = softmax(input[..., NUM_CLS + 16:NUM_CLS + 32], -1)
    box_2 = softmax(input[..., NUM_CLS + 32:NUM_CLS + 48], -1)
    box_3 = softmax(input[..., NUM_CLS + 48:NUM_CLS + 64], -1)
    
    result = np.zeros((grid_h, grid_w, 1, 4))
    result[..., 0] = np.dot(box_0, constant_martix)[..., 0]
    result[..., 1] = np.dot(box_1, constant_martix)[..., 0]
    result[..., 2] = np.dot(box_2, constant_martix)[..., 0]
    result[..., 3] = np.dot(box_3, constant_martix)[..., 0]

    col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
    row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)

    col = col.reshape(grid_h, grid_w, 1, 1)
    row = row.reshape(grid_h, grid_w, 1, 1)
    grid = np.concatenate((col, row), axis=-1)

    result[..., 0:2] = (0.5 - result[..., 0:2] + grid) / (grid_w, grid_h)
    result[..., 2:4] = (0.5 + result[..., 2:4] + grid) / (grid_w, grid_h)

    return result, box_class_probs

def filter_boxes(boxes, box_class_probs):

    box_classes = np.argmax(box_class_probs, axis=-1)
    box_class_scores = np.max(box_class_probs, axis=-1)
    pos = np.where(box_class_scores >= OBJ_THRESH)

    boxes = boxes[pos]
    classes = box_classes[pos]
    scores = box_class_scores[pos]

    return boxes, classes, scores

def nms_boxes(boxes, scores):

    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]

    areas = (x2 - x1) * (y2 - y1)
    order = scores.argsort()[::-1]

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

        xx1 = np.maximum(x1[i], x1[order[1:]])
        yy1 = np.maximum(y1[i], y1[order[1:]])
        xx2 = np.minimum(x2[i], x2[order[1:]])
        yy2 = np.minimum(y2[i], y2[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 yolov3_post_process(input_data):
    boxes, classes, scores = [], [], []
    for i in range(3):
        result, confidence = process(input_data[i])
        b, c, s = filter_boxes(result, confidence)
        boxes.append(b)
        classes.append(c)
        scores.append(s)

    boxes = np.concatenate(boxes)
    classes = np.concatenate(classes)
    scores = np.concatenate(scores)

    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)

        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, scores, classes

def draw(image, boxes, scores, classes):

    for box, score, cl in zip(boxes, scores, classes):
        x1, y1, x2, y2 = box
        print('class: {}, score: {}'.format(CLASSES[cl], score))
        print('box coordinate left,top,right,down: [{}, {}, {}, {}]'.format(x1, y1, x2, y2))
        x1 *= image.shape[1]
        y1 *= image.shape[0]
        x2 *= image.shape[1]
        y2 *= image.shape[0]
        left = max(0, np.floor(x1 + 0.5).astype(int))
        top = max(0, np.floor(y1 + 0.5).astype(int))
        right = min(image.shape[1], np.floor(x2 + 0.5).astype(int))
        bottom = min(image.shape[0], np.floor(y2 + 0.5).astype(int))

        cv.rectangle(image, (left, top), (right, bottom), (255, 0, 0), 2)
        cv.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
                    (left, top - 6),
                    cv.FONT_HERSHEY_SIMPLEX,
                    0.6, (0, 0, 255), 2)


if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument("--library", help="Path to C static library file")
    parser.add_argument("--model", help="Path to nbg file")
    parser.add_argument("--picture", help="Path to input picture")
    parser.add_argument("--level", help="Information printer level: 0/1/2")

    args = parser.parse_args()
    if args.model :
        if os.path.exists(args.model) == False:
            sys.exit('Model \'{}\' not exist'.format(args.model))
        model = args.model
    else :
        sys.exit("NBG file not found !!! Please use format: --model")
    if args.picture :
        if os.path.exists(args.picture) == False:
            sys.exit('Input picture \'{}\' not exist'.format(args.picture))
        picture = args.picture
    else :
        sys.exit("Input picture not found !!! Please use format: --picture")
    if args.library :
        if os.path.exists(args.library) == False:
            sys.exit('C static library \'{}\' not exist'.format(args.library))
        library = args.library
    else :
        sys.exit("C static library not found !!! Please use format: --library")
    if args.level == '1' or args.level == '2' :
        level = int(args.level)
    else :
        level = 0

    yolov3 = KSNN('VIM3')
    print(' |---+ KSNN Version: {} +---| '.format(yolov3.get_nn_version()))

    print('Start init neural network ...')
    yolov3.nn_init(library=library, model=model, level=level)
    print('Done.')

    print('Get input data ...')
    cv_img =  list()
    orig_img = cv.imread(picture, cv.IMREAD_COLOR)
    img = cv.resize(orig_img, (640, 640)).astype(np.float32)
    img[:, :, 0] = img[:, :, 0] - mean[0]
    img[:, :, 1] = img[:, :, 1] - mean[1]
    img[:, :, 2] = img[:, :, 2] - mean[2]
    img = img / var[0]
    
    img = img.transpose(2, 0, 1)
    #img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
    cv_img.append(img)
    print('Done.')

    print('Start inference ...')
    start = time.time()

    '''
        default input_tensor is 1
    '''
    data = yolov3.nn_inference(cv_img, platform='ONNX', reorder='2 1 0', output_tensor=3, output_format=output_format.OUT_FORMAT_FLOAT32)
    end = time.time()
    print('Done. inference time: ', end - start)

    input0_data = data[2]
    input1_data = data[1]
    input2_data = data[0]

    input0_data = input0_data.reshape(SPAN, LISTSIZE, GRID0, GRID0)
    input1_data = input1_data.reshape(SPAN, LISTSIZE, GRID1, GRID1)
    input2_data = input2_data.reshape(SPAN, LISTSIZE, GRID2, GRID2)

    input_data = list()
    input_data.append(np.transpose(input0_data, (2, 3, 0, 1)))
    input_data.append(np.transpose(input1_data, (2, 3, 0, 1)))
    input_data.append(np.transpose(input2_data, (2, 3, 0, 1)))
    
    boxes, scores, classes = yolov3_post_process(input_data)

    if boxes is not None:
        draw(orig_img, boxes, scores, classes)

    cv.imwrite("./result.jpg", orig_img)
    cv.imshow("results", orig_img)
    cv.waitKey(0)



这是用ksnn1.4推理的终端结果

这是利用ksnn1.3推理的终端结果

另外附上自定义的yolov8n.onnx的netron输出图

以及我的output

@Louis-Cheng-Liu @numbqq @Frank 请帮助查看一下这个问题 谢谢!

@MichaelXin 您可以查看是否有现成的实现可以尝试:

您好,我们测试了 Khadas 官方提供的 YOLOv8 标准 .nb 模型及其配套的 .so 库文件,用的就是您给的链接里的demo,出现了完全相同的问题,检测结果也依旧异常。

我想问一下,您成功跑通过的demo依赖的镜像和驱动版本是多少?

我们板子上用的是:

VIM3_Ubuntu-gnome-focal_Linux-4.9_arm64_SD-USB_V1.0.7-210625

Galcore version 6.4.4.3.310723AAA

Ubuntu20.04.2

是否这个版本与您demo所需的版本不匹配?@Electr1

这个操作系统太旧了,已经停止支持,或许可以尝试使用更新的操作系统和最新的库。

https://dl.khadas.com/products/vim3/firmware/ubuntu/generic/ubuntu-24.04/vim3-ubuntu-24.04-gnome-linux-5.15-fenix-1.7.5-250925.img.xz

我并非该主题示例的创建者,您可能需要查看原始主题。

好的谢谢您,我会根据您提供的版本参考进行版本升级等相关尝试