Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d537d40
Fix image loading in Three.js example to send pixel data to worker
google-labs-jules[bot] Nov 28, 2025
d7dca1f
Fix Three.js examples marker detection: use decoded pixel data and fu…
google-labs-jules[bot] Nov 29, 2025
3a5edc5
Fix Three.js examples: full-res processing and correct matrix usage
google-labs-jules[bot] Nov 29, 2025
00ef670
Fix Three.js examples: sync worker loop, fix image loading and matrix…
google-labs-jules[bot] Nov 29, 2025
2b3eb8a
Fix Three.js examples: grayscale loop, sync logic, and image loading
google-labs-jules[bot] Nov 29, 2025
856e5c8
Fix Three.js examples: deadlocks, orientation, and full-res tracking
google-labs-jules[bot] Nov 29, 2025
e6dc1c2
Fix Three.js examples: flip orientation, sync loop, fix projection
google-labs-jules[bot] Nov 30, 2025
0a8cae6
Fix Three.js examples: detection loop, grayscale logic, and matrix usage
google-labs-jules[bot] Nov 30, 2025
2ea4e86
Fix Three.js examples: use viewMatrix_GL and standard orientation
google-labs-jules[bot] Nov 30, 2025
731901c
Fix Three.js examples: matrix usage, detection loop, and data format
google-labs-jules[bot] Nov 30, 2025
49f2f9c
Fix Three.js examples: matrix coordinates, detection loop, and graysc…
google-labs-jules[bot] Nov 30, 2025
a3ce1cb
Fix Three.js examples: coordinate system, detection loop, and format
google-labs-jules[bot] Nov 30, 2025
9c51eda
Fix Three.js examples: use transMatrix, detection, and stability
google-labs-jules[bot] Nov 30, 2025
c51b38e
Fix Three.js examples: restore matrixGL_RH, detection, and stability
google-labs-jules[bot] Nov 30, 2025
1caf5aa
Fix Three.js examples: stabilize worker, fix coordinates, and error h…
google-labs-jules[bot] Nov 30, 2025
fbca61b
Fix Three.js examples: crash handler, coordinates, and detection
google-labs-jules[bot] Nov 30, 2025
886a2c7
Fix Three.js detection, crashes, and Z-axis orientation
google-labs-jules[bot] Nov 30, 2025
eb53ae1
Fix Three.js detection, crashes, and coordinate alignment
google-labs-jules[bot] Nov 30, 2025
bb918c5
Fix Three.js example detection, crashes, and matrix coordinates
google-labs-jules[bot] Nov 30, 2025
b1d3130
opencv lib with simd support
kalwalt Dec 1, 2025
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
21 changes: 21 additions & 0 deletions build/opencv_js.js

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions build_cv_w_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ fi

if [ ! -d "opencv_js" ] ; then
mkdir opencv_js
sudo chmod -R 777 opencv_js
# No sudo needed here, the user running the script owns the new directory.
# On Linux, Docker permissions are handled by the -u flag later.
chmod -R 777 opencv_js
echo "mkdir opencv_js"
fi

Expand All @@ -39,12 +41,19 @@ do
done

SIMD=" "

if [ $BUILD_SIMD ] ; then
SIMD=" --simd "
fi

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) -e "EMSCRIPTEN=/emsdk/upstream/emscripten" emscripten/emsdk:3.1.26 emcmake python3 ./opencv/platforms/js/build_js.py opencv_js --config="./opencv.webarkit_config.py" $SIMD --build_wasm --cmake_option="-DBUILD_opencv_dnn=OFF" --cmake_option="-DBUILD_opencv_objdetect=OFF" --cmake_option="-DBUILD_opencv_photo=OFF" --cmake_option="-DBUILD_opencv_imgcodecs=ON" --cmake_option="-DBUILD_opencv_xfeatures2d=ON" --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/" --build_flags=" -fwasm-exceptions -mbulk-memory -mnontrapping-fptoint -sWASM_BIGINT -sSUPPORT_LONGJMP=wasm "
# Add user flag for Linux to avoid root-owned files, but not for Windows (Git Bash).
USER_FLAG=""
if [[ "$(uname -s)" == "Linux" ]]; then
USER_FLAG="-u $(id -u):$(id -g)"
fi

# The fix is to call python3 directly. The build_js.py script will invoke emcmake itself.
# Using --workdir=//src prevents Git Bash on Windows from mangling the path.
docker run --rm -v "$(pwd)":/src --workdir=//src ${USER_FLAG} -e "EMSCRIPTEN=/emsdk/upstream/emscripten" emscripten/emsdk:3.1.26 python3 ./opencv/platforms/js/build_js.py opencv_js --config="./opencv.webarkit_config.py" $SIMD --build_wasm --cmake_option="-DBUILD_opencv_dnn=OFF" --cmake_option="-DBUILD_opencv_objdetect=OFF" --cmake_option="-DBUILD_opencv_photo=OFF" --cmake_option="-DBUILD_opencv_imgcodecs=ON" --cmake_option="-DBUILD_opencv_xfeatures2d=ON" --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/" --build_flags=" -fwasm-exceptions -mbulk-memory -mnontrapping-fptoint -sWASM_BIGINT -sSUPPORT_LONGJMP=wasm "

# copy the output to the build folder
#cp -r opencv_js/bin/opencv_js.js build
122 changes: 75 additions & 47 deletions examples/threejs_worker_ES6.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@ const setMatrix = function (matrix, value) {
}
};

function toGrayscale(data, width, height, flip = false) {
const gray = new Uint8Array(width * height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = y * width + x;
const r = data[i * 4];
const g = data[i * 4 + 1];
const b = data[i * 4 + 2];

let grayIndex;
if (flip) {
grayIndex = (height - 1 - y) * width + x;
} else {
grayIndex = i;
}

gray[grayIndex] = (0.299 * r + 0.587 * g + 0.114 * b);
}
}
return gray;
}

function start(markerUrl, video, input_width, input_height, render_update, track_update) {
let vw, vh;
let sw, sh;
Expand Down Expand Up @@ -60,7 +82,7 @@ function start(markerUrl, video, input_width, input_height, render_update, track
scene.add(root);

sphere.material.flatShading;
sphere.scale.set(.5, .5, .5);
sphere.scale.set(1, 1, 1);

root.matrixAutoUpdate = false;
root.add(sphere);
Expand All @@ -69,18 +91,17 @@ function start(markerUrl, video, input_width, input_height, render_update, track
vw = input_width;
vh = input_height;

pscale = 320 / Math.max(vw, vh / 3 * 4);
sscale = isMobile() ? window.outerWidth / input_width : 1;

sw = vw * sscale;
sh = vh * sscale;

w = vw * pscale;
h = vh * pscale;
pw = Math.max(w, h / 3 * 4);
ph = Math.max(h, w / 4 * 3);
ox = (pw - w) / 2;
oy = (ph - h) / 2;
w = vw;
h = vh;
pw = vw;
ph = vh;
ox = 0;
oy = 0;
canvas_process.style.clientWidth = pw + "px";
canvas_process.style.clientHeight = ph + "px";
canvas_process.width = pw;
Expand All @@ -94,25 +115,31 @@ function start(markerUrl, video, input_width, input_height, render_update, track

const type = setTrackerType();
const loadImage = (URL) => {
fetch(URL)
.then(response => response.arrayBuffer())
.then(buff => {
let buffer = new Uint8Array(buff);
worker.postMessage({
type: "initTracker",
trackerType: type,
imageData: buffer,
//imgWidth: refIm.width,
imgWidth: 1637,
//imgHeight: refIm.height,
imgHeight: 2048,
//videoWidth: oWidth,
//videoHeight: oHeight,
videoWidth: vw,
videoHeight: vh,
});
return buffer;
})
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, img.width, img.height);
// Use standard orientation (no flip)
const gray = toGrayscale(imageData.data, img.width, img.height, false);
worker.postMessage({
type: "initTracker",
trackerType: type,
imageData: gray,
imgWidth: img.width,
imgHeight: img.height,
videoWidth: vw,
videoHeight: vh,
}, [gray.buffer]);
resolve();
};
img.onerror = reject;
img.src = URL;
});
}

loadImage(markerUrl)
Expand All @@ -122,17 +149,6 @@ function start(markerUrl, video, input_width, input_height, render_update, track
switch (msg.type) {
case "loadedTracker": {
const proj = JSON.parse(msg.cameraProjMat);
//console.log("proj: ", proj);
const ratioW = pw / w;
const ratioH = ph / h;
proj[0] *= ratioW;
proj[4] *= ratioW;
proj[8] *= ratioW;
proj[12] *= ratioW;
proj[1] *= ratioH;
proj[5] *= ratioH;
proj[9] *= ratioH;
proj[13] *= ratioH;
setMatrix(camera.projectionMatrix, proj);
process();
break;
Expand Down Expand Up @@ -162,7 +178,7 @@ function start(markerUrl, video, input_width, input_height, render_update, track
}
}
track_update();
//process();
process();
};
};

Expand All @@ -172,10 +188,24 @@ function start(markerUrl, video, input_width, input_height, render_update, track
if (!msg) {
world = null;
} else {
world = JSON.parse(msg.pose);
//world = JSON.parse(msg.matrixGL_RH);
//world = JSON.parse(msg.viewMatrix_GL);
const m = JSON.parse(msg.matrixGL_RH);

// Invert X and Y axes to fix "Top Right" vs "Bottom Left" quadrant mismatch.
// Do NOT invert Z, as that caused visibility issues.

// Invert X
m[0] = -m[0];
m[4] = -m[4];
m[8] = -m[8];
m[12] = -m[12];

// Invert Y
m[1] = -m[1];
m[5] = -m[5];
m[9] = -m[9];
m[13] = -m[13];

world = m;
}
};

Expand All @@ -201,17 +231,15 @@ function start(markerUrl, video, input_width, input_height, render_update, track
};

const process = function () {
context_process.fillStyle = 'black';
context_process.fillRect(0, 0, pw, ph);
context_process.drawImage(video, 0, 0, vw, vh, ox, oy, w, h);
context_process.drawImage(video, 0, 0, vw, vh);

const imageData = context_process.getImageData(0, 0, pw, ph);
const imageData = context_process.getImageData(0, 0, vw, vh);
// Send RGBA to worker to offload conversion and avoid main thread jank
worker.postMessage({ type: 'process', imagedata: imageData }, [imageData.data.buffer]);
}

const tick = function () {
draw();
process();
requestAnimationFrame(tick);
};

Expand Down
69 changes: 35 additions & 34 deletions examples/threejs_worker_ES6_jsfeatNext.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function start(markerUrl, video, input_width, input_height, render_update, track
var marker;

sphere.material.flatShading;
sphere.scale.set(.5, .5, .5);
sphere.scale.set(1, 1, 1);

root.matrixAutoUpdate = false;
root.add(sphere);
Expand All @@ -95,19 +95,18 @@ function start(markerUrl, video, input_width, input_height, render_update, track
//vw = oWidth;
//vh = oHeight;

pscale = 320 / Math.max(vw, vh / 3 * 4);
sscale = isMobile() ? window.outerWidth / input_width : 1;
//sscale = isMobile() ? window.outerWidth / oWidth : 1;

sw = vw * sscale;
sh = vh * sscale;

w = vw * pscale;
h = vh * pscale;
pw = Math.max(w, h / 3 * 4);
ph = Math.max(h, w / 4 * 3);
ox = (pw - w) / 2;
oy = (ph - h) / 2;
w = vw;
h = vh;
pw = vw;
ph = vh;
ox = 0;
oy = 0;
canvas_process.style.clientWidth = pw + "px";
canvas_process.style.clientHeight = ph + "px";
canvas_process.width = pw;
Expand All @@ -124,30 +123,33 @@ function start(markerUrl, video, input_width, input_height, render_update, track

var type = setTrackerType();
const loadImage = (URL) => {
fetch(URL)
.then(response => response.arrayBuffer())
.then(buff => {
let buffer = new Uint8Array(buff);
let img_u8_tracker = new jsfeat.matrix_t(image_W, image_H, jsfeat.U8_t | jsfeat.C1_t);
imgproc.grayscale(buffer, image_W, image_H, img_u8_tracker);
//var grayT = color2gray(buffer, refIm.width * refIm.height);
worker.postMessage({
type: "initTracker",
trackerType: type,
imageData: img_u8_tracker.data,
//imageData: grayT,
//imageData: buffer,
//imgWidth: refIm.width,
imgWidth: image_W,
//imgHeight: refIm.height,
imgHeight: image_H,
//videoWidth: oWidth,
//videoHeight: oHeight,
videoWidth: vw,
videoHeight: vh,
});
return buffer;
})
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, img.width, img.height);

let img_u8_tracker = new jsfeat.matrix_t(img.width, img.height, jsfeat.U8_t | jsfeat.C1_t);
imgproc.grayscale(imageData.data, img.width, img.height, img_u8_tracker);

worker.postMessage({
type: "initTracker",
trackerType: type,
imageData: img_u8_tracker.data,
imgWidth: img.width,
imgHeight: img.height,
videoWidth: vw,
videoHeight: vh,
}, [img_u8_tracker.data.buffer]);
resolve();
};
img.onerror = reject;
img.src = URL;
});
}

loadImage(markerUrl)
Expand Down Expand Up @@ -199,7 +201,7 @@ function start(markerUrl, video, input_width, input_height, render_update, track
}
}
track_update();
//process();
process();
};
};

Expand Down Expand Up @@ -257,7 +259,6 @@ function start(markerUrl, video, input_width, input_height, render_update, track

var tick = function () {
draw();
process();
//update();
requestAnimationFrame(tick);
};
Expand Down
Loading