The tutorial I linked could use a little work. It’s not including a large portion of code required to make it work. It also requires the user to manually enter the model and config file, which is not ideal by any means. Below is an updated version that doesnt require any file input, as long as you can hold the files on your server.
const classify = () => {
let inputSize = [224, 224];
let mean = [104, 117, 123];
let std = 1;
let swapRB = false;
// record if need softmax function for post-processing
let needSoftmax = false;
// url for label file, can from local or Internet
const labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/3.4/samples/data/dnn/classification_classes_ILSVRC2012.txt";
const loadLables = async (labelsUrl: string) => {
let response = await fetch(labelsUrl);
let label: string | string[] = await response.text();
label = label.split('\n');
return label;
}
const getBlobFromImage = (inputSize: number[], mean: number[], std: number, swapRB: boolean, image: HTMLCanvasElement) => {
console.log(image)
let mat; if (typeof (image) === 'string') { mat = cv.imread(image); } else { mat = cv.imread(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;
}
const loadModel = async (path: string, name: string) => {
try {
const response = await fetch(path);
const buffer = await response.arrayBuffer();
const data = new Uint8Array(buffer);
cv.FS_createDataFile('/', name, data, true, false, false);
return name;
} catch (error) {
console.error('Error loading model:', error);
return null;
}
};
const 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;
}
}
const 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;
}
const main = async function () {
if (refCanvas.current) {
try {
const labels = await loadLables(labelsUrl);
const input = getBlobFromImage(inputSize, mean, std, swapRB, refCanvas.current);
const configPath = await loadModel('/classify/deploy.prototxt', 'deploy.prototxt');
const modelPath = await loadModel('/classify/squeezenet_v1.0.caffemodel', 'squeezenet_v1.0.caffemodel');
console.log(configPath, modelPath)
let net = cv.readNet(configPath, modelPath);
console.log(net)
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);
console.log(classes)
input.delete();
net.delete();
result.delete();
} catch (e) {
console.log(e)
}
}
}
main();
}