Другое

Как исправить ReferenceError: document is not defined в OpenCV.js Node.js

Узнайте, как решить проблему 'ReferenceError: document is not defined' при использовании OpenCV.js в Node.js. Полное руководство с несколькими решениями, лучшими практиками и советами по устранению неполадок для WebAssembly-модулей, скомпилированных для браузера, в серверных средах.

Как исправить ошибку ‘ReferenceError: document is not defined’ при использовании OpenCV.js в Node.js?

Я пытаюсь использовать OpenCV.js в среде Node.js с помощью пакета @techstark/opencv-js. Я установил примеры из репозитория GitHub и использую Node v22.18.0.

Моя простая программа:

javascript
const { getOpenCv } = require("./opencv.js");

async function main() {
  const { cv } = await getOpenCv();
  console.log("OpenCV.js is ready!");
  console.log(cv.getBuildInformation());
  const inputImage = cv.imread("Lenna.png");
  console.log("Done");
}

main();

Когда я запускаю это с помощью npm run commonjs, я получаю следующую ошибку:

(build info, as expected)
(a very long block of obfuscated code)

abort(ReferenceError: document is not defined). Build with -s ASSERTIONS=1 for more info. (Use node --trace-uncaught ... to show where the exception was thrown)

Node.js v22.18.0

Я пробовал более раннюю версию с похожим результатом. Не мог бы кто-нибудь помочь мне определить, является ли это проблемой пакета techstark, порта OpenCV.js, проблемой совместимости с Node.js или чем-то в моей настройке?

Ошибка “ReferenceError: document is not defined” в OpenCV.js

Ошибка “ReferenceError: document is not defined” возникает потому, что OpenCV.js разработан для браузерных сред и ожидает наличие браузерных объектов, таких как document, которые Node.js не предоставляет. Это фундаментальная проблема совместимости между скомпилированными для браузера модулями WebAssembly и средой Node.js.

Содержание

Понимание основной причины

OpenCV.js компилируется в WebAssembly с использованием Emscripten, который по умолчанию ориентирован на браузерные среды. Скомпилированный код содержит ссылки на браузерные API, такие как document, window и XMLHttpRequest, которые не существуют в Node.js. При запуске OpenCV.js в Node.js эти ссылки вызывают ошибку ReferenceError, с которой вы столкнулись.

Как объясняется на Mozilla Developer Network, модули WebAssembly, скомпилированные для браузеров, могут содержать браузерные привязки, которые необходимо учитывать при запуске в серверных средах.


Решение 1: Использование пакета @techstark/opencv-js

Пакет @techstark/opencv-js специально разработан для работы в среде Node.js. Однако его нужно использовать правильно с надлежащей инициализацией:

javascript
import cvModule from "@techstark/opencv-js";

async function getOpenCv() {
  let cv;
  if (cvModule instanceof Promise) {
    cv = await cvModule;
  } else {
    await new Promise((resolve) => {
      cvModule.onRuntimeInitialized = () => resolve();
    });
    cv = cvModule;
  }
  return { cv };
}

async function main() {
  const { cv } = await getOpenCv();
  console.log("OpenCV.js готов к работе!");
  console.log(cv.getBuildInformation());
  
  // Примечание: cv.imread() все еще требует canvas или file reader в Node.js
  // Вам понадобится дополнительный код для чтения файлов
  const inputImage = cv.imread("Lenna.png"); // Это может потребовать корректировки
  console.log("Готово");
}

main();

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

  • Всегда используйте оберточную функцию getOpenCv()
  • Дождитесь инициализации времени выполнения
  • Пакет автоматически обрабатывает замену некоторых браузерных API

Согласно официальной документации npm, этот подход обеспечивает лучшую совместимость с Node.js за счет реализации полифиллов для отсутствующих браузерных API.


Решение 2: Ручная настройка среды Emscripten

Для полного контроля над средой OpenCV.js вы можете собрать его с флагами, специфичными для Node.js, с использованием Emscripten:

Шаг 1: Установка Emscripten

bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

Шаг 2: Сборка OpenCV.js с флагами Node.js

bash
emcmake python ./platforms/js/build_js.py build_js \
  --build_wasm \
  --build_flags="-sNODEJS_CATCH_EXIT=0" \
  --build_flags="-sNODEJS_CATCH_REJECTION=0" \
  --build_flags="-sENVIRONMENT=node"

Шаг 3: Настройка модуля

javascript
const cv = require('./opencv.js');

// Настройка модуля для среды Node.js
Module = {
  onRuntimeInitialized: function() {
    console.log('OpenCV.js готов к работе!');
    console.log(cv.getBuildInformation());
    
    // Ваш код OpenCV здесь
    const inputImage = cv.imread("Lenna.png");
    console.log("Готово");
  },
  // Специфические настройки для Node.js
  arguments: [],
  locateFile: function(filename) {
    return filename;
  }
};

// Загрузка OpenCV.js
cv = require('./opencv.js');

Как объясняется в официальной документации OpenCV, этот подход настраивает Emscripten на использование API Node.js вместо браузерных API.


Решение 3: Использование JSDOM для симуляции браузерной среды

Если вы хотите сохранить структуру кода, аналогичную браузерной, вы можете использовать JSDOM для симуляции браузерной среды:

Шаг 1: Установка JSDOM

bash
npm install jsdom

Шаг 2: Модификация вашего кода

javascript
const { JSDOM } = require('jsdom');
const { getOpenCv } = require("./opencv.js");

// Создание DOM среды
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
global.document = dom.window.document;
global.window = dom.window;
global.navigator = dom.window.navigator;

async function main() {
  const { cv } = await getOpenCv();
  console.log("OpenCV.js готов к работе!");
  console.log(cv.getBuildInformation());
  
  // Теперь вы можете использовать браузерные функции OpenCV
  const inputImage = cv.imread("Lenna.png");
  console.log("Готово");
}

main();

Преимущества:

  • Поддерживает совместимость с браузерными API
  • Позволяет существующему браузерному коду OpenCV работать с минимальными изменениями
  • Хорошо подходит для тестирования и разработки

Недостатки:

  • Добавляет накладные расходы
  • Может не поддерживать все браузерные функции

Решение 4: Сборка кастомного OpenCV.js с флагами Node.js

Для наиболее надежного решения соберите OpenCV.js специально для среды Node.js:

bash
# Использование Docker (рекомендуется)
docker run --rm -v $(pwd):/src -u 1000:1000 emscripten/emsdk:2.0.10 \
  emcmake python3 ./platforms/js/build_js.py build_js \
  --build_wasm \
  --build_flags="-sENVIRONMENT=node" \
  --build_flags="-sNODEJS_CATCH_EXIT=0" \
  --build_flags="-sNODEJS_CATCH_REJECTION=0" \
  --build_flags="-sERROR_ON_UNDEFINED_SYMBOLS=0" \
  --build_flags="-sALLOW_MEMORY_GROWTH=1"

Использование в Node.js:

javascript
const cv = require('./opencv.js');

// Специфическая инициализация для Node.js
Module = {
  onRuntimeInitialized: function() {
    console.log('OpenCV.js успешно инициализирован');
    
    // Ваш код OpenCV
    const mat = new cv.Mat();
    console.log(mat.size());
    mat.delete();
  }
};

// Загрузка модуля
require('./opencv.js');

Этот подход, как обсуждается на форуме OpenCV, обеспечивает наиболее надежную совместимость с Node.js путем явной ориентации на среду Node.js во время компиляции.


Лучшие практики и рекомендации

  1. Используйте правильный пакет: Для большинства проектов Node.js рекомендуется использовать @techstark/opencv-js, так как он обеспечивает лучшую совместимость.

  2. Всегда ждите инициализации: OpenCV.js работает асинхронно и требует надлежащей инициализации перед использованием.

  3. Обрабатывайте чтение файлов: Node.js не имеет того же доступа к файлам, что и браузеры. Вам понадобятся дополнительные библиотеки, такие как fs и canvas:

javascript
const fs = require('fs');
const { createCanvas } = require('canvas');

async function loadImage(path) {
  const imageData = fs.readFileSync(path);
  const img = new Image();
  img.src = imageData;
  
  return new Promise((resolve) => {
    img.onload = () => {
      const canvas = createCanvas(img.width, img.height);
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0);
      resolve(canvas);
    };
  });
}
  1. Управление памятью: Память WebAssembly ограничена. Используйте mat.delete() для освобождения памяти:
javascript
function processImage() {
  const mat = cv.imread('input.png');
  // Обработка изображения...
  mat.delete(); // Важно: освободить память
}
  1. Обработка ошибок: Всегда оборачивайте операции OpenCV в блоки try-catch:
javascript
try {
  const { cv } = await getOpenCv();
  // Ваш код OpenCV
} catch (error) {
  console.error('Ошибка инициализации OpenCV:', error);
}

Устранение распространенных проблем

Проблема: Ошибка “cv is not defined”

  • Убедитесь, что вы используете правильный синтаксис импорта/требования
  • Дождитесь инициализации времени выполнения перед использованием функций OpenCV
  • Проверьте, правильно ли загружен модуль

Проблема: Проблемы с памятью

  • Увеличьте лимит памяти WebAssembly: --build_flags="-sALLOW_MEMORY_GROWTH=1"
  • Вручную удаляйте Mats при завершении работы: mat.delete()

Проблема: Проблемы с чтением файлов

  • Используйте модуль Node.js fs с библиотекой canvas для чтения изображений
  • При необходимости преобразовывайте файлы в data URL

Проблема: Ошибки сборки

  • Используйте Docker для последовательных сборок
  • Убедитесь, что Emscripten правильно установлен и активирован
  • Проверьте требования к сборке OpenCV

Заключение

Ошибка “ReferenceError: document is not defined” — это распространенная проблема при использовании скомпилированных для браузера модулей WebAssembly, таких как OpenCV.js, в Node.js. Лучшее решение зависит от ваших конкретных потребностей:

  1. Для большинства проектов: Используйте @techstark/opencv-js с правильной инициализацией
  2. Для лучшего контроля: Соберите OpenCV.js с флагами Emscripten, специфичными для Node.js
  3. Для браузероподобной разработки: Используйте JSDOM для симуляции браузерной среды
  4. Для производства: Соберите кастомный OpenCV.js с оптимальными настройками для Node.js

Помните, что использование OpenCV.js в Node.js требует дополнительной настройки для операций с файлами и управления памятью по сравнению с использованием в браузере. Всегда ждите инициализации времени выполнения и правильно очищайте память WebAssembly, чтобы избежать утечек памяти.

Источники

  1. OpenCV: Использование OpenCV.js в Node.js
  2. Документация пакета TechStark opencv-js
  3. Stack Overflow: opencv-js в node выдает abort(ReferenceError: document is not defined)
  4. DEV Community: require(‘./opencv.js’) недостаточно для использования OpenCV.js
  5. Форум OpenCV: OpenCV.js, собранный с Emscripten, не работает с NodeJS
  6. GitHub: Issues TechStark/opencv-js
  7. Конфигурация среды Emscripten
Авторы
Проверено модерацией
Модерация