[前端面包]别动我的摄像头!Tensorflow下的图像分类识别
"嘀——检测到人类,危险系数:996加班脸"
深度学习一直是个很热的话题,作为一个前端程序员,我开始思考如何在Web中集成图像识别的技术,通过网页判断摄像头前方的是人还是鬼,对物体进行分类;
首先是根据需求选择适合的技术方案:
-
轻量级任务(如颜色检测、边缘识别):使用传统计算机视觉库(如OpenCV.js)
-
复杂任务(如物体分类、人脸识别):使用深度学习框架(如TensorFlow.js)或预训练模型
-
高精度需求:调用云服务API(如Google Cloud Vision)
对于前端网页的集成,我们可以使用Google的TensorFlow进行深度学习的模型推理;他内置了WebGL的后端,可以使运算和识别的速度更快,我们先从初始的MobileNet开始讲起
MobileNet
MobileNets 是一个小型、低延迟、低功耗的模型,其参数可满足各种使用情况下的资源限制;它们可用于分类、检测、嵌入和分割,与其他流行的大规模模型(如 Inception)的使用方式类似
计算延迟度
参数数量比较图
运用
我们可以用两种方法引入MobileNet库:
script标签引入:
//这里不需要导入语句 const img = document.getElementById('img'); //加载模型 mobilenet.load().then(model => { //分辨图片 model.classify(img).then(predictions => { console.log('Predictions: '); console.log(predictions); }); });
NPM包引入:
npm install @tensorflow/tfjs npm install @tensorflow-models/mobilenet
API
加载模型
mobilenet是module的name,所以ES6语法中引入script时不需要加上type="module"
mobilenet.load({ version: 1,//版本信息 alpha?: 0.25 | .50 | .75 | 1.0,//模型参数,数字越大分辨能力越强 modelUrl?: string//模型地址 inputRange?: [number, number]//输入范围(可选) } )
开始识别
在MobileNet中你可以使用classify识别方法识别五种对象:
- Tensor3D
- ImageData
- HTMLImageElement
- HTMLCanvasElement
- HTMLVideoElement
model.classify( img: tf.Tensor3D | ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement, topk?: number )
识别出来的结果为Promise数组:
[{
className:" ",//预测对象
probability:" "//置信度
}]
迁移学习
你可以使用infer函数来获取图像的嵌入式来进行迁移学习,嵌入的大小取决于模型的alpha参数
model.infer( img: tf.Tensor3D | ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement, embedding = false )
使用示例
基于图像的识别
Document
TensorFlow.js MobileNet
import '@tensorflow/tfjs-backend-cpu'; import '@tensorflow/tfjs-backend-webgl'; import * as mobilenet from '@tensorflow-models/mobilenet'; //定义初始信息 const img = document.getElementById('img') const result = document.getElementById('result') const version=2//版本信息 const alpha=0.5//模型大小 //异步加载模型 async function run(){ const model=await mobilenet.load({version,alpha})//加载模型 //预测图片 const predictions = await model.classify(img) console.log(predictions) //解构数组 const [prediction1,prediction2,prediction3] = predictions result.innerHTML = `
预测结果:${prediction1.className}
预测概率:${(prediction1.probability*100).toFixed(2)}%
预测结果:${prediction2.className}
预测概率:${(prediction2.probability*100).toFixed(2)}%
预测结果:${prediction3.className}
预测概率:${(prediction3.probability*100).toFixed(2)}%
` //获取预测概率 const logits = model.infer(img) console.log(logits) logits.print(true) //获取特征值 const embeddings =model.infer(img,true) console.log(embeddings) embeddings.print(true) } run()基于摄像头的识别
对于网页端摄像头,我们需要编写初始化摄像头组件,通过getUserMedia获取摄像头信息
浏览器端MobileNet识别 let net; let webcamElement = document.getElementById('webcam'); let predictionsElement = document.getElementById('predictions'); async function setupWebcam() { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); webcamElement.srcObject = stream; return new Promise((resolve) => { webcamElement.onloadedmetadata = () => resolve(); }); } async function loadModel() { net = await mobilenet.load({ version: 2, alpha: 1.0 }); console.log('MobileNet模型加载完成'); } async function predict() { const predictions = await net.classify(webcamElement); predictionsElement.innerHTML = predictions .map(p => `${p.className} (${Math.round(p.probability * 100)}%)`) .join('
'); requestAnimationFrame(predict); } (async () => { await setupWebcam(); await loadModel(); predict(); })();MobileNet已经可以满足基础的使用,但是却有几个缺点:
- 识别精准度不高,只能识别物体
- 返回对象只有预测对象和置信度,无法获取具体轮廓
所以为了深化学习,我们可以使用其他模型进行运算:YOLO、COCO-SSD ,下面我以COCO-SSD作为举例
COCO-SSD
COCOSSD通过轻量级卷积神经网络作为基础网络,用特征图实现锚框,NMS去除重复检测,具有高效性,适用性广的特点
运用
我们同样可以使用两种方法使用COCOSSD
script标签引入:
const img = document.getElementById('img'); //加载模型 cocoSsd.load().then(model => { //检测图片上的对象 model.detect(img).then(predictions => { console.log('Predictions: ', predictions); }); });
NPM包引入:
//我们需要WebGL作为后端,并将其添加到package.json作为前置依赖项 require('@tensorflow/tfjs-backend-cpu'); require('@tensorflow/tfjs-backend-webgl'); const cocoSsd = require('@tensorflow-models/coco-ssd'); (async () => { const img = document.getElementById('img'); const model = await cocoSsd.load(); const predictions = await model.detect(img); console.log('Predictions: '); console.log(predictions); })();
API
加载模型
跟MobileNet的使用方式相同,在使用ES6语法后coco-ssd是module语法
export interface ModelConfig { base?: ObjectDetectionBaseModel; //原始模型,可以为'mobilenet_v1','mobilenet_v2'或者'lite_mobilenet_v2' //默认是lite_mobilenet_v2 //lite_mobilenet_v2加载速度最快,体量最轻,mobilenet_v2识别最准 modelUrl?: string;//模型路径 } cocoSsd.load(config: ModelConfig = {});
开始识别
于MobileNet不同的是,COCOSSD多了两个参数
- maxNumBoxes: 画面上识别的最大对象框数
- minScore:展示对象框的最小置信度,值为0~1,默认为0.5
model.detect( img: tf.Tensor3D | ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement, maxNumBoxes: number, minScore: number )
识别的对象为Promise对象数组:
[{ bbox: [x, y, width, height],//坐标起点,对象框的宽和高 class: "person",//预测对象 score: 0.8380282521247864//置信度 }, { bbox: [x, y, width, height], class: "kite", score: 0.74644153267145157 }]
高阶用法
该模型是基于Tensorflow的对象检测API,你可以从这里下载原始模型;Cocossd在此基础上进行了下列优化以提高浏览器的执行性能
- 删除原始模型的后处理图
- 使用单类NMS而不是原始的多类NMS
- 在CPU后端执行NMS操作,避免纹理下载延迟
tensorflowjs_converter --input_format=tf_frozen_model \
--output_format=tfjs_graph_model \
--output_node_names='Postprocessor/ExpandDims_1,Postprocessor/Slice' \
./frozen_inference_graph.pb \
./web_model
使用示例
基于图片的识别
import '@tensorflow/tfjs-backend-cpu'; import '@tensorflow/tfjs-backend-webgl'; import * as cocoSsd from '@tensorflow-models/coco-ssd'; import imageURL from './image1.jpg'; import image2URL from './image2.jpg'; let modelPromise; window.onload = () => modelPromise = cocoSsd.load(); const button = document.getElementById('toggle'); button.onclick = () => { image.src = image.src.endsWith(imageURL) ? image2URL : imageURL; }; const select = document.getElementById('base_model'); select.onchange = async (event) => { const model = await modelPromise; model.dispose(); modelPromise = cocoSsd.load( {base: event.srcElement.options[event.srcElement.selectedIndex].value}); }; const image = document.getElementById('image'); image.src = imageURL; const runButton = document.getElementById('run'); runButton.onclick = async () => { const model = await modelPromise; console.log('model loaded'); console.time('predict1'); const result = await model.detect(image); console.timeEnd('predict1'); const c = document.getElementById('canvas'); const context = c.getContext('2d'); context.drawImage(image, 0, 0); context.font = '10px Arial'; console.log('number of detections: ', result.length); for (let i = 0; i 10 ? result[i].bbox[1] - 5 : 10); } };
描绘对象框
async drawBox(imageElement, predictions) { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); //设定canvas尺寸 canvas.width = imageElement.width; canvas.height = imageElement.height; //在图像上画图 context.drawImage(imageElement, 0, 0, canvas.width, canvas.height); for (let prediction of predictions) { const [x, y, width, height] = prediction.bbox; const text = `${prediction.class} (${(prediction.score * 100).toFixed(2)}%)`; //边框 context.strokeStyle = 'yellow'; context.lineWidth = 8; context.strokeRect(x, y, width, height); //字体样式 context.font = '28px Arial'; context.fillStyle = 'yellow'; //文字宽度和高度 const textWidth = context.measureText(text).width; const textHeight = 28 * 1.5; const padding = 8; //文字背景框 context.fillStyle = 'white'; context.fillRect(x - padding, y - 20 - textHeight - padding, textWidth + padding * 2, textHeight + padding * 2); /画文字 context.fillStyle = 'black'; // 文字顏色 context.fillText(text, x + padding / 2, y - 10 - textHeight / 2); } }
本段代码及图片转载自August
结语
轻量级的模型使用可以用cocossd来进行场景识别,大家可以跟着敲一遍,了解开箱即用的MobileNet,以及进阶使用大模型训练
“前端也能干深度学习的活!”