Как исправить 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.
Моя простая программа:
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.
Содержание
- Понимание основной причины
- Решение 1: Использование пакета @techstark/opencv-js
- Решение 2: Ручная настройка среды Emscripten
- Решение 3: Использование JSDOM для симуляции браузерной среды
- Решение 4: Сборка кастомного OpenCV.js с флагами 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. Однако его нужно использовать правильно с надлежащей инициализацией:
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
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
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: Настройка модуля
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
npm install jsdom
Шаг 2: Модификация вашего кода
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:
# Использование 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:
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 во время компиляции.
Лучшие практики и рекомендации
-
Используйте правильный пакет: Для большинства проектов Node.js рекомендуется использовать
@techstark/opencv-js, так как он обеспечивает лучшую совместимость. -
Всегда ждите инициализации: OpenCV.js работает асинхронно и требует надлежащей инициализации перед использованием.
-
Обрабатывайте чтение файлов: Node.js не имеет того же доступа к файлам, что и браузеры. Вам понадобятся дополнительные библиотеки, такие как
fsиcanvas:
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);
};
});
}
- Управление памятью: Память WebAssembly ограничена. Используйте
mat.delete()для освобождения памяти:
function processImage() {
const mat = cv.imread('input.png');
// Обработка изображения...
mat.delete(); // Важно: освободить память
}
- Обработка ошибок: Всегда оборачивайте операции OpenCV в блоки try-catch:
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. Лучшее решение зависит от ваших конкретных потребностей:
- Для большинства проектов: Используйте
@techstark/opencv-jsс правильной инициализацией - Для лучшего контроля: Соберите OpenCV.js с флагами Emscripten, специфичными для Node.js
- Для браузероподобной разработки: Используйте JSDOM для симуляции браузерной среды
- Для производства: Соберите кастомный OpenCV.js с оптимальными настройками для Node.js
Помните, что использование OpenCV.js в Node.js требует дополнительной настройки для операций с файлами и управления памятью по сравнению с использованием в браузере. Всегда ждите инициализации времени выполнения и правильно очищайте память WebAssembly, чтобы избежать утечек памяти.
Источники
- OpenCV: Использование OpenCV.js в Node.js
- Документация пакета TechStark opencv-js
- Stack Overflow: opencv-js в node выдает abort(ReferenceError: document is not defined)
- DEV Community: require(‘./opencv.js’) недостаточно для использования OpenCV.js
- Форум OpenCV: OpenCV.js, собранный с Emscripten, не работает с NodeJS
- GitHub: Issues TechStark/opencv-js
- Конфигурация среды Emscripten