ONNX Runtime Web (ORT Web) предоставляет возможность запускать модели машинного обучения прямо в браузере с использованием JavaScript. Основная цель — обеспечить выполнение предобученных моделей ONNX с высокой производительностью на стороне клиента. Для работы с сегментацией это особенно важно, поскольку результаты модели обычно представляют собой многоканальные тензоры, которые необходимо правильно интерпретировать и визуализировать.
Подключение библиотеки осуществляется через пакет
onnxruntime-web, который можно установить через npm:
import * as ort from 'onnxruntime-web';
После этого создаётся сессия для конкретной модели ONNX:
const session = await ort.InferenceSession.create('model.onnx');
Сегментационные модели в ONNX обычно возвращают тензоры с
размерностью [1, C, H, W], где:
1 — размер батча;C — количество классов или каналов;H и W — высота и ширина изображения.Каждый канал содержит вероятности принадлежности пикселя к
соответствующему классу. В некоторых моделях выход может быть
[1, H, W], где значения пикселей уже содержат индексы
классов.
Для дальнейшей работы необходимо преобразовать многоканальный тензор
в карту масок. Это делается с помощью операции argmax по
канальному измерению:
function getSegmentationMask(outputTensor) {
const [batch, channels, height, width] = outputTensor.dims;
const data = outputTensor.data;
const mask = new Uint8Array(height * width);
for (let h = 0; h < height; h++) {
for (let w = 0; w < width; w++) {
let maxVal = -Infinity;
let classIdx = 0;
for (let c = 0; c < channels; c++) {
const value = data[c * height * width + h * width + w];
if (value > maxVal) {
maxVal = value;
classIdx = c;
}
}
mask[h * width + w] = classIdx;
}
}
return { mask, width, height };
}
Для визуализации объектов часто требуется преобразовать растровую маску в полигоны. Наиболее распространённый метод — алгоритм Marching Squares, который позволяет выделить контуры классов.
Пример генерации контуров:
import { findContours } from 'marchingsquares';
function maskToPolygons(maskData) {
const { mask, width, height } = maskData;
const polygonsByClass = {};
// Для каждого класса
const classes = [...new Set(mask)];
classes.forEach(cls => {
const binaryMask = [];
for (let i = 0; i < mask.length; i++) {
binaryMask[i] = mask[i] === cls ? 1 : 0;
}
const contours = findContours(binaryMask, width);
polygonsByClass[cls] = contours.map(contour => contour.map(([x, y]) => ({ x, y })));
});
return polygonsByClass;
}
После генерации полигонов их можно отобразить на HTML Canvas:
function drawPolygons(ctx, polygonsByClass, colors) {
Object.entries(polygonsByClass).forEach(([cls, polygons]) => {
ctx.fillStyle = colors[cls] || 'rgba(255,0,0,0.5)';
polygons.forEach(polygon => {
ctx.beginPath();
polygon.forEach((point, index) => {
if (index === 0) ctx.moveTo(point.x, point.y);
else ctx.lineTo(point.x, point.y);
});
ctx.closePath();
ctx.fill();
});
});
}
Uint8Array или Float32Array минимизирует
накладные расходы.run и
create возвращают промисы, что обеспечивает неблокирующее
выполнение в интерфейсе пользователя.Эта последовательность операций — от получения тензора до визуализации полигонов — обеспечивает полный цикл постобработки сегментационных моделей в браузере с использованием ONNX Runtime Web. Она сочетает точность распознавания с гибкостью в представлении и оптимизацией вычислений.