Работа с изображениями: Canvas API, OffscreenCanvas

ONNX Runtime Web (ORT Web) предоставляет возможность запускать модели машинного обучения прямо в браузере с использованием JavaScript. Одним из частых сценариев является обработка изображений, будь то классификация, детекция объектов или сегментация. Для эффективной работы с изображениями ключевую роль играет интеграция с Canvas API и OffscreenCanvas, обеспечивающая гибкую подготовку данных для модели.


Подготовка изображений с Canvas API

Canvas API позволяет рисовать изображения на элементе <canvas> и получать доступ к пиксельным данным через объект ImageData. Основные этапы работы:

  1. Создание элемента Canvas и контекста:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
  1. Загрузка изображения и отрисовка на Canvas:
const image = new Image();
image.src = 'image.jpg';
image.onl oad = () => {
    canvas.width = image.width;
    canvas.height = image.height;
    ctx.drawImage(image, 0, 0);
};
  1. Получение пиксельных данных:
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const { data, width, height } = imageData;
  • data — это массив байтов в формате RGBA, где каждый пиксель представлен четырьмя последовательными числами.
  • width и height определяют размер изображения, необходимый для нормализации и изменения формы данных перед передачей в модель.

Преобразование данных в формат ONNX Runtime

Модель ONNX обычно ожидает входной тензор в формате [N, C, H, W] (batch, channels, height, width) с плавающей точкой. Для преобразования используется Float32Array:

function preprocessImage(imageData) {
    const { data, width, height } = imageData;
    const floatData = new Float32Array(width * height * 3); // RGB
    for (let i = 0; i < width * height; i++) {
        floatData[i * 3] = data[i * 4] / 255.0;       // R
        floatData[i * 3 + 1] = data[i * 4 + 1] / 255.0; // G
        floatData[i * 3 + 2] = data[i * 4 + 2] / 255.0; // B
    }
    return floatData;
}
  • Важно нормализовать значения в диапазон [0, 1] или согласно параметрам модели.
  • Четко учитывать порядок каналов (RGB/BGR), так как многие модели ONNX были обучены на BGR.

Использование OffscreenCanvas для асинхронной обработки

OffscreenCanvas позволяет создавать canvas вне DOM и выполнять обработку изображений в Web Worker, что ускоряет работу и разгружает основной поток:

const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext('2d');
ctx.drawImage(imageBitmap, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
  • Асинхронная обработка: через postMessage можно передавать ImageBitmap или ImageData между Worker и основным потоком.
  • Производительность: OffscreenCanvas работает с GPU и позволяет применять фильтры или масштабирование без блокировки интерфейса.

Пример передачи изображения в Web Worker:

// main.js
const worker = new Worker('worker.js');
const imageBitmap = await createImageBitmap(image);
worker.postMessage({ imageBitmap }, [imageBitmap]);

// worker.js
self.onmess age = async (event) => {
    const { imageBitmap } = event.data;
    const offscreen = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
    const ctx = offscreen.getContext('2d');
    ctx.drawImage(imageBitmap, 0, 0);
    const imageData = ctx.getImageData(0, 0, offscreen.width, offscreen.height);
    const tensorData = preprocessImage(imageData);
    // Передача tensorData в ONNX Runtime Web
};

Интеграция с ONNX Runtime Web

После подготовки тензора его можно передавать в модель ONNX Runtime Web через ort.InferenceSession:

import * as ort from 'onnxruntime-web';

async function runModel(tensorData, width, height) {
    const session = await ort.InferenceSession.create('model.onnx');
    const inputTensor = new ort.Tensor('float32', tensorData, [1, 3, height, width]);
    const feeds = { input: inputTensor };
    const results = await session.run(feeds);
    return results.output.data;
}
  • Названия входных и выходных тензоров должны соответствовать определению модели.
  • Размерность [1, 3, H, W] соответствует одной RGB-картинке (batch size = 1).

Масштабирование и предобработка

Для моделей с фиксированным размером входа необходимо масштабировать изображение:

ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
const imageData = ctx.getImageData(0, 0, targetWidth, targetHeight);
const tensorData = preprocessImage(imageData);
  • targetWidth и targetHeight выбираются согласно архитектуре модели.
  • Дополнительно можно применять нормализацию по среднему и стандартному отклонению, если это требуется моделью:
const mean = [0.485, 0.456, 0.406];
const std = [0.229, 0.224, 0.225];
for (let i = 0; i < floatData.length / 3; i++) {
    floatData[i * 3] = (floatData[i * 3] - mean[0]) / std[0];
    floatData[i * 3 + 1] = (floatData[i * 3 + 1] - mean[1]) / std[1];
    floatData[i * 3 + 2] = (floatData[i * 3 + 2] - mean[2]) / std[2];
}

Применение OffscreenCanvas для фильтрации и постобработки

Кроме масштабирования, OffscreenCanvas позволяет применять различные графические эффекты перед подачей на модель:

  • Изменение контраста и яркости через ctx.filter.
  • Повороты и отражения через ctx.rotate и ctx.scale.
  • Извлечение регионов интереса (ROI) через ctx.drawImage с указанием координат.

Такой подход повышает гибкость подготовки данных и снижает нагрузку на основной поток браузера.


Ключевые моменты

  • Canvas API обеспечивает прямой доступ к пикселям изображения.
  • OffscreenCanvas позволяет обрабатывать изображения в Web Worker, разгружая UI.
  • Преобразование данных в Float32Array и правильная нормализация критичны для корректной работы модели.
  • Внимание к размерности и порядку каналов (RGB/BGR) предотвращает ошибки в инференсе.
  • Масштабирование, фильтры и ROI могут быть выполнены прямо на Canvas перед созданием тензора.

Использование этих инструментов совместно с ONNX Runtime Web обеспечивает эффективную и гибкую обработку изображений для задач машинного обучения в браузере.