Підготуйте зображення перед виконанням обчислень
| Поле | Значення |
|---|---|
| Категорія | Моделі та інференс |
| Складність | Середній |
| Орієнтовний час читання | 15-20 minutes |
| Мітки | preprocessing, normalization, image |
Скомпільована модель очікує, що її вхідні дані будуть мати чітко визначену форму та діапазон значень: фіксований порядок кольорів, фіксовані розміри та алгоритм нормалізації, з яким її було навчено. Попередня обробка — це етап, на якому необроблений декодований образ перетворюється на саме той тензор, який потрібен моделі. Якщо зробити це неправильно, модель все одно працюватиме, але видаватиме безглузді результати, тому попередня обробка — це перше, що потрібно перевірити, коли розгорнута модель «виглядає несправною».
У цьому розділі налаштовуються параметри попередньої обробки, які ви найчастіше використовуєте: формат кольору, розміри вхідних/вихідних даних, поведінка під час зміни розміру та нормалізація для кожного каналу mean/stddev. Потім аналізується граф попередньої обробки моделі перед тим, як один детермінований тензор буде передано через всю модель. В результаті ви визначите повний контракт попередньої обробки, приєднаєте його до моделі та підтвердите, що налаштований маршрут існує.
Покроковий огляд
Налаштуйте контракт попередньої обробки
Ці параметри визначають умови, які застосовуються на етапі попередньої обробки. format (або color_convert.input_format) фіксує порядок кольорів на вході; поля input_max_* визначають діапазон динамічних вхідних даних, які прийматиме середовище виконання; розміри зміни масштабу/вих ідних даних визначають розмір тензора, що генерується для виведення; а normalize разом із константами mean / stddev для кожного каналу застосовують масштабування значень. Константи нормалізації повинні відповідати рецепту, який використовувався під час навчання моделі — невідповідність статистичних даних є найпоширенішою причиною низької впевненості у вихідних даних.
Поля розташовані в Model::Options::preprocess — color_convert.input_format приймає перелік PreprocessColorFormat, normalize.enable є AutoFlag, а normalize.mean / normalize.stddev є std::array<float, 3>.
simaai::neat::Model::Options opt;
opt.preprocess.color_convert.input_format = simaai::neat::PreprocessColorFormat::BGR;
opt.preprocess.input_max_width = size;
opt.preprocess.input_max_height = size;
opt.preprocess.input_max_depth = 3;
opt.preprocess.resize.width = size;
opt.preprocess.resize.height = size;
opt.preprocess.resize.width = size;
opt.preprocess.resize.height = size;
opt.preprocess.normalize.enable = simaai::neat::AutoFlag::On;
opt.preprocess.normalize.mean = std::array<float, 3>{0.5f, 0.5f, 0.5f};
opt.preprocess.normalize.stddev = std::array<float, 3>{0.5f, 0.5f, 0.5f};
Створення моделі
Створення Model на основі шляху до архіву та параметрів прив’язує ваш контракт попередньої обробки до завантаженої моделі. З цього моменту модель містить визначення попередньої обробки, тому будь-який етап або запуск, отриманий на її основі, повторно використовує той самий набір параметрів.
simaai::neat::Model model(model_path, opt);
Перевірка попередньої обробки окремо
У цьому розділі розглядається фрагмент попередньої обробки перед запуском повної моделі, щоб ви могли підтвердити існування шляху, перш ніж налагоджувати будь-які наступні етапи.
stages::Preproc(frames, model) виконує етап попередньої обробки самостійно та повертає попередньо оброблені дані. Tensor безпосередньо — ми читаємо pre.shape.size() (ранг) і pre.dtype щоб підтвердити, що дія контракту розпочалася.
simaai::neat::TensorList pre_outputs =
simaai::neat::stages::Preproc(std::vector<cv::Mat>{bgr}, model);
if (pre_outputs.empty())
throw std::runtime_error("preprocess produced no outputs");
simaai::neat::Tensor pre = pre_outputs.front();
if (pre.shape.empty())
throw std::runtime_error("preprocess output shape is empty");
Запуск
Запустіть команди Python і C++ (попередньо скомпільовані) з Neat встановити в кореневу директорію **(директорію, яка містить) share/ і lib/); виконайте команди збірки з вихідного коду з кореневої директорії репозиторію.
C++ (prebuilt):
./lib/sima-neat/tutorials/tutorial_006_preprocess_images \
--model /tmp/resnet_50.tar.gz --size 224
C++ (build from source):
./build.sh --target tutorial_006_preprocess_images
./build/tutorials-standalone/tutorial_006_preprocess_images \
--model /tmp/resnet_50.tar.gz --size 224
Очікуваний результат (під час компіляції C++ виводиться ранг і перелік типів даних попередньо обробленого тензора):
preproc_rank=3
preproc_dtype=1
[OK] 006_preprocess_images
(Під час компіляції Python виводиться preproc_graph=ready, опис графа та output_count=...). Щоб інтегрувати вихідний код C++ з цього розділу у власний проєкт за допомогою спеціального файлу CMakeLists.txt (додаткова тека не потрібна), див. розділ Як запускати навчальні матеріали на головній сторінці.
Повний початковий код
Показати повні програми
// Run preprocessing standalone via stages::Preproc and inspect the resulting tensor.
//
// Usage:
// tutorial_006_preprocess_images --model /path/to/resnet_50.tar.gz [--size 224]
#include "neat.h"
#include "pipeline/StageRun.h"
#include <opencv2/core.hpp>
#include <array>
#include <iostream>
#include <stdexcept>
#include <string>
namespace {
bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}
int parse_int_arg(int argc, char** argv, const std::string& key, int def) {
std::string value;
if (!get_arg(argc, argv, key, value))
return def;
return std::stoi(value);
}
} // namespace
int main(int argc, char** argv) {
try {
std::string model_path;
if (!get_arg(argc, argv, "--model", model_path)) {
std::cerr << "Usage: tutorial_006_preprocess_images --model <path> [--size <n>]\n";
return 1;
}
const int size = parse_int_arg(argc, argv, "--size", 224);
simaai::neat::Model::Options opt;
opt.preprocess.color_convert.input_format = simaai::neat::PreprocessColorFormat::BGR;
opt.preprocess.input_max_width = size;
opt.preprocess.input_max_height = size;
opt.preprocess.input_max_depth = 3;
opt.preprocess.resize.width = size;
opt.preprocess.resize.height = size;
opt.preprocess.resize.width = size;
opt.preprocess.resize.height = size;
opt.preprocess.normalize.enable = simaai::neat::AutoFlag::On;
opt.preprocess.normalize.mean = std::array<float, 3>{0.5f, 0.5f, 0.5f};
opt.preprocess.normalize.stddev = std::array<float, 3>{0.5f, 0.5f, 0.5f};
simaai::neat::Model model(model_path, opt);
cv::Mat bgr(size, size, CV_8UC3, cv::Scalar(40, 80, 120));
if (!bgr.isContinuous())
bgr = bgr.clone();
// CORE LOGIC
// stages::Preproc runs just the preprocessing step from the model's Options
// and returns the preprocessed Tensor.
simaai::neat::TensorList pre_outputs =
simaai::neat::stages::Preproc(std::vector<cv::Mat>{bgr}, model);
if (pre_outputs.empty())
throw std::runtime_error("preprocess produced no outputs");
simaai::neat::Tensor pre = pre_outputs.front();
if (pre.shape.empty())
throw std::runtime_error("preprocess output shape is empty");
std::cout << "preproc_rank=" << pre.shape.size() << "\n";
std::cout << "preproc_dtype=" << static_cast<int>(pre.dtype) << "\n";
std::cout << "[OK] 006_preprocess_images\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}