├── .gitignore ├── README.md ├── faceBlur.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.zip 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda Face Blur 2 | 3 | Lambda that blurs faces on S3 images detected by [AWS Rekognition](https://aws.amazon.com/rekognition/). 4 | 5 | ![](https://gitcdn.link/repo/veelenga/bin/master/aws-lambda-face-blur/demo.jpeg) 6 | 7 | [Credits ](https://unsplash.com/photos/JpCOGj0uIlI) 8 | 9 | 10 | ## Usage 11 | 12 | ```sh 13 | $ npm install 14 | $ zip -r ./face_blur.zip * 15 | ``` 16 | 17 | Deploy archive to AWS. 18 | -------------------------------------------------------------------------------- /faceBlur.js: -------------------------------------------------------------------------------- 1 | const async = require('async'), 2 | AWS = require('aws-sdk'), 3 | gm = require('gm').subClass({ imageMagick: true }); 4 | 5 | const s3 = new AWS.S3(); 6 | const rekognition = new AWS.Rekognition(); 7 | 8 | const destDir = 'no-face'; 9 | 10 | exports.handler = (event, context, callback) => { 11 | const srcBucket = event.Records[0].s3.bucket.name; 12 | const srcKey = event.Records[0].s3.object.key; 13 | 14 | async.waterfall([ 15 | function download(next) { 16 | s3.getObject({ 17 | Bucket: srcBucket, 18 | Key: srcKey 19 | }, next); 20 | }, 21 | 22 | function detectFaces(response, next) { 23 | var params = { 24 | Image: { 25 | S3Object: { 26 | Bucket: srcBucket, 27 | Name: srcKey 28 | } 29 | }, 30 | Attributes: ['DEFAULT'] 31 | }; 32 | 33 | rekognition.detectFaces(params, (err, data) => { 34 | if (err){ 35 | next(err); 36 | } else { 37 | next(null, response, data.FaceDetails); 38 | } 39 | }); 40 | }, 41 | 42 | function blur(response, faceDetails, next) { 43 | let img = gm(response.Body); 44 | 45 | img.size(function(err, value){ 46 | if (err) { 47 | next(err); 48 | } else { 49 | faceDetails.forEach((faceDetail) => { 50 | const box = faceDetail.BoundingBox, 51 | width = box.Width * value.width, 52 | height = box.Height * value.height, 53 | left = box.Left * value.width, 54 | top = box.Top * value.height; 55 | 56 | img.region(width, height, left, top).blur(0, 50); 57 | }); 58 | 59 | img.toBuffer(function(err, buffer) { 60 | if(err) { 61 | next(err); 62 | } else { 63 | next(null, response.ContentType, buffer); 64 | } 65 | }); 66 | } 67 | }); 68 | }, 69 | 70 | function putObject(contentType, buffer, next) { 71 | let dest = srcKey.split("/"); 72 | dest.shift(); 73 | dest.unshift(destDir); 74 | 75 | let destKey = dest.join("/"); 76 | 77 | let obj = { Bucket : srcBucket, Key: destKey, Body : buffer, ContentType : contentType, ACL:'public-read' }; 78 | 79 | s3.putObject(obj, function(err, result) { 80 | if (err) { 81 | next(err); 82 | } else { 83 | next(null); 84 | } 85 | }); 86 | } 87 | ], 88 | 89 | function (err) { 90 | if (err) { 91 | console.error(err); 92 | callback(err); 93 | } else { 94 | callback(null, 'success'); 95 | } 96 | }); 97 | }; 98 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-lambda-face-blur", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "array-parallel": { 8 | "version": "0.1.3", 9 | "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", 10 | "integrity": "sha1-j3hTCJJu1apHjEfmTRszS2wMlH0=" 11 | }, 12 | "array-series": { 13 | "version": "0.1.5", 14 | "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", 15 | "integrity": "sha1-3103v8XC7wdV4qpPkv6ufUtaly8=" 16 | }, 17 | "async": { 18 | "version": "2.6.4", 19 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 20 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 21 | "requires": { 22 | "lodash": "^4.17.14" 23 | } 24 | }, 25 | "cross-spawn": { 26 | "version": "4.0.2", 27 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", 28 | "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", 29 | "requires": { 30 | "lru-cache": "4.1.1", 31 | "which": "1.3.0" 32 | } 33 | }, 34 | "debug": { 35 | "version": "2.2.0", 36 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 37 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 38 | "requires": { 39 | "ms": "0.7.1" 40 | } 41 | }, 42 | "gm": { 43 | "version": "1.23.0", 44 | "resolved": "https://registry.npmjs.org/gm/-/gm-1.23.0.tgz", 45 | "integrity": "sha1-gKL+nL8TFRUCSEZERlhGEmn1JmE=", 46 | "requires": { 47 | "array-parallel": "0.1.3", 48 | "array-series": "0.1.5", 49 | "cross-spawn": "4.0.2", 50 | "debug": "2.2.0" 51 | } 52 | }, 53 | "isexe": { 54 | "version": "2.0.0", 55 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 56 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 57 | }, 58 | "lodash": { 59 | "version": "4.17.21", 60 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 61 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 62 | }, 63 | "lru-cache": { 64 | "version": "4.1.1", 65 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", 66 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", 67 | "requires": { 68 | "pseudomap": "1.0.2", 69 | "yallist": "2.1.2" 70 | } 71 | }, 72 | "ms": { 73 | "version": "0.7.1", 74 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 75 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" 76 | }, 77 | "pseudomap": { 78 | "version": "1.0.2", 79 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 80 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 81 | }, 82 | "which": { 83 | "version": "1.3.0", 84 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 85 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 86 | "requires": { 87 | "isexe": "2.0.0" 88 | } 89 | }, 90 | "yallist": { 91 | "version": "2.1.2", 92 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 93 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-lambda-face-blur", 3 | "version": "1.0.0", 4 | "description": "AWS Lambda to detect and blur faces on S3 images.", 5 | "main": "faceBlur.js", 6 | "author": "Vitalii Elenhaupt", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/veelenga/aws-lambda-face-blur" 11 | }, 12 | "keywords": [ 13 | "aws", 14 | "lambda", 15 | "s3", 16 | "rekognition", 17 | "face detect", 18 | "face blur" 19 | ], 20 | "dependencies": { 21 | "async": "^2.6.4", 22 | "gm": "^1.23.0" 23 | } 24 | } 25 | --------------------------------------------------------------------------------