Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules/
/dist/src/
.idea/
test-tsx-issue.js
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,35 @@ module.exports = {

The TypeScript type declarations may not be up to date with the latest OpenCV.js. Refer to [cvKeys.json](doc/cvKeys.json) to check the available methods and properties at runtime.

# Browser vs Node.js Compatibility

This package works in both browser and Node.js environments. However, some functions are **browser-only** and will throw clear errors when used in Node.js:

- **`cv.imshow()`** - Requires HTML Canvas element (browser only)
- For Node.js, use alternative methods like `cv.imwrite()` to save images to files
- **`cv.VideoCapture()`** - Requires HTML Video element (browser only)

All other OpenCV functionality (Mat operations, image processing, computer vision algorithms, etc.) works in both environments.

### Example Node.js Usage

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

async function main() {
const cv = await cvModule;

// ✓ Works in Node.js
const mat = new cv.Mat(100, 100, cv.CV_8UC3);
cv.GaussianBlur(mat, mat, new cv.Size(5, 5), 0);

// ✗ Throws error in Node.js (browser only)
// cv.imshow("canvas", mat); // Error: cv.imshow() is only available in browser environments

mat.delete();
}
```

# Star History

[![Star History Chart](https://api.star-history.com/svg?repos=techstark/opencv-js&type=Date)](https://star-history.com/#techstark/opencv-js&Date)
2 changes: 1 addition & 1 deletion dist/opencv.js

Large diffs are not rendered by default.

49 changes: 35 additions & 14 deletions dist/opencv.js.patch
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
diff --git a/dist/opencv.js b/dist/opencv.js
index af4111b..3ba8a69 100644
--- a/dist/opencv.js
+++ b/dist/opencv.js
@@ -41,7 +41,7 @@ else if (typeof define === 'function' && define['amd'])
define([], () => cv);

if (typeof Module === 'undefined')
- Module = {};
+ var Module = {};
return cv(Module);
}));

\ No newline at end of file
This patch file documents the changes made to opencv.js to fix Node.js/tsx compatibility issues.

PATCH 1: Fix Module variable declaration
----------------------------------------
Location: Line ~41-44
Original:
if (typeof Module === 'undefined')
Module = {};

Modified:
if (typeof Module === 'undefined')
var Module = {};

PATCH 2: Add environment check to imshow function
-------------------------------------------------
Location: Line ~30 (within Module["imshow"] function)
Original:
Module["imshow"]=function(canvasSource,mat){var canvas=null;if(typeof canvasSource==="string"){canvas=document.getElementById(canvasSource)}

Modified:
Module["imshow"]=function(canvasSource,mat){if(typeof document==="undefined"){throw new Error("cv.imshow() is only available in browser environments. It requires DOM API (canvas element) which is not available in Node.js. For Node.js, please use alternative methods like cv.imwrite() to save images to files.")}var canvas=null;if(typeof canvasSource==="string"){canvas=document.getElementById(canvasSource)}

PATCH 3: Add environment check to VideoCapture constructor
----------------------------------------------------------
Location: Line ~30 (within Module["VideoCapture"] function)
Original:
Module["VideoCapture"]=function(videoSource){var video=null;if(typeof videoSource==="string"){video=document.getElementById(videoSource)}

Modified:
Module["VideoCapture"]=function(videoSource){if(typeof document==="undefined"){throw new Error("cv.VideoCapture() is only available in browser environments. It requires DOM API (video element) which is not available in Node.js.")}var video=null;if(typeof videoSource==="string"){video=document.getElementById(videoSource)}

To apply these patches, run:
node scripts/apply-opencv-patch.js

or manually edit dist/opencv.js with the changes above.
13 changes: 7 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions scripts/apply-opencv-patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env node

/**
* Apply patches to opencv.js for Node.js/tsx compatibility
*
* This script patches the opencv.js file to:
* 1. Fix Module variable declaration
* 2. Add environment checks for browser-only functions (imshow, VideoCapture)
*/

const fs = require('fs');
const path = require('path');

const OPENCV_PATH = path.join(__dirname, '..', 'dist', 'opencv.js');

// Error messages for browser-only functions
const IMSHOW_ERROR_MSG =
'cv.imshow() is only available in browser environments. ' +
'It requires DOM API (canvas element) which is not available in Node.js. ' +
'For Node.js, please use alternative methods like cv.imwrite() to save images to files.';

const VIDEO_CAPTURE_ERROR_MSG =
'cv.VideoCapture() is only available in browser environments. ' +
'It requires DOM API (video element) which is not available in Node.js.';

console.log('Applying patches to opencv.js...\n');

// Check if opencv.js exists
if (!fs.existsSync(OPENCV_PATH)) {
console.error(`Error: opencv.js not found at ${OPENCV_PATH}`);
console.error('Please ensure you are running this script from the project root.');
process.exit(1);
}

// Read the opencv.js file
let content = fs.readFileSync(OPENCV_PATH, 'utf-8');
let patchCount = 0;

// Patch 1: Fix the Module variable declaration issue
console.log('Patch 1: Module variable declaration');
const oldModuleDecl = ' if (typeof Module === \'undefined\')\n Module = {};';
const newModuleDecl = ' if (typeof Module === \'undefined\')\n var Module = {};';

if (content.includes(oldModuleDecl)) {
content = content.replace(oldModuleDecl, newModuleDecl);
console.log(' ✓ Applied\n');
patchCount++;
} else if (content.includes(newModuleDecl)) {
console.log(' ⚠ Already applied\n');
} else {
console.log(' ✗ Pattern not found (may already be patched differently)\n');
}

// Patch 2: Add environment check to imshow function
console.log('Patch 2: imshow environment check');
const oldImshow =
'Module["imshow"]=function(canvasSource,mat){var canvas=null;' +
'if(typeof canvasSource==="string"){canvas=document.getElementById(canvasSource)}';

const newImshow =
'Module["imshow"]=function(canvasSource,mat){' +
'if(typeof document==="undefined"){throw new Error("' + IMSHOW_ERROR_MSG + '")}' +
'var canvas=null;if(typeof canvasSource==="string"){canvas=document.getElementById(canvasSource)}';

if (content.includes(oldImshow)) {
content = content.replace(oldImshow, newImshow);
console.log(' ✓ Applied\n');
patchCount++;
} else if (content.includes(newImshow)) {
console.log(' ⚠ Already applied\n');
} else {
console.log(' ⚠ Pattern not found (may already be patched differently)\n');
}

// Patch 3: Add environment check to VideoCapture function
console.log('Patch 3: VideoCapture environment check');
const oldVideoCapture =
'Module["VideoCapture"]=function(videoSource){var video=null;' +
'if(typeof videoSource==="string"){video=document.getElementById(videoSource)}';

const newVideoCapture =
'Module["VideoCapture"]=function(videoSource){' +
'if(typeof document==="undefined"){throw new Error("' + VIDEO_CAPTURE_ERROR_MSG + '")}' +
'var video=null;if(typeof videoSource==="string"){video=document.getElementById(videoSource)}';

if (content.includes(oldVideoCapture)) {
content = content.replace(oldVideoCapture, newVideoCapture);
console.log(' ✓ Applied\n');
patchCount++;
} else if (content.includes(newVideoCapture)) {
console.log(' ⚠ Already applied\n');
} else {
console.log(' ⚠ Pattern not found (may already be patched differently)\n');
}

// Write the patched file
if (patchCount > 0) {
fs.writeFileSync(OPENCV_PATH, content);
console.log(`✓ Successfully applied ${patchCount} patch(es) to opencv.js`);
} else {
console.log('ℹ No new patches applied (all already present)');
}
44 changes: 44 additions & 0 deletions test/nodejs-compatibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { setupOpenCv } from "./cv";

beforeAll(setupOpenCv);

describe("Node.js Environment Compatibility", () => {
test("should create Mat objects successfully in Node.js", () => {
const mat = new cv.Mat(100, 100, cv.CV_8UC3, new cv.Scalar(255, 0, 0));
expect(mat.rows).toBe(100);
expect(mat.cols).toBe(100);
expect(mat.channels()).toBe(3);
mat.delete();
});

test("should throw clear error when calling imshow in Node.js", () => {
const mat = new cv.Mat(10, 10, cv.CV_8UC1);
expect(() => {
cv.imshow("test", mat);
}).toThrow(
"cv.imshow() is only available in browser environments. It requires DOM API (canvas element) which is not available in Node.js. For Node.js, please use alternative methods like cv.imwrite() to save images to files."
);
mat.delete();
});

test("should throw clear error when calling VideoCapture in Node.js", () => {
expect(() => {
new cv.VideoCapture("test");
}).toThrow(
"cv.VideoCapture() is only available in browser environments. It requires DOM API (video element) which is not available in Node.js."
);
});

test("should perform other OpenCV operations successfully in Node.js", () => {
const mat = new cv.Mat(100, 100, cv.CV_8UC1);
const result = new cv.Mat();

// Test GaussianBlur
cv.GaussianBlur(mat, result, new cv.Size(5, 5), 0);
expect(result.rows).toBe(100);
expect(result.cols).toBe(100);

mat.delete();
result.delete();
});
});