From 94f4678d3adddbe633372f78d533504deb6158fa Mon Sep 17 00:00:00 2001 From: WU Jia <35290826+kaingwade@users.noreply.github.com> Date: Sat, 27 Apr 2024 19:37:40 +0800 Subject: [PATCH] Merge pull request #25324 from kaingwade:clean_haarcascades_jsbindings Fix broken js build after moving HaarCascades to contrib #25324 The HaarCascades related are not completely cleaned up #25311 after #25198, which breaks the JavaScript build. The PR is to fix the issue. Related PR: opencv/opencv_contrib#3712 --- doc/CMakeLists.txt | 6 - .../js_assets/js_face_detection.html | 100 ----------- .../js_assets/js_face_detection_camera.html | 142 ---------------- doc/js_tutorials/js_assets/lena_yunet.jpg | Bin 0 -> 29954 bytes .../js_setup/js_nodejs/js_nodejs.markdown | 157 +++++++++++++----- modules/js/CMakeLists.txt | 11 -- modules/js/test/tests.html | 4 +- 7 files changed, 117 insertions(+), 303 deletions(-) delete mode 100644 doc/js_tutorials/js_assets/js_face_detection.html delete mode 100644 doc/js_tutorials/js_assets/js_face_detection_camera.html create mode 100644 doc/js_tutorials/js_assets/lena_yunet.jpg diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1b478cb6fd..e80bb717c1 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -251,12 +251,6 @@ if(DOXYGEN_FOUND) list(APPEND js_assets "${OPENCV_JS_LOCATION}") endif() - # copy haar cascade files - # set(haar_cascade_files "") - # set(data_harrcascades_path "${OpenCV_SOURCE_DIR}/data/haarcascades/") - # list(APPEND js_tutorials_assets_deps "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml") - # list(APPEND js_assets "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml") - foreach(f ${js_assets}) get_filename_component(fname "${f}" NAME) add_custom_command(OUTPUT "${opencv_tutorial_html_dir}/${fname}" diff --git a/doc/js_tutorials/js_assets/js_face_detection.html b/doc/js_tutorials/js_assets/js_face_detection.html deleted file mode 100644 index 3925458ecf..0000000000 --- a/doc/js_tutorials/js_assets/js_face_detection.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - -Face Detection Example - - - -

Face Detection Example

-

- <canvas> elements named canvasInput and canvasOutput have been prepared.
- Click Try it button to see the result. You can choose another image.
- You can change the code in the <textarea> to investigate more. -

-
-
- -

-
-
- - - - - - - - - -
- - - -
-
canvasInput
-
-
canvasOutput
-
-
- - - - - diff --git a/doc/js_tutorials/js_assets/js_face_detection_camera.html b/doc/js_tutorials/js_assets/js_face_detection_camera.html deleted file mode 100644 index f839cd7470..0000000000 --- a/doc/js_tutorials/js_assets/js_face_detection_camera.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - -Face Detection Camera Example - - - -

Face Detection Camera Example

-

- Click Start/Stop button to start or stop the camera capture.
- The videoInput is a <video> element used as face detector input. - The canvasOutput is a <canvas> element used as face detector output.
- The code of <textarea> will be executed when video is started. - You can modify the code to investigate more. -

-
-
- -
-

-
- - - - - - - - - - - - - -
- - - -
-
videoInput
-
-
canvasOutput
-
-
- - - - - - diff --git a/doc/js_tutorials/js_assets/lena_yunet.jpg b/doc/js_tutorials/js_assets/lena_yunet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd258b4568e220eccb77efbf5d6c803f8b8be0b8 GIT binary patch literal 29954 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 literal 0 HcmV?d00001 diff --git a/doc/js_tutorials/js_setup/js_nodejs/js_nodejs.markdown b/doc/js_tutorials/js_setup/js_nodejs/js_nodejs.markdown index 46cab5aa33..f2d7a16a79 100644 --- a/doc/js_tutorials/js_setup/js_nodejs/js_nodejs.markdown +++ b/doc/js_tutorials/js_setup/js_nodejs/js_nodejs.markdown @@ -227,52 +227,128 @@ In the browser, this filesystem is emulated in memory while in Node.js there's a ### The example ### -The following is an adaptation of @ref tutorial_js_face_detection. - @code{.js} const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas'); const { JSDOM } = require('jsdom'); const { writeFileSync, existsSync, mkdirSync } = require('fs'); +const https = require('https'); (async () => { - await loadOpenCV(); +const createFileFromUrl = function (path, url, maxRedirects = 10) { + console.log('Downloading ' + url + '...'); + return new Promise((resolve, reject) => { + const download = (url, redirectCount) => { + if (redirectCount > maxRedirects) { + reject(new Error('Too many redirects')); + } else { + let connection = https.get(url, (response) => { + if (response.statusCode === 200) { + let data = []; + response.on('data', (chunk) => { + data.push(chunk); + }); - const image = await loadImage('lena.jpg'); - const src = cv.imread(image); - let gray = new cv.Mat(); - cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0); - let faces = new cv.RectVector(); - let eyes = new cv.RectVector(); - let faceCascade = new cv.CascadeClassifier(); - let eyeCascade = new cv.CascadeClassifier(); + response.on('end', () => { + try { + writeFileSync(path, Buffer.concat(data)); + resolve(); + } catch (err) { + reject(new Error('Failed to write file ' + path)); + } + }); + } else if (response.statusCode === 302 || response.statusCode === 301) { + connection.abort(); + download(response.headers.location, redirectCount + 1); + } else { + reject(new Error('Failed to load ' + url + ' status: ' + response.statusCode)); + } + }).on('error', (err) => { + reject(new Error('Network Error: ' + err.message)); + }); + } + }; + download(url, 0); + }); +}; - // Load pre-trained classifier files. Notice how we reference local files using relative paths just - // like we normally would do - faceCascade.load('./haarcascade_frontalface_default.xml'); - eyeCascade.load('./haarcascade_eye.xml'); +if (!existsSync('./face_detection_yunet_2023mar.onnx')) { + await createFileFromUrl('./face_detection_yunet_2023mar.onnx', 'https://media.githubusercontent.com/media/opencv/opencv_zoo/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx') +} - let mSize = new cv.Size(0, 0); - faceCascade.detectMultiScale(gray, faces, 1.1, 3, 0, mSize, mSize); - for (let i = 0; i < faces.size(); ++i) { - let roiGray = gray.roi(faces.get(i)); - let roiSrc = src.roi(faces.get(i)); - let point1 = new cv.Point(faces.get(i).x, faces.get(i).y); - let point2 = new cv.Point(faces.get(i).x + faces.get(i).width, faces.get(i).y + faces.get(i).height); - cv.rectangle(src, point1, point2, [255, 0, 0, 255]); - eyeCascade.detectMultiScale(roiGray, eyes); - for (let j = 0; j < eyes.size(); ++j) { - let point1 = new cv.Point(eyes.get(j).x, eyes.get(j).y); - let point2 = new cv.Point(eyes.get(j).x + eyes.get(j).width, eyes.get(j).y + eyes.get(j).height); - cv.rectangle(roiSrc, point1, point2, [0, 0, 255, 255]); - } - roiGray.delete(); - roiSrc.delete(); +if (!existsSync('./opencv.js')) { + await createFileFromUrl('./opencv.js', 'https://docs.opencv.org/5.x/opencv.js') +} + +if (!existsSync('./lena.jpg')) { + await createFileFromUrl('./lena.jpg', 'https://docs.opencv.org/5.x/lena.jpg') +} + +await loadOpenCV(); + +const image = await loadImage('./lena.jpg'); +const src = cv.imread(image); +let srcBGR = new cv.Mat(); +cv.cvtColor(src, srcBGR, cv.COLOR_RGBA2BGR); + +// Load the deep learning model file. Notice how we reference local files using relative paths just +// like we normally would do +let netDet = new cv.FaceDetectorYN("./face_detection_yunet_2023mar.onnx", "", new cv.Size(320, 320), 0.9, 0.3, 5000); +netDet.setInputSize(new cv.Size(src.cols, src.rows)); +let out = new cv.Mat(); +netDet.detect(srcBGR, out); + +let faces = []; +for (let i = 0, n = out.data32F.length; i < n; i += 15) { + let left = out.data32F[i]; + let top = out.data32F[i + 1]; + let right = (out.data32F[i] + out.data32F[i + 2]); + let bottom = (out.data32F[i + 1] + out.data32F[i + 3]); + left = Math.min(Math.max(0, left), src.cols - 1); + top = Math.min(Math.max(0, top), src.rows - 1); + right = Math.min(Math.max(0, right), src.cols - 1); + bottom = Math.min(Math.max(0, bottom), src.rows - 1); + + if (left < right && top < bottom) { + faces.push({ + x: left, + y: top, + width: right - left, + height: bottom - top, + x1: out.data32F[i + 4] < 0 || out.data32F[i + 4] > src.cols - 1 ? -1 : out.data32F[i + 4], + y1: out.data32F[i + 5] < 0 || out.data32F[i + 5] > src.rows - 1 ? -1 : out.data32F[i + 5], + x2: out.data32F[i + 6] < 0 || out.data32F[i + 6] > src.cols - 1 ? -1 : out.data32F[i + 6], + y2: out.data32F[i + 7] < 0 || out.data32F[i + 7] > src.rows - 1 ? -1 : out.data32F[i + 7], + x3: out.data32F[i + 8] < 0 || out.data32F[i + 8] > src.cols - 1 ? -1 : out.data32F[i + 8], + y3: out.data32F[i + 9] < 0 || out.data32F[i + 9] > src.rows - 1 ? -1 : out.data32F[i + 9], + x4: out.data32F[i + 10] < 0 || out.data32F[i + 10] > src.cols - 1 ? -1 : out.data32F[i + 10], + y4: out.data32F[i + 11] < 0 || out.data32F[i + 11] > src.rows - 1 ? -1 : out.data32F[i + 11], + x5: out.data32F[i + 12] < 0 || out.data32F[i + 12] > src.cols - 1 ? -1 : out.data32F[i + 12], + y5: out.data32F[i + 13] < 0 || out.data32F[i + 13] > src.rows - 1 ? -1 : out.data32F[i + 13], + confidence: out.data32F[i + 14] + }) } +} +out.delete(); - const canvas = createCanvas(image.width, image.height); - cv.imshow(canvas, src); - writeFileSync('output3.jpg', canvas.toBuffer('image/jpeg')); - src.delete(); gray.delete(); faceCascade.delete(); eyeCascade.delete(); faces.delete(); eyes.delete() +faces.forEach(function(rect) { + cv.rectangle(src, {x: rect.x, y: rect.y}, {x: rect.x + rect.width, y: rect.y + rect.height}, [0, 255, 0, 255]); + if(rect.x1>0 && rect.y1>0) + cv.circle(src, {x: rect.x1, y: rect.y1}, 2, [255, 0, 0, 255], 2) + if(rect.x2>0 && rect.y2>0) + cv.circle(src, {x: rect.x2, y: rect.y2}, 2, [0, 0, 255, 255], 2) + if(rect.x3>0 && rect.y3>0) + cv.circle(src, {x: rect.x3, y: rect.y3}, 2, [0, 255, 0, 255], 2) + if(rect.x4>0 && rect.y4>0) + cv.circle(src, {x: rect.x4, y: rect.y4}, 2, [255, 0, 255, 255], 2) + if(rect.x5>0 && rect.y5>0) + cv.circle(src, {x: rect.x5, y: rect.y5}, 2, [0, 255, 255, 255], 2) +}); + +const canvas = createCanvas(image.width, image.height); +cv.imshow(canvas, src); +writeFileSync('output3.jpg', canvas.toBuffer('image/jpeg')); +console.log('The result is saved.') +src.delete(); srcBGR.delete(); })(); /** @@ -287,7 +363,7 @@ const { writeFileSync, existsSync, mkdirSync } = require('fs'); */ function loadOpenCV(rootDir = '/work', localRootDir = process.cwd()) { if(global.Module && global.Module.onRuntimeInitialized && global.cv && global.cv.imread) { - return Promise.resolve() + Promise.resolve() } return new Promise(resolve => { installDOM() @@ -333,13 +409,12 @@ function installDOM(){ ### Execute it ### - Save the file as `exampleNodeCanvasData.js`. -- Make sure the files `aarcascade_frontalface_default.xml` and `haarcascade_eye.xml` are present in project's directory. They can be obtained from [OpenCV sources](https://github.com/opencv/opencv/tree/5.x/data/haarcascades). -- Make sure a sample image file `lena.jpg` exists in project's directory. It should display people's faces for this example to make sense. The following image is known to work: +- The files `face_detection_yunet_2023mar.onnx`, `lena.jpg` and `opencv.js` will be downloaded if they not present in project's directory. -![image](js_assets/lena.jpg) - -The following command should generate the file `output3.jpg`: +The following command should generate the file `output3.jpg` look the image below: @code{.bash} node exampleNodeCanvasData.js @endcode + +![image](js_assets/lena_yunet.jpg) diff --git a/modules/js/CMakeLists.txt b/modules/js/CMakeLists.txt index 19f0b19790..d2a08fc61e 100644 --- a/modules/js/CMakeLists.txt +++ b/modules/js/CMakeLists.txt @@ -112,17 +112,6 @@ foreach(f ${test_files}) list(APPEND opencv_test_js_file_deps "${test_dir}/${f}" "${opencv_test_js_bin_dir}/${f}") endforeach() -# copy test data -set(test_data "haarcascade_frontalface_default.xml") -set(test_data_path "${PROJECT_SOURCE_DIR}/../../data/haarcascades/${test_data}") - -add_custom_command(OUTPUT "${opencv_test_js_bin_dir}/${test_data}" - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${test_data_path}" "${opencv_test_js_bin_dir}/${test_data}" - DEPENDS "${test_data_path}" - COMMENT "Copying ${test_data}" - ) -list(APPEND opencv_test_js_file_deps "${test_data_path}" "${opencv_test_js_bin_dir}/${test_data}") - add_custom_target(${PROJECT_NAME}_test DEPENDS ${OCV_JS_PATH} ${opencv_test_js_file_deps}) diff --git a/modules/js/test/tests.html b/modules/js/test/tests.html index b20013ec63..deec57a706 100644 --- a/modules/js/test/tests.html +++ b/modules/js/test/tests.html @@ -43,9 +43,7 @@ // Helper for opencv.js (see below) var Module = { - preRun: [function() { - Module.FS_createPreloadedFile('/', 'haarcascade_frontalface_default.xml', 'haarcascade_frontalface_default.xml', true, false); - }], + preRun: [], postRun: [] , onRuntimeInitialized: function() { console.log("Emscripten runtime is ready, launching QUnit tests...");