Pioneer-RKNN — нейросети на борту
pioneer_rknn — набор классов и функций для работы с моделями машинного обучения на бортовом нейроускорителе Пионера Мини 2. Поддерживаются детекция объектов (YOLO), сегментация (YOLOSeg), оценка поз (YOLOPose), распознавание текста (PaddleOCR) и загрузка собственных моделей. Инференс выполняется прямо на коптере — без внешнего сервера.
Pioneer-RKNN
Представляет собой набор классов и функций для работы с моделями машинного обучения
Поддержка Pioneer-RKNN квадрокоптерами серии Пионер
| Квадрокоптеры | Pioneer-RKNN | Взаимодействие через: |
|---|---|---|
| Мини 2 | ✅ | PioneerOS (предустановлен) |
| Мини | ❌ | ❌ |
| Базовый | ✅ | модуль radxa zero |
| FPV | ❌ | ❌ |
| Макс (ROS программирование) | ❌ | ❌ |
AI модели и поддерживаемые архитектуры
| Yolo | YoloSeg | YoloPose | PaddleOCR |
|---|---|---|---|
| yolov8 | yolov8-seg | yolov8-pose | PP-OCRv5_mobile_det |
| yolov11 | yolov11-seg | PP-OCRv5_mobile_rec | |
| yolov26 | yolov26-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)