執行應用程式

以下程式碼所產生的檢測結果,會繪製在原始影像上。
這與小型「應用程式」中的 YOLOv8 推論相同:與直接呼叫 Model.run(...)(如 執行模型 中所示)不同,您將模型組合成一個 Graph,即一個帶有輸入、模型和輸出的命名圖流程,然後建立並推送/拉取。以下程式碼以 Python 和 C++ 兩種語言呈現,請在每個程式碼區塊中選擇對應的語言標籤。
對於這個第一個應用程式,其結構刻意設計得簡單:
- 一個命名的「輸入」(
nodes.input("image"))標示資料進入應用程式的位置。 - 一個「模型」(
graph.add(model))將模型作為圖中的一個步驟執行。 - 一個命名的「輸出」(
nodes.output("detections"))標示應用程式讀取結果的位置。
相同的 API 後續可以擴展到更複雜的應用程式;這裡的目標是核心的組合模式。
在任何程式碼區塊中使用「Python / C++」選項卡——您的選擇將遵循網站範圍內的語言選擇器,因此每個程式碼片段和整個程式都會一起切換。
設定專案
-
建立一個資料夾,用於存放模型和輸入圖像:
sdk or devkit$mkdir -p assets -
下載模型:
sdk or devkit$sima-cli modelzoo -v 2.0.0 get yolo_v8ssima-cli 模型下載如果
sima-cli將模型寫入的目錄與assets目錄不同,請將該檔案複製到assets/yolo_v8s_mpk.tar.gz。 -
下載範例圖片,從檔案中下載,並將其儲存為
assets/tutorial_sample_image.png。
逐步講解程式碼
該程式由八個簡短的程式碼片段組成。切換每個程式碼片段的語言標籤。
1. 讀取圖片
#include <opencv2/opencv.hpp>
cv::Mat bgr = cv::imread("assets/tutorial_sample_image.png");
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
OpenCV 讀取 BGR 格式;YOLOv8 則預期為 RGB 格式。這個步驟並非 Neat —— 您的應用程式會從檔案、相機或解碼器取得像素;Neat 將在下一個步驟中生效。
2. 描述模型選項
#include <neat.h>
namespace neat = simaai::neat;
neat::Model::Options opt;
opt.preprocess.kind = neat::InputKind::Image;
opt.preprocess.preset = neat::NormalizePreset::COCO_YOLO;
opt.decode_type = neat::BoxDecodeType::YoloV8;
opt.score_threshold = 0.25f;
opt.nms_iou_threshold = 0.45f;
opt.top_k = 100;
ModelOptions 在單一物件中宣告模型路徑:說明 Neat 如何預處理輸入像素,以及如何解碼檢測器輸出。
| 欄位 | 設定內容 |
|---|---|
preprocess.kind = Image | 輸入為原始像素,而非預先調整過的張量。 |
preprocess.preset = COCO_YOLO | 調整大小 + 填滿邊框以符合模型輸入,RGB 格式,縮放至 1/255,不進行平均值減法。 |
decode_type = YoloV8 | 檢測頭解碼器系列。 |
score_threshold / nms_iou_threshold / top_k | 置信度下限、NMS 重疊度,以及保留的最大框數。 |
3. 載入模型
neat::Model model("assets/yolo_v8s_mpk.tar.gz", opt);
Model 會讀取 .tar.gz,並根據您提供的 ModelOptions 驗證其「MPK 合約」,然後建立模型片段。目前還沒有執行任何操作。
4. 將您的影像封裝成一個 Tensor
neat::Tensor input = neat::Tensor::from_cv_mat(
rgb,
neat::ImageSpec::PixelFormat::RGB);
Tensor 是 Neat 的類型化資料容器——包含形狀、資料類型、佈局,以及框架需要用來解讀位元組的像素格式。必須傳遞 PixelFormat,這樣 Neat 才能知道佈局,而不僅僅是位元組。
5. 組合圖
neat::Graph graph("hello_neat_app");
graph.add(neat::nodes::Input("image"));
graph.add(model);
graph.add(neat::nodes::Output("detections"));
一個 Graph 是應用程式流程。每個 add(...) 都會附加下一個步驟,因此這會建立線性流程 image → model → detections。來自步驟 3 的模型片段會變成其中的一個步驟。
6. 建立並執行圖
neat::Run run = graph.build();
run.push("image", neat::TensorList{input});
run.close_input();
neat::TensorList outputs = run.pull_tensors("detections", /*timeout_ms=*/2000);
run.close();
build() 將公開的圖轉換為單一可執行的執行階段圖,同時保留您的節點名稱。然後,您將輸入 push 到具名輸入中,當不再有輸入時,呼叫 close_input(),並從具名輸出中以逾時方式 pull 結果。pull_tensors 會傳回一個 TensorList,其形狀與 Model.run 會產生的形狀相同——這裡是封裝後的 YOLOv8 BBOX 輸出。