Based on the answer supplied by @Sean, I wanted to add another way to get the bytes from a URL request using axios and passed to rekognition.detectLabels()
-- or other various detection methods for Amazon Rekognition.
I went ahead create a promise for fs.readFile
that should work with the async/await structure. Then some regex to determine if you need a URL fetch or file read as a fallback.
I've also added a check for Gray
and World Of Warcraft
for the labels. Not sure if anyone else experiences that but lorempixel seems to throw those labels every once in a while. I've seen them show on an all black image before as well.
/* jshint esversion: 6, node:true, devel: true, undef: true, unused: true */
const AWS = require('aws-sdk'),
axios = require('axios'),
fs = require('fs'),
path = require('path');
// Get credentials from environmental variables.
const {S3_ACCESS_KEY, S3_SECRET_ACCESS_KEY, S3_REGION} = process.env;
// Set AWS credentials.
AWS.config.update({
accessKeyId: S3_ACCESS_KEY,
secretAccessKey: S3_SECRET_ACCESS_KEY,
region: S3_REGION
});
const rekognition = new AWS.Rekognition({
apiVersion: '2016-06-27'
});
startDetection();
// ----------------
async function startDetection() {
let found = {};
found = await detectFromPath(path.join(__dirname, 'test.jpg'));
console.log(found);
found = await detectFromPath('https://upload.wikimedia.org/wikipedia/commons/9/96/Bill_Nye%2C_Barack_Obama_and_Neil_deGrasse_Tyson_selfie_2014.jpg');
console.log(found);
found = await detectFromPath('http://placekitten.com/g/200/300');
console.log(found);
found = await detectFromPath('https://loremflickr.com/g/320/240/text');
console.log(found);
found = await detectFromPath('http://lorempixel.com/400/200/sports/');
console.log(found);
// Sometimes 'Grey' and 'World Of Warcraft' are the only labels...
if (found && found.labels.length === 2 && found.labels.some(i => i.Name === 'Gray') && found.labels.some(i => i.Name === 'World Of Warcraft')) {
console.log('⚠️', '\n\tMaybe this is a bad image...`Gray` and `World Of Warcraft`???\n');
}
}
// ----------------
/**
* @param {string} path URL or filepath on your local machine.
* @param {Number} maxLabels
* @param {Number} minConfidence
* @param {array} attributes
*/
async function detectFromPath(path, maxLabels, minConfidence, attributes) {
// Convert path to base64 Buffer data.
const bytes = (/^https?:\/\//gm.exec(path)) ?
await getBase64BufferFromURL(path) :
await getBase64BufferFromFile(path);
// Invalid data.
if (!bytes)
return {
path,
faces: [],
labels: [],
text: [],
celebs: [],
moderation: []
};
// Pass buffer to rekognition methods.
let labels = await detectLabelsFromBytes(bytes, maxLabels, minConfidence),
text = await detectTextFromBytes(bytes),
faces = await detectFacesFromBytes(bytes, attributes),
celebs = await recognizeCelebritiesFromBytes(bytes),
moderation = await detectModerationLabelsFromBytes(bytes, minConfidence);
// Filter out specific values.
labels = labels && labels.Labels ? labels.Labels : [];
faces = faces && faces.FaceDetails ? faces.FaceDetails : [];
text = text && text.TextDetections ? text.TextDetections.map(i => i.DetectedText) : [];
celebs = celebs && celebs.CelebrityFaces ? celebs.CelebrityFaces.map(i => ({
Name: i.Name,
MatchConfidence: i.MatchConfidence
})) : [];
moderation = moderation && moderation.ModerationLabels ? moderation.ModerationLabels.map(i => ({
Name: i.Name,
Confidence: i.Confidence
})) : [];
// Return collection.
return {
path,
faces,
labels,
text,
celebs,
moderation
};
}
/**
* https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback
*
* @param {string} filename
*/
function getBase64BufferFromFile(filename) {
return (new Promise(function(resolve, reject) {
fs.readFile(filename, 'base64', (err, data) => {
if (err) return reject(err);
resolve(new Buffer(data, 'base64'));
});
})).catch(error => {
console.log('[ERROR]', error);
});
}
/**
* https://github.com/axios/axios
*
* @param {string} url
*/
function getBase64BufferFromURL(url) {
return axios
.get(url, {
responseType: 'arraybuffer'
})
.then(response => new Buffer(response.data, 'base64'))
.catch(error => {
console.log('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/labels.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#detectLabels-property
*
* @param {Buffer} bytes
* @param {Number} maxLabels
* @param {Number} minConfidence
*/
function detectLabelsFromBytes(bytes, maxLabels, minConfidence) {
return rekognition
.detectLabels({
Image: {
Bytes: bytes
},
MaxLabels: typeof maxLabels !== 'undefined' ? maxLabels : 1000,
MinConfidence: typeof minConfidence !== 'undefined' ? minConfidence : 50.0
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/text-detection.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#detectText-property
*
* @param {Buffer} bytes
*/
function detectTextFromBytes(bytes) {
return rekognition
.detectText({
Image: {
Bytes: bytes
}
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/celebrities.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#recognizeCelebrities-property
*
* @param {Buffer} bytes
*/
function recognizeCelebritiesFromBytes(bytes) {
return rekognition
.recognizeCelebrities({
Image: {
Bytes: bytes
}
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/moderation.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#detectModerationLabels-property
*
* @param {Buffer} bytes
* @param {Number} minConfidence
*/
function detectModerationLabelsFromBytes(bytes, minConfidence) {
return rekognition
.detectModerationLabels({
Image: {
Bytes: bytes
},
MinConfidence: typeof minConfidence !== 'undefined' ? minConfidence : 60.0
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/faces.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#detectFaces-property
*
* @param {Buffer} bytes
* @param {array} attributes Attributes can be "ALL" or "DEFAULT". "DEFAULT" includes: BoundingBox, Confidence, Landmarks, Pose, and Quality.
*/
function detectFacesFromBytes(bytes, attributes) {
return rekognition
.detectFaces({
Image: {
Bytes: bytes
},
Attributes: typeof attributes !== 'undefined' ? attributes : ['ALL']
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
/**
* https://docs.aws.amazon.com/rekognition/latest/dg/API_CompareFaces.html
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Rekognition.html#compareFaces-property
*
* @param {Buffer} sourceBytes
* @param {Buffer} targetBytes
* @param {Number} similarityThreshold
*/
function compareFaces(sourceBytes, targetBytes, similarityThreshold) {
return rekognition
.detectModerationLabels({
SourceImage: {
Bytes: sourceBytes
},
TargetImage: {
Bytes: targetBytes
},
SimilarityThreshold: typeof similarityThreshold !== 'undefined' ? similarityThreshold : 0.0
})
.promise()
.catch(error => {
console.error('[ERROR]', error);
});
}
Resources:
AWS JavaScript SDK Reference:
Reference: