Pioneer-RKNN — нейросети на борту

pioneer_rknn — набор классов и функций для работы с моделями машинного обучения на бортовом нейроускорителе Пионера Мини 2. Поддерживаются детекция объектов (YOLO), сегментация (YOLOSeg), оценка поз (YOLOPose), распознавание текста (PaddleOCR) и загрузка собственных моделей. Инференс выполняется прямо на коптере — без внешнего сервера.

Pioneer-RKNN

Представляет собой набор классов и функций для работы с моделями машинного обучения

Поддержка Pioneer-RKNN квадрокоптерами серии Пионер

КвадрокоптерыPioneer-RKNNВзаимодействие через:
Мини 2PioneerOS (предустановлен)
Мини
Базовыймодуль radxa zero
FPV
Макс (ROS программирование)

AI модели и поддерживаемые архитектуры

YoloYoloSegYoloPosePaddleOCR
yolov8yolov8-segyolov8-posePP-OCRv5_mobile_det
yolov11yolov11-segPP-OCRv5_mobile_rec
yolov26yolov26-seg

Запуск Python скрипта

В целях безопасности, при выполнении полётного задания, автопилот проверяет "наличие пилота" и при его отсутствии откажется выполнять полёт. Подключите пульт управления и переведите тумблер SWB в нижнее положение, при необходимости, проверку можно отключить отредактировав параметр Copter_flyWithoutRc = 1 в конфигураторе Pioneer Station, а непосредственно запуск производится в используемой вами IDE, например, Visual Studio Code.

Примеры скриптов

Более подробное описание примеров для работы с библиотекой Pioneer-RKNN в GitFlic.

Переопределение метода run для использования с собственной моделью

from pioneer_rknn import ModelContainer # импортируем класс ModelContainer из библиотеки pioneer_rknn

class MyAiClass(ModelContainer): # создаем собственный класс наследуя возможности класса ModelContainer arch = ["myarch"] # имя архитектуры вашей модели, необходимо для поиска в регистре моделей

# создаем конструктор класса def init(self, model_path: str | None = None, model_name: str | None = None, npu_core: list[int] | None = None) -> None: super().init(model_path, model_name, npu_core) # вызываем конструктор родительского класса

# создаем функцию обработки результатов def post_process(self, outputs: list) -> list: # здесь можно добавить вашу собственную обработку результатов return outputs # возвращаем результат обработки

# создаем функцию run для инференса def run(self, inputs: list) -> list | None: outputs = super().run(inputs) # вызываем метод run родительского класса, получаем необработанные данные из NPU return self.post_process(outputs) # возвращаем объекту результат инференса полученных ранее данных после собственной обработки

Yolo - детекция всех классов

import cv2
import numpy as np
from pioneer_rknn import Yolo
from pioneer_sdk2 import Camera, ImageViewer, CameraType

# Глобальные переменные — изображения и результаты детекции image_1 = None image_2 = None object_1 = None object_2 = None

# Список классов объектов (содержит все возможные объекты в YOLO) classNames = ["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 resize_img(img): """ Изменяет размер изображения до IMG_SIZE и добавляет ось batch

Args: img (numpy.ndarray): Исходное изображение

Returns: numpy.ndarray: Изображение с размером IMG_SIZE и добавленной осью batch """ # Изменяем размер изображения до IMG_SIZE img_copy = cv2.resize(img, IMG_SIZE)

# Добавляем ось batch (для RKNN) img_copy = np.expand_dims(img_copy, 0) return img_copy

def yolo_find_on_image(img, results, classNames, class_filter=None, score_thr=None): """ Фильтрует результаты детекции YOLO по классам и уверенности

Args: img (numpy.ndarray): Исходное изображение results (tuple): Результаты детекции (boxes, classes, scores) classNames (list): Список названий классов class_filter (list or None): Список классов для фильтрации score_thr (float or None): Порог уверенности

Returns: dict: Фильтрованные результаты детекции """ boxes, classes, scores = results # Распаковываем результаты

# Если нет данных — возвращаем пустой словарь if boxes is None or classes is None or scores is None or len(boxes) == 0: return { "boxes": np.zeros((0, 4), dtype=np.float32), "classes": np.zeros((0,), dtype=np.int32), "scores": np.zeros((0,), dtype=np.float32), "orig_shape": img.shape[:2], "classNames": classNames }

# Преобразуем данные в массивы NumPy boxes = np.array(boxes) classes = np.array(classes) scores = np.array(scores)

indices = np.arange(len(boxes)) # Индексы всех детекций

# Фильтрация по классам (если указано) if class_filter is not None: allowed = [classNames.index(cls_name) for cls_name in class_filter if cls_name in classNames] indices = [i for i in indices if classes[i] in allowed]

# Фильтрация по уверенности (если указано) if score_thr is not None: indices = [i for i in indices if scores[i] >= score_thr]

# Возвращаем фильтрованные данные return { "boxes": boxes[indices], "classes": classes[indices], "scores": scores[indices], "orig_shape": img.shape[:2], "classNames": classNames }

def draw_yolo_on_image(img, yolo_obj, color=(0,255,0), thickness=2): """ Отрисовывает детекции YOLO на изображении

Args: img (numpy.ndarray): Изображение для отрисовки yolo_obj (dict): Результаты детекции color (tuple): Цвет рамки thickness (int): Толщина линии """ boxes = yolo_obj["boxes"] classes = yolo_obj["classes"] classNames = yolo_obj["classNames"] orig_h, orig_w = yolo_obj["orig_shape"]

# Множители для масштабирования координат scale_x = orig_w / IMG_SIZE[0] scale_y = orig_h / IMG_SIZE[1]

# Отрисовываем каждую детекцию for box, cl in zip(boxes, classes): left, top, right, bottom = [int(coord) for coord in box] # Координаты бокса

# Масштабируем координаты к оригинальному изображению left = int(left * scale_x) right = int(right * scale_x) top = int(top * scale_y) bottom = int(bottom * scale_y)

# Рисуем рамку cv2.rectangle(img, (left, top), (right, bottom), color, thickness)

# Рисуем надпись label = f"{classNames[cl]}" cv2.putText(img, label, (left, top-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2)

# Инициализация камер cam_main, cam_opt = Camera(camera_type=CameraType.MAIN), Camera(camera_type=CameraType.OPT)

iv = ImageViewer() # Инициализация отображения изображений

def hex_to_bgr(hex_color: str) -> tuple[int, int, int]: """ Преобразует HEX-цвет в BGR (OpenCV формат)

Args: hex_color (str): HEX цвет в формате #RRGGBB

Returns: tuple[int, int, int]: Цвет в формате BGR """ # Убираем символ # if hex_color.startswith("#"): hex_color = hex_color[1:]

# Проверяем длину if len(hex_color) != 6: raise ValueError("Hex color must be in format #RRGGBB")

# Преобразуем HEX в BGR r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16)

return (b, g, r)

if name == "main": print("Выбрана модель:Yolo") IMG_SIZE = (640, 640)

# Загрузка модели Yolo model = Yolo(model_name="yolov8n") while True: # Получаем кадры с камер image_1 = cam_main.get_cv_frame() image_2 = cam_opt.get_cv_frame()

# Выполняем детекцию на первом и втором кадре по всем классам object_1 = yolo_find_on_image(image_1, results_image_1 := model.run([resize_img(image_1)]), classNames, class_filter=None) object_2 = yolo_find_on_image(image_2, results_image_2 := model.run([resize_img(image_2)]), classNames, class_filter=None)

# Отрисовываем детекции draw_yolo_on_image(image_1, object_1, hex_to_bgr("#ff4040")) draw_yolo_on_image(image_2, object_2, hex_to_bgr("#000099"))

# Отображаем изображения iv.imshow(name='first', frame=image_1) iv.imshow(name='second', frame=image_2)

Yolo - детекция выбранных классов

import cv2
import numpy as np
from pioneer_rknn import Yolo
from pioneer_sdk2 import Camera, ImageViewer, CameraType

# Глобальные переменные — изображения и результаты детекции image_1 = None image_2 = None object_1 = None object_2 = None

# Список классов объектов (содержит все возможные объекты в YOLO) classNames = ["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 resize_img(img): """ Изменяет размер изображения до IMG_SIZE и добавляет ось batch

Args: img (numpy.ndarray): Исходное изображение

Returns: numpy.ndarray: Изображение с размером IMG_SIZE и добавленной осью batch """ # Изменяем размер изображения до IMG_SIZE img_copy = cv2.resize(img, IMG_SIZE)

# Добавляем ось batch (для RKNN) img_copy = np.expand_dims(img_copy, 0) return img_copy

def yolo_find_on_image(img, results, classNames, class_filter=None, score_thr=None): """ Фильтрует результаты детекции YOLO по классам и уверенности

Args: img (numpy.ndarray): Исходное изображение results (tuple): Результаты детекции (boxes, classes, scores) classNames (list): Список названий классов class_filter (list or None): Список классов для фильтрации score_thr (float or None): Порог уверенности

Returns: dict: Фильтрованные результаты детекции """ boxes, classes, scores = results # Распаковываем результаты

# Если нет данных — возвращаем пустой словарь if boxes is None or classes is None or scores is None or len(boxes) == 0: return { "boxes": np.zeros((0, 4), dtype=np.float32), "classes": np.zeros((0,), dtype=np.int32), "scores": np.zeros((0,), dtype=np.float32), "orig_shape": img.shape[:2], "classNames": classNames }

# Преобразуем данные в массивы NumPy boxes = np.array(boxes) classes = np.array(classes) scores = np.array(scores)

indices = np.arange(len(boxes)) # Индексы всех детекций

# Фильтрация по классам (если указано) if class_filter is not None: allowed = [classNames.index(cls_name) for cls_name in class_filter if cls_name in classNames] indices = [i for i in indices if classes[i] in allowed]

# Фильтрация по уверенности (если указано) if score_thr is not None: indices = [i for i in indices if scores[i] >= score_thr]

# Возвращаем фильтрованные данные return { "boxes": boxes[indices], "classes": classes[indices], "scores": scores[indices], "orig_shape": img.shape[:2], "classNames": classNames }

def draw_yolo_on_image(img, yolo_obj, color=(0,255,0), thickness=2): """ Отрисовывает детекции YOLO на изображении

Args: img (numpy.ndarray): Изображение для отрисовки yolo_obj (dict): Результаты детекции color (tuple): Цвет рамки thickness (int): Толщина линии """ boxes = yolo_obj["boxes"] classes = yolo_obj["classes"] classNames = yolo_obj["classNames"] orig_h, orig_w = yolo_obj["orig_shape"]

# Множители для масштабирования координат scale_x = orig_w / IMG_SIZE[0] scale_y = orig_h / IMG_SIZE[1]

# Отрисовываем каждую детекцию for box, cl in zip(boxes, classes): left, top, right, bottom = [int(coord) for coord in box] # Координаты бокса

# Масштабируем координаты к оригинальному изображению left = int(left * scale_x) right = int(right * scale_x) top = int(top * scale_y) bottom = int(bottom * scale_y)

# Рисуем рамку cv2.rectangle(img, (left, top), (right, bottom), color, thickness)

# Рисуем надпись label = f"{classNames[cl]}" cv2.putText(img, label, (left, top-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2)

# Инициализация камер cam_main, cam_opt = Camera(camera_type=CameraType.MAIN), Camera(camera_type=CameraType.OPT)

iv = ImageViewer() # Инициализация отображения изображений

def hex_to_bgr(hex_color: str) -> tuple[int, int, int]: """ Преобразует HEX-цвет в BGR (OpenCV формат)

Args: hex_color (str): HEX цвет в формате #RRGGBB

Returns: tuple[int, int, int]: Цвет в формате BGR """ # Убираем символ # if hex_color.startswith("#"): hex_color = hex_color[1:]

# Проверяем длину if len(hex_color) != 6: raise ValueError("Hex color must be in format #RRGGBB")

# Преобразуем HEX в BGR r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16)

return (b, g, r)

if name == "main": print("Выбрана модель:Yolo") IMG_SIZE = (640, 640)

# Загрузка модели Yolo model = Yolo(model_name="yolov8n") while True: # Получаем кадры с камер image_1 = cam_main.get_cv_frame() image_2 = cam_opt.get_cv_frame()

# Выполняем детекцию на первом кадре (по клавиатуре) object_1 = yolo_find_on_image(image_1, results_image_1 := model.run([resize_img(image_1)]), classNames, class_filter=["keyboard"])

# Выполняем детекцию на втором кадре (по человеку) object_2 = yolo_find_on_image(image_2, results_image_2 := model.run([resize_img(image_2)]), classNames, class_filter=["person"])

# Отрисовываем детекции draw_yolo_on_image(image_1, object_1, hex_to_bgr("#ff4040")) draw_yolo_on_image(image_2, object_2, hex_to_bgr("#000099"))

# Отображаем изображения iv.imshow(name='first', frame=image_1) iv.imshow(name='second', frame=image_2)

YoloPose - детекция жестов

import cv2
import sys
from collections import defaultdict
import numpy as np
from pioneer_rknn import YoloPose
from pioneer_sdk2 import Camera, ImageViewer, CameraType
import math
from collections import deque, defaultdict

# Глобальные переменные — изображение и результат детекции image_1 = None object_1 = None

# Список классов объектов classNames = ["person"]

# Пороги для обработки ключевых точек MIN_KPT_CONF = 0.20 # Минимальная уверенность ключевой точки STABLE_FRAMES = 18 # Количество кадров для стабильности HYSTERESIS_FRAMES = 8 # Количество кадров для гистерезиса (переключения) THRESHOLD = HYSTERESIS_FRAMES # Используется как порог для переключения

# Цветовая палитра для отрисовки POSE_PALETTE = np.array( [ [255, 128, 0], [255, 153, 51], [255, 178, 102], [230, 230, 0], [255, 153, 255], [153, 204, 255], [255, 102, 255], [255, 51, 255], [102, 178, 255], [51, 153, 255], [255, 153, 153], [255, 102, 102], [255, 51, 51], [153, 255, 153], [102, 255, 102], [51, 255, 51], [0, 255, 0], [0, 0, 255], [255, 0, 0], [255, 255, 255] ], dtype=np.uint8 )

# Цвета для ключевых точек KPT_COLOR = POSE_PALETTE[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]]

# Пары связей (кости) для отрисовки SKELETON_PAIRS = [ [16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11], [2, 3], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7] ]

# Цвета для костей LIMB_COLOR = POSE_PALETTE[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]] SKELETON = [(a - 1, b - 1) for a, b in SKELETON_PAIRS]

# Индексы ключевых точек (COCO-17) LS, RS, LW, RW, LH, RH = 5, 6, 9, 10, 11, 12

# Размер изображения IMG_SIZE = (640, 640)

# Инициализация камер cam_main, cam_opt = Camera(camera_type=CameraType.MAIN), Camera(camera_type=CameraType.OPT)

# Инициализация отображения изображений iv = ImageViewer()

def resize_img(img): """ Изменяет размер изображения до IMG_SIZE и добавляет ось batch

Args: img (numpy.ndarray): Исходное изображение

Returns: numpy.ndarray: Изображение с размером IMG_SIZE и добавленной осью batch """ # Изменяем размер изображения img_copy = cv2.resize(img, IMG_SIZE)

# Добавляем ось batch (для RKNN) img_copy = np.expand_dims(img_copy, 0) return img_copy

def find_poses(img, det_boxes): """ Преобразует детекции в формат ключевых точек и боксов

Args: img (numpy.ndarray): Исходное изображение det_boxes (list): Список детекций с координатами и ключевыми точками

Returns: dict: Словарь с poses и total_dets """ ih, iw = img.shape[:2] # Высота и ширина исходного изображения

# Множители для масштабирования координат сетки → оригинальное изображение sx = iw / float(IMG_SIZE[0]) sy = ih / float(IMG_SIZE[1])

poses = [] # Список найденных поз for box in det_boxes: x1, y1, x2, y2 = float(box.xmin), float(box.ymin), float(box.xmax), float(box.ymax)

# Получаем массив ключевых точек и фильтруем недопустимые karr = get_kpts_array(np.asarray(box.keypoint)) if karr is None: continue

# Масштабируем ключевые точки и bbox к оригинальному изображению karr = karr.copy() karr[:, 0] *= sx karr[:, 1] *= sy bbox_scaled = [x1 * sx, y1 * sy, x2 * sx, y2 * sy]

poses.append({ 'bbox': bbox_scaled, 'kpts': karr, })

return { 'poses': poses, 'total_dets': len(det_boxes), }

def draw_pose(frame, kpts_arr, thickness=3, bbox=None, label=None, label_color=(50, 220, 50)): """ Отрисовывает позу на кадре

Args: frame (numpy.ndarray): Кадр для отрисовки kpts_arr (numpy.ndarray): Массив ключевых точек (17x3) thickness (int): Толщина линии bbox (list): Координаты бокса label (str): Название жеста label_color (tuple): Цвет надписи """ xy = kpts_arr[:, :2].astype(int) # Координаты точек conf = kpts_arr[:, 2] # Уверенность точек valid = (conf > MIN_KPT_CONF) & ~np.all(xy == 0, axis=1) # Фильтр недопустимых точек

# Отрисовываем точки for idx, (x, y) in enumerate(xy): if valid[idx]: col = tuple(int(c) for c in KPT_COLOR[idx]) cv2.circle(frame, (x, y), thickness + 2, col, -1, lineType=cv2.LINE_AA)

# Отрисовываем кости for i, (p1, p2) in enumerate(SKELETON): if valid[p1] and valid[p2]: pt1, pt2 = tuple(xy[p1]), tuple(xy[p2]) col = tuple(int(c) for c in LIMB_COLOR[i]) cv2.line(frame, pt1, pt2, col, thickness, lineType=cv2.LINE_AA)

# Отрисовываем бокс и надпись if bbox is not None: x1, y1, x2, y2 = map(int, bbox) cv2.rectangle(frame, (x1, y1), (x2, y2), (13, 76, 236), thickness + 1) if label: cv2.putText( frame, label, (x1, max(0, y1 - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.8, label_color, max(1, thickness - 1), lineType=cv2.LINE_AA, )

def get_kpts_array(kpts_obj): """ Преобразует объект ключевых точек в массив numpy

Args: kpts_obj (object): Объект ключевых точек (обычно .data или list)

Returns: numpy.ndarray: Массив ключевых точек (17x3) или None если ошибки """ # Если объект имеет атрибут .data — используем его if hasattr(kpts_obj, 'data'): raw = kpts_obj.data try: raw = raw.cpu().numpy() except Exception: raw = np.asarray(raw) raw = np.squeeze(raw) arr = np.asarray(raw) else: arr = np.asarray(kpts_obj)

if arr.size == 0: return None

# Преобразуем в массив (..., 3) arr = arr.astype(float).reshape(-1) if arr.size % 3 != 0: return None arr = arr.reshape(-1, 3)

# Проверяем размер: минимум 17 точек if arr.shape[0] < 17: return None arr = arr[:17, :] return arr

def _angle_deg(dx, dy): """ Вычисляет угол между вектором и вертикальной осью

Args: dx (float): Смещение по X dy (float): Смещение по Y

Returns: float: Угол в градусах """ # Возвращает абсолютное значение угла return abs(math.degrees(math.atan2(dx, dy)))

# Состояние временного сглаживания (функциональное) def smoother_new(stable_frames=STABLE_FRAMES, hysteresis_frames=HYSTERESIS_FRAMES): """ Создаёт состояние для временного сглаживания

Args: stable_frames (int): Количество кадров для стабильности hysteresis_frames (int): Количество кадров для гистерезиса

Returns: dict: Состояние с историей и текущим значением """ return { 'history': deque(maxlen=max(stable_frames, hysteresis_frames)), 'stable_frames': stable_frames, 'hysteresis_frames': hysteresis_frames, 'current': 'none', }

def smoother_update(state, gesture): """ Обновляет состояние сглаживания

Args: state (dict): Состояние сглаживания gesture (str): Новый жест

Returns: tuple: (current_gesture, is_stable) """ # Добавляем новый жест в историю state['history'].append(gesture)

# Подсчитываем частоту жестов counts = defaultdict(int) for g in state['history']: counts[g] += 1

# Определяем победителя по частоте winner = max(counts.items(), key=lambda x: x[1])[0]

# Если победитель изменился и его частота >= hysteresis_frames — обновляем текущий if winner != state['current']: if counts[winner] >= state['hysteresis_frames']: state['current'] = winner

# Проверяем, стабилен ли текущий жест stable = counts[state['current']] >= state['stable_frames'] return state['current'], stable

# Используется для трекинга по IoU (функциональное) def iou_xyxy(a, b): """ Вычисляет IoU между двумя bbox

Args: a (list): Бокс A [x1, y1, x2, y2] b (list): Бокс B [x1, y1, x2, y2]

Returns: float: IoU значение """ ax1, ay1, ax2, ay2 = a bx1, by1, bx2, by2 = b

# Вычисляем пересечение ix1, iy1 = max(ax1, bx1), max(ay1, by1) ix2, iy2 = min(ax2, bx2), min(ay2, by2) iw, ih = max(0, ix2 - ix1), max(0, iy2 - iy1) inter = iw * ih

# Вычисляем площади area_a = max(0, ax2 - ax1) * max(0, ay2 - ay1) area_b = max(0, bx2 - bx1) * max(0, by2 - by1) union = area_a + area_b - inter + 1e-6 return inter / union

def tracker_new(iou_thr=0.35): """ Создаёт состояние трекера

Args: iou_thr (float): Порог IoU для совпадения

Returns: dict: Состояние трекера """ return { 'tracks': {}, # tid -> {'bbox': list[4], 'smoother': smoother_state} 'next_id': 1, 'iou_thr': iou_thr, }

def tracker_match_and_update(state, boxes): """ Сопоставляет и обновляет треки по IoU

Args: state (dict): Состояние трекера boxes (list): Список новых боксов

Returns: dict: Сопоставление (j -> tid) """ assigns = {} # Сопоставления unmatched = set(range(len(boxes))) # Не сопоставленные

# Пытаемся сопоставить существующие треки for tid, tr in list(state['tracks'].items()): best_j, best_iou = None, 0.0 for j in list(unmatched): iou = iou_xyxy(tr['bbox'], boxes[j]) if iou > best_iou: best_iou, best_j = iou, j if best_j is not None and best_iou >= state['iou_thr']: assigns[best_j] = tid unmatched.remove(best_j) state['tracks'][tid]['bbox'] = boxes[best_j]

# Удаляем старые треки (не использованные) for j in unmatched: tid = state['next_id'] state['next_id'] += 1 assigns[j] = tid state['tracks'][tid] = { 'bbox': boxes[j], 'smoother': smoother_new(), }

# Prune unmatched old tracks (keep only currently assigned) keep_ids = set(assigns.values()) state['tracks'] = {tid: tr for tid, tr in state['tracks'].items() if tid in keep_ids}

return assigns

def draw_poses(img, result, tracker_state): """ Отрисовывает позы на изображении и обновляет трекер

Args: img (numpy.ndarray): Изображение result (dict): Результат детекции tracker_state (dict): Состояние трекера """ poses = result.get('poses', []) total_dets = result.get('total_dets', 0)

if poses: boxes = [p['bbox'] for p in poses] assigns = tracker_match_and_update(tracker_state, boxes)

for j, tid in assigns.items(): karr = poses[j]['kpts']

gesture = classify_gesture(karr) current, stable = smoother_update( tracker_state['tracks'][tid]['smoother'], gesture )

label_color = (0, 255, 0) if stable and current != 'none' else (0, 200, 255) draw_pose(img, karr, thickness=3, bbox=boxes[j], label=current, label_color=label_color)

if stable and current != 'none': #print(f"EXECUTE -> id={tid} gesture={current}") pass else: # Если нет поз — всё равно очищаем трекер if total_dets > 0: print(f"No drawable poses parsed; raw detections: {total_dets}") tracker_state['tracks'].clear()

def classify_gesture(kp): """ Определяет жест по ключевым точкам

Args: kp (numpy.ndarray): Массив ключевых точек (17x3)

Returns: str: Название жеста """ # Используем нужные индексы needed = [LS, RS, LW, RW, LH, RH]

# Если уверенность меньше порога — возвращаем "none" if (kp[needed, 2] < MIN_KPT_CONF).any(): return 'none'

lx, ly = kp[LS][:2] rx, ry = kp[RS][:2] lwx, lwy = kp[LW][:2] rwx, rwy = kp[RW][:2] lhy, rhy = kp[LH][1], kp[RH][1]

shoulder_span = max(1.0, abs(rx - lx)) torso_len = max(1.0, max(abs(ly - lhy), abs(ry - rhy)))

# Относительное смещение запястий к плечам (вверх — положительное) dx_l, dy_l_up = lwx - lx, ly - lwy dx_r, dy_r_up = rwx - rx, ry - rwy

len_l = math.hypot(dx_l, dy_l_up) len_r = math.hypot(dx_r, dy_r_up)

ang_l = _angle_deg(dx_l, dy_l_up) ang_r = _angle_deg(dx_r, dy_r_up)

ANG_UP_THR = 25 ANG_SIDE_THR = 25 LEN_UP_K = 0.32 LEN_SIDE_K = 0.48 OFF_UP_K = 0.28 OFF_SIDE_K = 0.55 VERT_TOL_K = 0.32

wrist_centre_thr_px = max(40.0, 0.18 * shoulder_span)

l_up = (ang_l < ANG_UP_THR and len_l > LEN_UP_K * torso_len) or (lwy < ly - OFF_UP_K * torso_len) r_up = (ang_r < ANG_UP_THR and len_r > LEN_UP_K * torso_len) or (rwy < ry - OFF_UP_K * torso_len)

l_side = ( abs(ang_l - 90) < ANG_SIDE_THR and len_l > LEN_SIDE_K * shoulder_span ) or ( lwx < lx - OFF_SIDE_K * shoulder_span and abs(lwy - ly) < VERT_TOL_K * torso_len )

r_side = ( abs(ang_r - 90) < ANG_SIDE_THR and len_r > LEN_SIDE_K * shoulder_span ) or ( rwx > rx + OFF_SIDE_K * shoulder_span and abs(rwy - ry) < VERT_TOL_K * torso_len )

if math.hypot(lwx - rwx, lwy - rwy) < wrist_centre_thr_px: return 'wrist_centre' if l_up and r_up: return 'two_hand_up' if l_side and r_side: return 'two_hand_side' if l_up: return 'left_up' if r_up: return 'right_up' if l_side: return 'left_side' if r_side: return 'right_side'

return 'none'

def should_execute(cmd, last_cmds_deque, target_pose, threshold=THRESHOLD): """ Проверяет, нужно ли выполнить команду (жест) после стабильного повторения

Args: cmd (str): Текущий жест last_cmds_deque (deque): История последних жестов target_pose (str): Целевой жест threshold (int): Порог количества кадров для стабильности

Returns: bool: True если нужно выполнить команду, иначе False """ # Если текущий жест не совпадает с целевым — очищаем историю if cmd != target_pose: last_cmds_deque.clear() return False

# Добавляем новый жест в историю last_cmds_deque.append(cmd)

# Если истории меньше порога — не выполняем if len(last_cmds_deque) < threshold: return False

# Если все элементы совпадают с целевым — очищаем историю и возвращаем True if all(c == target_pose for c in last_cmds_deque): last_cmds_deque.clear() return True

return False

def _select_primary_pose(poses): """ Выбирает основную позу (с наибольшей площадью bbox)

Args: poses (list): Список найденных поз

Returns: dict or None: Поза с наибольшей площадью или None если нет поз """ # Если нет поз — возвращаем None if not poses: return None

# Вспомогательная функция для вычисления площади bbox def _area(p): x1, y1, x2, y2 = p['bbox'] return max(0.0, x2 - x1) * max(0.0, y2 - y1)

# Возвращаем позу с наибольшей площадью return max(poses, key=_area)

def pose_compare(result, target_pose, last_cmds, threshold=THRESHOLD, selector='largest'): """ Сравнивает результат детекции с целевым жестом и проверяет, нужно ли выполнить его

Args: result (dict): Результат детекции target_pose (str): Целевой жест last_cmds (deque or dict): История команд или словарь с историей по жестам threshold (int): Порог количества кадров для стабильности selector (str): Метод выбора позы ('largest' или 'first')

Returns: bool: True если нужно выполнить команду, иначе False """ poses = result.get('poses', [])

pose_entry = _select_primary_pose(poses) if selector == 'largest' else (poses[0] if poses else None) if pose_entry is None: return False

# Определяем жест текущей позы pose_name = classify_gesture(pose_entry['kpts'])

# Получаем deque для целевого жеста (если last_cmds — словарь) dq = last_cmds.get(target_pose) if isinstance(last_cmds, dict) else last_cmds if dq is None: # Если не найдено — создаём новый deque dq = deque(maxlen=threshold) if isinstance(last_cmds, dict): last_cmds[target_pose] = dq # Проверяем, нужно ли выполнить команду if should_execute(pose_name, dq, target_pose, threshold): #print(f"***************** EXECUTE → {pose_name}") return True return False

# Инициализация истории команд для всех жестов last_cmds = {pose_name: deque(maxlen=8) for pose_name in [ 'left_up', 'right_up', 'left_side', 'right_side', 'two_hand_up', 'two_hand_side', 'wrist_centre' ]}

def main_demonstration(): # Инициализация трекера tracker_state = tracker_new(iou_thr=0.35) print("Выбрана модель: YoloPose")

# Загрузка модели YoloPose model = YoloPose(model_name="yolov8n-pose") while True: # Получаем кадр с камеры image_1 = cam_main.get_cv_frame()

# Выполняем детекцию object_1 = find_poses(image_1, model.run([resize_img(image_1)]))

# Отрисовываем позы и обновляем трекер draw_poses(image_1, object_1, tracker_state)

# Отображаем изображение iv.imshow(name='first', frame=image_1)

def main_compare(): # Инициализация трекера tracker_state = tracker_new(iou_thr=0.35) print("Выбрана модель: YoloPose")

# Загрузка модели YoloPose model = YoloPose(model_name="yolov8n-pose") while True: # Получаем кадр с камеры image_1 = cam_main.get_cv_frame()

# Выполняем детекцию object_1 = find_poses(image_1, model.run([resize_img(image_1)]))

# Выполняем сравнение if pose_compare(object_1, "left_side", last_cmds): print('left') elif pose_compare(object_1, "right_side", last_cmds): print('right')

# Отрисовываем позы и обновляем трекер draw_poses(image_1, object_1, tracker_state)

# Отображаем изображение iv.imshow(name='first', frame=image_1)

if name == "main": if len(sys.argv) < 2: print("Использование: python3 demonstration_pose.py [demonstration|compare]") sys.exit(1)

arg = sys.argv[1].lower()

if arg == "demonstration": main_demonstration() elif arg == "compare": main_compare() else: print(f"Неизвестный аргумент: {arg}")

YoloSeg - детекция всех классов

import cv2
import numpy as np
from pioneer_rknn import YoloSeg
from pioneer_sdk2 import Camera, ImageViewer, CameraType

# Глобальные переменные — изображения и результаты детекции image_1 = None object_1 = None

classNames = ["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 resize_img(img): """ Изменяет размер изображения до IMG_SIZE и добавляет ось batch

Args: img (numpy.ndarray): Исходное изображение

Returns: numpy.ndarray: Изображение с размером IMG_SIZE и добавленной осью batch """ # Изменяем размер изображения до IMG_SIZE img_copy = cv2.resize(img, IMG_SIZE)

# Добавляем ось batch (для RKNN) img_copy = np.expand_dims(img_copy, 0) return img_copy

def yolo_find_on_image(img, results, classNames, class_filter=None, score_thr=None): """ Фильтрует результаты детекции YOLO по классам и уверенности

Args: img (numpy.ndarray): Исходное изображение results (tuple): Результаты детекции (boxes, classes, scores) classNames (list): Список названий классов class_filter (list or None): Список классов для фильтрации score_thr (float or None): Порог уверенности

Returns: dict: Фильтрованные результаты детекции """ boxes, classes, scores, masks = results # Распаковываем результаты

# Если нет данных — возвращаем пустой словарь if boxes is None or classes is None or scores is None or masks is None or len(boxes) == 0: return { "boxes": np.zeros((0, 4), dtype=np.float32), "classes": np.zeros((0,), dtype=np.int32), "scores": np.zeros((0,), dtype=np.float32), "masks": np.zeros((0,), dtype=np.float32), "orig_shape": img.shape[:2], "classNames": classNames }

# Преобразуем данные в массивы NumPy boxes = np.array(boxes) classes = np.array(classes) scores = np.array(scores) masks = np.array(masks)

indices = np.arange(len(boxes)) # Индексы всех детекций

# Фильтрация по классам (если указано) if class_filter is not None: allowed = [classNames.index(cls_name) for cls_name in class_filter if cls_name in classNames] indices = [i for i in indices if classes[i] in allowed]

# Фильтрация по уверенности (если указано) if score_thr is not None: indices = [i for i in indices if scores[i] >= score_thr]

# Возвращаем фильтрованные данные return { "boxes": boxes[indices], "classes": classes[indices], "scores": scores[indices], "masks": masks[indices], "orig_shape": img.shape[:2], "classNames": classNames }

def _class_color(class_id: int) -> tuple: """Генерирует цвет для класса на основе его id

Args: class_id (int): ID класса в списке классов

Returns: tuple: B,G и R каналы для всех каналов """ rng = np.random.default_rng(class_id) b, g, r = rng.integers(50, 255, size=3) return (int(b), int(g), int(r))

def draw_yolo_on_image(img, yolo_obj, color=(0,255,0), thickness=2, mask_alpha=0.7): """ Отрисовывает детекции YOLO на изображении. Маски раскрашиваются разными цветами по классам, рамки/подписи - цветом color

Args: img (numpy.ndarray): Изображение для отрисовки yolo_obj (dict): Результаты детекции color (tuple): Цвет рамки/подписи (единый, например чтобы отличать камеры) thickness (int): Толщина линии mask_alpha (float): Прозрачность наложения масок (0..1) """ boxes = yolo_obj["boxes"] classes = yolo_obj["classes"] classNames = yolo_obj["classNames"] masks = yolo_obj["masks"] orig_h, orig_w = yolo_obj["orig_shape"]

scale_x = orig_w / IMG_SIZE[0] scale_y = orig_h / IMG_SIZE[1]

# Наложение масок — разными цветами по классам if len(masks) > 0: overlay = img.copy() for mask, cl in zip(masks, classes): mask_color = _class_color(int(cl)) mask_resized = cv2.resize( mask.astype(np.uint8), (orig_w, orig_h), interpolation=cv2.INTER_NEAREST ) overlay[mask_resized == 1] = mask_color # Задаём пикселям, принадлежащим маске, сгенерированный цвет

img[:] = cv2.addWeighted(overlay, mask_alpha, img, 1 - mask_alpha, 0) # Складываем раскрашенные маски с исходным изображением

# Подписываем детекции for box, cl in zip(boxes, classes): left, top, _, _ = [int(coord) for coord in box]

left = int(left * scale_x) top = int(top * scale_y)

label = f"{classNames[cl]}" cv2.putText(img, label, (left, top-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2)

# Инициализация камер cam_main = Camera(camera_type=CameraType.MAIN) iv = ImageViewer() # Инициализация отображения изображений

def hex_to_bgr(hex_color: str) -> tuple[int, int, int]: """ Преобразует HEX-цвет в BGR (OpenCV формат)

Args: hex_color (str): HEX цвет в формате #RRGGBB

Returns: tuple[int, int, int]: Цвет в формате BGR """ # Убираем символ # if hex_color.startswith("#"): hex_color = hex_color[1:]

# Проверяем длину if len(hex_color) != 6: raise ValueError("Hex color must be in format #RRGGBB")

# Преобразуем HEX в BGR r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16)

return (b, g, r)

if name == "main": print("Выбрана модель:Yolo") IMG_SIZE = (640, 640)

# Загрузка модели Yolo model = YoloSeg(model_name="yolov8n-seg") while True:

# Получаем кадры с камер image_1 = cam_main.get_cv_frame()

# Выполняем детекцию object_1 = yolo_find_on_image(image_1, results_image_1 := model.run([resize_img(image_1)]), classNames, class_filter=None)

# Отрисовываем детекции draw_yolo_on_image(image_1, object_1, hex_to_bgr("#ff4040"))

# Отображаем изображения iv.imshow(name='first', frame=image_1)

PaddleOCR - детекция текстовых регионов

import cv2
import numpy as np
from pioneer_rknn import PaddleOCR
from pioneer_sdk2 import Camera, ImageViewer, CameraType
from PIL import ImageFont, ImageDraw, Image

# Глобальные переменные — изображение, результаты и текст image_1 = None object_1 = None text = None

# Инициализация камер cam_main, cam_opt = Camera(camera_type=CameraType.MAIN), Camera(camera_type=CameraType.OPT)

# Инициализация Image Viewer'a iv = ImageViewer()

def draw_text_in_corner(text, frame, color): """ Выводит текст в левом верхнем углу изображения

Args: text (str): Текст для отрисовки frame (numpy.ndarray): Изображение color (tuple): Цвет текста (BGR)

Returns: numpy.ndarray: Изображение с добавленным текстом """ # Преобразуем OpenCV-изображение в PIL-формат (RGB) frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

# Создаем объект для рисования draw = ImageDraw.Draw(frame_pil)

# Загружаем шрифт (DejaVuSans) font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 28)

lines = text.split("\n") # Разбиваем текст на строки x, y = 10, 30 # Начальные координаты

# Рисуем каждую строку for line in lines: draw.text((x, y), line, font=font, fill=(*color[::-1], 255)) # RGB! y += 34

# Обратно в BGR frame_out = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR) return frame_out

def hex_to_bgr(hex_color: str) -> tuple[int, int, int]: """ Преобразует HEX-цвет в BGR (OpenCV формат)

Args: hex_color (str): HEX цвет в формате #RRGGBB

Returns: tuple[int, int, int]: Цвет в формате BGR """ # Убираем символ # if hex_color.startswith("#"): hex_color = hex_color[1:]

# Проверяем длину if len(hex_color) != 6: raise ValueError("Hex color must be in format #RRGGBB")

# Преобразуем HEX в BGR r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16)

return (b, g, r)

if name == "main": print("Выбрана модель: PaddleOCR") # Загрузка модели PaddleOCR с детекцией и распознаванием model = PaddleOCR(det_model_name="PP-OCRv5_mobile_det", rec_model_name="eslav_PP-OCRv5_mobile_rec")

while True: # Получаем кадр с камеры image_1 = cam_main.get_cv_frame()

# Выполняем OCR-детекцию и распознавание object_1 = [model.run(image_1)]

# Извлекаем текст из результатов text = "\n".join([text for group in object_1[0][1] for (text, _) in group])

# Выводим текст в левом верхнем углу image_1 = draw_text_in_corner(text=text, frame=image_1, color=hex_to_bgr("#ff4040"))

# Отображаем изображение iv.imshow(name='first', frame=image_1)

PaddleOCR - детекция текстовых регионов и вывод слов в консоль

import cv2
import numpy as np
from pioneer_rknn import PaddleOCR
from pioneer_sdk2 import Camera, ImageViewer, CameraType

# Глобальные переменные — изображение, результаты и текст image_1 = None object_1 = None text = None

# Инициализация камер cam_main, cam_opt = Camera(camera_type=CameraType.MAIN), Camera(camera_type=CameraType.OPT)

# Инициализация Image Viewer'a iv = ImageViewer()

def draw_bboxes_on_image(img, output): """ Отрисовывает bounding boxes на изображении

Args: img (numpy.ndarray): Изображение для отрисовки output (object): Результаты детекции OCR

Returns: numpy.ndarray: Изображение с отрисованными боксами """ # Функция проверяет, является ли объект bbox-подобным def is_bbox_like(x): # Если ndarray и 2D с 2 колонками и минимум 4 строками — валидно if isinstance(x, np.ndarray) and x.ndim == 2 and x.shape[1] == 2 and x.shape[0] >= 4: return True

# Если list/tuple и минимум 4 элемента — проверяем преобразование if isinstance(x, (list, tuple)) and len(x) >= 4: try: arr = np.array(x, dtype=float) return arr.ndim == 2 and arr.shape[1] == 2 and arr.shape[0] >= 4 except Exception: return False return False

# Рекурсивная функция для отрисовки любого объекта def _draw_any(obj, level=0): # Пытается нарисовать, если валидный bbox if is_bbox_like(obj): pts = np.array(obj, dtype=np.int32).reshape((-1, 1, 2)) cv2.polylines(img, [pts], isClosed=True, color=(155,155,0), thickness=2) return # Если это лист/tuple — проходимся рекурсивно if isinstance(obj, (list, tuple)): for elem in obj: _draw_any(elem, level+1)

# Запускаем отрисовку _draw_any(output) return img

if name == "main": print("Выбрана модель: PaddleOCR") # Загрузка модели PaddleOCR с детекцией и распознаванием model = PaddleOCR(det_model_name="PP-OCRv5_mobile_det", rec_model_name="eslav_PP-OCRv5_mobile_rec")

while True: # Получаем кадр с камеры image_1 = cam_main.get_cv_frame()

# Выполняем OCR-детекцию и распознавание object_1 = [model.run(image_1)]

# Извлекаем текст из результатов text = "\n".join([text for group in object_1[0][1] for (text, _) in group])

# Отрисовываем боксы на изображении draw_bboxes_on_image(image_1, object_1[0])

# Отображаем изображение iv.imshow(name='first', frame=image_1)

# Пишем результат в консоль print('\n', "-" * 30, '\n\n', text)

Более подробное описание примеров для работы с библиотекой Pioneer-RKNN в GitFlic.

Представляет собой набор классов и функций для работы с моделями машинного обучения

Более подробное описание примеров для работы с библиотекой Pioneer-RKNN в GitFlic.

Класс Yolo

Инкапсулирует логику работы с моделью YOLO - загружает модель, выполняет инференс по входному изображению и проводит постобработку для получения координат объектов, классов и уверенности детекции

Инициализация класса

from pioneer_rknn import Yolo # импортируем класс Yolo из библиотеки pioneer_rknn

yolo = Yolo( # создаём экземпляр класса Yolo model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.25, # порог 25%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.45, # порог 45%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoU > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

Методы класса

Выполнить инференс данных на Yolo

from pioneer_rknn import Yolo # импортируем класс Yolo из библиотеки pioneer_rknn

yolo = Yolo( # создаём экземпляр класса Yolo model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.25, # порог 25%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.45, # порог 45%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoU > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

print(yolo.run(inputs = [])) # выполняем инференс и обработку списка тензоров модели # возвращает кортеж (boxes, classes, scores) или None, если нет результата

Класс YoloSeg

Расширяет функциональность Yolo для решения задачи сегментации экземпляров (instance segmentation). Помимо детекции объектов, извлекает маски пикселей для каждого обнаруженного объекта и выполняет постобработку для получения контуров и итоговых бинарных масок с привязкой к соответствующим боксам и классам

Инициализация класса

from pioneer_rknn import YoloSeg # импортируем класс YoloSeg из библиотеки pioneer_rknn

yoloseg = YoloSeg( # создаём экземпляр класса YoloSeg model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n-seg", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.25, # порог 25%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.45, # порог 45%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoW > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

Методы класса

Выполнить инференс данных на YoloSeg

from pioneer_rknn import YoloSeg # импортируем класс YoloSeg из библиотеки pioneer_rknn

yoloseg = YoloSeg( # создаем экземпляра класса YoloSeg model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n-seg", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.25, # порог 25%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.45, # порог 45%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoW > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

print(yoloseg.run(inputs = [])) # выполняем инференс и обработку списка тензоров модели # возвращает кортеж (boxes, classes, scores, masks) или None, если нет результа

Класс YoloPose

Расширяет функциональность Yolo для решения задачи оценки позиции (pose estimation). Помимо детекции объектов, извлекает ключевые точки скелета для каждого обнаруженного объекта и выполняет постобработку для получения координат и достоверности ключевых точек

Инициализация класса

from pioneer_rknn import YoloPose # импортируем класс YoloPose из библиотеки pioneer_rknn

yolopose = YoloPose( # создаём экземпляр класса YoloPose model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n-pose", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.4, # порог 40%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.5, # порог 50%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoU > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

Методы класса

Выполнить инференс данных на YoloPose

from pioneer_rknn import YoloPose # импортируем класс YoloPose из библиотеки pioneer_rknn

yolopose = YoloPose( # создаём экземпляр класса YoloPose model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n-pose", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0], # номер NPU-ядра для вычислений (если None, будет выбрано автоматически) object_thresh = 0.4, # порог 40%, если выше, то объект в кадре считается искомым классом, например: human # формула: Уверенность модели в идентификации > object_thresh = human nms_thresh = 0.5, # порог 50%, если выше, то объект в кадре считается обнаруженным повторно, удаляет дубликат рамки # формула: IoU > nms_thresh = delete img_width = 640, # ширина изображения подаваемого для работы с моделью img_height = 640) # высота изображения подаваемого для работы с моделью

print(yolopose.run(inputs = list[np.ndarray])) # выполняем инференс входных тензоров и последующую обработку выходов # возвращает список обнаруженных боксов с ключевыми точками

Класс PaddleOCR

Инкапсулирует пайплайн OCR - выделение текстовых областей, чем занимается модель детекции, а далее на выделенных областях распознаётся символьный текст, за это отвечает модель распознавания текстовых символов

Описание класса

from pioneer_rknn import PaddleOCR # импортируем класс PaddleOCR из библиотеки pioneer_rknn

paddle = PaddleOCR( # создаём экземпляр класса PaddleOCR det_model_path = None, # путь к detector модели в формате .rknn (указывать при отсутствии det_model_name) rec_model_path = None, # путь к recognizer модели в формате .rknn (указывать при отсутствии rec_model_name) det_model_name = "PP-OCRv5_mobile_det", # имя detector модели в регистре моделей (указывать при отсутствии det_model_path) rec_model_name = "eslav_PP-OCRv5_mobile_rec", # имя recognizer модели в регистре моделей (указывать при отсутствии rec_model_path) npu_core = [0]) # номер NPU-ядра для вычислений (если None, будет выбрано автоматически)

Методы класса

Выполнить инференс данных на PaddleOCR

from pioneer_rknn import PaddleOCR # импортируем класс PaddleOCR из библиотеки pioneer_rknn

paddle = PaddleOCR( # создаём экземпляр класса PaddleOCR det_model_path = None, # путь к detector модели в формате .rknn (указывать при отсутствии det_model_name) rec_model_path = None, # путь к recognizer модели в формате .rknn (указывать при отсутствии rec_model_name) det_model_name = "PP-OCRv5_mobile_det", # имя detector модели в регистре моделей (указывать при отсутствии det_model_path) rec_model_name = "eslav_PP-OCRv5_mobile_rec", # имя recognizer модели в регистре моделей (указывать при отсутствии rec_model_path) npu_core = [0]) # номер NPU-ядра для вычислений (если None, будет выбрано автоматически)

print(paddle.run(img = list[np.ndarray])) # выполняем инференс изображения и последующую обработку выходов # возвращает: список найденных четырехугольников # список результатов распознавания текста # (None, None), если текстовые области не обнаружены

None

Класс ModelContainer

Обеспечивает базовые функции для загрузки и выполнения инференса

Инициализация класса

from pioneer_rknn import ModelContainer       # импортируем класс ModelContainer из библиотеки pioneer_rknn

ai_1 = ModelContainer( # создаём экземпляр класса ModelContainer model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0]) # номер NPU-ядра для вычислений (если None, будет выбрано автоматически)

Методы класса

Выполнить инференс данных

from pioneer_rknn import ModelContainer     # импортируем класс ModelContainer из библиотеки pioneer_rknn

ai_1 = ModelContainer( # создаём экземпляр класса ModelContainer model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0]) # номер NPU-ядра для вычислений (если None, будет выбрано автоматически)

result = ai_1.run(inputs) # метод run выполняет инференс входных данных (inputs) и возвращает результат # inputs принимает список[list], кортеж(tuple) или переменную # метод возвращает результат инференса или пустой список, если ресурсы RKNN уже освобождены

Освободить ресурсы RKNN

from pioneer_rknn import ModelContainer     # импортируем класс ModelContainer из библиотеки pioneer_rknn

ai_1 = ModelContainer( # создаём экземпляр класса ModelContainer model_path = None, # путь к модели в формате .rknn (указывать при отсутствии model_name) model_name = "yolov8n", # имя модели в регистре моделей (указывать при отсутствии model_path) npu_core = [0]) # номер NPU-ядра для вычислений (если None, будет выбрано автоматически)

ai_1.release() # метод release освобождает ресурсы NPU # применять следует после выполнения необходимых вычислений на NPU # выполняется автоматически после завершения исполнения программы

"yolov8n",

Обеспечивает базовые функции для загрузки и выполнения инференса

Класс ModelRegistry

Предоставляет интерфейс для взаимодействия с регистром моделей, позволяя получать список моделей, информацию о конкретной модели, удалять и загружать модели

Инициализация класса

from pioneer_rknn import ModelRegistry # импортируем класс ModelRegistry из библиотеки pioneer_rknn

ai_reg = ModelRegistry( # создаём экземпляр класса ModelRegistry url = "http://127.0.0.1:7777/model") # адрес регистра моделей по умолчанию

Методы класса

Получить список моделей

from pioneer_rknn import ModelRegistry # импортируем класс ModelRegistry из библиотеки pioneer_rknn

ai_reg = ModelRegistry( # создаём экземпляр класса ModelRegistry url = "http://127.0.0.1:7777/model") # адрес регистра моделей по умолчанию

print(ai_reg.list_model()) # печатаем словарь с информацией о моделях или None, если ошибка

Получить информацию о модели по ее имени

from pioneer_rknn import ModelRegistry # импортируем класс ModelRegistry из библиотеки pioneer_rknn

ai_reg = ModelRegistry( # создаём экземпляр класса ModelRegistry url = "http://127.0.0.1:7777/model") # адрес регистра моделей по умолчанию

print(ai_reg.get_model_info(name = "yolov8n")) # печатаем словарь с информацией о указанной модели или None, если ошибка

Удалить модель по ее имени

from pioneer_rknn import ModelRegistry # импортируем класс ModelRegistry из библиотеки pioneer_rknn

ai_reg = ModelRegistry( # создаём экземпляр класса ModelRegistry url = "http://127.0.0.1:7777/model") # адрес регистра моделей по умолчанию

print(ai_reg.delete_model(name = "your_model")) # печатаем словарь с результатом удаления или None, если ошибка

Загрузить модель в регистр

from pioneer_rknn import ModelRegistry # импортируем класс ModelRegistry из библиотеки pioneer_rknn

ai_reg = ModelRegistry( # создаём экземпляр класса ModelRegistry url = "http://127.0.0.1:7777/model") # адрес регистра моделей по умолчанию

ai_reg.upload_model( # загружаем модель, содержит аргументы: name = "your_model", # имя модели (будет отображаться в регистре после загрузки) version = "your_version", # версия модели (если пустая строка, будет указано автоматически) filepath = "your_path", # путь к файлу модели (модель должна быть в формате onnx (аргумент opset=19)). arch = "custom") # архитектура модели (если архитектура не поддерживается, следует указать custom)