forked from Azure-Samples/cognitive-services-quickstart-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFaceQuickstart.js
More file actions
488 lines (449 loc) · 20.3 KB
/
FaceQuickstart.js
File metadata and controls
488 lines (449 loc) · 20.3 KB
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
'use strict';
var msRest = require("@azure/ms-rest-js");
var Face = require("@azure/cognitiveservices-face");
const FileSet = require('file-set');
const createReadStream = require('fs').createReadStream
const uuidV4 = require('uuid/v4');
/**
* Install the following modules:
* npm install @azure/ms-rest-js
* npm install @azure/cognitiveservices-face
*
* Collect local images:
* This quickstart uses a variety of both URL images and local ones. Download all the local ones from here:
* https://github.com/Azure-Samples/cognitive-services-sample-data-files/tree/master/Face/images
* Put these images in your root folder.
*/
/**
* This sample includes:
* - Detect Faces: using a single-faced image and a group image, gets the IDs of all eligible faces.
* - Find Similar: using the single-faced image as a query, it searches the group image for a similar face.
* - Verify: compares two images to see if they are of the same person. Detects their faces first.
* - Person Group: creates a person group and uses it to identify faces in other images.
* - Identify: identify a face in a photo from a select person group.
* - Snapshot: move a person group or facelist from one region (or Azure subscription) to another.
*/
/**
* Shared variables
* These variables are shared by more than one example below.
*/
// An image with only one face
let singleFaceImageUrl = 'https://www.biography.com/.image/t_share/MTQ1MzAyNzYzOTgxNTE0NTEz/john-f-kennedy---mini-biography.jpg';
// An image with several faces
let groupImageUrl = 'http://www.historyplace.com/kennedy/president-family-portrait-closeup.jpg';
const IMAGE_BASE_URL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/Face/images/'
// Person Group ID must be lower case, alphanumeric, with '-' and/or '_'.
const PERSON_GROUP_ID = 'my-unique-person-group'
/**
* AUTHENTICATE
* Used for all examples.
*/
let key = 'PASTE_YOUR_FACE_SUBSCRIPTION_KEY_HERE';
let endpoint = 'PASTE_YOUR_FACE_ENDPOINT_HERE';
let credentials = new msRest.ApiKeyCredentials({ inHeader: { 'Ocp-Apim-Subscription-Key': key } });
let client = new Face.FaceClient(credentials, endpoint);
/**
* END - AUTHENTICATE
*/
async function main() {
/**
* DETECT FACES
* Detects the faces in the source image, then in the target image.
*/
console.log("---------------------------------")
console.log("DETECT FACES")
// Detect a single face in an image. Returns a Promise<DetectedFace[]>.
// NOTE: FaceDetectWithUrlOptionalParams.returnFaceId default value is true. See:
// https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/cognitiveservices/cognitiveservices-face/src/models/index.ts#L891
let singleDetectedFace = await client.face.detectWithUrl(singleFaceImageUrl)
.then((faces) => {
console.log(`Face ID found in single face image: \n${faces[0].faceId}`)
return faces[0].faceId;
}).catch((err) => {
console.log('No faces detected in single face image:' + singleFaceImageUrl)
throw err
})
console.log()
// Detect the faces in a group image. API call returns a Promise<DetectedFace[]>.
let groupDetectedFaces = await client.face.detectWithUrl(groupImageUrl)
.then((faces) => {
console.log('Face IDs found in group image:')
// Initialize empty array of strings
var faceIds = new Array(faces.length)
for (let face of faces) {
faceIds.unshift(face.faceId)
console.log(face.faceId)
}
var filteredIds = faceIds.filter((id) => {
return id != null
})
return filteredIds
}).catch((err) => {
console.log(`No faces detected in group image: ${groupImageUrl}.`)
throw err
})
console.log()
/**
* END - DETECT FACES
*/
/**
* FIND SIMILAR
* Find the similar face in the target image, then display face attributes.
* Uses the group of detected faces array that was detected in Detect Faces.
*/
console.log("---------------------------------")
console.log("FIND SIMILAR")
// The options parameter in findSimilar(faceIds, options)
// is a Models.FaceFindSimilarOptionalParams parameter (in index.ts)
// API call returns a Promise<SimilarFace[]>.
await client.face.findSimilar(singleDetectedFace, { faceIds: groupDetectedFaces })
.then((similars) => {
console.log('Similar faces found in group image:')
for (let similar of similars) {
// Search target image list for source face
console.log(`Face ID: ${similar.faceId}.`)
// The group image in this example contains a similar face that is turned,
// so confidence will be lower than a face looking straight ahead.
console.log(`Confidence: ${Number(similar.confidence).toFixed(2) * 100}%`)
}
}).catch((err) => {
console.log(`No similar faces found in group image: ${groupImageUrl}.`)
throw err
});
console.log()
/**
* END - FIND SIMILAR
*/
/**
* VERIFY
* Verify-Face-To-Face: Verify whether two faces belong to a same person or
* whether one face belongs to a person.
*/
console.log("---------------------------------")
console.log("VERIFY")
// Create an array to hold the target photos
let targetImageFileNames = ['Family1-Dad1.jpg', 'Family1-Dad2.jpg']
// Then declare your source photos, they'll be used to query the target images
let sourceImageFileNames = ['Family1-Dad3.jpg', 'Family1-Son1.jpg']
console.log('Detect faces in source images:')
// Detect faces in the source image array, then get their IDs
let sourceFaces = await Promise.all(sourceImageFileNames.map(async (imageName) => {
// Returns a Promise<DetectedFace[]>
return client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
.then((faces) => {
console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
let id = faces[0].faceId
return { id }
}).catch((err) => {
console.log(`No face detected in: ${sourceImageFileNames[0]}.`)
throw err;
})
}))
console.log()
// Get objects out of sourceFaces array,
// then get IDs and put in new array for the API call params
var sourceFaceIds = []
for (var dict of sourceFaces) {
sourceFaceIds.push(dict['id'])
}
console.log('Detect faces in target images:')
// Detect faces in the target image array, then get their IDs
let targetFaces = await Promise.all(targetImageFileNames.map(async (imageName) => {
// Returns a Promise<DetectedFace[]>
return client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
.then((faces) => {
console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
let id = faces[0].faceId
return { id }
}).catch((err) => {
console.log(`No face detected in: ${targetImageFileNames[0]}.`)
throw err;
})
}))
// Get objects out of targetFaces array,
// then get IDs and put in new array for the API call params
var targetFaceIds = []
for (var dict of targetFaces) {
targetFaceIds.push(dict['id'])
}
console.log()
// Compare all source images with a target image
// (target imnages are of same person, so we compare only 1 to both source images)
await Promise.all(sourceFaceIds.map(async (sourceId) => {
// Returns a Promise<Verify Result>
return client.face.verifyFaceToFace(sourceId, targetFaceIds[0])
.then((result) => {
console.log(`Are image IDs \n${sourceId} and \n${targetFaceIds[0]} identical? ${result.isIdentical}`)
// Confidence score 0.0-1.0:
// lower means less of a match, higher is more of a match
console.log(`with ${Math.round(result.confidence * 100)}% confidence.`)
console.log()
}).catch((err) => {
throw err;
})
}))
/**
* END - VERIFY
*/
/**
* PERSON GROUP
* This example creates a person group from several images of different people.
* The group can then be used in other Face API calls to identify, verify, or do snapshot operations.
*/
console.log("---------------------------------")
console.log("PERSON GROUP")
// Create empty Person Group. Returns a Promise<RestResponse>.
await client.personGroup.create(PERSON_GROUP_ID, { 'name': PERSON_GROUP_ID })
.then(() => {
}).catch((err) => {
console.log(err)
throw err
})
console.log('Person group ID: ' + PERSON_GROUP_ID + ' was created.')
// Define woman friend, add them to person group, returns a Promise<Person>
let woman = await client.personGroupPerson.create(PERSON_GROUP_ID, { "name": "Woman" })
.then((wFace) => {
console.log('Person ' + wFace.personId + ' was created.')
return wFace
}).catch((err) => {
throw err
})
// Define man friend, add them to person group, returns a Promise<Person>
let man = await client.personGroupPerson.create(PERSON_GROUP_ID, { "name": "Man" })
.then((mFace) => {
console.log('Person ' + mFace.personId + ' was created.')
return mFace
}).catch((err) => {
throw err
})
// Define child friend, add them to person group, returns a Promise<Person>
let child = await client.personGroupPerson.create(PERSON_GROUP_ID, { "name": "Child" })
.then((chFace) => {
console.log('Person ' + chFace.personId + ' was created.')
return chFace
}).catch((err) => {
throw err
})
console.log()
// Detect faces and register to correct person
// Make array of jpeg images of friends in working directory
let womanImages = new FileSet("w*.jpg").files
let manImages = new FileSet("m*.jpg").files
let childImages = new FileSet("c*.jpg").files
// Add images to the person group person "woman"
for (let wImage of womanImages) {
// Returns a Promise<PersistedFace>
await client.personGroupPerson.addFaceFromStream(PERSON_GROUP_ID,
woman.personId, () => createReadStream(wImage))
.then((face) => {
console.log('ID ' + face.persistedFaceId + ' was added to a person group person called Woman.')
})
}
// Add images to the person group person "man"
for (let mImage of manImages) {
// Returns a Promise<PersistedFace>
await client.personGroupPerson.addFaceFromStream(PERSON_GROUP_ID,
man.personId, () => createReadStream(mImage))
.then((face) => {
console.log('ID ' + face.persistedFaceId + ' was added to a person group person called Man.')
})
}
// Add images to the person group person "child"
for (let chImage of childImages) {
// Returns a Promise<PersistedFace>
await client.personGroupPerson.addFaceFromStream(PERSON_GROUP_ID,
child.personId, () => createReadStream(chImage))
.then((face) => {
console.log('ID ' + face.persistedFaceId + ' was added to a person group person called Child.')
})
}
console.log()
// After all Persons added to person group, train the person group.
console.log('Training the person group...')
await client.personGroup.train(PERSON_GROUP_ID)
while (true) {
let status = await client.personGroup.getTrainingStatus(PERSON_GROUP_ID)
.then((trainingStatusResponse) => {
return trainingStatusResponse
})
console.log('Training status: ' + status.status)
if (status.status == 'failed') {
console.log('Training the person group has failed.')
break;
}
if (status.status == 'succeeded') {
console.log('Training the person group was a success.')
break;
}
await sleep(1000)
}
console.log()
/**
* END - PERSON GROUP
*/
/**
* IDENTIFY
* Identifies a face from a defined person group.
* It uses the detected faces API call from the DETECT FACES example.
*/
console.log("---------------------------------")
console.log("IDENTIFY")
// Detect the faces from the group photo
let groupPhoto = 'test-image-person-group.jpg'
// API call returns a Promise<DetectedFace[]>, then add their IDs to an array.
var faceIds = []
let groupDetectedFacesIdentify =
await client.face.detectWithStream(() => createReadStream(groupPhoto))
.then((faces) => {
console.log('Face IDs found in target image:')
// Initialize empty array of strings
faceIds = new Array(faces.length)
for (let face of faces) {
faceIds.unshift(face.faceId)
console.log(face.faceId)
}
var filteredIds = faceIds.filter((id) => {
return id != null
})
return filteredIds
}).catch((err) => {
console.log(`No faces detected in target image: ${groupImageUrl}.`)
throw err
})
console.log()
// Identify the faces, API call returns a Promise<IdentifyResult[]>.
// The optional parameter is from Models.FaceIdentifyOptionalParams
// Matches will be drawn from the Person objects created in the Person Group example.
await client.face.identify(groupDetectedFacesIdentify, { "personGroupId": PERSON_GROUP_ID })
// API call returns a Promise<IdentifyResult[]>
.then((identifyResults) => {
for (let result of identifyResults) {
// result.candidates is an IdentifyCandidate[]
for (let candidate of result.candidates) {
console.log('Person ' + candidate.personId
+ ' is identified in the photo with '
+ Math.round(candidate.confidence * 100) + '% confidence.')
}
}
}).catch((err) => {
console.log(err)
throw err
})
console.log()
/**
* END - IDENTIFY
*/
// Helper method to sleep/wait
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* SNAPSHOT
* This example transfers a person group from one region to another region.
* You can also transfer facelists and large person groups or large facelists.
* You can also transfer it to another subscription(change the target subscription key).
* It uses the same client as the above examples for its source client.
*/
console.log("---------------------------------")
console.log("SNAPSHOT")
// Authenticate 2, need an additional (target) client for this example.
let sourceClient = client // renaming to be relevent to this example
// Your general Azure subscription ID is needed for this example only.
// Since we are moving a person group from one region to another in the same
// subscription, our source ID and target ID are the same.
// Add your subscription ID (not to be confused with a subscription key) to your
// environment variables. Find in the Azure portal on your Face overview page.
let subscriptionID = process.env['AZURE_SUBSCRIPTION_ID']
// Add a 2nd Face subscription key to your environment variables. Make sure the
// 2nd set of key and endpoint is in the region or subscription you want to
// transfer to.
let credentials2 = new msRest.ApiKeyCredentials({
inHeader: { 'Ocp-Apim-Subscription-Key': process.env['FACE_SUBSCRIPTION_KEY2'] }
});
let targetClient = new Face.FaceClient(credentials2, process.env['FACE_ENDPOINT2']);
// Take the snapshot from your source region or Face subscription
console.log('Taking the snapshot.')
let takeOperationId = null
await sourceClient.snapshot.take('PersonGroup', PERSON_GROUP_ID, [subscriptionID])
.then((takeResponse) => {
// Get the operation ID at the end of the response URL operation location
takeOperationId = takeResponse.operationLocation.substring(takeResponse.operationLocation.lastIndexOf('/') + 1)
console.log('Operation ID (take): ' + takeOperationId)
}).catch((err) => {
console.log(err)
throw err
})
// With the operation ID, wait for the 'take' operation to complete
let snapshotID = null
// Create an ID for the target region or subscription (can be any alphanumeric, '-', or '_' character)
let personGroupIdTarget = uuidV4() // generates a random uuid
console.log()
console.log('Wait for the snapshot (take) to complete...')
while (true) {
// Get the status of the 'take' operation, the OpeationStatus interface is returned
let status = await sourceClient.snapshot.getOperationStatus(takeOperationId)
.then((takeOperationStatus) => {
return takeOperationStatus
})
console.log('Operation status (take): ' + status.status)
if (status.status == "failed") {
console.log('The taking of the snapshot has failed.')
break;
}
if (status.status == "succeeded") {
// From successful result of the take operation status, get the snapshot ID
snapshotID = status.resourceLocation.substring(status.resourceLocation.lastIndexOf('/') + 1)
console.log('Snapshot ID: ' + snapshotID)
break;
}
// Wait one second
await sleep(1000)
}
console.log()
let applyOperationId = null
// With the snapshot ID, call the 'apply' function on your target client
console.log('Applying the snapshot.')
await targetClient.snapshot.apply(snapshotID, personGroupIdTarget)
.then((applyResponse) => {
// Get the operation ID from the 'apply' response
applyOperationId = applyResponse.operationLocation.substring(applyResponse.operationLocation.lastIndexOf('/') + 1)
console.log('Operation ID (apply): ' + applyOperationId)
}).catch((err) => {
console.log(err)
throw err
})
// Wait for the 'apply' function to complete
console.log()
console.log('Wait for the snapshot (apply) to complete...')
while (true) {
// Get the status of the 'apply' operation, using its operation ID
let status = await sourceClient.snapshot.getOperationStatus(applyOperationId)
.then((applyOperationStatus) => {
return applyOperationStatus
})
console.log('Operation status (apply): ' + status.status)
if (status.status == 'failed') {
console.log('The applying of the snapshot has failed.')
break;
}
if (status.status == 'succeeded') {
break;
}
// Wait one second
await sleep(1000)
}
console.log()
console.log('Snapshot transfer is complete.')
console.log()
console.log("---------------------------------")
console.log("DELETE")
// After the quickstart, delete the person group and facelists from Azure account(s)
client.personGroup.deleteMethod(PERSON_GROUP_ID)
console.log('The source region/subscription person group has been deleted.')
targetClient.personGroup.deleteMethod(personGroupIdTarget)
console.log('The target region/subscription person group has been deleted.')
console.log()
console.log("---------------------------------")
console.log("End of quickstart.")
}
main();