mirror of https://github.com/opencv/opencv.git
commit
6fdb7aee84
51 changed files with 2878 additions and 110 deletions
@ -0,0 +1 @@ |
||||
set(OPENCV_SKIP_LINK_AS_NEEDED 1) |
@ -0,0 +1,119 @@ |
||||
getBlobFromImage = function(inputSize, mean, std, swapRB, image) { |
||||
let mat; |
||||
if (typeof(image) === 'string') { |
||||
mat = cv.imread(image); |
||||
} else { |
||||
mat = image; |
||||
} |
||||
|
||||
let matC3 = new cv.Mat(mat.matSize[0], mat.matSize[1], cv.CV_8UC3); |
||||
cv.cvtColor(mat, matC3, cv.COLOR_RGBA2BGR); |
||||
let input = cv.blobFromImage(matC3, std, new cv.Size(inputSize[0], inputSize[1]), |
||||
new cv.Scalar(mean[0], mean[1], mean[2]), swapRB); |
||||
|
||||
matC3.delete(); |
||||
return input; |
||||
} |
||||
|
||||
loadLables = async function(labelsUrl) { |
||||
let response = await fetch(labelsUrl); |
||||
let label = await response.text(); |
||||
label = label.split('\n'); |
||||
return label; |
||||
} |
||||
|
||||
loadModel = async function(e) { |
||||
return new Promise((resolve) => { |
||||
let file = e.target.files[0]; |
||||
let path = file.name; |
||||
let reader = new FileReader(); |
||||
reader.readAsArrayBuffer(file); |
||||
reader.onload = function(ev) { |
||||
if (reader.readyState === 2) { |
||||
let buffer = reader.result; |
||||
let data = new Uint8Array(buffer); |
||||
cv.FS_createDataFile('/', path, data, true, false, false); |
||||
resolve(path); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
getTopClasses = function(probs, labels, topK = 3) { |
||||
probs = Array.from(probs); |
||||
let indexes = probs.map((prob, index) => [prob, index]); |
||||
let sorted = indexes.sort((a, b) => { |
||||
if (a[0] === b[0]) {return 0;} |
||||
return a[0] < b[0] ? -1 : 1; |
||||
}); |
||||
sorted.reverse(); |
||||
let classes = []; |
||||
for (let i = 0; i < topK; ++i) { |
||||
let prob = sorted[i][0]; |
||||
let index = sorted[i][1]; |
||||
let c = { |
||||
label: labels[index], |
||||
prob: (prob * 100).toFixed(2) |
||||
} |
||||
classes.push(c); |
||||
} |
||||
return classes; |
||||
} |
||||
|
||||
loadImageToCanvas = function(e, canvasId) { |
||||
let files = e.target.files; |
||||
let imgUrl = URL.createObjectURL(files[0]); |
||||
let canvas = document.getElementById(canvasId); |
||||
let ctx = canvas.getContext('2d'); |
||||
let img = new Image(); |
||||
img.crossOrigin = 'anonymous'; |
||||
img.src = imgUrl; |
||||
img.onload = function() { |
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); |
||||
}; |
||||
} |
||||
|
||||
drawInfoTable = async function(jsonUrl, divId) { |
||||
let response = await fetch(jsonUrl); |
||||
let json = await response.json(); |
||||
|
||||
let appendix = document.getElementById(divId); |
||||
for (key of Object.keys(json)) { |
||||
let h3 = document.createElement('h3'); |
||||
h3.textContent = key + " model"; |
||||
appendix.appendChild(h3); |
||||
|
||||
let table = document.createElement('table'); |
||||
let head_tr = document.createElement('tr'); |
||||
for (head of Object.keys(json[key][0])) { |
||||
let th = document.createElement('th'); |
||||
th.textContent = head; |
||||
th.style.border = "1px solid black"; |
||||
head_tr.appendChild(th); |
||||
} |
||||
table.appendChild(head_tr) |
||||
|
||||
for (model of json[key]) { |
||||
let tr = document.createElement('tr'); |
||||
for (params of Object.keys(model)) { |
||||
let td = document.createElement('td'); |
||||
td.style.border = "1px solid black"; |
||||
if (params !== "modelUrl" && params !== "configUrl" && params !== "labelsUrl") { |
||||
td.textContent = model[params]; |
||||
tr.appendChild(td); |
||||
} else { |
||||
let a = document.createElement('a'); |
||||
let link = document.createTextNode('link'); |
||||
a.append(link); |
||||
a.href = model[params]; |
||||
td.appendChild(a); |
||||
tr.appendChild(td); |
||||
} |
||||
} |
||||
table.appendChild(tr); |
||||
} |
||||
table.style.width = "800px"; |
||||
table.style.borderCollapse = "collapse"; |
||||
appendix.appendChild(table); |
||||
} |
||||
} |
@ -0,0 +1,263 @@ |
||||
<!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.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/master/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.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const probs = softmax(result); |
||||
const classes = getTopClasses(probs, labels); |
||||
|
||||
updateResult(classes, time); |
||||
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,65 @@ |
||||
{ |
||||
"caffe": [ |
||||
{ |
||||
"model": "alexnet", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"needSoftmax": "false", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
}, |
||||
{ |
||||
"model": "densenet", |
||||
"mean": "127.5, 127.5, 127.5", |
||||
"std": "0.007843", |
||||
"swapRB": "false", |
||||
"needSoftmax": "true", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
}, |
||||
{ |
||||
"model": "googlenet", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"needSoftmax": "false", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
}, |
||||
{ |
||||
"model": "squeezenet", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"needSoftmax": "false", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
}, |
||||
{ |
||||
"model": "VGG", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"needSoftmax": "false", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
} |
||||
], |
||||
"tensorflow": [ |
||||
{ |
||||
"model": "inception", |
||||
"mean": "123, 117, 104", |
||||
"std": "1", |
||||
"swapRB": "true", |
||||
"needSoftmax": "false", |
||||
"labelsUrl": "https://raw.githubusercontent.com/petewarden/tf_ios_makefile_example/master/data/imagenet_comp_graph_label_strings.txt", |
||||
"modelUrl": "https://raw.githubusercontent.com/petewarden/tf_ios_makefile_example/master/data/tensorflow_inception_graph.pb" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,281 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Image Classification Example with Camera</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Image Classification Example with Camera</h2> |
||||
<p> |
||||
This tutorial shows you how to write an image classification example with camera.<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>Start/Stop</b> button to start or stop the camera capture.<br> |
||||
</p> |
||||
|
||||
<div class="control"><button id="startAndStop" disabled>Start</button></div> |
||||
<div> |
||||
<table cellpadding="0" cellspacing="0" width="0" border="0"> |
||||
<tr> |
||||
<td> |
||||
<video id="videoInput" width="400" height="400"></video> |
||||
</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"> |
||||
videoInput |
||||
</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.The function to capture video from camera, and the main loop in which will do inference once.</p> |
||||
<textarea class="code" rows="35" 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/master/samples/data/dnn/classification_classes_ILSVRC2012.txt"; |
||||
</script> |
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet"> |
||||
let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4); |
||||
let cap = new cv.VideoCapture(video); |
||||
|
||||
main = async function(frame) { |
||||
const labels = await loadLables(labelsUrl); |
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, frame); |
||||
let net = cv.readNet(configPath, modelPath); |
||||
net.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const probs = softmax(result); |
||||
const classes = getTopClasses(probs, labels); |
||||
|
||||
updateResult(classes, time); |
||||
setTimeout(processVideo, 0); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
|
||||
function processVideo() { |
||||
try { |
||||
if (!streaming) { |
||||
return; |
||||
} |
||||
cap.read(frame); |
||||
main(frame); |
||||
} catch (err) { |
||||
utils.printError(err); |
||||
} |
||||
} |
||||
|
||||
setTimeout(processVideo, 0); |
||||
</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 video = document.getElementById('videoInput'); |
||||
let streaming = false; |
||||
let startAndStop = document.getElementById('startAndStop'); |
||||
startAndStop.addEventListener('click', () => { |
||||
if (!streaming) { |
||||
utils.clearError(); |
||||
utils.startCamera('qvga', onVideoStarted, 'videoInput'); |
||||
} else { |
||||
utils.stopCamera(); |
||||
onVideoStopped(); |
||||
} |
||||
}); |
||||
|
||||
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(() => { |
||||
startAndStop.removeAttribute('disabled'); |
||||
|
||||
}); |
||||
|
||||
var main = async function(frame) {}; |
||||
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 onVideoStarted() { |
||||
streaming = true; |
||||
startAndStop.innerText = 'Stop'; |
||||
videoInput.width = videoInput.videoWidth; |
||||
videoInput.height = videoInput.videoHeight; |
||||
utils.executeCode('codeEditor'); |
||||
utils.executeCode('codeEditor1'); |
||||
} |
||||
|
||||
function onVideoStopped() { |
||||
streaming = false; |
||||
startAndStop.innerText = 'Start'; |
||||
initStatus(); |
||||
} |
||||
|
||||
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,387 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Object Detection Example</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Object Detection Example</h2> |
||||
<p> |
||||
This tutorial shows you how to write an object detection 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> |
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*"> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<p id='status' align="left"></p> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
modelFile <input type="file" id="modelFile" name="file"> |
||||
</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="15" 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="16" 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 get boxes from output and draw boxes into the image.</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 = [300, 300]; |
||||
mean = [127.5, 127.5, 127.5]; |
||||
std = 0.007843; |
||||
swapRB = false; |
||||
confThreshold = 0.5; |
||||
nmsThreshold = 0.4; |
||||
|
||||
// The type of output, can be YOLO or SSD |
||||
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"; |
||||
</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.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const output = postProcess(result, labels); |
||||
|
||||
updateResult(output, time); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
</script> |
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet"> |
||||
postProcess = function(result, labels) { |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
const outputWidth = canvasOutput.width; |
||||
const outputHeight = canvasOutput.height; |
||||
const resultData = result.data32F; |
||||
|
||||
// Get the boxes(with class and confidence) from the output |
||||
let boxes = []; |
||||
switch(outType) { |
||||
case "YOLO": { |
||||
const vecNum = result.matSize[0]; |
||||
const vecLength = result.matSize[1]; |
||||
const classNum = vecLength - 5; |
||||
|
||||
for (let i = 0; i < vecNum; ++i) { |
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength); |
||||
let scores = vector.slice(5, vecLength); |
||||
let classId = scores.indexOf(Math.max(...scores)); |
||||
let confidence = scores[classId]; |
||||
if (confidence > confThreshold) { |
||||
let center_x = Math.round(vector[0] * outputWidth); |
||||
let center_y = Math.round(vector[1] * outputHeight); |
||||
let width = Math.round(vector[2] * outputWidth); |
||||
let height = Math.round(vector[3] * outputHeight); |
||||
let left = Math.round(center_x - width / 2); |
||||
let top = Math.round(center_y - height / 2); |
||||
|
||||
let box = { |
||||
scores: scores, |
||||
classId: classId, |
||||
confidence: confidence, |
||||
bounding: [left, top, width, height], |
||||
toDraw: true |
||||
} |
||||
boxes.push(box); |
||||
} |
||||
} |
||||
|
||||
// NMS(Non Maximum Suppression) algorithm |
||||
let boxNum = boxes.length; |
||||
let tmp_boxes = []; |
||||
let sorted_boxes = []; |
||||
for (let c = 0; c < classNum; ++c) { |
||||
for (let i = 0; i < boxes.length; ++i) { |
||||
tmp_boxes[i] = [boxes[i], i]; |
||||
} |
||||
sorted_boxes = tmp_boxes.sort((a, b) => { return (b[0].scores[c] - a[0].scores[c]); }); |
||||
for (let i = 0; i < boxNum; ++i) { |
||||
if (sorted_boxes[i][0].scores[c] === 0) continue; |
||||
else { |
||||
for (let j = i + 1; j < boxNum; ++j) { |
||||
if (IOU(sorted_boxes[i][0], sorted_boxes[j][0]) >= nmsThreshold) { |
||||
boxes[sorted_boxes[j][1]].toDraw = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} break; |
||||
case "SSD": { |
||||
const vecNum = result.matSize[2]; |
||||
const vecLength = 7; |
||||
|
||||
for (let i = 0; i < vecNum; ++i) { |
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength); |
||||
let confidence = vector[2]; |
||||
if (confidence > confThreshold) { |
||||
let left, top, right, bottom, width, height; |
||||
left = Math.round(vector[3]); |
||||
top = Math.round(vector[4]); |
||||
right = Math.round(vector[5]); |
||||
bottom = Math.round(vector[6]); |
||||
width = right - left + 1; |
||||
height = bottom - top + 1; |
||||
if (width <= 2 || height <= 2) { |
||||
left = Math.round(vector[3] * outputWidth); |
||||
top = Math.round(vector[4] * outputHeight); |
||||
right = Math.round(vector[5] * outputWidth); |
||||
bottom = Math.round(vector[6] * outputHeight); |
||||
width = right - left + 1; |
||||
height = bottom - top + 1; |
||||
} |
||||
let box = { |
||||
classId: vector[1] - 1, |
||||
confidence: confidence, |
||||
bounding: [left, top, width, height], |
||||
toDraw: true |
||||
} |
||||
boxes.push(box); |
||||
} |
||||
} |
||||
} break; |
||||
default: |
||||
console.error(`Unsupported output type ${outType}`) |
||||
} |
||||
|
||||
// Draw the saved box into the image |
||||
let image = cv.imread("canvasInput"); |
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3); |
||||
cv.cvtColor(image, output, cv.COLOR_RGBA2RGB); |
||||
let boxNum = boxes.length; |
||||
for (let i = 0; i < boxNum; ++i) { |
||||
if (boxes[i].toDraw) { |
||||
drawBox(boxes[i]); |
||||
} |
||||
} |
||||
|
||||
return output; |
||||
|
||||
|
||||
// Calculate the IOU(Intersection over Union) of two boxes |
||||
function IOU(box1, box2) { |
||||
let bounding1 = box1.bounding; |
||||
let bounding2 = box2.bounding; |
||||
let s1 = bounding1[2] * bounding1[3]; |
||||
let s2 = bounding2[2] * bounding2[3]; |
||||
|
||||
let left1 = bounding1[0]; |
||||
let right1 = left1 + bounding1[2]; |
||||
let left2 = bounding2[0]; |
||||
let right2 = left2 + bounding2[2]; |
||||
let overlapW = calOverlap([left1, right1], [left2, right2]); |
||||
|
||||
let top1 = bounding2[1]; |
||||
let bottom1 = top1 + bounding1[3]; |
||||
let top2 = bounding2[1]; |
||||
let bottom2 = top2 + bounding2[3]; |
||||
let overlapH = calOverlap([top1, bottom1], [top2, bottom2]); |
||||
|
||||
let overlapS = overlapW * overlapH; |
||||
return overlapS / (s1 + s2 + overlapS); |
||||
} |
||||
|
||||
// Calculate the overlap range of two vector |
||||
function calOverlap(range1, range2) { |
||||
let min1 = range1[0]; |
||||
let max1 = range1[1]; |
||||
let min2 = range2[0]; |
||||
let max2 = range2[1]; |
||||
|
||||
if (min2 > min1 && min2 < max1) { |
||||
return max1 - min2; |
||||
} else if (max2 > min1 && max2 < max1) { |
||||
return max2 - min1; |
||||
} else { |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
// Draw one predict box into the origin image |
||||
function drawBox(box) { |
||||
let bounding = box.bounding; |
||||
let left = bounding[0]; |
||||
let top = bounding[1]; |
||||
let width = bounding[2]; |
||||
let height = bounding[3]; |
||||
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + height), |
||||
new cv.Scalar(0, 255, 0)); |
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + 15), |
||||
new cv.Scalar(255, 255, 255), cv.FILLED); |
||||
let text = `${labels[box.classId]}: ${box.confidence.toFixed(4)}`; |
||||
cv.putText(output, text, new cv.Point(left, top + 10), cv.FONT_HERSHEY_SIMPLEX, 0.3, |
||||
new cv.Scalar(0, 0, 0)); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<script type="text/javascript"> |
||||
let jsonUrl = "js_object_detection_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 canvas = document.getElementById('canvasInput'); |
||||
let ctx = canvas.getContext('2d'); |
||||
let img = new Image(); |
||||
img.crossOrigin = 'anonymous'; |
||||
img.src = 'lena.png'; |
||||
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 postProcess = function(result, labels) {}; |
||||
|
||||
utils.executeCode('codeEditor1'); |
||||
utils.executeCode('codeEditor2'); |
||||
utils.executeCode('codeEditor3'); |
||||
utils.executeCode('codeEditor4'); |
||||
utils.executeCode('codeEditor5'); |
||||
|
||||
|
||||
function updateResult(output, time) { |
||||
try{ |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
canvasOutput.style.visibility = "visible"; |
||||
cv.imshow('canvasOutput', output); |
||||
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('canvasOutput').style.visibility = "hidden"; |
||||
utils.clearError(); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,39 @@ |
||||
{ |
||||
"caffe": [ |
||||
{ |
||||
"model": "mobilenet_SSD", |
||||
"inputSize": "300, 300", |
||||
"mean": "127.5, 127.5, 127.5", |
||||
"std": "0.007843", |
||||
"swapRB": "false", |
||||
"outType": "SSD", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
}, |
||||
{ |
||||
"model": "VGG_SSD", |
||||
"inputSize": "300, 300", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"outType": "SSD", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
} |
||||
], |
||||
"darknet": [ |
||||
{ |
||||
"model": "yolov2_tiny", |
||||
"inputSize": "416, 416", |
||||
"mean": "0, 0, 0", |
||||
"std": "0.00392", |
||||
"swapRB": "false", |
||||
"outType": "YOLO", |
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/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" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,402 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Object Detection Example with Camera</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Object Detection Example with Camera </h2> |
||||
<p> |
||||
This tutorial shows you how to write an object detection example with camera.<br> |
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</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>Start/Stop</b> button to start or stop the camera capture.<br> |
||||
</p> |
||||
|
||||
<div class="control"><button id="startAndStop" disabled>Start</button></div> |
||||
<div> |
||||
<table cellpadding="0" cellspacing="0" width="0" border="0"> |
||||
<tr> |
||||
<td> |
||||
<video id="videoInput" width="400" height="400"></video> |
||||
</td> |
||||
<td> |
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
videoInput |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<p id='status' align="left"></p> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
modelFile <input type="file" id="modelFile" name="file"> |
||||
</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="15" cols="100" id="codeEditor" spellcheck="false"></textarea> |
||||
<p>2.The function to capture video from camera, and the main loop in which will do inference once.</p> |
||||
<textarea class="code" rows="34" 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 get boxes from output and draw boxes into the image.</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 = [300, 300]; |
||||
mean = [127.5, 127.5, 127.5]; |
||||
std = 0.007843; |
||||
swapRB = false; |
||||
confThreshold = 0.5; |
||||
nmsThreshold = 0.4; |
||||
|
||||
// the type of output, can be YOLO or SSD |
||||
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"; |
||||
</script> |
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet"> |
||||
let frame = new cv.Mat(videoInput.height, videoInput.width, cv.CV_8UC4); |
||||
let cap = new cv.VideoCapture(videoInput); |
||||
|
||||
main = async function(frame) { |
||||
const labels = await loadLables(labelsUrl); |
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, frame); |
||||
let net = cv.readNet(configPath, modelPath); |
||||
net.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const output = postProcess(result, labels, frame); |
||||
|
||||
updateResult(output, time); |
||||
setTimeout(processVideo, 0); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
|
||||
function processVideo() { |
||||
try { |
||||
if (!streaming) { |
||||
return; |
||||
} |
||||
cap.read(frame); |
||||
main(frame); |
||||
} catch (err) { |
||||
utils.printError(err); |
||||
} |
||||
} |
||||
|
||||
setTimeout(processVideo, 0); |
||||
</script> |
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet"> |
||||
postProcess = function(result, labels, frame) { |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
const outputWidth = canvasOutput.width; |
||||
const outputHeight = canvasOutput.height; |
||||
const resultData = result.data32F; |
||||
|
||||
// Get the boxes(with class and confidence) from the output |
||||
let boxes = []; |
||||
switch(outType) { |
||||
case "YOLO": { |
||||
const vecNum = result.matSize[0]; |
||||
const vecLength = result.matSize[1]; |
||||
const classNum = vecLength - 5; |
||||
|
||||
for (let i = 0; i < vecNum; ++i) { |
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength); |
||||
let scores = vector.slice(5, vecLength); |
||||
let classId = scores.indexOf(Math.max(...scores)); |
||||
let confidence = scores[classId]; |
||||
if (confidence > confThreshold) { |
||||
let center_x = Math.round(vector[0] * outputWidth); |
||||
let center_y = Math.round(vector[1] * outputHeight); |
||||
let width = Math.round(vector[2] * outputWidth); |
||||
let height = Math.round(vector[3] * outputHeight); |
||||
let left = Math.round(center_x - width / 2); |
||||
let top = Math.round(center_y - height / 2); |
||||
|
||||
let box = { |
||||
scores: scores, |
||||
classId: classId, |
||||
confidence: confidence, |
||||
bounding: [left, top, width, height], |
||||
toDraw: true |
||||
} |
||||
boxes.push(box); |
||||
} |
||||
} |
||||
|
||||
// NMS(Non Maximum Suppression) algorithm |
||||
let boxNum = boxes.length; |
||||
let tmp_boxes = []; |
||||
let sorted_boxes = []; |
||||
for (let c = 0; c < classNum; ++c) { |
||||
for (let i = 0; i < boxes.length; ++i) { |
||||
tmp_boxes[i] = [boxes[i], i]; |
||||
} |
||||
sorted_boxes = tmp_boxes.sort((a, b) => { return (b[0].scores[c] - a[0].scores[c]); }); |
||||
for (let i = 0; i < boxNum; ++i) { |
||||
if (sorted_boxes[i][0].scores[c] === 0) continue; |
||||
else { |
||||
for (let j = i + 1; j < boxNum; ++j) { |
||||
if (IOU(sorted_boxes[i][0], sorted_boxes[j][0]) >= nmsThreshold) { |
||||
boxes[sorted_boxes[j][1]].toDraw = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} break; |
||||
case "SSD": { |
||||
const vecNum = result.matSize[2]; |
||||
const vecLength = 7; |
||||
|
||||
for (let i = 0; i < vecNum; ++i) { |
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength); |
||||
let confidence = vector[2]; |
||||
if (confidence > confThreshold) { |
||||
let left, top, right, bottom, width, height; |
||||
left = Math.round(vector[3]); |
||||
top = Math.round(vector[4]); |
||||
right = Math.round(vector[5]); |
||||
bottom = Math.round(vector[6]); |
||||
width = right - left + 1; |
||||
height = bottom - top + 1; |
||||
if (width <= 2 || height <= 2) { |
||||
left = Math.round(vector[3] * outputWidth); |
||||
top = Math.round(vector[4] * outputHeight); |
||||
right = Math.round(vector[5] * outputWidth); |
||||
bottom = Math.round(vector[6] * outputHeight); |
||||
width = right - left + 1; |
||||
height = bottom - top + 1; |
||||
} |
||||
let box = { |
||||
classId: vector[1] - 1, |
||||
confidence: confidence, |
||||
bounding: [left, top, width, height], |
||||
toDraw: true |
||||
} |
||||
boxes.push(box); |
||||
} |
||||
} |
||||
} break; |
||||
default: |
||||
console.error(`Unsupported output type ${outType}`) |
||||
} |
||||
|
||||
// Draw the saved box into the image |
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3); |
||||
cv.cvtColor(frame, output, cv.COLOR_RGBA2RGB); |
||||
let boxNum = boxes.length; |
||||
for (let i = 0; i < boxNum; ++i) { |
||||
if (boxes[i].toDraw) { |
||||
drawBox(boxes[i]); |
||||
} |
||||
} |
||||
|
||||
return output; |
||||
|
||||
|
||||
// Calculate the IOU(Intersection over Union) of two boxes |
||||
function IOU(box1, box2) { |
||||
let bounding1 = box1.bounding; |
||||
let bounding2 = box2.bounding; |
||||
let s1 = bounding1[2] * bounding1[3]; |
||||
let s2 = bounding2[2] * bounding2[3]; |
||||
|
||||
let left1 = bounding1[0]; |
||||
let right1 = left1 + bounding1[2]; |
||||
let left2 = bounding2[0]; |
||||
let right2 = left2 + bounding2[2]; |
||||
let overlapW = calOverlap([left1, right1], [left2, right2]); |
||||
|
||||
let top1 = bounding2[1]; |
||||
let bottom1 = top1 + bounding1[3]; |
||||
let top2 = bounding2[1]; |
||||
let bottom2 = top2 + bounding2[3]; |
||||
let overlapH = calOverlap([top1, bottom1], [top2, bottom2]); |
||||
|
||||
let overlapS = overlapW * overlapH; |
||||
return overlapS / (s1 + s2 + overlapS); |
||||
} |
||||
|
||||
// Calculate the overlap range of two vector |
||||
function calOverlap(range1, range2) { |
||||
let min1 = range1[0]; |
||||
let max1 = range1[1]; |
||||
let min2 = range2[0]; |
||||
let max2 = range2[1]; |
||||
|
||||
if (min2 > min1 && min2 < max1) { |
||||
return max1 - min2; |
||||
} else if (max2 > min1 && max2 < max1) { |
||||
return max2 - min1; |
||||
} else { |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
// Draw one predict box into the origin image |
||||
function drawBox(box) { |
||||
let bounding = box.bounding; |
||||
let left = bounding[0]; |
||||
let top = bounding[1]; |
||||
let width = bounding[2]; |
||||
let height = bounding[3]; |
||||
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + height), |
||||
new cv.Scalar(0, 255, 0)); |
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + 15), |
||||
new cv.Scalar(255, 255, 255), cv.FILLED); |
||||
let text = `${labels[box.classId]}: ${box.confidence.toFixed(4)}`; |
||||
cv.putText(output, text, new cv.Point(left, top + 10), cv.FONT_HERSHEY_SIMPLEX, 0.3, |
||||
new cv.Scalar(0, 0, 0)); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<script type="text/javascript"> |
||||
let jsonUrl = "js_object_detection_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 videoInput = document.getElementById('videoInput'); |
||||
let streaming = false; |
||||
let startAndStop = document.getElementById('startAndStop'); |
||||
startAndStop.addEventListener('click', () => { |
||||
if (!streaming) { |
||||
utils.clearError(); |
||||
utils.startCamera('qvga', onVideoStarted, 'videoInput'); |
||||
} else { |
||||
utils.stopCamera(); |
||||
onVideoStopped(); |
||||
} |
||||
}); |
||||
|
||||
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(() => { |
||||
startAndStop.removeAttribute('disabled'); |
||||
}); |
||||
|
||||
var main = async function(frame) {}; |
||||
var postProcess = function(result, labels, frame) {}; |
||||
|
||||
utils.executeCode('codeEditor1'); |
||||
utils.executeCode('codeEditor2'); |
||||
utils.executeCode('codeEditor3'); |
||||
utils.executeCode('codeEditor4'); |
||||
utils.executeCode('codeEditor5'); |
||||
|
||||
function onVideoStarted() { |
||||
streaming = true; |
||||
startAndStop.innerText = 'Stop'; |
||||
videoInput.width = videoInput.videoWidth; |
||||
videoInput.height = videoInput.videoHeight; |
||||
utils.executeCode('codeEditor'); |
||||
utils.executeCode('codeEditor1'); |
||||
} |
||||
|
||||
function onVideoStopped() { |
||||
streaming = false; |
||||
startAndStop.innerText = 'Start'; |
||||
initStatus(); |
||||
} |
||||
|
||||
function updateResult(output, time) { |
||||
try{ |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
canvasOutput.style.visibility = "visible"; |
||||
cv.imshow('canvasOutput', output); |
||||
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('canvasOutput').style.visibility = "hidden"; |
||||
utils.clearError(); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,327 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Pose Estimation Example</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Pose Estimation Example</h2> |
||||
<p> |
||||
This tutorial shows you how to write an pose estimation example with OpenCV.js.<br> |
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</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="250"></canvas> |
||||
</td> |
||||
<td> |
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="250"></canvas> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*"> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<p id='status' align="left"></p> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
modelFile <input type="file" id="modelFile" name="file"> |
||||
</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="9" 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="15" cols="100" id="codeEditor1" spellcheck="false"></textarea> |
||||
<p>3.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="codeEditor2" spellcheck="false"></textarea> |
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p> |
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea> |
||||
<p>5.The pairs of keypoints of different dataset.</p> |
||||
<textarea class="code" rows="30" cols="100" id="codeEditor4" spellcheck="false"></textarea> |
||||
<p>6.The post-processing, including get the predicted points and draw lines into the image.</p> |
||||
<textarea class="code" rows="30" 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 = [368, 368]; |
||||
mean = [0, 0, 0]; |
||||
std = 0.00392; |
||||
swapRB = false; |
||||
threshold = 0.1; |
||||
|
||||
// the pairs of keypoint, can be "COCO", "MPI" and "BODY_25" |
||||
dataset = "COCO"; |
||||
</script> |
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet"> |
||||
main = async function() { |
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput'); |
||||
let net = cv.readNet(configPath, modelPath); |
||||
net.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const output = postProcess(result); |
||||
|
||||
updateResult(output, time); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
</script> |
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet"> |
||||
BODY_PARTS = {}; |
||||
POSE_PAIRS = []; |
||||
|
||||
if (dataset === 'COCO') { |
||||
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4, |
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9, |
||||
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14, |
||||
"LEye": 15, "REar": 16, "LEar": 17, "Background": 18 }; |
||||
|
||||
POSE_PAIRS = [ ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"], |
||||
["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"], |
||||
["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"], |
||||
["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"], |
||||
["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"] ] |
||||
} else if (dataset === 'MPI') { |
||||
BODY_PARTS = { "Head": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4, |
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9, |
||||
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "Chest": 14, |
||||
"Background": 15 } |
||||
|
||||
POSE_PAIRS = [ ["Head", "Neck"], ["Neck", "RShoulder"], ["RShoulder", "RElbow"], |
||||
["RElbow", "RWrist"], ["Neck", "LShoulder"], ["LShoulder", "LElbow"], |
||||
["LElbow", "LWrist"], ["Neck", "Chest"], ["Chest", "RHip"], ["RHip", "RKnee"], |
||||
["RKnee", "RAnkle"], ["Chest", "LHip"], ["LHip", "LKnee"], ["LKnee", "LAnkle"] ] |
||||
} else if (dataset === 'BODY_25') { |
||||
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4, |
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "MidHip": 8, "RHip": 9, |
||||
"RKnee": 10, "RAnkle": 11, "LHip": 12, "LKnee": 13, "LAnkle": 14, |
||||
"REye": 15, "LEye": 16, "REar": 17, "LEar": 18, "LBigToe": 19, |
||||
"LSmallToe": 20, "LHeel": 21, "RBigToe": 22, "RSmallToe": 23, |
||||
"RHeel": 24, "Background": 25 } |
||||
|
||||
POSE_PAIRS = [ ["Neck", "Nose"], ["Neck", "RShoulder"], |
||||
["Neck", "LShoulder"], ["RShoulder", "RElbow"], |
||||
["RElbow", "RWrist"], ["LShoulder", "LElbow"], |
||||
["LElbow", "LWrist"], ["Nose", "REye"], |
||||
["REye", "REar"], ["Neck", "LEye"], |
||||
["LEye", "LEar"], ["Neck", "MidHip"], |
||||
["MidHip", "RHip"], ["RHip", "RKnee"], |
||||
["RKnee", "RAnkle"], ["RAnkle", "RBigToe"], |
||||
["RBigToe", "RSmallToe"], ["RAnkle", "RHeel"], |
||||
["MidHip", "LHip"], ["LHip", "LKnee"], |
||||
["LKnee", "LAnkle"], ["LAnkle", "LBigToe"], |
||||
["LBigToe", "LSmallToe"], ["LAnkle", "LHeel"] ] |
||||
} |
||||
</script> |
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet"> |
||||
postProcess = function(result) { |
||||
const resultData = result.data32F; |
||||
const matSize = result.matSize; |
||||
const size1 = matSize[1]; |
||||
const size2 = matSize[2]; |
||||
const size3 = matSize[3]; |
||||
const mapSize = size2 * size3; |
||||
|
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
const outputWidth = canvasOutput.width; |
||||
const outputHeight = canvasOutput.height; |
||||
|
||||
let image = cv.imread("canvasInput"); |
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3); |
||||
cv.cvtColor(image, output, cv.COLOR_RGBA2RGB); |
||||
|
||||
// get position of keypoints from output |
||||
let points = []; |
||||
for (let i = 0; i < Object.keys(BODY_PARTS).length; ++i) { |
||||
heatMap = resultData.slice(i*mapSize, (i+1)*mapSize); |
||||
|
||||
let maxIndex = 0; |
||||
let maxConf = heatMap[0]; |
||||
for (index in heatMap) { |
||||
if (heatMap[index] > heatMap[maxIndex]) { |
||||
maxIndex = index; |
||||
maxConf = heatMap[index]; |
||||
} |
||||
} |
||||
|
||||
if (maxConf > threshold) { |
||||
indexX = maxIndex % size3; |
||||
indexY = maxIndex / size3; |
||||
|
||||
x = outputWidth * indexX / size3; |
||||
y = outputHeight * indexY / size2; |
||||
|
||||
points[i] = [Math.round(x), Math.round(y)]; |
||||
} |
||||
} |
||||
|
||||
// draw the points and lines into the image |
||||
for (pair of POSE_PAIRS) { |
||||
partFrom = pair[0]; |
||||
partTo = pair[1]; |
||||
idFrom = BODY_PARTS[partFrom]; |
||||
idTo = BODY_PARTS[partTo]; |
||||
pointFrom = points[idFrom]; |
||||
pointTo = points[idTo]; |
||||
|
||||
if (points[idFrom] && points[idTo]) { |
||||
cv.line(output, new cv.Point(pointFrom[0], pointFrom[1]), |
||||
new cv.Point(pointTo[0], pointTo[1]), new cv.Scalar(0, 255, 0), 3); |
||||
cv.ellipse(output, new cv.Point(pointFrom[0], pointFrom[1]), new cv.Size(3, 3), 0, 0, 360, |
||||
new cv.Scalar(0, 0, 255), cv.FILLED); |
||||
cv.ellipse(output, new cv.Point(pointTo[0], pointTo[1]), new cv.Size(3, 3), 0, 0, 360, |
||||
new cv.Scalar(0, 0, 255), cv.FILLED); |
||||
} |
||||
} |
||||
|
||||
return output; |
||||
} |
||||
</script> |
||||
|
||||
<script type="text/javascript"> |
||||
let jsonUrl = "js_pose_estimation_model_info.json"; |
||||
drawInfoTable(jsonUrl, 'appendix'); |
||||
|
||||
let utils = new Utils('errorMessage'); |
||||
utils.loadCode('codeSnippet', 'codeEditor'); |
||||
utils.loadCode('codeSnippet1', 'codeEditor1'); |
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString(); |
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode; |
||||
let loadModelCode = 'loadModel = ' + loadModel.toString(); |
||||
document.getElementById('codeEditor3').value = loadModelCode; |
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4'); |
||||
utils.loadCode('codeSnippet5', 'codeEditor5'); |
||||
|
||||
let canvas = document.getElementById('canvasInput'); |
||||
let ctx = canvas.getContext('2d'); |
||||
let img = new Image(); |
||||
img.crossOrigin = 'anonymous'; |
||||
img.src = 'roi.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 postProcess = function(result) {}; |
||||
|
||||
utils.executeCode('codeEditor'); |
||||
utils.executeCode('codeEditor1'); |
||||
utils.executeCode('codeEditor2'); |
||||
utils.executeCode('codeEditor3'); |
||||
utils.executeCode('codeEditor4'); |
||||
utils.executeCode('codeEditor5'); |
||||
|
||||
function updateResult(output, time) { |
||||
try{ |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
canvasOutput.style.visibility = "visible"; |
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4); |
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height)); |
||||
cv.imshow('canvasOutput', resized); |
||||
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('canvasOutput').style.visibility = "hidden"; |
||||
utils.clearError(); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,34 @@ |
||||
{ |
||||
"caffe": [ |
||||
{ |
||||
"model": "body_25", |
||||
"inputSize": "368, 368", |
||||
"mean": "0, 0, 0", |
||||
"std": "0.00392", |
||||
"swapRB": "false", |
||||
"dataset": "BODY_25", |
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/body_25/pose_iter_584000.caffemodel", |
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/body_25/pose_deploy.prototxt" |
||||
}, |
||||
{ |
||||
"model": "coco", |
||||
"inputSize": "368, 368", |
||||
"mean": "0, 0, 0", |
||||
"std": "0.00392", |
||||
"swapRB": "false", |
||||
"dataset": "COCO", |
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/coco/pose_iter_440000.caffemodel", |
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/coco/pose_deploy_linevec.prototxt" |
||||
}, |
||||
{ |
||||
"model": "mpi", |
||||
"inputSize": "368, 368", |
||||
"mean": "0, 0, 0", |
||||
"std": "0.00392", |
||||
"swapRB": "false", |
||||
"dataset": "MPI", |
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/mpi/pose_iter_160000.caffemodel", |
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/mpi/pose_deploy_linevec.prototxt" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,243 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Semantic Segmentation Example</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Semantic Segmentation Example</h2> |
||||
<p> |
||||
This tutorial shows you how to write an semantic segmentation example with OpenCV.js.<br> |
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</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> |
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*"> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<p id='status' align="left"></p> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
modelFile <input type="file" id="modelFile" name="file"> |
||||
</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="5" 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="16" cols="100" id="codeEditor1" spellcheck="false"></textarea> |
||||
<p>3.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="codeEditor2" spellcheck="false"></textarea> |
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p> |
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea> |
||||
<p>5.The post-processing, including gengerate colors for different classes and argmax to get the classes for each pixel.</p> |
||||
<textarea class="code" rows="34" cols="100" id="codeEditor4" 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 = [513, 513]; |
||||
mean = [127.5, 127.5, 127.5]; |
||||
std = 0.007843; |
||||
swapRB = false; |
||||
</script> |
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet"> |
||||
main = async function() { |
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput'); |
||||
let net = cv.readNet(configPath, modelPath); |
||||
net.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const colors = generateColors(result); |
||||
const output = argmax(result, colors); |
||||
|
||||
updateResult(output, time); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
</script> |
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet"> |
||||
generateColors = function(result) { |
||||
const numClasses = result.matSize[1]; |
||||
let colors = [0,0,0]; |
||||
while(colors.length < numClasses*3){ |
||||
colors.push(Math.round((Math.random()*255 + colors[colors.length-3]) / 2)); |
||||
} |
||||
return colors; |
||||
} |
||||
|
||||
argmax = function(result, colors) { |
||||
const C = result.matSize[1]; |
||||
const H = result.matSize[2]; |
||||
const W = result.matSize[3]; |
||||
const resultData = result.data32F; |
||||
const imgSize = H*W; |
||||
|
||||
let classId = []; |
||||
for (i = 0; i<imgSize; ++i) { |
||||
let id = 0; |
||||
for (j = 0; j < C; ++j) { |
||||
if (resultData[j*imgSize+i] > resultData[id*imgSize+i]) { |
||||
id = j; |
||||
} |
||||
} |
||||
classId.push(colors[id*3]); |
||||
classId.push(colors[id*3+1]); |
||||
classId.push(colors[id*3+2]); |
||||
classId.push(255); |
||||
} |
||||
|
||||
output = cv.matFromArray(H,W,cv.CV_8UC4,classId); |
||||
return output; |
||||
} |
||||
</script> |
||||
|
||||
<script type="text/javascript"> |
||||
let jsonUrl = "js_semantic_segmentation_model_info.json"; |
||||
drawInfoTable(jsonUrl, 'appendix'); |
||||
|
||||
let utils = new Utils('errorMessage'); |
||||
utils.loadCode('codeSnippet', 'codeEditor'); |
||||
utils.loadCode('codeSnippet1', 'codeEditor1'); |
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString(); |
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode; |
||||
let loadModelCode = 'loadModel = ' + loadModel.toString(); |
||||
document.getElementById('codeEditor3').value = loadModelCode; |
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4'); |
||||
|
||||
let canvas = document.getElementById('canvasInput'); |
||||
let ctx = canvas.getContext('2d'); |
||||
let img = new Image(); |
||||
img.crossOrigin = 'anonymous'; |
||||
img.src = 'roi.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 generateColors = function(result) {}; |
||||
var argmax = function(result, colors) {}; |
||||
|
||||
utils.executeCode('codeEditor1'); |
||||
utils.executeCode('codeEditor2'); |
||||
utils.executeCode('codeEditor3'); |
||||
utils.executeCode('codeEditor4'); |
||||
|
||||
function updateResult(output, time) { |
||||
try{ |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
canvasOutput.style.visibility = "visible"; |
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4); |
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height)); |
||||
cv.imshow('canvasOutput', resized); |
||||
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('canvasOutput').style.visibility = "hidden"; |
||||
utils.clearError(); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,12 @@ |
||||
{ |
||||
"tensorflow": [ |
||||
{ |
||||
"model": "deeplabv3", |
||||
"inputSize": "513, 513", |
||||
"mean": "127.5, 127.5, 127.5", |
||||
"std": "0.007843", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://drive.google.com/uc?id=1v-hfGenaE9tiGOzo5qdgMNG_gqQ5-Xn4&export=download" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,228 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>Style Transfer Example</title> |
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<h2>Style Transfer Example</h2> |
||||
<p> |
||||
This tutorial shows you how to write an style transfer 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> |
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*"> |
||||
</div> |
||||
</td> |
||||
<td> |
||||
<p id='status' align="left"></p> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<div class="caption"> |
||||
modelFile <input type="file" id="modelFile" name="file"> |
||||
</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="5" 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="15" cols="100" id="codeEditor1" spellcheck="false"></textarea> |
||||
<p>3.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="codeEditor2" spellcheck="false"></textarea> |
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p> |
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea> |
||||
<p>5.The post-processing, including scaling and reordering.</p> |
||||
<textarea class="code" rows="21" cols="100" id="codeEditor4" 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; |
||||
</script> |
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet"> |
||||
main = async function() { |
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput'); |
||||
let net = cv.readNet(configPath, modelPath); |
||||
net.setInput(input); |
||||
const start = performance.now(); |
||||
const result = net.forward(); |
||||
const time = performance.now()-start; |
||||
const output = postProcess(result); |
||||
|
||||
updateResult(output, time); |
||||
input.delete(); |
||||
net.delete(); |
||||
result.delete(); |
||||
} |
||||
</script> |
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet"> |
||||
postProcess = function(result) { |
||||
const resultData = result.data32F; |
||||
const C = result.matSize[1]; |
||||
const H = result.matSize[2]; |
||||
const W = result.matSize[3]; |
||||
const mean = [104, 117, 123]; |
||||
|
||||
let normData = []; |
||||
for (let h = 0; h < H; ++h) { |
||||
for (let w = 0; w < W; ++w) { |
||||
for (let c = 0; c < C; ++c) { |
||||
normData.push(resultData[c*H*W + h*W + w] + mean[c]); |
||||
} |
||||
normData.push(255); |
||||
} |
||||
} |
||||
|
||||
let output = new cv.matFromArray(H, W, cv.CV_8UC4, normData); |
||||
return output; |
||||
} |
||||
</script> |
||||
|
||||
<script type="text/javascript"> |
||||
let jsonUrl = "js_style_transfer_model_info.json"; |
||||
drawInfoTable(jsonUrl, 'appendix'); |
||||
|
||||
let utils = new Utils('errorMessage'); |
||||
utils.loadCode('codeSnippet', 'codeEditor'); |
||||
utils.loadCode('codeSnippet1', 'codeEditor1'); |
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString(); |
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode; |
||||
let loadModelCode = 'loadModel = ' + loadModel.toString(); |
||||
document.getElementById('codeEditor3').value = loadModelCode; |
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4'); |
||||
|
||||
let canvas = document.getElementById('canvasInput'); |
||||
let ctx = canvas.getContext('2d'); |
||||
let img = new Image(); |
||||
img.crossOrigin = 'anonymous'; |
||||
img.src = 'lena.png'; |
||||
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 postProcess = function(result) {}; |
||||
|
||||
utils.executeCode('codeEditor1'); |
||||
utils.executeCode('codeEditor2'); |
||||
utils.executeCode('codeEditor3'); |
||||
utils.executeCode('codeEditor4'); |
||||
|
||||
function updateResult(output, time) { |
||||
try{ |
||||
let canvasOutput = document.getElementById('canvasOutput'); |
||||
canvasOutput.style.visibility = "visible"; |
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4); |
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height)); |
||||
cv.imshow('canvasOutput', resized); |
||||
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('canvasOutput').style.visibility = "hidden"; |
||||
utils.clearError(); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,76 @@ |
||||
{ |
||||
"torch": [ |
||||
{ |
||||
"model": "candy.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/candy.t7" |
||||
}, |
||||
{ |
||||
"model": "composition_vii.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/composition_vii.t7" |
||||
}, |
||||
{ |
||||
"model": "feathers.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/feathers.t7" |
||||
}, |
||||
{ |
||||
"model": "la_muse.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/la_muse.t7" |
||||
}, |
||||
{ |
||||
"model": "mosaic.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/mosaic.t7" |
||||
}, |
||||
{ |
||||
"model": "starry_night.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/starry_night.t7" |
||||
}, |
||||
{ |
||||
"model": "the_scream.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/the_scream.t7" |
||||
}, |
||||
{ |
||||
"model": "the_wave.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/the_wave.t7" |
||||
}, |
||||
{ |
||||
"model": "udnie.t7", |
||||
"inputSize": "224, 224", |
||||
"mean": "104, 117, 123", |
||||
"std": "1", |
||||
"swapRB": "false", |
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/udnie.t7" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,13 @@ |
||||
Image Classification Example {#tutorial_js_image_classification} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for image classification. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_image_classification.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,15 @@ |
||||
Image Classification Example with Camera {#tutorial_js_image_classification_with_camera} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for image classification example with camera. |
||||
|
||||
@note If you don't know how to capture video from camera, please review @ref tutorial_js_video_display. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_image_classification_with_camera.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,13 @@ |
||||
Object Detection Example {#tutorial_js_object_detection} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for object detection. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_object_detection.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,13 @@ |
||||
Object Detection Example with Camera{#tutorial_js_object_detection_with_camera} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for object detection with camera. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_object_detection_with_camera.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,13 @@ |
||||
Pose Estimation Example {#tutorial_js_pose_estimation} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for pose estimation. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_pose_estimation.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,13 @@ |
||||
Semantic Segmentation Example {#tutorial_js_semantic_segmentation} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for semantic segmentation. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_semantic_segmentation.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,13 @@ |
||||
Style Transfer Example {#tutorial_js_style_transfer} |
||||
======================================= |
||||
|
||||
Goal |
||||
---- |
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for style transfer. |
||||
|
||||
\htmlonly |
||||
<iframe src="../../js_style_transfer.html" width="100%" |
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';"> |
||||
</iframe> |
||||
\endhtmlonly |
@ -0,0 +1,30 @@ |
||||
Deep Neural Networks (dnn module) {#tutorial_js_table_of_contents_dnn} |
||||
============ |
||||
|
||||
- @subpage tutorial_js_image_classification |
||||
|
||||
Image classification example |
||||
|
||||
- @subpage tutorial_js_image_classification_with_camera |
||||
|
||||
Image classification example with camera |
||||
|
||||
- @subpage tutorial_js_object_detection |
||||
|
||||
Object detection example |
||||
|
||||
- @subpage tutorial_js_object_detection_with_camera |
||||
|
||||
Object detection example with camera |
||||
|
||||
- @subpage tutorial_js_semantic_segmentation |
||||
|
||||
Semantic segmentation example |
||||
|
||||
- @subpage tutorial_js_style_transfer |
||||
|
||||
Style transfer example |
||||
|
||||
- @subpage tutorial_js_pose_estimation |
||||
|
||||
Pose estimation example |
@ -0,0 +1,13 @@ |
||||
# get list of modules to wrap |
||||
if(HAVE_opencv_js) |
||||
message(STATUS "Wrapped in JavaScript(js):") |
||||
endif() |
||||
set(OPENCV_JS_MODULES "") |
||||
foreach(m ${OPENCV_MODULES_BUILD}) |
||||
if(";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";js;" AND HAVE_${m}) |
||||
list(APPEND OPENCV_JS_MODULES ${m}) |
||||
if(HAVE_opencv_js) |
||||
message(STATUS " ${m}") |
||||
endif() |
||||
endif() |
||||
endforeach() |
@ -0,0 +1,74 @@ |
||||
set(MODULE_NAME "js_bindings_generator") |
||||
set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) |
||||
ocv_add_module(${MODULE_NAME} INTERNAL) |
||||
|
||||
set(OPENCV_JS_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "") |
||||
file(REMOVE_RECURSE "${OPENCV_JS_BINDINGS_DIR}/gen") |
||||
file(MAKE_DIRECTORY "${OPENCV_JS_BINDINGS_DIR}/gen") |
||||
file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_js_source") # force re-run after CMake |
||||
|
||||
# This file is included from a subdirectory |
||||
set(JS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") |
||||
include(${JS_SOURCE_DIR}/common.cmake) # fill OPENCV_JS_MODULES |
||||
|
||||
set(opencv_hdrs "") |
||||
foreach(m ${OPENCV_JS_MODULES}) |
||||
list(APPEND opencv_hdrs ${OPENCV_MODULE_${m}_HEADERS}) |
||||
endforeach(m) |
||||
|
||||
# header blacklist |
||||
ocv_list_filterout(opencv_hdrs "modules/.*.h$") |
||||
ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda") |
||||
ocv_list_filterout(opencv_hdrs "modules/core/.*/opencl") |
||||
ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/opengl.hpp") |
||||
ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/ocl.hpp") |
||||
ocv_list_filterout(opencv_hdrs "modules/cuda.*") |
||||
ocv_list_filterout(opencv_hdrs "modules/cudev") |
||||
ocv_list_filterout(opencv_hdrs "modules/core/.*/hal/") |
||||
ocv_list_filterout(opencv_hdrs "modules/.*/detection_based_tracker.hpp") # Conditional compilation |
||||
ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/utils/.*") |
||||
|
||||
ocv_update_file("${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs}") |
||||
|
||||
set(bindings_cpp "${OPENCV_JS_BINDINGS_DIR}/gen/bindings.cpp") |
||||
|
||||
set(scripts_hdr_parser "${JS_SOURCE_DIR}/../python/src2/hdr_parser.py") |
||||
|
||||
if(DEFINED ENV{OPENCV_JS_WHITELIST}) |
||||
set(OPENCV_JS_WHITELIST_FILE "$ENV{OPENCV_JS_WHITELIST}") |
||||
else() |
||||
set(OPENCV_JS_WHITELIST_FILE "${OpenCV_SOURCE_DIR}/platforms/js/opencv_js.config.py") |
||||
endif() |
||||
|
||||
add_custom_command( |
||||
OUTPUT ${bindings_cpp} "${OPENCV_DEPHELPER}/gen_opencv_js_source" |
||||
COMMAND |
||||
${PYTHON_DEFAULT_EXECUTABLE} |
||||
"${CMAKE_CURRENT_SOURCE_DIR}/embindgen.py" |
||||
"${scripts_hdr_parser}" |
||||
"${bindings_cpp}" |
||||
"${CMAKE_CURRENT_BINARY_DIR}/headers.txt" |
||||
"${JS_SOURCE_DIR}/src/core_bindings.cpp" |
||||
"${OPENCV_JS_WHITELIST_FILE}" |
||||
COMMAND |
||||
${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/gen_opencv_js_source" |
||||
WORKING_DIRECTORY |
||||
"${CMAKE_CURRENT_BINARY_DIR}/gen" |
||||
DEPENDS |
||||
${JS_SOURCE_DIR}/src/core_bindings.cpp |
||||
${CMAKE_CURRENT_SOURCE_DIR}/embindgen.py |
||||
${CMAKE_CURRENT_SOURCE_DIR}/templates.py |
||||
${scripts_hdr_parser} |
||||
#(not needed - generated by CMake) ${CMAKE_CURRENT_BINARY_DIR}/headers.txt |
||||
${opencv_hdrs} |
||||
COMMENT "Generate source files for JavaScript bindings" |
||||
) |
||||
|
||||
add_custom_target(gen_opencv_js_source |
||||
# excluded from all: ALL |
||||
DEPENDS ${bindings_cpp} "${OPENCV_DEPHELPER}/gen_opencv_js_source" |
||||
SOURCES |
||||
${JS_SOURCE_DIR}/src/core_bindings.cpp |
||||
${CMAKE_CURRENT_SOURCE_DIR}/embindgen.py |
||||
${CMAKE_CURRENT_SOURCE_DIR}/templates.py |
||||
) |
Loading…
Reference in new issue