-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfaceDetection.js
216 lines (174 loc) · 6.66 KB
/
faceDetection.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
const fs = require('fs')
const rd = require('readline')
const path = require('path')
const { fr, getDataPath, getAppdataPath, ensureDataDirExists, ensureAppdataDirExists, cv } = require('./commons')
//Function that makes sure the window kills process on exit and the directories exist
const faceInit = function() {
fr.winKillProcessOnExit()
ensureDataDirExists()
ensureAppdataDirExists()
}
//Utility function to get files in directory
const getFaceFiles = function( callback ){
ensureDataDirExists()
let srcPath = getDataPath() + '/faces/'
fs.readdir(srcPath, (err, items) => {
items = items.map(file => (srcPath + file))
// Execute the callback with the files
if (typeof callback == "function")
callback(items)
})
}
//Function that detects and saves faces
const detectFaces = function( srcPath, rescaleFactor ){
faceInit();
rescaleFactor = typeof rescaleFactor == "undefined" ? 1 : rescaleFactor
const detector = fr.FaceDetector()
const cvMat = cv.imread( srcPath ).rescale(rescaleFactor)
const img = fr.CvImage(cvMat)
console.log('detecting faces')
const faceRects = detector.locateFaces(img)
const faces = faceRects
.map( mmodRect => fr.toCvRect( mmodRect.rect ))
.map( cvRect => cvMat.getRegion(cvRect).copy().resizeToMax(150) )
// If no faces were detected
if (faces.length == 0) {
return false;
}
return faces
}
// Function that crops and saves faces found in photos
const cropFaces = function( srcPath ){
// We load the requested image
const faces = detectFaces( srcPath )
const winNameReg = srcPath.match( RegExp('[a-zA-Z0-9]+(?=\.JPG)', 'gi') )
faces.forEach((face, i) => {
//cv.imshow( winNameReg + "-" + i, face)
cv.imwrite( getDataPath() + '/faces/' + winNameReg + '.jpg', face )
});
}
//Function that recognizes faces
const recognizeFaces = function( srcPath, rescaleFactor ){
faceInit();
console.log('Recognizing faces')
// We loook for faces in image provided
const faces = detectFaces( srcPath, rescaleFactor )
const unkownThreshold = 0.6
// Set the path of the trained model
const trainedModelFile = `faceRecognition1Model_150.json`
const trainedModelFilePath = path.resolve(getAppdataPath(), trainedModelFile)
const recognizer = fr.FaceRecognizer()
//Make sure the trained model exists
if (fs.existsSync(trainedModelFilePath)) {
recognizer.load( require( trainedModelFilePath ) )
console.log(faces.length + " faces found in image. Beginning recognition...")
faces.forEach((cvMat, i) => {
const img = fr.CvImage(cvMat)
const prediction = recognizer.predictBest(img, unkownThreshold)
console.log('Face %d: %s (%s)', i, prediction.className, prediction.distance)
cv.imshow( "Face " + i, cvMat)
cv.waitKey()
})
return true
}
else {
console.log("Trained model not found")
return false;
}
}
//Function that trains the system
const trainModel = function(){
//We ask an input for each file
getFaceFiles((files) => {
let names = []
let filesCopy = files.slice()
//If images were found
if (files.length > 0) {
//We load previously created models
const trainedDictFile = `faceRecognition1ModelDict_150.json`
const trainedDictFilePath = path.resolve(getAppdataPath(), trainedDictFile)
const recognizer = fr.FaceRecognizer()
let prevData = {"images": {}, "names": []}
//We load previously created models
if (fs.existsSync(trainedDictFilePath)) {
prevData = require(trainedDictFilePath)
}
//We process every file sequentially
let seqProcess = file => {
//We show the picture
let cvMat = cv.imread( file )
cv.imshow( "Who is this?", cvMat)
// We get the name suggestion
let imgName = file.replace( ( getDataPath() + '/faces/' ), '' )
let possibleName = typeof prevData[ 'images' ][ imgName ] !== "undefined" ? prevData['names'][ prevData[ 'images' ][ imgName ] ] : ( names.length > 0 ? names[ names.length -1 ] : '' )
// Ask the user for the person's name
let rl = rd.createInterface( process.stdin, process.stdout )
rl.setPrompt('What is the name of the person in the picture ' + ( possibleName != "" ? ('( Hit enter for ' + possibleName + ' ) ') : '') + '> ')
rl.prompt()
//Once the name was received
rl.on('line', (line) => {
names.push( ( possibleName != "" ? possibleName : line ) )
rl.close()
cv.destroyAllWindows()
// If there are still files to process
if (files.length > 0){
seqProcess(files.shift())
}
else {
//We start the training process
beginTraining(filesCopy, names)
}
})
cv.waitKey()
}
//Start the process
seqProcess(files.shift())
}
})
}
//Function that generates a model with the input received
const beginTraining = function(files, names) {
ensureAppdataDirExists()
console.log('Starting training')
// Set the path of the trained models
const trainedModelFile = `faceRecognition1Model_150.json`
const trainedDictFile = `faceRecognition1ModelDict_150.json`
const trainedModelFilePath = path.resolve(getAppdataPath(), trainedModelFile)
const trainedDictFilePath = path.resolve(getAppdataPath(), trainedDictFile)
const recognizer = fr.FaceRecognizer()
//We load each image into its classifier
let imagesByClass = {}
let imagesByName = {}
files.forEach((file, i) => {
imagesByClass[ names[i] ] = typeof imagesByClass[ names[i] ] == "undefined" ? [] : imagesByClass[ names[i] ]
imagesByClass[ names[i] ].push( fr.loadImage(file) )
const imgName = file.replace( ( getDataPath() + '/faces/' ), '' )
imagesByName[ imgName ] = i
})
//We add each face to the recognizer
Object.keys(imagesByClass).forEach( name => {
recognizer.addFaces(imagesByClass[name], name)
})
//We save both files
fs.writeFileSync(trainedModelFilePath, JSON.stringify(recognizer.serialize()))
fs.writeFileSync(trainedDictFilePath, JSON.stringify( {"images": imagesByName, "names": names} ))
}
//The functions are exported
exports.recognizeFaces = recognizeFaces
exports.trainModel = trainModel
exports.cropFaces = cropFaces
//###### USAGE ######
//Use to recognize faces in one miage
//recognizeFaces( getDataPath() + '/photos/bbt1.jpg', 0.7 ) //50%
//recognizeFaces( getDataPath() + '/photos/bbt2.jpg' ) //50%
//recognizeFaces( getDataPath() + '/photos/bbt3.jpg' ) //WEIRD - only detects 1 face
//recognizeFaces( getDataPath() + '/photos/bbt4.jpg' ) //100%
//recognizeFaces( getDataPath() + '/photos/bbt5.jpg' ) //50%
//recognizeFaces( getDataPath() + '/photos/GOPR0287.JPG', 0.2 )
//Use to train the model using the images on file
//trainModel()
//Use this while retrieving a picture from the gopro
//let index = 287;
//for (let i=0; i<1; i++){
// cropFaces( getDataPath() + '/photos/GOPR0' + (index++) + '.JPG' )
//}