学了一段时间的神经网络,尝试了几个版本的yolo模型和其他类型神经网络模型。
前两天看到了js onnx运行时,就测试了下。
先试了试刚在测试的对抗网络,生成人脸……经过了几次尝试修改调用成功了,看上去效果还不错,于是就再试试yolov7了。
yolov7调用是比较顺利的,只是还需后续处理才能得到最后的结果。这个处理代码网上没找到现成的js代码,自己写写算了。
参考C++的代码写了下
function yolov7(data, score_threshold, nms_threshold) {
let netAnchors = [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]];
let netStride = [8, 16, 32, 64]
let confThreshold = score_threshold;
let datas = [];
for (let t in data) {
datas.push(data[t]);
}
datas.sort(function (a, b) { return b.dims[2] - a.dims[2]; });
let res = [];
for (let i = 0; i < datas.length; i++) {
res = res.concat(get(datas[i], netAnchors[i], netStride[i]));
}
res = nms(res, score_threshold, nms_threshold);
return res;
function sigmoid(v) {
return 1.0 / (1 + Math.exp(-v));
}
function getMaxIndex(data, start, len) {
let i = 0;
let v = data[start];
for (let j = 1; j < len; j++) {
if (data[start + j] > v) {
v = data[start + j];
i = j;
}
}
return { index: i, score: v };
}
function get(data, anchors, stride) {
let gh = data.dims[2];
let gw = data.dims[3];
let c = data.dims[4];
let classCount = c - 5;
let res = [];
let p = 0;
for (let i = 0; i < data.dims[1]; i++) {
let aw = anchors[i * 2];
let ah = anchors[i * 2 + 1];
for (let gy = 0; gy < gh; gy++) {
for (let gx = 0; gx < gw; gx++) {
let boxScore = sigmoid(data.data[p + 4]);
if (boxScore >= confThreshold) {
let indexScore = getMaxIndex(data.data, p + 5, classCount);
indexScore.score = sigmoid(indexScore.score);
if (indexScore.score >= confThreshold) {
let x = (sigmoid(data.data[p]) * 2 - 0.5 + gx) * stride;
let y = (sigmoid(data.data[p + 1]) * 2 - 0.5 + gy) * stride;
let w = Math.pow(sigmoid(data.data[p + 2]) * 2, 2) * aw;
let h = Math.pow(sigmoid(data.data[p + 3]) * 2, 2) * ah;
res.push({
x: (x - 0.5 * w),
y: (y - 0.5 * h),
w: w,
h: h,
index: indexScore.index,
score: indexScore.score
});
}
}
p += c;
}
}
}
return res;
}
function iou(a, b) {
var aa = a.w * a.h;
var ba = b.w * b.h;
var x = Math.max(a.x, b.x);
var y = Math.max(a.y, b.y);
var xx = Math.min(a.x + a.w, b.x + b.w);
var yy = Math.min(a.y + a.h, b.y + b.h);
var w = xx - x;
var h = yy - y;
if (w <= 0 || h <= 0) return 0;
var area = w * h;
return area / (aa + ba - area);
}
function nms(boxs, score_threshold, nms_threshold) {
let score_indexs = [];
for (var i = 0; i < boxs.length; i++) {
if (boxs[i].score > score_threshold)
score_indexs.push({ second: i, first: boxs[i].score });
}
score_indexs.sort(function (a, b) { return b.first - a.first });
let res = [];
let adaptive_threshold = nms_threshold;
for (let i = 0; i < score_indexs.length; i++) {
let idx = score_indexs[i].second;
let keep = true;
for (let k = 0; k < res.length && keep; k++) {
let k_idx = res[k];
let v = iou(boxs[idx], boxs[k_idx]);
keep = v <= adaptive_threshold;
}
if (keep) {
res.push(idx);
}
}
for (let i = 0; i < res.length; i++) {
res[i] = boxs[res[i]];
}
return res;
}
}
调用
<!--运行时的js -->
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script>
<canvas width="800" height="800" id="can"></canvas>
<script>
let can = document.getElementById("can");
let ctx = can.getContext("2d");
function getData() {
let w = can.width;
let h = can.height;
let wh = w * h;
let img = ctx.getImageData(0, 0, w, h).data;
let datas = new Array(3 * wh);
let j = 0;
for (let i = 0; i < img.length; i += 4) {
datas[j] = img[i] / 255.0;
datas[wh + j] = img[i + 1] / 255.0;
datas[wh * 2 + j] = img[i + 2] / 255.0;
j++;
}
return datas;
}
function drawBoxs(boxs, labs) {
ctx.strokeStyle = "#ff0000";
ctx.strokeWidth = 1;
ctx.font = '18px Arial';
ctx.beginPath();
for (let i = 0; i < boxs.length; i++) {
ctx.rect(boxs[i].x, boxs[i].y, boxs[i].w, boxs[i].h);
ctx.strokeText(labs[boxs[i].index], boxs[i].x, boxs[i].y);
}
ctx.stroke();
}
async function mainYoloV7(imageData, width, height) {
try {
const session = await ort.InferenceSession.create('./best.onnx');
const dataA = Float32Array.from(imageData);
const tensorA = new ort.Tensor('float32', dataA, [1, 3, height, width]);
const feeds = { images: tensorA };
let results = await session.run(feeds);
let boxs = yolov7(results, 0.26, 0.4);
drawBoxs(boxs, "0,1,2,3,4,5,6,7,8,9,-".split(','));
} catch (e) {
document.write(`failed to inference ONNX model: ${e}.`);
console.log(e);
}
}
let image = document.createElement("img");
image.onload = function () {
can.width = parseInt(image.width / 32) * 32;
can.height = parseInt(image.height / 32) * 32;
ctx.drawImage(image, 0, 0);
mainYoloV7(getData(), can.width, can.height);
}
image.src = "2.png";
</script>
以上调用是在 yolov7 tiny 的模型测试的,导出 onnx 时 --grid 参数=False
简单测试结果