yolov5在pc上正常,一上开发板就跑飞

Viewed 15

问题描述


如标题,我的yolov5是从官方模型用工具剪枝的,原模型,剪枝模型,转为onnx和kmodel后的模型在pc端全都能正常使用,在开发板端就不行了,没找到明显的问题(也有可能是resize问题,但我确实不知道怎么改),可能是我对官方工具的理解有些出入,请大佬帮忙看看

硬件板卡


k230_canmv_v3

软件版本


rtos+linux

复现步骤


代码在主体就发不出来
void Model::preprocess(uintptr_t vaddr, uintptr_t paddr)
{
// input tensor
dims_t in_shape{1,720,1280,3};
auto in_tensor = host_runtime_tensor::create(dt_uint8, in_shape, { (gsl::byte *)vaddr, compute_size(in_shape) },false,hrt::pool_shared,paddr).expect("cannot create input tensor");
hrt::sync(in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");
// output tensor
dims_t out_shape=interp_.input_shape(0);
printf("kmodel:%dx%dx%dx%d\n",kmodel_shape[0],kmodel_shape[1],kmodel_shape[2],kmodel_shape[3]);
printf("shape:%dx%dx%dx%d\n",out_shape[0],out_shape[1],out_shape[2],out_shape[3]);
// config ai2d
ai2d_datatype_t ai2d_dtype { ai2d_format::RGB_packed, ai2d_format::NCHW_FMT, dt_uint8,dt_float32};//在头文件里找到相似定义(simple_types)
ai2d_crop_param_t crop_param { false, 0, 0, 720,1280};//横向裁减2/3,false了,美使用
ai2d_shift_param_t shift_param { false, 0 };
ai2d_pad_param_t pad_param { true, { { 0, 0 }, { 0, 0 }, { 70, 70 }, { 0, 0 } }, ai2d_pad_mode::constant, { 114,114,114 } };//pad模式常数
ai2d_resize_param_t resize_param { true, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
ai2d_affine_param_t affine_param { false};

// run
ai2d_builder builder { in_shape, out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param };
auto start = std::chrono::steady_clock::now();
builder.build_schedule().expect("error occurred in ai2d build_schedule");
builder.invoke(in_tensor, ai2d_out_tensor_).expect("error occurred in ai2d invoke");
auto stop = std::chrono::steady_clock::now();
double duration = std::chrono::duration<double, std::milli>(stop - start).count();
std::cout << "ai2d run: duration = " << duration << " ms, fps = " << 1000 / duration << std::endl;

return;

}
int Model::run(uintptr_t vaddr, uintptr_t paddr)
{
preprocess(vaddr, paddr);
kpu_run();
return postprocess();
}
// ==================== NMS后处理 ====================

int Model::nms_filter(const float* model_output, float conf_threshold, float iou_threshold)
{
static int n=0;
// 直接写文件,最简版
FILE *fp = fopen("out.txt", "a");
fprintf(fp,"第%d次\n",n++);
for (int i = 0; i < 6300; i++) {
for (int j = 0; j < 7; j++) {
fprintf(fp, "%.6f ", model_output[i * 7 + j]);
}
fprintf(fp, "\t\n");
}
fclose(fp);
const int num_boxes = 6300; // 固定总框数
const int num_classes = 2; // 固定2分类
int valid_boxes[num_boxes]; // 存储有效框索引
int stride[num_boxes];
int num_valid = 0;
float x=0,y=0,w=0,h=0;
float max_conf=0,conf=0,max_conf_o=0;
float conf_i=0,conf_j=0;
k_vo_draw_frame frame{false,0,0,0,0,0};
int class_id=0;
//清除框(便顺序了)
for(int n=0;n<16;n++)
{
frame.draw_en=false;
frame.frame_num=n;
int ret=kd_mpi_vo_draw_frame(&frame);
if(ret)
{
printf("clear triangle error!!!\n");
}
}
// ====================== 1. 筛选有效框(置信度过滤) ======================
for (int i = 0; i < num_boxes; i++)
{
max_conf = 0;
// 6300×7结构:[x,y,w,h,conf,cls0,cls1]

    conf = model_output[i * 7 + 4];
    if (conf > max_conf)
        max_conf = conf;
    // 低于阈值跳过
    if (max_conf < conf_threshold)
        continue;

    valid_boxes[num_valid++] = i;
    
}
printf("num_valid:%d\n",num_valid);
if (num_valid <= 0)
    return 0;

for (int i = 0; i < num_valid - 1; i++)
{
int idx_i = valid_boxes[i];
// 只用第5个值作为置信度
float score_i = model_output[idx_i * 7 + 4];

for (int j = i + 1; j < num_valid; j++)
{
    int idx_j = valid_boxes[j];
    // 只用第5个值作为置信度
    float score_j = model_output[idx_j * 7 + 4];

    // 降序:分数小的往后排
    if (score_i < score_j)
    {
        // 交换 box 索引
        swap_float(valid_boxes[i], valid_boxes[j]);
        // 同时更新 score_i
        swap_float(score_i, score_j);
    }
}

}
// ====================== 3. NMS 抑制 ======================
int removed[num_valid];
for (int i = 0; i < num_valid; i++)
removed[i] = 0;

int keep_count = 0;

for (int i = 0; i < num_valid; i++)
{
    if (removed[i])
        continue;

    keep_count++;
    int a_idx = valid_boxes[i];

    float ax = model_output[a_idx * 7 + 0];
    float ay = model_output[a_idx * 7 + 1];
    float aw = model_output[a_idx * 7 + 2];
    float ah = model_output[a_idx * 7 + 3];

    float a_x1 = ax - aw * 0.5f;
    float a_y1 = ay - ah * 0.5f;
    float a_x2 = ax + aw * 0.5f;
    float a_y2 = ay + ah * 0.5f;

    for (int j = i + 1; j < num_valid; j++)
    {
        if (removed[j])
            continue;

        int b_idx = valid_boxes[j];
        float bx = model_output[b_idx * 7 + 0];
        float by = model_output[b_idx * 7 + 1];
        float bw = model_output[b_idx * 7 + 2];
        float bh = model_output[b_idx * 7 + 3];

        float b_x1 = bx - bw * 0.5f;
        float b_y1 = by - bh * 0.5f;
        float b_x2 = bx + bw * 0.5f;
        float b_y2 = by + bh * 0.5f;

        // 计算 IoU
        float inter_x1 = (a_x1 > b_x1) ? a_x1 : b_x1;
        float inter_y1 = (a_y1 > b_y1) ? a_y1 : b_y1;
        float inter_x2 = (a_x2 < b_x2) ? a_x2 : b_x2;
        float inter_y2 = (a_y2 < b_y2) ? a_y2 : b_y2;

        float iou = 0.0f;
        if (inter_x2 > inter_x1 && inter_y2 > inter_y1)
        {
            float inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1);
            float a_area = aw * ah;
            float b_area = bw * bh;
            iou = inter_area / (a_area + b_area - inter_area);
        }

        if (iou > iou_threshold)
            removed[j] = 1;
    }
}

// ====================== 4. 输出最终保留的框 ======================
printf("\n===== NMS 最终保留框 =====\n");
//清除框

int final_count = 0;

for (int i = 0,p=0; i < num_valid; i++)
{

    if (!removed[i])
    {
        int idx = valid_boxes[i];
        float x = model_output[idx * 7 + 0];
        float y = model_output[idx * 7 + 1];
        float w = model_output[idx * 7 + 2];
        float h = model_output[idx * 7 + 3];

        float conf = 0;
        class_id = 0;
        for (int c = 0; c < num_classes; c++)
        {
            float v = model_output[idx * 7 + 5 + c];
            if (v > conf) {
                conf = v;
                class_id = c;
            }
        }

        // 输出坐标
	frame.draw_en=true;
frame.line_x_start = (x-w*0.5f) * (1280.0f / 320.0f);
frame.line_y_start = (y-h*0.5f) * (1280.0f / 320.0f);
frame.line_x_end   = (x + w*0.5f) * (1280.0f / 320.0f);
frame.line_y_end   = (y + h*0.5f) * (1280.0f / 320.0f);
        frame.frame_num=p++;
        int ret=kd_mpi_vo_draw_frame(&frame);
        final_count++;
printf("x:%d,y:%d,w:%d,h:%d\n",frame.line_x_start,frame.line_y_start,frame.line_x_end,frame.line_y_end);
    }
}
printf("numg valid:%d\n",num_valid);

return final_count;

}
int Model::postprocess()
{

float *out;
auto tensor = output_tensor(0);
    auto buf = tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_read).unwrap().buffer();
    out = reinterpret_cast<float *>(buf.data());
auto input_shape = interp_.input_shape(0);
printf("in_shape:%dx%dx%d\n",input_shape[0],input_shape[1],input_shape[2]);
auto output_shape = interp_.output_shape(0);
printf("out_shape:%dx%dx%d\n",output_shape[0],output_shape[1],output_shape[2]);

int final_boxes = nms_filter(
out, // 输入
0.5f, // 置信度阈值
0.45f // IOU 阈值
);
printf("final_boxes:%d\n",final_boxes);

return final_boxes;

}

硬件板卡


k230_canmv_v3

软件版本


rtos+linux

1 Answers

这是模拟代码
import os
import copy
import argparse
import numpy as np
import onnx
import onnxruntime as ort
import nncase
import sys
import subprocess
import cv2

result = subprocess.run(["pip", "show", "nncase"], capture_output=True)
split_flag = "\n"
if sys.platform == "win32":
split_flag = "\r\n"

location_s = [i for i in result.stdout.decode().split(split_flag) if i.startswith("Location:")]
location = location_s[0].split(": ")[1]

if "PATH" in os.environ:
os.environ["PATH"] += os.pathsep + location
else:
os.environ["PATH"] = location

def read_model_file(model_file):
with open(model_file, 'rb') as f:
model_content = f.read()
return model_content

def cosine(gt, pred):
return (gt @ pred) / (np.linalg.norm(gt, 2) * np.linalg.norm(pred, 2))

==================== YOLOv5 前处理 ====================

def letterbox(img, new_shape=(320, 320), color=(114, 114, 114)):
shape = img.shape[:2]
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
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]
dw, dh = dw / 2, dh / 2
if shape[::-1] != new_unpad:
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
return img, r, (dw, dh)

def preprocess_image(image_path):
img = cv2.imread(image_path)
# 保存原始图用于画图
ori_img = img.copy()
img, scale, (pad_w, pad_h) = letterbox(img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = img.astype(np.float32) / 255.0
blob = img.transpose(2, 0, 1)
blob = blob[None, ...]
return blob, scale, pad_w, pad_h, ori_img

==================== YOLOv5 后处理 ====================

def postprocess(output, scale, pad_w, pad_h, conf_thres=0.15, iou_thres=0.45):
predictions = output[0]
obj_conf = predictions[:, 4]
cls_max = np.maximum(predictions[:, 5], predictions[:, 6])
final_conf = obj_conf * cls_max
mask = final_conf >= conf_thres
filtered = predictions[mask]
confs = final_conf[mask]
dets = []
for p, c in zip(filtered, confs):
x, y, w, h = p[0:4]
# 坐标映射回原图
x1 = int((x - w/2 - pad_w) / scale)
y1 = int((y - h/2 - pad_h) / scale)
x2 = int((x + w/2 - pad_w) / scale)
y2 = int((y + h/2 - pad_h) / scale)
cls = 0 if p[5] > p[6] else 1
dets.append([x1, y1, x2, y2, c, cls])

# NMS
dets = sorted(dets, key=lambda x: x[4], reverse=True)
keep = []
for d in dets:
    keep_flag = True
    for k in keep:
        ix1 = max(d[0], k[0])
        iy1 = max(d[1], k[1])
        ix2 = min(d[2], k[2])
        iy2 = min(d[3], k[3])
        area = max(0, ix2-ix1) * max(0, iy2-iy1)
        iou = area / ((d[2]-d[0])*(d[3]-d[1]) + (k[2]-k[0])*(k[3]-k[1]) - area + 1e-8)
        if iou > iou_thres:
            keep_flag = False
            break
    if keep_flag:
        keep.append(d)
return keep

==================== 画图函数 ====================

def draw_and_save(img, dets, save_path="result.jpg"):
for x1, y1, x2, y2, conf, cls in dets:
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(img, f"{conf:.2f}", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imwrite(save_path, img)

==================== main ====================

def main():
parser = argparse.ArgumentParser(prog="nncase")
parser.add_argument("--model", type=str, help='original model file')
parser.add_argument("--kmodel", type=str, help='kmodel file')
parser.add_argument("--image", type=str, help='test image path')
args = parser.parse_args()

# 前处理(融合)
blob, scale, pad_w, pad_h, ori_img = preprocess_image(args.image)
blob.tofile("input.bin")

# ONNX 推理
ort_session = ort.InferenceSession(args.model)
output_names = [node.name for node in ort_session.get_outputs()]
model_input = ort_session.get_inputs()[0]
model_input_name = model_input.name
model_input_shape = (1,3,320,320)

model_input_data = np.fromfile("input.bin", dtype=np.float32).reshape(model_input_shape)
cpu_results = ort_session.run(output_names, {model_input_name: model_input_data})

# kmodel 推理
sim = nncase.Simulator()
kmodel = read_model_file(args.kmodel)
sim.load_model(kmodel)

input = np.fromfile("input.bin", dtype=np.float32).reshape([1,3,320,320])
sim.set_input_tensor(0, nncase.RuntimeTensor.from_numpy(input))

sim.run()
nncase_results = []
for i in range(sim.outputs_size):
    nncase_result = sim.get_output_tensor(i).to_numpy()
    nncase_results.append(copy.deepcopy(nncase_result))

# 保存输出
with open("out1.txt", "w") as f:
    for idx, res in enumerate(cpu_results):
        f.write(f"========== ONNX Output {idx} ==========\n")
        f.write(f"Shape: {res.shape}\n")
        f.write(f"Data:\n{res}\n\n")
print(" ONNX 输出已保存 → out1.txt")

with open("out2.txt", "w") as f:
    for idx, res in enumerate(nncase_results):
        f.write(f"========== KMODEL Output {idx} ==========\n")
        f.write(f"Shape: {res.shape}\n")
        f.write(f"Data:\n{res}\n\n")
print(" Kmodel 输出已保存 → out2.txt")

# 余弦对比
print("\n========== 余弦相似度对比 ==========")
for i in range(len(cpu_results)):
    cos = cosine(cpu_results[i].flatten(), nncase_results[i].flatten())
    print(f'Output {i} cosine: {cos:.6f}')

# 后处理(融合)
print("\n========== ONNX 后处理结果 ==========")
boxes_onnx = postprocess(cpu_results[0], scale, pad_w, pad_h)
print(f"检测目标:{len(boxes_onnx)}")

print("\n========== Kmodel 后处理结果 ==========")
boxes_kmodel = postprocess(nncase_results[0], scale, pad_w, pad_h)
print(f"检测目标:{len(boxes_kmodel)}")

# ==================== 画图输出图片====================
draw_and_save(ori_img, boxes_onnx, "result_onnx.jpg")
draw_and_save(ori_img.copy(), boxes_kmodel, "result_kmodel.jpg")
print("\n  检测完成!结果图已保存:")
print("   - result_onnx.jpg")
print("   - result_kmodel.jpg")

if name == 'main':
main()
#as