#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 10 13:10:49 2020
@author: rhs
"""
import boto3
import pandas as pd
from functools import reduce
import os
from PIL import Image, ImageDraw, ExifTags, ImageColor, ImageFont
import re
from .base import BaseImageDataHandler
[docs]class ImageFaceAnalysis(BaseImageDataHandler):
"""
The ImageFaceAnalysis class implements AWS Rekognition API endpoint for detecting and analyzing faces in images.
For more information about the API, visit: https://docs.aws.amazon.com/rekognition/latest/dg/faces.html
"""
def __init__(self, personal_acces_key, secret_access_key):
super().__init__(personal_acces_key, secret_access_key)
self.funcDict = {
"emotions": self._getEmotions,
"age": self._getAge,
'features': self._getFeatures}
self.imageList = None
self.response = None
[docs] def initialize(self, imageFileList, region = 'us-east-1'):
"""
Initializes the actual analysis.
Parameters
----------
imageFileList : list,
The list of images to run through the API
Provide full path if images are not in same folder.
Region : string, Default: 'us-east-1'
Your AWS region
Returns
-------
Class object
Example
-------
"""
self.imageList = imageFileList
self.response = [self._get_response(image, region) for image in self.imageList]
[docs] def get(self, attributes = 'all'):
"""
Get the face attributes in a flat data format (not-nested).
Parameters
----------
attributes : list ('emotions', 'age', 'features'), default = 'all'
Returns
-------
Pandas DataFrame
Example
-------
"""
if attributes == 'all':
attributes = ['emotions', 'age', 'features']
df_list = [self._dataExtractor(n) for n in attributes]
df_list.append(self._getBaseData())
return self._reduce_data(df_list = df_list, join_cols = ['imageName', 'faceID'])
[docs] def getResponse(self):
"""
Returns entire response from the AWS Rekognition API in nested format
Returns
-------
List of dicts
Example
-------
"""
return self.response
[docs] def draw(self, outputPath, images = None, conf_threshold = 0, font_size = 16):
"""
Returns boxes with face ID, emotions and confidence level
Parameters
----------
outputPath: str
Folder location for images with boxes
images: str, optional, Default = None
choose subset of images by providing image-names. If none, takes all images as input
conf_threshold: int, optional, default = 0
Only draw boxes with confidence level above the threshold
font_size: int, optional, default = 16
Size of the font
Returns
-------
Images with boxes
Example
-------
"""
if images is None:
iterlist = self.imageList
else:
iterlist = images
clean_imageNames = [re.split(' |/|\\\\', pathNames)[-1] for pathNames in iterlist]
for imageFile in range(len(iterlist)):
with open(iterlist[imageFile], 'rb') as image:
draw_image = Image.open(image)
imgWidth, imgHeight = draw_image.size
draw = ImageDraw.Draw(draw_image)
id_counter = 0
for label in self.response[imageFile]['FaceDetails']:
id_counter += 1
box = label['BoundingBox']
left = imgWidth * box['Left']
top = imgHeight * box['Top']
width = imgWidth * box['Width']
height = imgHeight * box['Height']
points = (
(left,top),
(left + width, top),
(left + width, top + height),
(left , top + height),
(left, top)
)
maxConfEmotion = max(label['Emotions'], key=lambda x:x['Confidence'])
if int(maxConfEmotion['Confidence']) >= conf_threshold:
draw.line(points, fill='#00d400', width=2)
usr_font = ImageFont.truetype("arial.ttf", font_size)
text_position = (left, top)
box_label = "FID: {id}, {emotion}: {conf}".format(id = str(id_counter), emotion = maxConfEmotion['Type'], conf = str(int(maxConfEmotion['Confidence'])))
draw.text(text_position, box_label, fill='RED', font = usr_font)
else:
continue
draw_image.save(outputPath + clean_imageNames[imageFile])
def _get_response(self, imageFile, region):
return super().client(region = region).detect_faces(Image={
'Bytes': open(imageFile, 'rb').read()}, Attributes = ['ALL'])
def _getEmotions(self):
holder_labels = []
for n in range(len(self.imageList)):
## If no labels detected, still save the info:
if len(self.response[n]['FaceDetails']) == 0:
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
holder_labels.append(temp_dict)
else:
label_counter = 1
for label in self.response[n]['FaceDetails']:
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
temp_dict["faceID"] = label_counter
temp_dict["Emotion"] = max(label['Emotions'], key=lambda x:x['Confidence'])['Type']
temp_dict['Emotion_conf'] = max(label['Emotions'], key=lambda x:x['Confidence'])['Confidence']
label_counter +=1 # update for the next label
holder_labels.append(temp_dict)
return pd.DataFrame(holder_labels)
def _getAge(self):
temp = []
for n in range(len(self.imageList)):
if len(self.response[n]['FaceDetails']) == 0:
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
temp.append(temp_dict)
else:
label_counter = 1
for label in self.response[n]['FaceDetails']:
temp_dict = {}
temp_dict['imageName'] = self.imageList[n]
temp_dict['faceID'] = label_counter
temp_dict['AgeRange_low'] = label['AgeRange']['Low']
temp_dict['AgeRange_high'] = label['AgeRange']['High']
label_counter += 1
temp.append(temp_dict)
return pd.DataFrame(temp)
def _getFeatures(self):
temp = []
for n in range(len(self.imageList)):
if len(self.response[n]['FaceDetails']) == 0:
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
temp.append(temp_dict)
else:
label_counter = 1
for label in self.response[n]['FaceDetails']:
temp_dict = {}
temp_dict['imageName'] = self.imageList[n]
temp_dict['faceID'] = label_counter
temp_dict['Beard'] = label['Beard']['Value']
temp_dict['Beard_conf'] = label['Beard']['Confidence']
temp_dict['Eyeglasses'] = label['Eyeglasses']['Value']
temp_dict['Eyeglasses_conf'] = label['Eyeglasses']['Confidence']
temp_dict['EyesOpen'] = label['EyesOpen']['Value']
temp_dict['EyeOpen_conf'] = label['EyesOpen']['Confidence']
temp_dict['Gender'] = label['Gender']['Value']
temp_dict['Gender_conf'] = label['Gender']['Confidence']
temp_dict['MouthOpen'] = label['MouthOpen']['Value']
temp_dict['MouthOpen_conf'] = label['MouthOpen']['Confidence']
temp_dict['Mustache'] = label['Mustache']['Value']
temp_dict['Mustache_conf'] = label['Mustache']['Confidence']
temp_dict['Smile'] = label['Smile']['Value']
temp_dict['Smile_conf'] = label['Smile']['Confidence']
temp_dict['Sunglasses'] = label['Sunglasses']['Value']
temp_dict['Sunglasses_conf'] = label['Sunglasses']['Confidence']
label_counter += 1
temp.append(temp_dict)
return pd.DataFrame(temp)
def _getBaseData(self):
temp = []
for n in range(len(self.imageList)):
if len(self.response[n]['FaceDetails']) == 0:
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
temp.append(temp_dict)
else:
label_counter = 1
for label in self.response[n]['FaceDetails']:
temp_dict = {}
temp_dict['imageName'] = self.imageList[n]
temp_dict['faceID'] = label_counter
temp_dict['faceConf'] = label['Confidence']
label_counter += 1
temp.append(temp_dict)
return pd.DataFrame(temp)
def _dataExtractor(self, attribute):
'''
Indsæt basedata-handler i denne del. Slet fra de enkelte extractor-funtioner.
byg de andre som funktion der returnerer dict.
'''
return self.funcDict[attribute]()
def _reduce_data(self, df_list, join_cols):
return reduce(lambda left,right: pd.merge(left,right, on = join_cols), df_list)
[docs]class ImageObjectAnalysis(BaseImageDataHandler):
"""
The ImageObjectAnalysis class implements AWS Rekognition API endpoint for detecting and analyzing objects and scenes in images.
For more information about the API, visit: https://docs.aws.amazon.com/rekognition/latest/dg/labels.html
"""
def __init__(self, personal_acces_key, secret_access_key):
super().__init__(personal_acces_key, secret_access_key)
[docs] def initialize(self, imageFileList, region = 'us-east-1'):
"""
Initializes the actual analysis.
Parameters
----------
imageFileList : list,
The list of images to run through the API
Provide full path if images are not in same folder.
Region : string, Default: 'us-east-1'
Your AWS region
Returns
-------
Class object
Example
-------
"""
self.imageList = imageFileList
self.response = [self._get_response(image, region) for image in self.imageList]
[docs] def get(self):
"""
Get the objects and scenes in a flat data format (not-nested).
Returns
-------
Pandas DataFrame
Example
-------
"""
temp = []
for n in range(len(self.imageList)):
## If no labels detected, still save the info:
if len(self.response[n]['Labels']) == 0:
#print ("No Labels Detected")
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
temp_dict["objectID"] = None
temp_dict["ObjectName"] = None
temp_dict["objectConf"] = None
temp.append(temp_dict)
else:
label_counter = 1
for label in self.response[n]['Labels']:
#print (label['Name'] + ' : ' + str(label['Confidence']))
temp_dict = {}
temp_dict["imageName"] = self.imageList[n]
#temp_dict["full_detect_labels_response"] = response
temp_dict["objectID"] = label_counter
temp_dict["object"] = label['Name']
temp_dict["objectConf"] = label['Confidence']
if len(label['Parents']) != 0:
temp_dict["parent"] = label['Parents'][0]['Name']
else:
temp_dict['parent'] = None
label_counter +=1 # update for the next label
temp.append(temp_dict)
return pd.DataFrame(temp)
def _get_response(self, imageFile, region):
return super().client(region = region).detect_labels(Image={
'Bytes': open(imageFile,'rb').read()})