Вузол
Node — це найменша складова одиниця в SiMa.ai Neat.
Вузли — це елементи, які з’єднуються в Graph: етапи декодування, етапи попередньої обробки, етапи моделювання, етапи подальшої обробки, джерела, приймачі та межі застосування.
Посилання:
Що таке вузол
Вузол надає один логічний елемент роботи, а також метадані, необхідні для без печного об’єднання цього елемента з іншими:
- робота під час виконання, наприклад, декодування, перетворення, попередня обробка, здійснення висновків, подальша обробка або поведінка приймача;
- контракти введення/виведення, такі як очікуваний тип медіаданих, розмірність тензора, тип даних або обмеження;
- детермінована схема іменування компонентів, завдяки чому
describe(), діагностичні інструменти, показники та засоби моніторингу залишаються стабільними.
Основне правило просте:
Graph = nodes wired together
Model — це джерело вузлів, що враховує модель. Model можна безпосередньо додати до Graph, або він може надавати фрагменти, які можна повторно використовувати на різних етапах, наприклад, попередню обробку, обчислення та подальшу обробку.
Попередньо створені групи вузлів.
Деякі поширені шаблони містять більше ніж один низькорівневий вузол. Neat надає їх у вигляді попередньо створених груп, щоб користувачам не потрібно було вручну налаштовувати кожен джерельний, декодуючий, перетворюючий елемент, чергу або кінцевий пункт.
Уявіть собі групу як міні-граф, який можна повторно використовувати: іменований набір вузлів із чітко визначеним призначенням.
Приклади:
VideoInputGroup(...): шлях до файлу/джерела відео + шлях декодування.RtspDecodedInput(...): джерело RTSP + шлях декодування.- фрагменти моделі маршруту: попередня обробка + виведення результатів + декодування/пост-обробка навколо скомпільованої моделі.
#include "neat/runtime.h"
#include "neat/node_groups.h"
simaai::neat::Graph graph;
simaai::neat::nodes::groups::VideoInputGroupOptions vopt;
vopt.path = "/data/sample.mp4";
graph.add(simaai::neat::nodes::groups::VideoInputGroup(vopt));
Використовуйте попередньо створені групи, коли структура є стандартною, а найцікавіша частина вашої програми не пов’язана з внутрішньою організацією обробки медіаданих. Якщо вам потрібна спеціальна топологія, створіть відповідний Graph самостійно, використовуючи більш низькорівневі вузли.
Оберіть вузол або групу.
Перегляньте цю карту, перш ніж відкривати повний довідник API.
| Завдання | З чого почати вивчення C++ | З чого почати вивчення Python. |
|---|---|---|
| Програма передає тензори, зображення або зразки. | nodes::Input("name", options) | pyneat.nodes.input("name", options) |
| Програма завантажує повний зразок. | nodes::Output("name", options) | pyneat.nodes.output("name", options) |
| Зчитуйте кадри з файлу. | nodes::FileInput(...) плюс вузли декодування або nodes::groups::VideoInputGroup(...). | pyneat.nodes.file_input(...) плюс декодування вузлів, або pyneat.groups.video_input(...). |
| Зчитайте одне зображення як джерело. | nodes::StillImageInput(...) | pyneat.nodes.still_image_input(...) |
| Зчитайте дані з камери MIPI/libcamera. | nodes::CameraInput(...) | pyneat.nodes.camera_input(...) |
| Зчитування з камери USB (UVC) MJPEG | nodes::UsbCameraInput(...) | pyneat.nodes.usb_camera_input(...) |
| Відтворіть RTSP-потік. | nodes::RTSPInput(...), nodes::groups::RtspEncodedInput(...) або nodes::groups::RtspDecodedInput(...) | pyneat.nodes.rtsp_input(...), pyneat.groups.rtsp_encoded_input(...) або pyneat.groups.rtsp_decoded_input(...) |
| Зчитування HTTP-потоку, що складається з частин у форматі MJPEG. | nodes::groups::HttpMjpegDecodedInput(...), або nodes::HttpSource(...), MultipartJpegDemux(), JpegParse() для ручного налаштування кадру. | pyneat.groups.http_mjpeg_decoded_input(...), або pyneat.nodes.http_source(...), pyneat.nodes.multipart_jpeg_demux(), pyneat.nodes.jpeg_parse(). |
| Декодуйте зображення. | nodes::ImageDecode() або nodes::JpegDecode() | pyneat.nodes.image_decode() або pyneat.nodes.jpeg_decode() |
| Кодування сирого відео | nodes::SimaEncode(SimaEncodeOptions{...}) | pyneat.nodes.sima_encode(options) |
| Декодуйте відео, закодоване певним алгоритмом. | nodes::SimaDecode(SimaDecodeOptions{...}) | pyneat.nodes.sima_decode(options) |
| Декодуйте кадри MJPEG з камери (4:2:0 або 4:2:2) | usb_camera_info(...).decoded_format як out_format для nodes::SimaDecode(...), і такий самий вхідний формат моделі | pyneat.usb_camera_info(...).decoded_format як out_format для pyneat.nodes.sima_decode(...), і такий самий вхідний формат моделі |
| Змінюйте масштаб або встановлюйте обмеження частоти відтворення відео. | nodes::VideoScale(), VideoRate(), ImageFreeze() | pyneat.nodes.video_scale(), video_rate(), image_freeze() |
| Створіть повторно використовуваний шлях для введення зображень. | nodes::groups::ImageInputGroup(...) | pyneat.groups.image_input(...) |
| Створіть повторно використовуваний шлях для введення відео. | nodes::groups::VideoInputGroup(...) | pyneat.groups.video_input(...) |
| Надсилайте відео. | nodes::groups::VideoSender(...) або допоміжні модулі для виведення даних у форматі H.264/UDP. | pyneat.groups.video_sender(...) або допоміжні інстру менти для виведення даних у форматі H.264/UDP. |
| Надсилайте метадані. | MetadataSender | pyneat.MetadataSender |
| Відображати результати виявлення. | nodes::SimaRender(...) | pyneat.nodes.sima_render(...) |
| Перетворіть логіти на індекс класу. | nodes::SimaArgMax(...) | pyneat.nodes.sima_argmax(...) |
| Вокселізуйте прохід LiDAR у стовпці. | nodes::SimaVoxelize(SimaVoxelizeOptions{...}) | pyneat.nodes.sima_voxelize(options) |
| Розподіліть ознаки стовпців на полотно BEV. | nodes::SimaPillarScatter(SimaPillarScatterOptions{...}) | pyneat.nodes.sima_pillar_scatter(options) |
| Об’єднайте ознаки камер у сітку BEV. | nodes::SimaBEVPoolPlan(...) і nodes::SimaBEVPool(...) | pyneat.nodes.sima_bev_pool_plan(options) і pyneat.nodes.sima_bev_pool(options) |
Використовуйте групи, коли вам потрібен перевірений фрагмент. Використовуйте окремі вузли, коли вам потрібна чітка топологія. Використовуйте допоміжні функції Graph, такі як graphs::Branch(...) і graphs::Combine(...), для розгалуження та зведення замість того, щоб приховувати топологію за допомогою дубльованих виходів.
nodes::H264Decode(...) і pyneat.nodes.h264_decode(...) є застарілими обгортками для забезпечення сумісності. Для нових графів декодування H.264 слід використовувати SimaDecode з SimaDecodeType::H264.
Закодовані шляхи декодування.
Обирайте API, виходячи з того, які дані отримує ваш граф:
| Вхідні дані, доступні для графа. | API | Вивід |
|---|---|---|
| Завершено розбір і декодування одиниць доступу. | SimaDecode(...) | Необроблений декодований відеопотік. |
| Потік RTSP, який має залишатися в кодованому вигляді. | RtspEncodedInput(...) | Розпізнано закодовані блоки доступу. |
| Потік RTSP, який потрібно декодувати. | RtspDecodedInput(...) | Необроблений декодований відеопотік. |
SimaDecode не встановлює з’єднання з RTSP, не розпаковує пакети RTP і не аналізує елементарний потік. Використовуйте його, коли попередній етап або застосунок вже надають повністю кодовані зразки.
| Закодовані вхідні дані. | Власна опція декодера. | Типовий шлях передавання даних від клієнта до сервера. |
|---|---|---|
| Одиниці доступу H.264/AVC | SimaDecodeType::H264 або SimaDecodeType::AVC | Розпакування та аналіз потоку RTSP H.264 або іншого проаналізованого джерела H.264. |
| Одиниці доступу H.265/HEVC | SimaDecodeType::H265 або SimaDecodeType::HEVC | Розпакування та аналіз потоку RTSP H.265 або іншого проаналізованого джерела H.265. |
| Окремі кадри у форматі JPEG. | SimaDecodeType::JPEG | JpegParse() або зразок кодованого JPEG-файлу, наданий застосунком. |
| Потік кадрів MJPEG | SimaDecodeType::MJPEG | HTTP-демультиплексування багаточастинних даних + JpegParse(), або депакетування JPEG за допомогою RTSP + JpegParse(). |
Псевдоніми обирають ідентичні шляхи декодера: H264 дорівнює AVC, а H265 дорівнює HEVC. Ті самі псевдоніми доступні через RtspCodec та FormatTag у C++ та Python.
Для вхідного потоку H.264 необхідно використовувати 8-бітну колірну субдискретизацію 4:2:0 і прогресивні кадри. Бітові потоки High 10, High 4:2:2 та черезрядкові бітові потоки не підтримуються.
Для вхідного потоку H.265 необхідно використовувати профіль HEVC Main з 8-бітною колірною субдискретизацією 4:2:0. Безпосередньо вхідний елементарний потік має бути розібраний, у форматі байтового потоку Annex-B, а також має бути вирівняний за одиницями доступу. Його набори параметрів (VPS, SPS та PPS) повинні бути передані декодеру до кадрів, які від них залежать. RtspEncodedInput обробляє демультиплексування RTP та розбір H.265 для шляху RTSP. Бітові потоки Main 10, 10-біт, 4:2:2 та монохромні 4:0:0 не підтримуються. Для контенту в градаціях сірого кодуйте 8-бітний потік 4:2:0, де площина Y містить зображення в градаціях сірого, а зразки U та V використовують нейтральне значення 128.
Перевірте потік перед його використанням:
ffprobe -v error -rtsp_transport tcp -select_streams v:0 \
-show_entries stream=codec_name,profile,pix_fmt \
-of default=noprint_wrappers=1 rtsp://camera.example/live
Декодер H.265 вимагає codec_name=hevc, profile=Main та pix_fmt=yuv420p.
SimaDecodeOptions::raw_output керує безпосереднім виходом декодера:
| Значення | Вихід декодера |
|---|---|
true (типове) | Вихідні буфери декодера: типово власний NV12 або перетворений I420. |
false | Перетворений вихід video/x-raw(memory:SystemMemory) для CPU-споживачів. |
SimaDecode запитує вихід декодера без копіювання для NV12. Вибір I420 використовує режим копіювання, що перетворює апаратні NV12 пікселі безпосередньо в кінцеві площини I420. Обидва режими використовують вихідний пул DMA-BUF декодера.
Значення raw_output=false додає програмний адаптер для виходу в системну пам’ять.
RunOptions::output_memory окремо визначає, чи кінцевий вихід спільно використовує базовий буфер (ZeroCopy), чи копіює його у сховище, що належить фреймворку (Owned).
Плагін декодера володіє апаратним сеансом. Core не резервує місткість через демон допуску й не утримує оренду декодера. Помилки запуску повідомляються звичайним шляхом помилок конвеєра.
Використовуйте dec_width, dec_height та dec_fps лише для перевизначення розмірів або частоти кадрів попереднього етапу; -1 залишає їх визначення попередньому етапу.
Ці параметри не резервують місткість декодера. Core передає явне значення num_buffers декодеру, який застосовує вимоги свого апаратного пулу; -1 зберігає політику декодера.
Залиште decoder_tuning порожнім, щоб зберегти типові параметри плагіна.
Явні значення, зокрема default і auto, передаються плагіну разом із
memory_opt та перевизначеннями кількості буферів, щоб декодер міг застосувати пріоритет налаштувань.
throughput-low-latency вимикає перевпорядкування декодованих зображень і використовує глибину команд один до 720p або два для більших кадрів.
Використовуйте цей режим лише для потоків без B-кадрів; для загальних потоків залишайте налаштування незаданим.
nodes::JpegDecode() і pyneat.nodes.jpeg_decode() — це окремі вузли декодування JPEG для CPU/GStreamer. Використовуйте їх для простих конвеєрів обробки зображень на CPU. Не використовуйте їх для нативного декодування відео MJPEG перед попередньою обробкою моделі.
Для RTSP віддавайте перевагу гр упам, що враховують кодек, коли форма джерела є стандартною. H.264 залишається значенням за замовчуванням. Явно виберіть H.265 або HEVC.
Декодування камер MJPEG
USB-камери надсилають MJPEG із субдискретизацією кольору 4:2:0 або 4:2:2; більшість використовує 4:2:2. Під час виведення без копіювання декодер записує лише власну субдискретизацію потоку, тому граф має знати її ще до побудови. Для USB-камери її повідомляє usb_camera_info(...) у полі decoded_format; використайте це значення як out_format декодера. Для іншого джерела MJPEG задайте out_format як NV12 для 4:2:0 або NV16 для 4:2:2. Модель читає NV16 напряму: задайте такий самий вхідний формат її попередньої обробки, і кадри 4:2:2 надходитимуть до моделі без етапу перетворення.
#include "neat.h"
// camera: your UsbCameraInputOptions; also pass info to UsbCameraInput(camera, info).
const simaai::neat::UsbCameraInfo info = simaai::neat::usb_camera_info(camera);
simaai::neat::SimaDecodeOptions decode;
decode.type = simaai::neat::SimaDecodeType::MJPEG;
decode.out_format = info.decoded_format; // NV12 or NV16
decode.dec_width = 1920;
decode.dec_height = 1080;
decode.dec_fps = 30;
decode.raw_output = true; // zero-copy: the decoder writes the camera's own sampling
graph.add(simaai::neat::nodes::JpegParse());
graph.add(simaai::neat::nodes::SimaDecode(decode));
simaai::neat::Model::Options model_options;
model_options.preprocess.color_convert.input_format =
decode.out_format == simaai::neat::FormatTag::NV16 ? simaai::neat::PreprocessColorFormat::NV16
: simaai::neat::PreprocessColorFormat::NV12;
HttpMjpegDecodedInput(...) і RtspDecodedInput(...) і надалі запитують у декодера NV12, тому підтримують лише потоки MJPEG 4:2:0.
Залиште вхідний потік RTSP у кодованому вигляді.
Використовуйте RtspEncodedInput(...), щоб розпакувати та проаналізувати потік даних без його декодування:
simaai::neat::nodes::groups::RtspEncodedInputOptions encoded;
encoded.url = "rtsp://camera.example/live";
encoded.codec = simaai::neat::nodes::groups::RtspCodec::HEVC;
encoded.source_fps = 30;
auto encoded_graph =
simaai::neat::nodes::groups::RtspEncodedInput(encoded);
RtspEncodedInput(...) зупиняється після депакетізації джерела та виконання операцій розбору/формування кадрів.
Використовуйте його, коли вашій програмі потрібні кодовані зразки, наприклад, для розділення одного розібраного потоку на графі для декодування та графі для виведення відео. Для H.265 значення source_fps записується в розібрані кодовані параметри, коли потік не містить інформацію про дійсний показник кадрів.
Декодуйте вхідний потік RTSP.
Використовуйте RtspDecodedInput(...), щоб додати SimaDecode(...) і повернути необроблене декодоване відео:
simaai::neat::nodes::groups::RtspDecodedInputOptions decoded;
decoded.url = "rtsp://camera.example/live";
decoded.codec = simaai::neat::nodes::groups::RtspCodec::H265;
decoded.source_fps = 30;
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::groups::RtspDecodedInput(decoded));
graph.add(simaai::neat::nodes::Output("frames"));
Для H.265, RtspDecodedInput(...) використовує source_fps як для оброблених метаданих, так і для декодера dec-fps. Встановіть це значення, коли метадані RTSP не містять дійсного значення частоти кадрів; без будь-якого з цих значень запуск декодера не відбудеться. Neat не перевіряє URL-адресу RTSP на наявність цього значення. Він визначає частоту кадрів джерела і не запитує перетворення частоти. Щоб змінити частоту кадрів вихідного потоку, увімкніть use_videorate і встановіть video_rate_fps. Перетворення частоти відкидає зайві кадри та зберігає часові позначки джерела. Фабрики nodes::VideoRate() і pyneat.nodes.video_rate() не приймають аргументів і завжди використовують drop-only=true, зокрема коли їх додають групи джерел. Вони не дублюють кадри для збільшення вихідної частоти.
Для HTTP multipart MJPEG використовуйте HttpMjpegDecodedInput(...). HTTP multipart MJPEG не є RTP, тому використовується HttpSource -> MultipartJpegDemux -> JpegParse -> SimaDecode(MJPEG) замість демультиплексування RTSP.
Групи, що належать джерелу, і групи, що належать приймачу.
Використовуйте групи, що належать джерелу, коли потрібно, щоб граф самостійно обробляв медіадані. Створюйте ці графи без вхідних даних від програми, а потім отримуйте вихідні дані або дозвольте кінцевим вузлам обробляти передавання. Використовуйте кінцеві групи, коли граф повинен передавати відео без того, щоб ваша програма обробляла кожен кадр; кінцевий граф все ще може отримувати дані від програми, якщо він також має Input межу.
| Потрібно | Група | Основні поля, які потрібно налаштувати насамперед. | Типові поля для подальшої перевірки |
|---|---|---|---|
| Статичне зображення, використане як елемент оформлення. | nodes::groups::ImageInputGroup(...) / pyneat.groups.image_input(...) | path | fps, imagefreeze_num_buffers, output_caps |
| Відеофайл | nodes::groups::VideoInputGroup(...) / pyneat.groups.video_input(...) | path | out_format, output_caps, sync_mode |
| Камера, що працює в режимі реального часу, з використанням MIPI/libcamera. | nodes::CameraInput(...) / pyneat.nodes.camera_input(...) | width, height, framerate_num, format; необов’язково camera_name | allow_cpu_fallback, поля черги, buffer_name |
| Транслюйте відеопотік у реальному часі у форматі RTSP з кодуванням H.264, H.265 або MJPEG. | nodes::groups::RtspEncodedInput(...) / pyneat.groups.rtsp_encoded_input(...) | url, codec, якщо не H.264. | source_fps, latency_ms, tcp, drop_on_latency, buffer_mode, тип корисного навантаження RTP. |
| Відображайте в реальному часі декодований RTSP-потік у форматах H.264, H.265 або MJPEG. | nodes::groups::RtspDecodedInput(...) / pyneat.groups.rtsp_decoded_input(...) | url, codec, якщо не H.264. | source_fps, latency_ms, tcp, drop_on_latency, buffer_mode, декодована ширина/висота, out_format, output_caps |
| HTTP-потік MJPEG, що використовує формат multipart. | nodes::groups::HttpMjpegDecodedInput(...) / pyneat.groups.http_mjpeg_decoded_input(...) | url | out_format, decoder_next_element, декодована ширина/висота/кількість кадрів за секунду, output_caps. |
| Вихід сирого або кодованого відео H.264/H.265/MJPEG | nodes::groups::VideoSender(...) / pyneat.groups.video_sender(...) | VideoSenderOptions::FromRaw(encode_options) або Passthrough(codec) | host, channel, video_port_base, параметри енкодера/RTP |
| Вивід RTP/H.264 через UDP. | nodes::groups::UdpH264OutputGroup(...) / pyneat.groups.udp_h264_output_group(...) | цільовий хост/порт, коли значення за замовчуванням не підходять. | h264_caps, payload_type, config_interval |
Граф, що належить джерелу, має іншу структуру в середовищі виконання, ніж граф, який передається застосунком:
source group -> model -> output
Для камер MIPI використовуйте CameraInput безпосередньо, замість того, щоб обгортати камеру в застосунок, який передає Input. Спочатку перевірте камеру за допомогою libcamera/GStreamer, а потім створіть граф у вигляді CameraInput -> model -> Output. Див. Використовуйте камеру MIPI. для отримання повної інформації.
Запустіть його за допомогою graph.build() або graph.run() без аргументу вхідних даних. Не додавайте Input("image") перед групою джерел, якщо ваш застосунок дійсно має другий загальнодоступний вхід для передачі даних.
simaai::neat::nodes::groups::RtspDecodedInputOptions source_options;
source_options.url = "rtsp://camera.example/live";
simaai::neat::Graph graph("rtsp_detector");
graph.add(simaai::neat::nodes::groups::RtspDecodedInput(source_options));
graph.add(model);
graph.add(simaai::neat::nodes::Output(
"detections",
simaai::neat::OutputOptions::Latest()));
auto run = graph.build();
Почніть із використання групи, коли стандартна схема підключення мережевих пристроїв. Переходьте до окремих вузлів лише тоді, коли вам потрібна спеціальна топологія або формат джерела, який не передбачений групою.
Типові вузли SiMa
Деякі вузли SiMa досить поширені, тому для них створено с пеціалізовані сторінки з довідковою інформацією або інструкції:
Preproc: об’єднана попередня обробка зображень CVU для зміни розміру, перетворення кольорів, нормалізації, квантування, теселяції та створення списків ROI для середовища виконання.CameraInput: кадри MIPI/libcamera, що належать джерелу, з адаптивною передачею даних у пам’ять SiMaAI для подальших етапів обробки CVU/MLA.SimaBoxDecode: постобробка для виявлення об’єктів, яка декодує вихідні дані моделі у вигляді обмежувальних рамок і використовує метадані попередньої обробки для відображення координат.
Кінцеві вузли: Input та Output.
Input і Output також є вузлами. Вони особливі, оскільки описують, звідки дані надходять або куди вони передаються у фрагменті графа.
Важлива концептуальна модель:
Input("name")таOutput("name")— це назви дверей.
Зовні, у фінальній версії застосунку, ці компоненти перетворюються на загальнодоступні API для середовища виконання:
Input("image")перетворюється наrun.push("image", ...).Output("classes")перетворюється наrun.pull("classes", ...).
У межах більшого графа ці самі «двері» є лише точками з’єднання. Neat видаляє внутрішні граничні вузли під час створення шляху для середовища виконання та безпосередньо пов’язує реальні компоненти.
simaai::neat::Graph route("route");
route.add(simaai::neat::nodes::Input("image"));
route.add(model);
route.add(simaai::neat::nodes::Output("classes"));
Використовується як повноцінний застосунок:
auto run = route.build();
run.push("image", simaai::neat::TensorList{image_tensor});
auto classes = run.pull("classes");
Використовується всередині більшої програми:
simaai::neat::Graph app("app");
app.connect(camera, route);
app.connect(route, telemetry);
У концептуальному плані внутрішні граничні вузли розташовані нижче, щоб забезпечити пряме підключення:
camera -> model -> telemetry
Ці імена все ще використовуються для діагностики, перевірки кінцевих точок, збору показників і візуалізації. Вони просто не створюють додаткових черг, копій або фіктивних об’єктів середовища виконання посеред роботи програми.
Мітки не є кінцевими точками.
Тримайте ці імена окремо:
| Ім’я | Що це таке. | Чи створює воно ефект push(...) / pull(...)? |
|---|---|---|
Graph("detector") | Мітка графа для діагностики, експорту та журналів. | Ні. |
nodes::Input("image") / pyneat.nodes.input("image") | Межа зони, в межах якої збираються дані від громадськості, коли вона залишається за межами остаточного графа. | Так: run.push("image", ...). |
nodes::Output("detections") / pyneat.nodes.output("detections") | Публічна межа вихідних даних, коли вона залишається на зовнішній стороні остаточного графа. | Так: run.pull("detections", ...). |
| Назви вузлів або елементів. | Назви бекендів, що використовуються в діагностиці та оптимізації конвеєра. | Ні, якщо тільки це не станеться через Input або Output. |
Якщо під час виконання програми виникає помилка, що вказує на невідому назву кінцевої точки, перевірте run.input_names() і run.output_names(), замість того щоб гадати. Назви – це не дорого; ось неправильні назви – це дорого.