Skip to content

Step-by-Step Guide to YoloV7 End-to-End Deployment

yolov7 is a well-known model for performing object detection.

This guide offers a comprehensive guide on how to use our model tools to compile and test the newly downloaded yolov7 model.

The article is divided into two sections:

  • Preparing and verifying the AI model for NPU (NEF) on a Linux PC
  • Running the AI NPU model (NEF) on KNEO Pi

Preparing and Verifying the AI Model for NPU (NEF) on a Linux PC

Step 1: Set Up the Environment and Data

  • Step 1-1: Set up the toolchain environment

    First, we need to download the latest toolchain Docker image, which includes all the necessary tools for the process.

    docker pull kneron/toolchain:latest
    

    Start the Docker container with a local folder mounted inside the container.

    docker run --rm -it -v /your/folder/path/for/docker_mount:/data1 kneron/toolchain:latest
    

    Install the necessary Python packages

    pip install opencv-python-headless
    pip install torchvision --index-url https://download.pytorch.org/whl/cpu
    
    ## required for Yolov7
    pip install pyyaml
    pip install scipy
    pip install "numpy>=1.18.5,<1.24.0"
    
  • Step 1-2: clone the model

    Navigate to the mounted folder and clone the public PyTorch-based PIDNet model from GitHub https://github.com/WongKinYiu/yolov7 using the following command:

    cd /data1 && git clone https://github.com/WongKinYiu/yolov7
    

    download yolov7-tiny weight and convert it to onnx

    cd /data1/yolov7
    python export.py --weights yolov7-tiny.pt --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 --max-wh 640
    cd /data1
    

    The ONNX model will be generated and saved as yolov7/yolov7-tiny.onnx

  • Step 1-3: prepare the images for model quantization

    We need to prepare some images in the mounted folder. Example input images can be found at http://doc.kneron.com/docs/toolchain/res/test_image10.zip.

    Here’s how you can obtain it:

    cd /data1
    wget http://doc.kneron.com/docs/toolchain/res/test_image10.zip
    unzip test_image10.zip
    
    Now we have images in folder test_image10/ at /data1, this is for quantization.

    Important

    These images are provided as examples. For improved quantization accuracy, users may need to select their own data.

    We also require additional images for accuracy testing. However, due to the complexity of the documentation, we are using only one image(yolov7/inference/images/bus.jpg) in the toolchain Docker for testing purposes.

Step 2: Import KTC and required lib in python shell (Inside docker container)

Now, we will go through the entire toolchain flow using the KTC (Kneron Toolchain) Python API in a Python shell.

  • run "python" to open to python shell:

    Figure 1. python shell

  • import KTC and others necessary libs
    import os 
    os.chdir('./yolov7')
    import sys
    sys.path.append('./')
    
    import cv2
    import torch
    import torchvision
    from numpy import random
    
    import numpy as np
    import onnxruntime as ort
    
    from utils.general import  non_max_suppression, scale_coords
    from utils.plots import plot_one_box
    
    import glob
    import os
    import kneronnxopt
    import onnx
    
    import ktc 
    

Step 3: Convert and optimize the pretrain model

We need to first convert the pretrained .pt model to an ONNX model.

ONNX_PATH = "./yolov7/yolov7-tiny.onnx" 
m = onnx.load(ONNX_PATH)
md_opt = ktc.onnx_optimizer.onnx2onnx_flow(m)

We now have the optimized ONNX model stored in the variable "m". You can save this ONNX model ('m') to disk for further inspection, such as using Netron or onnxruntime.

onnx.save(md_opt,'/data1/kneopi_yolov7-tiny_opt.onnx')
Here we save it to /data1/kneopi_yolov7-tiny_opt.onnx for further verification in Step 5.

Step 4: IP Evaluation

To ensure the ONNX model functions as expected, it's important to evaluate its performance and check for any unsupported operators or CPU nodes within the model.

km = ktc.ModelConfig(11111, "0001", "730", onnx_model=md_opt)

# npu(only) performance simulation
eval_result = km.evaluate()
print("\nNpu performance evaluation result:\n" + str(eval_result))

You can find the estimated FPS (NPU only) and a detailed report in the kneron_flow/model_fx_report.json and kneron_flow/model_fx_report.html files.

cat kneron_flow/model_fx_report.json
{
    "docker_version": "kneron/toolchain:v0.26.0",
    "comments": "",
    "kdp730/input bitwidth": "int8",
    "kdp730/output bitwidth": "int8",
    "kdp730/cpu bitwidth": "int8",
    "kdp730/datapath bitwidth": "int8",
    "kdp730/weight bitwidth": "int8",
    "kdp730/ip_eval/fps": "56.4618",
    "kdp730/ip_eval/ITC(ms)": "17.7111 ms",
    "kdp730/ip_eval/C(GOPs)": "6.86438e+09",
    "kdp730/ip_eval/RDMA bandwidth GB/s": "8 GB/s",
    "kdp730/ip_eval/WDMA bandwidth GB/s": "8 GB/s",
    "kdp730/ip_eval/GETW bandwidth GB/s": "4.5 GB/s",
    "kdp730/ip_eval/RV(mb)": 24.1696,
    "kdp730/ip_eval/WV(mb)": 18.3712,
    "kdp730/ip_eval/cpu_node": "N/A",
    "kdp730/kne": "models_730.kne",
    "kdp730/nef": "models_730.nef",
    "kdp730/bie": "input.kdp730.scaled.release.bie",
    "kdp730/onnx": "input.kdp730.graph_opt.onnx",
    "gen fx model report": "model_fx_report.html",
    "gen fx model json": "model_fx_report.json"
}

The estimated FPS is 56.4618 (for NPU only)

Step 5: Check ONNX model and Pre&Post process are good

If we obtain the correct detection result from the ONNX model, along with the proper pre and post-processing, everything should be functioning correctly.

First, we need to review the pre and post-processing methods. You can find the relevant information in the following code: https://github.com/WongKinYiu/yolov7/blob/main/detect.py and https://github.com/WongKinYiu/yolov7/blob/main/models/yolo.py.

Here is the extracted pre-processing method:

import os 
os.chdir('./yolov7')
import sys
sys.path.append('./')
from utils.general import  non_max_suppression, scale_coords
from utils.plots import plot_one_box

def preprocess(img, model_input_w, model_input_h):

    img = cv2.resize(img, (model_input_w, model_input_h)).astype(np.float32)
    img = np.transpose(img, (2,0,1))
    img /= 255.0
    img = np.expand_dims(img, axis=[0])

    return img

def post_process(model_inf_data):
    pred = []
    for o_n in model_inf_data:
        pred.append(torch.from_numpy(o_n))

    anchors = []
    anchors.append([12,16, 19,36, 40,28])  
    anchors.append([36,75, 76,55, 72,146])  
    anchors.append([142,110, 192,243, 459,401])  

    nl = len(anchors)  # number of detection layers
    grid = [torch.zeros(1)] * nl  # init grid

    a = torch.tensor(anchors).float().view(nl, -1, 2)
    anchor_grid = a.clone().view(nl, 1, -1, 1, 1, 2)
    stride = torch.tensor([8. , 16. , 32.])  

    for idx, every_stride in enumerate(np.array(stride.view(-1, 1, 1)).squeeze()):
        anchors[idx] /= every_stride

    scaled_res = []

    for i in range(len(anchors)):
        if grid[i].shape[2:4] != pred[i].shape[2:4]:
            bs, _, ny, nx, _ = pred[i].shape
            grid[i] = _make_grid(nx, ny).to('cpu')

        y = sigmoid(pred[i])

        y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + grid[i]) * stride[i]  # xy
        y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * anchor_grid[i]  # wh

        scaled_res.append(y.reshape([bs,-1,85]))

    concat_pred = torch.concat(scaled_res,dim=1)

    # Apply NMS
    batch_det = non_max_suppression(concat_pred, conf_thres=0.25, iou_thres=0.45)
    return batch_det
To verify that the exported ONNX model and the extracted pre/post-processing are correct, we use ONNX Runtime to ensure everything is functioning properly.

## onnx model check using onnxruntime
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# onnx inference using onnxruntime
OPTIMIZED_ONNX_PATH = "/data1/kneopi_yolov7-tiny_opt.onnx" 
ort_session = ort.InferenceSession(OPTIMIZED_ONNX_PATH)
model_pred = ort_session.run(None, {'images': img})

# onnx output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

# visualize segmentation result to img
if len(det):
    cls_names = ['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']
    colors = [[random.randint(0, 255) for _ in range(3)] for _ in cls_names]

    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_onnxruntime_inf.jpg")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)
you can see the result image on kneopi_onnxruntime_inf.png

Now, we can check the ONNX inference result with ktc api ktc.kneron_inference.

## onnx model check using ktc.inference
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# onnx inference using ktc.inference
OPTIMIZED_ONNX_PATH = "/data1/kneopi_yolov7-tiny_opt.onnx" 
model_pred = ktc.kneron_inference([img], input_names=['images'], onnx_file=OPTIMIZED_ONNX_PATH, platform=730)

# onnx output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

# visualize detection result to img
if len(det):
    cls_names = ['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']
    colors = [[random.randint(0, 255) for _ in range(3)] for _ in cls_names]

    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_tc_onnx_inf.jpg")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)
you can see the result image on kneopi_tc_onnx_inf.png

Note

We are using only one image as an example. It's a good idea to use more data to assess accuracy.

Step 6: Quantization

We identified the preprocessing method in Step 5.

Perform the same steps on our quantization data and add it to a list.

# load and normalize all image data from folder
q_imgs_path = "/data1/test_image10/"
images_list = glob.glob(q_imgs_path+'*'+ '.jpg')

normalized_img_list = []
for img_path in images_list:

    img_name = img_path.split("/")[-1]
    img = cv2.imread(os.path.join(q_imgs_path, img_name), cv2.IMREAD_COLOR)

    model_input_w, model_input_h = 640, 640
    img = preprocess(img, model_input_w, model_input_h)

    normalized_img_list.append(img)

then do quantization:

# fix point analysis
bie_model_path = km.analysis({"images": normalized_img_list})
print("\nFix point analysis done. Save bie model to '" + str(bie_model_path) + "'")

the bie model will be generated at /data1/kneron_flow/input.kdp730.scaled.release.bie.

Step 7: Check BIE model accuracy is good enough

After quantization, the model accuracy slightly drop is expected. We had better check the accuracy is good enough to use.

Toolchain api ktc.kneron_inference can help us to check. The usage of ktc.kneron_inference is similar to Step 4, only one different is at the 2nd parameter, changed from onnx_file to bie_file:

## bie model check using ktc.inference
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# bie inference using ktc.inference
BIE_PATH = "/data1/kneron_flow/input.kdp730.scaled.release.bie" 
model_pred = ktc.kneron_inference([img], input_names=['images'], bie_file=BIE_PATH, platform=730)


# bie output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

# visualize segmentation result to img
if len(det):
    cls_names = ['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']
    colors = [[random.randint(0, 255) for _ in range(3)] for _ in cls_names]

    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_tc_bie_inf.png")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)

you can see the result image on kneopi_tc_bie_inf.png

It is slightly different from Step 1-3, but it looks good enough.

Note

We are currently using only one image as an example. It’s a good idea to use more data to check the accuracy.

Step 8: Compile

The final step is to compile the BIE model into a NEF model.

# compile
nef_model_path = ktc.compile([km])
print("\nCompile done. Save Nef file to '" + str(nef_model_path) + "'")

You can find the nef file under /data1/kneron_flow/models_730.nef. The models_730.nef is the final compiled model.

To learn the usage of generated NEF model on KL730, please check example section: KL730End2EndTutorialYoloV7

(optional) Step 9. Check NEF model

The Toolchain API ktc.inference supports performing NEF model inference. Its usage is similar to Step 5 and Step 7, but with one difference:

  • The second parameter in ktc.kneron_inference should be the nef_file. The code appears as follows:

    ## nef model check using ktc.inference
    im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')
    
    # resize and normalize input data
    img = preprocess(im0, model_input_w = 640, model_input_h = 640)
    
    # nef inference using ktc.inference
    NEF_PATH = "/data1/kneron_flow/models_730.nef" 
    model_pred = ktc.kneron_inference([img], input_names=['images'], nef_file=NEF_PATH, platform=730)
    
    
    # nef output data processing
    batch_det = post_process(model_pred)
    det = batch_det[0] #only one image
    
    # visualize segmentation result to img
    if len(det):
        cls_names = ['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']
        colors = [[random.randint(0, 255) for _ in range(3)] for _ in cls_names]
    
        # Rescale boxes from img_size to im0 size
        det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
    
        # Write results
        for *xyxy, conf, cls in reversed(det):
            label = f'{cls_names[int(cls)]} {conf:.2f}'
            plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)
    
        # Save results (image with detections)
        save_path = os.path.abspath("/data1/kneopi_tc_nef_inf.png")
        cv2.imwrite(save_path, im0)
    
        print("save result to " + save_path)
    

You can view the result image in kneopi_tc_nef_inf.png.

The NEF model results should match exactly with those of the BIE model.

Appendix - The entire process

The entire model conversion process from ONNX to NEF (Step 2 - 9) can be written into a single Python script.

Python script for the entire process
import os 
os.chdir('./yolov7')
import sys
sys.path.append('./')

import cv2
import torch
import torchvision
from numpy import random

import numpy as np
import onnxruntime as ort

from utils.general import  non_max_suppression, scale_coords
from utils.plots import plot_one_box

import ktc 

import glob
import os
import kneronnxopt
import onnx


def _make_grid(nx=20, ny=20):
    yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
    return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()

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

def preprocess(img, model_input_w, model_input_h):

    img = cv2.resize(img, (model_input_w, model_input_h)).astype(np.float32)
    img = np.transpose(img, (2,0,1))

    img /= 255.0
    img = np.expand_dims(img, axis=[0])

    return img

def post_process(model_inf_data):
    pred = []
    for o_n in model_inf_data:
        pred.append(torch.from_numpy(o_n))

    anchors = []
    anchors.append([12,16, 19,36, 40,28])  
    anchors.append([36,75, 76,55, 72,146])  
    anchors.append([142,110, 192,243, 459,401])  

    nl = len(anchors)  # number of detection layers
    grid = [torch.zeros(1)] * nl  # init grid

    a = torch.tensor(anchors).float().view(nl, -1, 2)
    anchor_grid = a.clone().view(nl, 1, -1, 1, 1, 2)
    stride = torch.tensor([8. , 16. , 32.])  

    for idx, every_stride in enumerate(np.array(stride.view(-1, 1, 1)).squeeze()):
        anchors[idx] /= every_stride

    scaled_res = []

    for i in range(len(anchors)):
        if grid[i].shape[2:4] != pred[i].shape[2:4]:
            bs, _, ny, nx, _ = pred[i].shape
            grid[i] = _make_grid(nx, ny).to('cpu')

        y = sigmoid(pred[i])

        y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + grid[i]) * stride[i]  # xy
        y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * anchor_grid[i]  # wh

        scaled_res.append(y.reshape([bs,-1,85]))

    concat_pred = torch.concat(scaled_res,dim=1)

    # Apply NMS
    batch_det = non_max_suppression(concat_pred, conf_thres=0.25, iou_thres=0.45)
    return batch_det


########### model convertion  ###################

## onnx optimization

ONNX_PATH = "/data1/yolov7/yolov7-tiny.onnx" 
m = onnx.load(ONNX_PATH)

md_opt = kneronnxopt.optimize(m)
onnx.save(md_opt,'/data1/kneopi_yolov7-tiny_opt.onnx')


km = ktc.ModelConfig(11111, "0001", "730", onnx_model=md_opt)

# npu(only) performance simulation
eval_result = km.evaluate()
print("\nNpu performance evaluation result:\n" + str(eval_result))


# load and normalize all image data from folder
q_imgs_path = "/data1/test_image10/"
images_list = glob.glob(q_imgs_path+'*'+ '.jpg')

normalized_img_list = []
for img_path in images_list:

    img_name = img_path.split("/")[-1]
    img = cv2.imread(os.path.join(q_imgs_path, img_name), cv2.IMREAD_COLOR)

    model_input_w, model_input_h = 640, 640
    img = preprocess(img, model_input_w, model_input_h)

    normalized_img_list.append(img)

# fix point analysis
bie_model_path = km.analysis({"images": normalized_img_list})
print("\nFix point analysis done. Save bie model to '" + str(bie_model_path) + "'")


# compile
nef_model_path = ktc.compile([km])
print("\nCompile done. Save Nef to " + nef_model_path)


########### inference simulation ###################

## onnx model check using onnxruntime
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# onnx inference using onnxruntime
OPTIMIZED_ONNX_PATH = "/data1/kneopi_yolov7-tiny_opt.onnx" 
ort_session = ort.InferenceSession(OPTIMIZED_ONNX_PATH)
model_pred = ort_session.run(None, {'images': img})

# onnx output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

cls_names = ['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']
colors = [[random.randint(0, 255) for _ in range(3)] for _ in cls_names]

# visualize segmentation result to img
if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_onnxruntime_inf.jpg")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)

## onnx model check using ktc.inference
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# onnx inference using ktc.inference
OPTIMIZED_ONNX_PATH = "/data1/kneopi_yolov7-tiny_opt.onnx" 
model_pred = ktc.kneron_inference([img], input_names=['images'], onnx_file=OPTIMIZED_ONNX_PATH, platform=730)

# onnx output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image


# visualize detection result to img
if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_tc_onnx_inf.jpg")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)


## bie model check using ktc.inference
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# bie inference using ktc.inference
BIE_PATH = "/data1/kneron_flow/input.kdp730.scaled.release.bie" 
model_pred = ktc.kneron_inference([img], input_names=['images'], bie_file=BIE_PATH, platform=730)


# bie output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

# visualize segmentation result to img
if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_tc_bie_inf.png")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)

## nef model check using ktc.inference
im0 = cv2.imread('/data1/yolov7/inference/images/bus.jpg')

# resize and normalize input data
img = preprocess(im0, model_input_w = 640, model_input_h = 640)

# nef inference using ktc.inference
NEF_PATH = "/data1/kneron_flow/models_730.nef" 
model_pred = ktc.kneron_inference([img], input_names=['images'], nef_file=NEF_PATH, platform=730)


# nef output data processing
batch_det = post_process(model_pred)
det = batch_det[0] #only one image

# visualize segmentation result to img
if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

    # Write results
    for *xyxy, conf, cls in reversed(det):
        label = f'{cls_names[int(cls)]} {conf:.2f}'
        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1)

    # Save results (image with detections)
    save_path = os.path.abspath("/data1/kneopi_tc_nef_inf.png")
    cv2.imwrite(save_path, im0)

    print("save result to " + save_path)

Run AI model on KNEO Pi

Step 1: Log into your KNEO Pi, check your IP address

Command reference
# find the IP address
ifconfig

# to restart nnm USB loopback mode if you have disabled it.
# must login as root
systemctl disable nnm-usb-companion
systemctl enable nnm-usb-loopback

# reboot
reboot

Step 2: Install the necessary packages on the KNEO Pi.

cd /home
python3 -m venv venv_yolov7_test
source venv_yolov7_test/bin/activate
python3 -m pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
python3 -m pip install opencv-python-headless
scp KneronPLUS-3.0.0-py3-none-any.whl alarm@<KNEOPI_IP_ADDRESS>:/tmp
python3 -m pip install /tmp/KneronPLUS-3.0.0-py3-none-any.whl
pacman -S libusb
<KNEOPI_IP_ADDRESS> is the IP of your KNEO Pi

How to use Python?

Refer to: Use Python

Step 3 Prepare the code and the NEF model.

  • Step 3-1: Transfer the official firmware and the generated NEF model from the PC to the Kneo-Pi (PC operation).

    scp models_730_GENERATED_FROM_STEP1.nef alarm@KNEOPI_IP_ADDRESS:/tmp
    scp OFFICIAL_RELEASED_FW.tar alarm@KNEOPI_IP_ADDRESS:/tmp
    
    <KNEOPI_IP_ADDRESS> is the IP of your KNEO Pi

  • Step 3-2: Prepare the data and sample code.

    mv /tmp/models_730_GENERATED_FROM_STEP1.nef /home/.
    mv /tmp/OFFICIAL_RELEASED_FW /home/.
    
    cd /home
    git clone KNEOPI-EXAMPLE-REPO
    
    cd /home/KNEOPI-EXAMPLE-REPO
    

Step 4 Run the sample code

python plus_python/KL730End2EndTutorialYoloV7.py -fw /home/OFFICIAL_RELEASED_FW.tar -m /home/models_730_GENERATED_FROM_STEP1.nef -img plus_python/res/images/bus.jpg

Warning

The code are modified from Step 9 of the previous section. For easier demo, we some extract postprocess from original yolov7 repository