// // Shinobi - Face Plugin // Copyright (C) 2016-2025 Moe Alam, moeiscool // // # Donate // // If you like what I am doing here and want me to continue please consider donating :) // PayPal : paypal@m03.ca // // Base Init >> var fs = require('fs'); var config = require('./conf.json') var dotenv = require('dotenv').config() var s try{ s = require('../pluginBase.js')(__dirname,config) }catch(err){ console.log(err) try{ s = require('./pluginBase.js')(__dirname,config) }catch(err){ console.log(err) return console.log(config.plug,'Plugin start has failed. pluginBase.js was not found.') } } // Base Init />> // Face - Face Recognition Init >> var weightLocation = __dirname + '/weights' const canvas = require('canvas') var tfjsSuffix = '' switch(config.tfjsBuild){ case'gpu': tfjsSuffix = '-gpu' break; case'cpu': break; default: try{ require(`@tensorflow/tfjs-node`) }catch(err){ console.log(err) } break; } var tf = require(`@tensorflow/tfjs-node${tfjsSuffix}`) faceapi = require('face-api.js') const { createCanvas, Image, ImageData, Canvas } = canvas faceapi.env.monkeyPatch({ Canvas, Image, ImageData }) s.monitorLock = {} // Face - Face Recognition Init />> // SsdMobilenetv1Options const minConfidence = 0.5 var addAwaitStatements = async function(){ await faceapi.nets.ssdMobilenetv1.loadFromDisk(weightLocation) await faceapi.nets.faceLandmark68Net.loadFromDisk(weightLocation) await faceapi.nets.faceRecognitionNet.loadFromDisk(weightLocation) const faceDetectionNet = faceapi.nets.ssdMobilenetv1 var faceDetectionOptions = new faceapi.SsdMobilenetv1Options({ minConfidence }); if(!fs.existsSync('./faces')){ fs.mkdirSync('./faces'); } var faces = fs.readdirSync('./faces') var labeledDescriptors = [] var faceMatcher var facesLoaded = 0 const createAllFaceDescriptors = (faces) => { s.detectObject = function(){} faceMatcher = null facesLoaded = 0 labeledDescriptors = [] const checkComplete = () => { ++facesLoaded if(facesLoaded === faces.length){ faceMatcher = new faceapi.FaceMatcher(labeledDescriptors) startDetecting() } } faces.forEach(function(personName){ var descriptors = [] var faceFolder = './faces/' + personName + '/' var imageList = fs.readdirSync(faceFolder) var foundImages = [] var faceResults = [] imageList.forEach(function(imageFile,number){ if(imageFile.indexOf('.jpg') > -1 || imageFile.indexOf('.jpeg') > -1){ foundImages.push(imageFile) } }) if(foundImages.length === 0){ checkComplete(facesLoaded,faces.length) }else{ console.log('Loading : ' + personName) foundImages.forEach(function(imageFile,number){ var image = new Image; image.onload = function() { faceapi .detectSingleFace(image) .withFaceLandmarks() .withFaceDescriptor() .then((singleResult) => { if (!singleResult) { return console.log('no faces',imageFile) } descriptors.push(singleResult.descriptor) faceResults.push(singleResult) if(number === foundImages.length - 1){ console.log('Loaded : ' + personName) labeledDescriptors.push(new faceapi.LabeledFaceDescriptors( personName, descriptors )) checkComplete() } }) .catch((error) => { console.log(error) }) } image.src = fs.readFileSync(faceFolder + imageFile) }) } }) } var startDetecting = function(){ console.log('Ready to Detect Faces') s.detectObject = function(buffer,d,tx,frameLocation){ var detectStuff = function(frameBuffer,callback){ try{ var startTime = new Date() var image = new Image; image.onload = async function() { faceapi.detectAllFaces(image, faceDetectionOptions) .withFaceLandmarks() .withFaceDescriptors() .then((data) => { if(data && data[0]){ if(faceMatcher){ data.forEach(fd => { var bestMatch = faceMatcher.findBestMatch(fd.descriptor) fd.detection.tag = bestMatch.toString() }) } var endTime = new Date() var matrices = [] try{ var imgHeight = data[0].detection._imageDims._height var imgWidth = data[0].detection._imageDims._width }catch(err){ var imgHeight = data[0]._detection._imageDims._height var imgWidth = data[0]._detection._imageDims._width } data.forEach(function(box){ var v = box.detection || box._detection var tag,confidence if(v.tag){ var split = v.tag.split('(') tag = split[0].trim() if(tag === 'unknown')tag = 'UNKNOWN FACE' if(split[1]){ confidence = split[1].replace(')','') }else{ confidence = v._score } }else{ tag = 'UNKNOWN FACE' confidence = v._score } matrices.push({ id: tag, x: v._box.x, y: v._box.y, width: v._box.width, height: v._box.height, tag: tag, confidence: v._score, }) }) if(matrices.length > 0){ tx({ f:'trigger', id:d.id, ke:d.ke, details:{ plug:config.plug, name:'face', reason:'object', matrices:matrices, imgHeight: imgHeight, imgWidth: imgWidth, ms: endTime - startTime }, }) } } }) .catch((err) => { console.log(err) }) } image.src = frameBuffer; }catch(err){ s.monitorLock[d.ke+d.id] = false console.log(err) } } if(frameLocation){ fs.readFile(frameLocation,function(err,buffer){ if(!err){ detectStuff(buffer) } fs.unlink(frameLocation,function(){ }) }) }else{ detectStuff(buffer) } } } if(faces.length === 0){ startDetecting() }else{ createAllFaceDescriptors(faces) } // add websocket handlers const io = s.getWebsocket() var faceDescriptorRefreshTimeout const onSocketEvent = (d) => { switch(d.f){ case'recompileFaceDescriptors': if(faceDescriptorRefreshTimeout)console.log('Cancelling previous recompilation request...') console.log('Recompiling Face Descriptors...') clearTimeout(faceDescriptorRefreshTimeout) faceDescriptorRefreshTimeout = setTimeout(()=>{ delete(faceDescriptorRefreshTimeout) createAllFaceDescriptors(d.faces) },10000) break; } } if(config.mode === 'host'){ io.on('connection', function (cn) { cn.on('f',onSocketEvent) }) }else{ io.on('f',onSocketEvent) } } addAwaitStatements()