1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Alekhin
2021-12-30 16:23:27 +00:00
951 changed files with 158314 additions and 152668 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ cv.normalize(roiHist, roiHist, 0, 255, cv.NORM_MINMAX);
// delete useless mats.
roi.delete(); hsvRoi.delete(); mask.delete(); low.delete(); high.delete(); hsvRoiVec.delete();
// Setup the termination criteria, either 10 iteration or move by atleast 1 pt
// Setup the termination criteria, either 10 iteration or move by at least 1 pt
let termCrit = new cv.TermCriteria(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1);
let hsv = new cv.Mat(video.height, video.width, cv.CV_8UC3);
@@ -116,7 +116,7 @@ swapRB = false;
needSoftmax = false;
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt";
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
@@ -6,7 +6,7 @@
"std": "1",
"swapRB": "false",
"needSoftmax": "false",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"modelUrl": "http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel",
"configUrl": "https://raw.githubusercontent.com/BVLC/caffe/master/models/bvlc_alexnet/deploy.prototxt"
},
@@ -16,7 +16,7 @@
"std": "0.007843",
"swapRB": "false",
"needSoftmax": "true",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"modelUrl": "https://drive.google.com/open?id=0B7ubpZO7HnlCcHlfNmJkU2VPelE",
"configUrl": "https://raw.githubusercontent.com/shicai/DenseNet-Caffe/master/DenseNet_121.prototxt"
},
@@ -26,7 +26,7 @@
"std": "1",
"swapRB": "false",
"needSoftmax": "false",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"modelUrl": "http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel",
"configUrl": "https://raw.githubusercontent.com/BVLC/caffe/master/models/bvlc_googlenet/deploy.prototxt"
},
@@ -36,7 +36,7 @@
"std": "1",
"swapRB": "false",
"needSoftmax": "false",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"modelUrl": "https://raw.githubusercontent.com/forresti/SqueezeNet/master/SqueezeNet_v1.0/squeezenet_v1.0.caffemodel",
"configUrl": "https://raw.githubusercontent.com/forresti/SqueezeNet/master/SqueezeNet_v1.0/deploy.prototxt"
},
@@ -46,7 +46,7 @@
"std": "1",
"swapRB": "false",
"needSoftmax": "false",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt",
"modelUrl": "http://www.robots.ox.ac.uk/~vgg/software/very_deep/caffe/VGG_ILSVRC_19_layers.caffemodel",
"configUrl": "https://gist.githubusercontent.com/ksimonyan/3785162f95cd2d5fee77/raw/f02f8769e64494bcd3d7e97d5d747ac275825721/VGG_ILSVRC_19_layers_deploy.prototxt"
}
@@ -0,0 +1,269 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Image Classification Example</title>
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
<script src="./webnn-polyfill.js"></script>
</head>
<body>
<h2>Image Classification Example</h2>
<p>
This tutorial shows you how to write an image classification example with OpenCV.js.<br>
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
Then You should change the parameters in the first code snippet according to the uploaded model.
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
</p>
<div class="control"><button id="tryIt" disabled>Try it</button></div>
<div>
<table cellpadding="0" cellspacing="0" width="0" border="0">
<tr>
<td>
<canvas id="canvasInput" width="400" height="400"></canvas>
</td>
<td>
<table style="visibility: hidden;" id="result">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col" width=300>Label</th>
<th scope="col">Probability</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td id="label0" align="center"></td>
<td id="prob0" align="center"></td>
</tr>
<tr>
<th scope="row">2</th>
<td id="label1" align="center"></td>
<td id="prob1" align="center"></td>
</tr>
<tr>
<th scope="row">3</th>
<td id="label2" align="center"></td>
<td id="prob2" align="center"></td>
</tr>
</tbody>
</table>
<p id='status' align="left"></p>
</td>
</tr>
<tr>
<td>
<div class="caption">
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
</div>
</td>
<td></td>
</tr>
<tr>
<td>
<div class="caption">
modelFile <input type="file" id="modelFile">
</div>
</td>
</tr>
<tr>
<td>
<div class="caption">
configFile <input type="file" id="configFile">
</div>
</td>
</tr>
</table>
</div>
<div>
<p class="err" id="errorMessage"></p>
</div>
<div>
<h3>Help function</h3>
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
<textarea class="code" rows="13" cols="100" id="codeEditor" spellcheck="false"></textarea>
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor1" spellcheck="false"></textarea>
<p>3.Load labels from txt file and process it into an array.</p>
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
<p>6.The post-processing, including softmax if needed and get the top classes from the output vector.</p>
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
</div>
<div id="appendix">
<h2>Model Info:</h2>
</div>
<script src="utils.js" type="text/javascript"></script>
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
<script id="codeSnippet" type="text/code-snippet">
inputSize = [224,224];
mean = [104, 117, 123];
std = 1;
swapRB = false;
// record if need softmax function for post-processing
needSoftmax = false;
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
main = async function() {
const labels = await loadLables(labelsUrl);
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
let net = cv.readNet(configPath, modelPath);
net.setPreferableBackend(6);
net.setInput(input);
let result = net.forward();
const start = performance.now();
for (i=0;i<200;i++)
{
result = net.forward();
}
const time = performance.now()-start;
const probs = softmax(result);
const classes = getTopClasses(probs, labels);
updateResult(classes, time/200);
input.delete();
net.delete();
result.delete();
}
</script>
<script id="codeSnippet5" type="text/code-snippet">
softmax = function(result) {
let arr = result.data32F;
if (needSoftmax) {
const maxNum = Math.max(...arr);
const expSum = arr.map((num) => Math.exp(num - maxNum)).reduce((a, b) => a + b);
return arr.map((value, index) => {
return Math.exp(value - maxNum) / expSum;
});
} else {
return arr;
}
}
</script>
<script type="text/javascript">
let jsonUrl = "js_image_classification_model_info.json";
drawInfoTable(jsonUrl, 'appendix');
let utils = new Utils('errorMessage');
utils.loadCode('codeSnippet', 'codeEditor');
utils.loadCode('codeSnippet1', 'codeEditor1');
let loadLablesCode = 'loadLables = ' + loadLables.toString();
document.getElementById('codeEditor2').value = loadLablesCode;
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
document.getElementById('codeEditor3').value = getBlobFromImageCode;
let loadModelCode = 'loadModel = ' + loadModel.toString();
document.getElementById('codeEditor4').value = loadModelCode;
utils.loadCode('codeSnippet5', 'codeEditor5');
let getTopClassesCode = 'getTopClasses = ' + getTopClasses.toString();
document.getElementById('codeEditor5').value += '\n' + '\n' + getTopClassesCode;
let canvas = document.getElementById('canvasInput');
let ctx = canvas.getContext('2d');
let img = new Image();
img.crossOrigin = 'anonymous';
img.src = 'space_shuttle.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
let tryIt = document.getElementById('tryIt');
tryIt.addEventListener('click', () => {
initStatus();
document.getElementById('status').innerHTML = 'Running function main()...';
utils.executeCode('codeEditor');
utils.executeCode('codeEditor1');
if (modelPath === "") {
document.getElementById('status').innerHTML = 'Runing failed.';
utils.printError('Please upload model file by clicking the button first.');
} else {
setTimeout(main, 1);
}
});
let fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (e) => {
initStatus();
loadImageToCanvas(e, 'canvasInput');
});
let configPath = "";
let configFile = document.getElementById('configFile');
configFile.addEventListener('change', async (e) => {
initStatus();
configPath = await loadModel(e);
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
});
let modelPath = "";
let modelFile = document.getElementById('modelFile');
modelFile.addEventListener('change', async (e) => {
initStatus();
modelPath = await loadModel(e);
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
configPath = "";
configFile.value = "";
});
utils.loadOpenCv(() => {
tryIt.removeAttribute('disabled');
});
var main = async function() {};
var softmax = function(result){};
var getTopClasses = function(mat, labels, topK = 3){};
utils.executeCode('codeEditor1');
utils.executeCode('codeEditor2');
utils.executeCode('codeEditor3');
utils.executeCode('codeEditor4');
utils.executeCode('codeEditor5');
function updateResult(classes, time) {
try{
classes.forEach((c,i) => {
let labelElement = document.getElementById('label'+i);
let probElement = document.getElementById('prob'+i);
labelElement.innerHTML = c.label;
probElement.innerHTML = c.prob + '%';
});
let result = document.getElementById('result');
result.style.visibility = 'visible';
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
<b>Inference time:</b> ${time.toFixed(2)} ms`;
} catch(e) {
console.log(e);
}
}
function initStatus() {
document.getElementById('status').innerHTML = '';
document.getElementById('result').style.visibility = 'hidden';
utils.clearError();
}
</script>
</body>
</html>
@@ -116,7 +116,7 @@ swapRB = false;
needSoftmax = false;
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt";
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
+1 -1
View File
@@ -77,7 +77,7 @@ cv.normalize(roiHist, roiHist, 0, 255, cv.NORM_MINMAX);
// delete useless mats.
roi.delete(); hsvRoi.delete(); mask.delete(); low.delete(); high.delete(); hsvRoiVec.delete();
// Setup the termination criteria, either 10 iteration or move by atleast 1 pt
// Setup the termination criteria, either 10 iteration or move by at least 1 pt
let termCrit = new cv.TermCriteria(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1);
let hsv = new cv.Mat(video.height, video.width, cv.CV_8UC3);
@@ -94,7 +94,7 @@ nmsThreshold = 0.4;
outType = "SSD";
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt";
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/object_detection_classes_pascal_voc.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
@@ -7,7 +7,7 @@
"std": "0.007843",
"swapRB": "false",
"outType": "SSD",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/object_detection_classes_pascal_voc.txt",
"modelUrl": "https://raw.githubusercontent.com/chuanqi305/MobileNet-SSD/master/mobilenet_iter_73000.caffemodel",
"configUrl": "https://raw.githubusercontent.com/chuanqi305/MobileNet-SSD/master/deploy.prototxt"
},
@@ -18,7 +18,7 @@
"std": "1",
"swapRB": "false",
"outType": "SSD",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/object_detection_classes_pascal_voc.txt",
"modelUrl": "https://drive.google.com/uc?id=0BzKzrI_SkD1_WVVTSmQxU0dVRzA&export=download",
"configUrl": "https://drive.google.com/uc?id=0BzKzrI_SkD1_WVVTSmQxU0dVRzA&export=download"
}
@@ -31,7 +31,7 @@
"std": "0.00392",
"swapRB": "false",
"outType": "YOLO",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_yolov3.txt",
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/object_detection_classes_yolov3.txt",
"modelUrl": "https://pjreddie.com/media/files/yolov2-tiny.weights",
"configUrl": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov2-tiny.cfg"
}
@@ -94,7 +94,7 @@ nmsThreshold = 0.4;
outType = "SSD";
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt";
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/object_detection_classes_pascal_voc.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
@@ -0,0 +1,268 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Image Classification Example</title>
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Image Classification Example</h2>
<p>
This tutorial shows you how to write an image classification example with OpenCV.js.<br>
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
Then You should change the parameters in the first code snippet according to the uploaded model.
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
</p>
<div class="control"><button id="tryIt" disabled>Try it</button></div>
<div>
<table cellpadding="0" cellspacing="0" width="0" border="0">
<tr>
<td>
<canvas id="canvasInput" width="400" height="400"></canvas>
</td>
<td>
<table style="visibility: hidden;" id="result">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col" width=300>Label</th>
<th scope="col">Probability</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td id="label0" align="center"></td>
<td id="prob0" align="center"></td>
</tr>
<tr>
<th scope="row">2</th>
<td id="label1" align="center"></td>
<td id="prob1" align="center"></td>
</tr>
<tr>
<th scope="row">3</th>
<td id="label2" align="center"></td>
<td id="prob2" align="center"></td>
</tr>
</tbody>
</table>
<p id='status' align="left"></p>
</td>
</tr>
<tr>
<td>
<div class="caption">
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
</div>
</td>
<td></td>
</tr>
<tr>
<td>
<div class="caption">
modelFile <input type="file" id="modelFile">
</div>
</td>
</tr>
<tr>
<td>
<div class="caption">
configFile <input type="file" id="configFile">
</div>
</td>
</tr>
</table>
</div>
<div>
<p class="err" id="errorMessage"></p>
</div>
<div>
<h3>Help function</h3>
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
<textarea class="code" rows="13" cols="100" id="codeEditor" spellcheck="false"></textarea>
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor1" spellcheck="false"></textarea>
<p>3.Load labels from txt file and process it into an array.</p>
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
<p>6.The post-processing, including softmax if needed and get the top classes from the output vector.</p>
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
</div>
<div id="appendix">
<h2>Model Info:</h2>
</div>
<script src="utils_webnn_electron.js" type="text/javascript"></script>
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
<script id="codeSnippet" type="text/code-snippet">
inputSize = [224,224];
mean = [104, 117, 123];
std = 1;
swapRB = false;
// record if need softmax function for post-processing
needSoftmax = false;
// url for label file, can from local or Internet
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/5.x/samples/data/dnn/classification_classes_ILSVRC2012.txt";
</script>
<script id="codeSnippet1" type="text/code-snippet">
main = async function() {
const labels = await loadLables(labelsUrl);
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
let net = cv.readNet(configPath, modelPath);
net.setPreferableBackend(6);
net.setInput(input);
let result = net.forward();
const start = performance.now();
for (i=0;i<200;i++)
{
result = net.forward();
}
const time = performance.now()-start;
const probs = softmax(result);
const classes = getTopClasses(probs, labels);
updateResult(classes, time/200);
input.delete();
net.delete();
result.delete();
}
</script>
<script id="codeSnippet5" type="text/code-snippet">
softmax = function(result) {
let arr = result.data32F;
if (needSoftmax) {
const maxNum = Math.max(...arr);
const expSum = arr.map((num) => Math.exp(num - maxNum)).reduce((a, b) => a + b);
return arr.map((value, index) => {
return Math.exp(value - maxNum) / expSum;
});
} else {
return arr;
}
}
</script>
<script type="text/javascript">
let jsonUrl = "js_image_classification_model_info.json";
drawInfoTable(jsonUrl, 'appendix');
let utils = new Utils('errorMessage');
utils.loadCode('codeSnippet', 'codeEditor');
utils.loadCode('codeSnippet1', 'codeEditor1');
let loadLablesCode = 'loadLables = ' + loadLables.toString();
document.getElementById('codeEditor2').value = loadLablesCode;
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
document.getElementById('codeEditor3').value = getBlobFromImageCode;
let loadModelCode = 'loadModel = ' + loadModel.toString();
document.getElementById('codeEditor4').value = loadModelCode;
utils.loadCode('codeSnippet5', 'codeEditor5');
let getTopClassesCode = 'getTopClasses = ' + getTopClasses.toString();
document.getElementById('codeEditor5').value += '\n' + '\n' + getTopClassesCode;
let canvas = document.getElementById('canvasInput');
let ctx = canvas.getContext('2d');
let img = new Image();
img.crossOrigin = 'anonymous';
img.src = 'space_shuttle.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
let tryIt = document.getElementById('tryIt');
tryIt.addEventListener('click', () => {
initStatus();
document.getElementById('status').innerHTML = 'Running function main()...';
utils.executeCode('codeEditor');
utils.executeCode('codeEditor1');
if (modelPath === "") {
document.getElementById('status').innerHTML = 'Runing failed.';
utils.printError('Please upload model file by clicking the button first.');
} else {
setTimeout(main, 1);
}
});
let fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (e) => {
initStatus();
loadImageToCanvas(e, 'canvasInput');
});
let configPath = "";
let configFile = document.getElementById('configFile');
configFile.addEventListener('change', async (e) => {
initStatus();
configPath = await loadModel(e);
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
});
let modelPath = "";
let modelFile = document.getElementById('modelFile');
modelFile.addEventListener('change', async (e) => {
initStatus();
modelPath = await loadModel(e);
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
configPath = "";
configFile.value = "";
});
utils.loadOpenCv(() => {
tryIt.removeAttribute('disabled');
});
var main = async function() {};
var softmax = function(result){};
var getTopClasses = function(mat, labels, topK = 3){};
utils.executeCode('codeEditor1');
utils.executeCode('codeEditor2');
utils.executeCode('codeEditor3');
utils.executeCode('codeEditor4');
utils.executeCode('codeEditor5');
function updateResult(classes, time) {
try{
classes.forEach((c,i) => {
let labelElement = document.getElementById('label'+i);
let probElement = document.getElementById('prob'+i);
labelElement.innerHTML = c.label;
probElement.innerHTML = c.prob + '%';
});
let result = document.getElementById('result');
result.style.visibility = 'visible';
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
<b>Inference time:</b> ${time.toFixed(2)} ms`;
} catch(e) {
console.log(e);
}
}
function initStatus() {
document.getElementById('status').innerHTML = '';
document.getElementById('result').style.visibility = 'hidden';
utils.clearError();
}
</script>
</body>
</html>
@@ -0,0 +1,56 @@
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow = {}
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1220,
height: 840,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: app.getAppPath()+"/node_setup.js"
}
})
// Load the index.html with 'numRunsParm' to run inference multiple times.
let url = `file://${__dirname}/js_image_classification_webnn_electron.html`
const numRunsParm = '?' + process.argv[2]
mainWindow.loadURL(url + numRunsParm)
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') app.quit()
})
app.on(
'activate',
function() {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) createWindow()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
@@ -0,0 +1,12 @@
const cv = require('./opencv');
const webnn = require(process.env.WEBNN_NATIVE_DIR+'/../../node/lib/webnn');
// navigator is undefined in node.js, but defined in electron.js.
if (global.navigator === undefined) {
global.navigator = {};
}
global.navigator.ml = webnn.ml;
global.MLContext = webnn.MLContext
global.MLGraphBuilder = webnn.MLGraphBuilder
global.MLGraph = webnn.MLGraph
global.MLOperand = webnn.MLOperand
global.cv = cv;
@@ -0,0 +1,14 @@
{
"name": "image_classification",
"version": "0.0.1",
"description": "An Electon.js example of image_classification using webnn-native",
"main": "main.js",
"author": "WebNN-native Authors",
"license": "Apache-2.0",
"scripts": {
"start": "electron ."
},
"dependencies": {
"electron": "^15.1.2"
}
}
@@ -0,0 +1,159 @@
function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
let self = this;
this.errorOutput = document.getElementById(errorOutputId);
const OPENCV_URL = 'opencv.js';
this.loadOpenCv = async function(onloadCallback) {
if (cv.getBuildInformation)
{
console.log(cv.getBuildInformation());
onloadCallback();
}
else
{
// WASM
if (cv instanceof Promise) {
cv = await cv;
console.log(cv.getBuildInformation());
onloadCallback();
} else {
cv['onRuntimeInitialized']=()=>{
console.log(cv.getBuildInformation());
onloadCallback();
}
}
}
};
this.createFileFromUrl = function(path, url, callback) {
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(ev) {
if (request.readyState === 4) {
if (request.status === 200) {
let data = new Uint8Array(request.response);
cv.FS_createDataFile('/', path, data, true, false, false);
callback();
} else {
self.printError('Failed to load ' + url + ' status: ' + request.status);
}
}
};
request.send();
};
this.loadImageToCanvas = function(url, cavansId) {
let canvas = document.getElementById(cavansId);
let ctx = canvas.getContext('2d');
let img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
};
img.src = url;
};
this.executeCode = function(textAreaId) {
try {
this.clearError();
let code = document.getElementById(textAreaId).value;
eval(code);
} catch (err) {
this.printError(err);
}
};
this.clearError = function() {
this.errorOutput.innerHTML = '';
};
this.printError = function(err) {
if (typeof err === 'undefined') {
err = '';
} else if (typeof err === 'number') {
if (!isNaN(err)) {
if (typeof cv !== 'undefined') {
err = 'Exception: ' + cv.exceptionFromPtr(err).msg;
}
}
} else if (typeof err === 'string') {
let ptr = Number(err.split(' ')[0]);
if (!isNaN(ptr)) {
if (typeof cv !== 'undefined') {
err = 'Exception: ' + cv.exceptionFromPtr(ptr).msg;
}
}
} else if (err instanceof Error) {
err = err.stack.replace(/\n/g, '<br>');
}
this.errorOutput.innerHTML = err;
};
this.loadCode = function(scriptId, textAreaId) {
let scriptNode = document.getElementById(scriptId);
let textArea = document.getElementById(textAreaId);
if (scriptNode.type !== 'text/code-snippet') {
throw Error('Unknown code snippet type');
}
textArea.value = scriptNode.text.replace(/^\n/, '');
};
this.addFileInputHandler = function(fileInputId, canvasId) {
let inputElement = document.getElementById(fileInputId);
inputElement.addEventListener('change', (e) => {
let files = e.target.files;
if (files.length > 0) {
let imgUrl = URL.createObjectURL(files[0]);
self.loadImageToCanvas(imgUrl, canvasId);
}
}, false);
};
function onVideoCanPlay() {
if (self.onCameraStartedCallback) {
self.onCameraStartedCallback(self.stream, self.video);
}
};
this.startCamera = function(resolution, callback, videoId) {
const constraints = {
'qvga': {width: {exact: 320}, height: {exact: 240}},
'vga': {width: {exact: 640}, height: {exact: 480}}};
let video = document.getElementById(videoId);
if (!video) {
video = document.createElement('video');
}
let videoConstraint = constraints[resolution];
if (!videoConstraint) {
videoConstraint = true;
}
navigator.mediaDevices.getUserMedia({video: videoConstraint, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
self.video = video;
self.stream = stream;
self.onCameraStartedCallback = callback;
video.addEventListener('canplay', onVideoCanPlay, false);
})
.catch(function(err) {
self.printError('Camera Error: ' + err.name + ' ' + err.message);
});
};
this.stopCamera = function() {
if (this.video) {
this.video.pause();
this.video.srcObject = null;
this.video.removeEventListener('canplay', onVideoCanPlay);
}
if (this.stream) {
this.stream.getVideoTracks()[0].stop();
}
};
};
@@ -333,7 +333,7 @@ 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/master/data/haarcascades).
- 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:
![image](lena.jpg)
@@ -145,6 +145,12 @@ Building OpenCV.js from Source
python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules"
@endcode
-# [optional] To enable WebNN backend, append `--webnn` option.
For example:
@code{.bash}
emcmake python ./opencv/platforms/js/build_js.py build_js --webnn
@endcode
Running OpenCV.js Tests
---------------------------------------
@@ -4,7 +4,9 @@ Using OpenCV.js {#tutorial_js_usage}
Steps
-----
In this tutorial, you will learn how to include and start to use `opencv.js` inside a web page. You can get a copy of `opencv.js` from `opencv-{VERSION_NUMBER}-docs.zip` in each [release](https://github.com/opencv/opencv/releases), or simply download the prebuilt script from the online documentations at "https://docs.opencv.org/{VERSION_NUMBER}/opencv.js" (For example, [https://docs.opencv.org/3.4.0/opencv.js](https://docs.opencv.org/3.4.0/opencv.js). Use `master` if you want the latest build). You can also build your own copy by following the tutorial on Build Opencv.js.
In this tutorial, you will learn how to include and start to use `opencv.js` inside a web page.
You can get a copy of `opencv.js` from `opencv-{VERSION_NUMBER}-docs.zip` in each [release](https://github.com/opencv/opencv/releases), or simply download the prebuilt script from the online documentations at "https://docs.opencv.org/{VERSION_NUMBER}/opencv.js" (For example, [https://docs.opencv.org/5.0.0/opencv.js](https://docs.opencv.org/5.0.0/opencv.js). Use `5.x` if you want the latest build).
You can also build your own copy by following the tutorial @ref tutorial_js_setup.
### Create a web page
@@ -133,9 +133,9 @@ Dense Optical Flow in OpenCV.js
Lucas-Kanade method computes optical flow for a sparse feature set (in our example, corners detected
using Shi-Tomasi algorithm). OpenCV.js provides another algorithm to find the dense optical flow. It
computes the optical flow for all the points in the frame. It is based on Gunner Farneback's
computes the optical flow for all the points in the frame. It is based on Gunnar Farneback's
algorithm which is explained in "Two-Frame Motion Estimation Based on Polynomial Expansion" by
Gunner Farneback in 2003.
Gunnar Farneback in 2003.
We use the function: **cv.calcOpticalFlowFarneback (prev, next, flow, pyrScale, levels, winsize,
iterations, polyN, polySigma, flags)**