Assicurare la qualità e l’autenticità di un dato può fare la differenza in determinate situazioni. Se parliamo di CAWI, gli strumenti a disposizione sono davvero tanti, tanto in fase di compilazione quanto in fase di post elaborazione. Spesso sono gli stessi Panel provider che eliminano gli speeders dalla conta delle complete, mentre i cheaters li si esclude con l’analisi della varianza sulle risposte date.

Ci sono anche contesti nei quali è fondamentale esser sicuri che il rispondente sia esattamente la persona che abbiamo reclutato o che abbia determinate caratteristiche, riferite ad esempio all’età o al sesso. In questi casi ci viene in aiuto la tecnologia: attraverso l’uso del deep learning possiamo stabilire se una foto acquisita in fase di intervista sia, entro un certo livello di confidenza, congruente con quanto dichiarato.

Il flusso di lavoro è il seguente:
1. il partecipante inizia la compilazione del questionario
2. il sistema online acquisisce il consenso al recupero delle immagini da webcam
3. il sistema cattura i fotogrammi in background ad un intervallo predefinito di tempo
4. il sistema invia i fotogrammi ad un servizio online che fornisce risposte in tempo reale
5. in tempo reale il sistema consente la compilazione del questionario o richiede ulteriori conferme

La parte più interessante di questo flusso è di sicuro il servizio online: si stratta di software in grado di effettuare computazioni basate su algoritmi di deep learning. Ragionando in termini di servizi online, è necessario creare un servizio che gestisca la memorizzazione del fotogramma e che quindi lo elabori restituendo un risultato.

Mettiamoci all’opera, semplificando un po’ i concetti, con Amazon AWS.

I servizi di cui abbiamo bisogno sono: API Gateway, per la gestione delle richieste HTTP RESTful, S3 per la memorizzazione delle immagini, AWS Lamba per l’esecuzione di funzioni serverless ed infine Amazon Rekognition per la parte di Image Analytics.

Configureremo API Gateway in modo da ricevere richieste POST con un allegato di tipo MIME. Allo stesso tempo ci occuperemo della creazione del bucket su S3 dove salvare le foto e della creazione di un ruolo che abbia accesso al bucket. Quindi è il momento di creare una funzione serverless Lambda che utilizzerà il ruolo appena creato.

Prima salviamo l’immagine (la routine per comodità è in Python 3.8).


import json
import boto3
import base64s3 = boto3.client('s3')
def lambda_handler(event, context):
    print event
    if event['httpMethod'] == 'POST' : 
        print event['body']
        data = json.loads(event['body'])
        name = data['name']
        image = data['file']
        image = image[image.find(",")+1:]
        dec = base64.b64decode(image + "===")
        s3.put_object(Bucket='strategoiexample011', Key=name, Body=dec)
        

Quindi la elaboriamo con il servizio Aws Rekognition (per comodità ho creato una funzione separata).

def detect_faces(photo, bucket):
  client=boto3.client('rekognition')
  response = client.detect_faces(Image={'S3Object'{'Bucket':bucket,'Name':photo}},Attributes ['ALL'])
  print('Detected faces for ' + photo)    
  for faceDetail in response['FaceDetails']:
     print('The detected face is between ' + str(faceDetail['AgeRange']['Low']) 
           + ' and ' + str(faceDetail['AgeRange']['High']) + ' years old')
     print('Here are the other attributes:')
     print(json.dumps(faceDetail, indent=4, sort_keys=True))
  return response['FaceDetails']

La funzione sarà invocata nel main handler creato prima, che restituirà quindi l’esito dell’analisi, cioè l’oggetto response[‘FaceDetails’].

In realtà con questi semplici step, abbiamo già creato il nostro servizio. Adesso è il momento di metterlo all’opera.
Facendo un upload della mia foto attraverso Postman, ho ottenuto questi risultati:



Request
{ "Image": { "Bytes": "..." }, "Attributes": [ "ALL" ] }

Response
{
"FaceDetails": [
{
"BoundingBox": {
"Width": 0.2542075514793396,
"Height": 0.3645738661289215,
"Left": 0.6322028040885925,
"Top": 0.3354707360267639
},
"AgeRange": {
"Low": 25,
"High": 39
},

"Smile": {
"Value": true,
"Confidence": 99.80260467529297
},
"Eyeglasses": {
"Value": false,
"Confidence": 99.90792083740234
},
"Sunglasses": {
"Value": false,
"Confidence": 99.97628784179688
},
"Gender": {
"Value": "Male",
"Confidence": 97.65699005126953
},

"Beard": {
"Value": true,
"Confidence": 78.71488189697266
},

"Mustache": {
"Value": false,
"Confidence": 90.36221313476562
},
"EyesOpen": {
"Value": true,
"Confidence": 99.66426849365234
},
"MouthOpen": {
"Value": true,
"Confidence": 90.6593246459961
},
"Emotions": [
{
"Type": "ANGRY",
"Confidence": 0.06492989510297775
},
{
"Type": "DISGUSTED",
"Confidence": 0.09222755581140518
},
{
"Type": "SAD",
"Confidence": 0.03406735882163048
},
{
"Type": "HAPPY",
"Confidence": 99.48297119140625
},
{
"Type": "CONFUSED",
"Confidence": 0.05608619377017021
},
{
"Type": "CALM",
"Confidence": 0.050491537898778915
},
{
"Type": "FEAR",
"Confidence": 0.039677493274211884
},
{
"Type": "SURPRISED",
"Confidence": 0.17954103648662567
}
],
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.6967881321907043,
"Y": 0.4715764820575714
},
{
"Type": "eyeRight",
"X": 0.8143302202224731,
"Y": 0.4946686625480652
},
{
"Type": "mouthLeft",
"X": 0.6859468817710876,
"Y": 0.5914708375930786
},
{
"Type": "mouthRight",
"X": 0.7835327386856079,
"Y": 0.6110738515853882
},
{
"Type": "nose",
"X": 0.7350462675094604,
"Y": 0.544281542301178
},
{
"Type": "leftEyeBrowLeft",
"X": 0.6600617170333862,
"Y": 0.43561387062072754
},
{
"Type": "leftEyeBrowRight",
"X": 0.7236730456352234,
"Y": 0.4404982328414917
},
{
"Type": "leftEyeBrowUp",
"X": 0.6928105354309082,
"Y": 0.4277587831020355
},
{
"Type": "rightEyeBrowLeft",
"X": 0.7925868630409241,
"Y": 0.45375072956085205
},
{
"Type": "rightEyeBrowRight",
"X": 0.8686988949775696,
"Y": 0.4767759144306183
},
{
"Type": "rightEyeBrowUp",
"X": 0.8305615186691284,
"Y": 0.4547572135925293
},
{
"Type": "leftEyeLeft",
"X": 0.6787296533584595,
"Y": 0.46668559312820435
},
{
"Type": "leftEyeRight",
"X": 0.7201690077781677,
"Y": 0.47640737891197205
},
{
"Type": "leftEyeUp",
"X": 0.6979344487190247,
"Y": 0.46520718932151794
},
{
"Type": "leftEyeDown",
"X": 0.6971128582954407,
"Y": 0.47618409991264343
},
{
"Type": "rightEyeLeft",
"X": 0.7896472811698914,
"Y": 0.4898136556148529
},
{
"Type": "rightEyeRight",
"X": 0.8342260122299194,
"Y": 0.4967969059944153
},
{
"Type": "rightEyeUp",
"X": 0.8132385611534119,
"Y": 0.48752060532569885
},
{
"Type": "rightEyeDown",
"X": 0.810785710811615,
"Y": 0.4982454180717468
},
{
"Type": "noseLeft",
"X": 0.7157284021377563,
"Y": 0.5546145439147949
},
{
"Type": "noseRight",
"X": 0.7596883773803711,
"Y": 0.5624375343322754
},
{
"Type": "mouthUp",
"X": 0.7317274808883667,
"Y": 0.5866525173187256
},
{
"Type": "mouthDown",
"X": 0.7267897129058838,
"Y": 0.6221946477890015
},
{
"Type": "leftPupil",
"X": 0.6967881321907043,
"Y": 0.4715764820575714
},
{
"Type": "rightPupil",
"X": 0.8143302202224731,
"Y": 0.4946686625480652
},
{
"Type": "upperJawlineLeft",
"X": 0.6413102149963379,
"Y": 0.46118950843811035
},
{
"Type": "midJawlineLeft",
"X": 0.6411712765693665,
"Y": 0.5949477553367615
},
{
"Type": "chinBottom",
"X": 0.7200525999069214,
"Y": 0.6850767731666565
},
{
"Type": "midJawlineRight",
"X": 0.8511054515838623,
"Y": 0.6353005766868591
},
{
"Type": "upperJawlineRight",
"X": 0.90281742811203,
"Y": 0.5114059448242188
}
],
"Pose": {
"Roll": 9.8366117477417,
"Yaw": -3.0201027393341064,
"Pitch": -3.0386173725128174
},
"Quality": {
"Brightness": 84.01234436035156,
"Sharpness": 94.08262634277344
},
"Confidence": 100
}
]
}

Ci ha preso!

Immaginate quindi di ripetere questi controlli in background su 10 o 20 fotogrammi acquisiti in tempo reale durante la compilazione e di bloccare la compilazione con delle domande di conferma, se i dati sono differenti dal dichiarato. Evidentemente la possibilità di compilazioni fatte in maniera fraudolenta si ridurrebbe a percentuali ad una cifra.