Перейти до основного вмісту

Підготуйте зображення перед виконанням обчислень

ПолеЗначення
КатегоріяМоделі та інференс
СкладністьСередній
Орієнтовний час читання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>.

tutorials/006_preprocess_images/preprocess_images.cpp
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 на основі шляху до архіву та параметрів прив’язує ваш контракт попередньої обробки до завантаженої моделі. З цього моменту модель містить визначення попередньої обробки, тому будь-який етап або запуск, отриманий на її основі, повторно використовує той самий набір параметрів.

tutorials/006_preprocess_images/preprocess_images.cpp
simaai::neat::Model model(model_path, opt);

Перевірка попередньої обробки окремо​

У цьому розділі розглядається фрагмент попередньої обробки перед запуском повної моделі, щоб ви могли підтвердити існування шляху, перш ніж налагоджувати будь-які наступні етапи.

stages::Preproc(frames, model) виконує етап попередньої обробки самостійно та повертає попередньо оброблені дані. Tensor безпосередньо — ми читаємо pre.shape.size() (ранг) і pre.dtype щоб підтвердити, що дія контракту розпочалася.

tutorials/006_preprocess_images/preprocess_images.cpp
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 (додаткова тека не потрібна), див. розділ Як запускати навчальні матеріали на головній сторінці.

Повний початковий код​

Показати повні програми
tutorials/006_preprocess_images/preprocess_images.cpp
// 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;
}
}

Джерело​