251 lines
9.8 KiB
JavaScript
251 lines
9.8 KiB
JavaScript
//
|
|
// 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()
|