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:
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
+268
@@ -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:
|
||||
|
||||

|
||||
|
||||
@@ -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)**
|
||||
|
||||
Reference in New Issue
Block a user