I'd like to try Rekognition's CompareFaces, but I don't see a full example of the syntax for using the HTTP API. Assuming I have two images, how would I call this API from Python to retrieve a similarity score?
Information about the code
There is little documentation around using the HTTP API for AWS Rekognition, but it is pretty simple to use the model that most code uses to hit AWS service HTTP endpoints.
Important information about the code that follows:
You must have
requests
installed. If you don't have it, you can run the following in your shell (doing it invirtualenv
is recommended).pip install requests
The
us-east-1
region is used. Rekognition is currently supported inus-east-1
,eu-west-1
, andus-west-2
so you can modify the code to support different region endpoints as you wish.It expects two files to exist on disk for reading, called
source.jpg
andtarget.jpg
.As she's in the most recent movie I saw, I'm using images of Felicity Jones from Star Wars: Rogue One as my source and target.
It includes code to do signing with AWS Signature Version 4. There are libraries out there that will do the signature generation for you, but I didn't want to rely too much on third party libs in order to demonstrate a complete example.
The AWS credentials you're using should have a valid policy for Rekognition.
It was written for Python 2.7 (shouldn't be terribly difficult to move to Python 3).
The Code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import base64
import datetime
import hashlib
import hmac
import json
import requests
# Key derivation functions
# http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, date_stamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), date_stamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
if __name__ == '__main__':
# Read credentials from the environment
access_key = os.environ.get('AWS_ACCESS_KEY_ID')
secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
# Uncomment this line if you use temporary credentials via STS or similar
#token = os.environ.get('AWS_SESSION_TOKEN')
if access_key is None or secret_key is None:
print('No access key is available.')
sys.exit()
# This code shows the v4 request signing process as shown in
# http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
host = 'rekognition.us-east-1.amazonaws.com'
endpoint = 'https://rekognition.us-east-1.amazonaws.com'
service = 'rekognition'
# Currently, all Rekognition actions require POST requests
method = 'POST'
region = 'us-east-1'
# This defines the service target and sub-service you want to hit
# In this case you want to use 'CompareFaces'
amz_target = 'RekognitionService.CompareFaces'
# Amazon content type - Rekognition expects 1.1 x-amz-json
content_type = 'application/x-amz-json-1.1'
# Create a date for headers and the credential string
now = datetime.datetime.utcnow()
amz_date = now.strftime('%Y%m%dT%H%M%SZ')
date_stamp = now.strftime('%Y%m%d') # Date w/o time, used in credential scope
# Canonical request information
canonical_uri = '/'
canonical_querystring = ''
canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-target:' + amz_target + '\n'
# list of signed headers
signed_headers = 'content-type;host;x-amz-date;x-amz-target'
# Our source image: http://i.imgur.com/OK8aDRq.jpg
with open('source.jpg', 'rb') as source_image:
source_bytes = base64.b64encode(source_image.read())
# Our target image: http://i.imgur.com/Xchqm1r.jpg
with open('target.jpg', 'rb') as target_image:
target_bytes = base64.b64encode(target_image.read())
# here we build the dictionary for our request data
# that we will convert to JSON
request_dict = {
'SimilarityThreshold': 75.0,
'SourceImage': {
'Bytes': source_bytes
},
'TargetImage': {
'Bytes': target_bytes
}
}
# Convert our dict to a JSON string as it will be used as our payload
request_parameters = json.dumps(request_dict)
# Generate a hash of our payload for verification by Rekognition
payload_hash = hashlib.sha256(request_parameters).hexdigest()
# All of this is
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()
signing_key = getSignatureKey(secret_key, date_stamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = { 'Content-Type': content_type,
'X-Amz-Date': amz_date,
'X-Amz-Target': amz_target,
# uncomment this if you uncommented the 'token' line earlier
#'X-Amz-Security-Token': token,
'Authorization': authorization_header}
r = requests.post(endpoint, data=request_parameters, headers=headers)
# Let's format the JSON string returned from the API for better output
formatted_text = json.dumps(json.loads(r.text), indent=4, sort_keys=True)
print('Response code: {}\n'.format(r.status_code))
print('Response body:\n{}'.format(formatted_text))
Code Output
If you get the code running, it should output something like this:
Response code: 200
Response body:
{
"FaceMatches": [],
"SourceImageFace": {
"BoundingBox": {
"Height": 0.9448398351669312,
"Left": 0.12222222238779068,
"Top": -0.017793593928217888,
"Width": 0.5899999737739563
},
"Confidence": 99.99041748046875
}
}
Really, just use boto3
The simplest thing you can do is to use boto3
.
The code would be simplified to something like the following, as all the signature generation and JSON work become unnecessary.
Be sure that you have configured boto3
with credentials in the environment or via the configuration file, or put your credentials inline with the code. For more information, see boto3
configuration.
The code for this uses the boto3
Rekognition API.
import pprint
import boto3
# Set this to whatever percentage of 'similarity'
# you'd want
SIMILARITY_THRESHOLD = 75.0
if __name__ == '__main__':
client = boto3.client('rekognition')
# Our source image: http://i.imgur.com/OK8aDRq.jpg
with open('source.jpg', 'rb') as source_image:
source_bytes = source_image.read()
# Our target image: http://i.imgur.com/Xchqm1r.jpg
with open('target.jpg', 'rb') as target_image:
target_bytes = target_image.read()
response = client.compare_faces(
SourceImage={ 'Bytes': source_bytes },
TargetImage={ 'Bytes': target_bytes },
SimilarityThreshold=SIMILARITY_THRESHOLD
)
pprint.pprint(response)
The above boto3
example should output this:
{u'FaceMatches': [],
'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
'content-length': '195',
'content-type': 'application/x-amz-json-1.1',
'date': 'Sat, 31 Dec 2016 23:15:56 GMT',
'x-amzn-requestid': '13edda2d-cfaf-11e6-9999-d3abf4c2feb3'},
'HTTPStatusCode': 200,
'RequestId': '13edda2d-cfaf-11e6-9999-d3abf4c2feb3',
'RetryAttempts': 0},
u'SourceImageFace': {u'BoundingBox': {u'Height': 0.9448398351669312,
u'Left': 0.12222222238779068,
u'Top': -0.017793593928217888,
u'Width': 0.5899999737739563},
u'Confidence': 99.99041748046875}}
boto3
as it certainly looks simpler. Shouldn't the initial call be something like: client = boto3.client('rekognition', aws_access_key_id=key, aws_secret_access_key=secret, region_name=region )
? –
Dayak boto3
: An error occurred (AccessDeniedException) when calling the CompareFaces operation: User: X is not authorized to perform: rekognition:CompareFaces.
Perhaps I need to double check my keys, but I wanted to check that the client is set up correctly. –
Dayak rekognition
resources. –
Cakewalk boto3
example, I get: botocore.exceptions.ClientError: An error occurred (InvalidImageFormatException) when calling the CompareFaces operation: Invalid image encoding
. I've additionally tried other images and formats and see the same error. –
Dayak FaceMatches
list in the above output: u'FaceMatches': [{u'Face': {u'BoundingBox': {}, u'Confidence': 99.9865951538086}, u'Similarity': 0.0}]
. With a score of 0, Rekognition was unable to match this challenging pair of images. –
Dayak © 2022 - 2024 — McMap. All rights reserved.
boto3
? – Cakewalk