diff --git a/.gitignore b/.gitignore index cfef3df..46ab33b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Distribution / packaging +*.pyc .Python .env node_modules diff --git a/doorman/__pycache__/__init__.cpython-36.pyc b/doorman/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 26fbd80..0000000 Binary files a/doorman/__pycache__/__init__.cpython-36.pyc and /dev/null differ diff --git a/doorman/__pycache__/guess.cpython-36.pyc b/doorman/__pycache__/guess.cpython-36.pyc deleted file mode 100644 index c605a08..0000000 Binary files a/doorman/__pycache__/guess.cpython-36.pyc and /dev/null differ diff --git a/doorman/__pycache__/train.cpython-36.pyc b/doorman/__pycache__/train.cpython-36.pyc deleted file mode 100644 index db21e9f..0000000 Binary files a/doorman/__pycache__/train.cpython-36.pyc and /dev/null differ diff --git a/doorman/__pycache__/unknown.cpython-36.pyc b/doorman/__pycache__/unknown.cpython-36.pyc deleted file mode 100644 index 8a2c656..0000000 Binary files a/doorman/__pycache__/unknown.cpython-36.pyc and /dev/null differ diff --git a/find_person.py b/find_person.py new file mode 100644 index 0000000..9a627bb --- /dev/null +++ b/find_person.py @@ -0,0 +1,123 @@ +import os +from threading import Timer +import time +import datetime +import awscam +import cv2 +from botocore.session import Session +from threading import Thread + +# Setup the S3 client +session = Session() +s3 = session.create_client('s3') +s3_bucket = 'doorman-faces' + +# setup the camera and frame +ret, frame = awscam.getLastFrame() +ret,jpeg = cv2.imencode('.jpg', frame) +Write_To_FIFO = True +class FIFO_Thread(Thread): + def __init__(self): + ''' Constructor. ''' + Thread.__init__(self) + + def run(self): + # write to tmp file for local debugging purpose + fifo_path = "/tmp/results.mjpeg" + if not os.path.exists(fifo_path): + os.mkfifo(fifo_path) + f = open(fifo_path,'w') + + # yay, succesful, let's start streaming to the file + while Write_To_FIFO: + try: + f.write(jpeg.tobytes()) + except IOError as e: + continue + +def greengrass_infinite_infer_run(): + try: + modelPath = "/opt/awscam/artifacts/mxnet_deploy_ssd_resnet50_300_FP16_FUSED.xml" + modelType = "ssd" + input_width = 300 + input_height = 300 + max_threshold = 0.60 # raise/lower this value based on your conditions + outMap = { 1: 'aeroplane', 2: 'bicycle', 3: 'bird', 4: 'boat', 5: 'bottle', 6: 'bus', 7 : 'car', 8 : 'cat', 9 : 'chair', 10 : 'cow', 11 : 'dinning table', 12 : 'dog', 13 : 'horse', 14 : 'motorbike', 15 : 'person', 16 : 'pottedplant', 17 : 'sheep', 18 : 'sofa', 19 : 'train', 20 : 'tvmonitor' } + results_thread = FIFO_Thread() + results_thread.start() + + # Load model to GPU + mcfg = {"GPU": 1} + model = awscam.Model(modelPath, mcfg) + + # try to get a frame from the camera + ret, frame = awscam.getLastFrame() + if ret == False: + raise Exception("Failed to get frame from the stream") + + yscale = float(frame.shape[0]/input_height) + xscale = float(frame.shape[1]/input_width) + + doInfer = True + while doInfer: + # Get a frame from the video stream + ret, frame = awscam.getLastFrame() + + # Raise an exception if failing to get a frame + if ret == False: + raise Exception("Failed to get frame from the stream") + + # Resize frame to fit model input requirement + frameResize = cv2.resize(frame, (input_width, input_height)) + + # Run model inference on the resized frame + inferOutput = model.doInference(frameResize) + + # Output inference result to the fifo file so it can be viewed with mplayer + parsed_results = model.parseResult(modelType, inferOutput)['ssd'] + label = '{' + for obj in parsed_results: + if obj['prob'] > max_threshold: + xmin = int( xscale * obj['xmin'] ) + int((obj['xmin'] - input_width/2) + input_width/2) + ymin = int( yscale * obj['ymin'] ) + xmax = int( xscale * obj['xmax'] ) + int((obj['xmax'] - input_width/2) + input_width/2) + ymax = int( yscale * obj['ymax'] ) + + # if a person was found, upload the target area to S3 for further inspection + if outMap[obj['label']] == 'person': + + # get the person image + person = frame[ymin:ymax, xmin:xmax] + + # create a nice s3 file key + s3_key = datetime.datetime.utcnow().strftime('%Y-%m-%d_%H_%M_%S.%f') + '.jpg' + encode_param=[int(cv2.IMWRITE_JPEG_QUALITY), 90] # 90% should be more than enough + _, jpg_data = cv2.imencode('.jpg', person, encode_param) + filename = "incoming/%s" % s3_key # the guess lambda function is listening here + response = s3.put_object(ACL='public-read', Body=jpg_data.tostring(),Bucket=s3_bucket,Key=filename) + + # draw a rectangle around the designated area, and tell what label was found + cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (255, 165, 20), 4) + label += '"{}": {:.2f},'.format(outMap[obj['label']], obj['prob'] ) + label_show = "{}: {:.2f}%".format(outMap[obj['label']], obj['prob']*100 ) + cv2.putText(frame, label_show, (xmin, ymin-15),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 165, 20), 4) + label += '"null": 0.0' + label += '}' + + global jpeg + ret,jpeg = cv2.imencode('.jpg', frame) + + except Exception as e: + print "Crap, something failed: %s" % str(e) + + # Asynchronously schedule this function to be run again in 15 seconds + Timer(15, greengrass_infinite_infer_run).start() + +# Execute the function above +greengrass_infinite_infer_run() + + +# This is a dummy handler and will not be invoked +# Instead the code above will be executed in an infinite loop for our example +def function_handler(event, context): + return diff --git a/handler.py b/handler.py index 107f122..8f2c3e8 100755 --- a/handler.py +++ b/handler.py @@ -16,105 +16,105 @@ if __name__ == "__main__": - # print(unknown( - # { - # "Records": [ - # { - # "eventVersion": "2.0", - # "eventTime": "1970-01-01T00:00:00.000Z", - # "requestParameters": { - # "sourceIPAddress": "127.0.0.1" - # }, - # "s3": { - # "configurationId": "testConfigRule", - # "object": { - # "eTag": "0123456789abcdef0123456789abcdef", - # "sequencer": "0A1B2C3D4E5F678901", - # "key": "detected/U033PFSFB/3aabdb9e2e5c4da13ec13b670cf4f574.jpg", - # "size": 1024 - # }, - # "bucket": { - # "arn": "arn:aws:s3:::doorman-faces", - # "name": "doorman-faces", - # "ownerIdentity": { - # "principalId": "EXAMPLE" - # } - # }, - # "s3SchemaVersion": "1.0" - # }, - # "responseElements": { - # "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", - # "x-amz-request-id": "EXAMPLE123456789" - # }, - # "awsRegion": "us-east-1", - # "eventName": "ObjectCreated:Put", - # "userIdentity": { - # "principalId": "EXAMPLE" - # }, - # "eventSource": "aws:s3" - # } - # ] - # }, {}) - # ) + print(unknown( + { + "Records": [ + { + "eventVersion": "2.0", + "eventTime": "1970-01-01T00:00:00.000Z", + "requestParameters": { + "sourceIPAddress": "127.0.0.1" + }, + "s3": { + "configurationId": "testConfigRule", + "object": { + "eTag": "0123456789abcdef0123456789abcdef", + "sequencer": "0A1B2C3D4E5F678901", + "key": "detected/U033PFSFB/1ef4dfe223eec2b6801aa4873cd3e350.jpg", + "size": 1024 + }, + "bucket": { + "arn": "arn:aws:s3:::doorman-faces", + "name": "doorman-faces", + "ownerIdentity": { + "principalId": "EXAMPLE" + } + }, + "s3SchemaVersion": "1.0" + }, + "responseElements": { + "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", + "x-amz-request-id": "EXAMPLE123456789" + }, + "awsRegion": "us-east-1", + "eventName": "ObjectCreated:Put", + "userIdentity": { + "principalId": "EXAMPLE" + }, + "eventSource": "aws:s3" + } + ] + }, {}) + ) - print(train({ - "resource": "/", - "path": "/", - "httpMethod": "POST", - "headers": { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "en-GB,en-US;q=0.8,en;q=0.6,zh-CN;q=0.4", - "cache-control": "max-age=0", - "CloudFront-Forwarded-Proto": "https", - "CloudFront-Is-Desktop-Viewer": "true", - "CloudFront-Is-Mobile-Viewer": "false", - "CloudFront-Is-SmartTV-Viewer": "false", - "CloudFront-Is-Tablet-Viewer": "false", - "CloudFront-Viewer-Country": "GB", - "content-type": "application/x-www-form-urlencoded", - "Host": "j3ap25j034.execute-api.eu-west-2.amazonaws.com", - "origin": "https://j3ap25j034.execute-api.eu-west-2.amazonaws.com", - "Referer": "https://j3ap25j034.execute-api.eu-west-2.amazonaws.com/dev/", - "upgrade-insecure-requests": "1", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", - "Via": "2.0 a3650115c5e21e2b5d133ce84464bea3.cloudfront.net (CloudFront)", - "X-Amz-Cf-Id": "0nDeiXnReyHYCkv8cc150MWCFCLFPbJoTs1mexDuKe2WJwK5ANgv2A==", - "X-Amzn-Trace-Id": "Root=1-597079de-75fec8453f6fd4812414a4cd", - "X-Forwarded-For": "50.129.117.14, 50.112.234.94", - "X-Forwarded-Port": "443", - "X-Forwarded-Proto": "https" - }, - "queryStringParameters": "", - "pathParameters": "None", - "stageVariables": "None", - "requestContext": { - "path": "/dev/", - "accountId": "125002137610", - "resourceId": "qdolsr1yhk", - "stage": "dev", - "requestId": "0f2431a2-6d2f-11e7-b75152aa497861", - "identity": { - "cognitoIdentityPoolId": None, - "accountId": None, - "cognitoIdentityId": None, - "caller": None, - "apiKey": "", - "sourceIp": "50.129.117.14", - "accessKey": None, - "cognitoAuthenticationType": None, - "cognitoAuthenticationProvider": None, - "userArn": None, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", - "user": None - }, - "resourcePath": "/", - "httpMethod": "POST", - "apiId": "j3azlsj0c4" - }, - "body": "payload=%7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22discard%22%2C%22type%22%3A%22select%22%2C%22selected_options%22%3A%5B%7B%22value%22%3A%22U033PFSFB%22%7D%5D%7D%5D%2C%22callback_id%22%3A%22unknown%5C%2Ftest.jpg%22%2C%22team%22%3A%7B%22id%22%3A%22T02HUP4V7%22%2C%22domain%22%3A%22unitt%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22C8HLA7R63%22%2C%22name%22%3A%22whodis%22%7D%2C%22user%22%3A%7B%22id%22%3A%22U033PFSFB%22%2C%22name%22%3A%22svdgraaf%22%7D%2C%22action_ts%22%3A%221513682920.702541%22%2C%22message_ts%22%3A%221513654753.000074%22%2C%22attachment_id%22%3A%221%22%2C%22token%22%3A%22QfG0e3Guj5VqB8FYRu6t6hsG%22%2C%22is_app_unfurl%22%3Afalse%2C%22original_message%22%3A%7B%22text%22%3A%22Whodis%3F%22%2C%22username%22%3A%22Doorman%22%2C%22bot_id%22%3A%22B8GHY4N9G%22%2C%22attachments%22%3A%5B%7B%22fallback%22%3A%22Nope%3F%22%2C%22image_url%22%3A%22https%3A%5C%2F%5C%2Fs3.amazonaws.com%5C%2Fdoorman-faces%5C%2Funknown%5C%2Ftest.jpg%22%2C%22image_width%22%3A720%2C%22image_height%22%3A480%2C%22image_bytes%22%3A85516%2C%22callback_id%22%3A%22unknown%5C%2Ftest.jpg%22%2C%22id%22%3A1%2C%22color%22%3A%223AA3E3%22%2C%22actions%22%3A%5B%7B%22id%22%3A%221%22%2C%22name%22%3A%22username%22%2C%22text%22%3A%22Select+a+username...%22%2C%22type%22%3A%22select%22%2C%22data_source%22%3A%22users%22%7D%5D%7D%5D%2C%22type%22%3A%22message%22%2C%22subtype%22%3A%22bot_message%22%2C%22ts%22%3A%221513654753.000074%22%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT02HUP4V7%5C%2F288116281232%5C%2FqaNuNww7z6pywPrk3jIHHcHF%22%2C%22trigger_id%22%3A%22288701768947.2606786993.b007d1a39a5076df879809512ef1d063%22%7D", - "isBase64Encoded": False - }, {})) + # print(train({ + # "resource": "/", + # "path": "/", + # "httpMethod": "POST", + # "headers": { + # "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + # "Accept-Encoding": "gzip, deflate, br", + # "Accept-Language": "en-GB,en-US;q=0.8,en;q=0.6,zh-CN;q=0.4", + # "cache-control": "max-age=0", + # "CloudFront-Forwarded-Proto": "https", + # "CloudFront-Is-Desktop-Viewer": "true", + # "CloudFront-Is-Mobile-Viewer": "false", + # "CloudFront-Is-SmartTV-Viewer": "false", + # "CloudFront-Is-Tablet-Viewer": "false", + # "CloudFront-Viewer-Country": "GB", + # "content-type": "application/x-www-form-urlencoded", + # "Host": "j3ap25j034.execute-api.eu-west-2.amazonaws.com", + # "origin": "https://j3ap25j034.execute-api.eu-west-2.amazonaws.com", + # "Referer": "https://j3ap25j034.execute-api.eu-west-2.amazonaws.com/dev/", + # "upgrade-insecure-requests": "1", + # "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", + # "Via": "2.0 a3650115c5e21e2b5d133ce84464bea3.cloudfront.net (CloudFront)", + # "X-Amz-Cf-Id": "0nDeiXnReyHYCkv8cc150MWCFCLFPbJoTs1mexDuKe2WJwK5ANgv2A==", + # "X-Amzn-Trace-Id": "Root=1-597079de-75fec8453f6fd4812414a4cd", + # "X-Forwarded-For": "50.129.117.14, 50.112.234.94", + # "X-Forwarded-Port": "443", + # "X-Forwarded-Proto": "https" + # }, + # "queryStringParameters": "", + # "pathParameters": "None", + # "stageVariables": "None", + # "requestContext": { + # "path": "/dev/", + # "accountId": "125002137610", + # "resourceId": "qdolsr1yhk", + # "stage": "dev", + # "requestId": "0f2431a2-6d2f-11e7-b75152aa497861", + # "identity": { + # "cognitoIdentityPoolId": None, + # "accountId": None, + # "cognitoIdentityId": None, + # "caller": None, + # "apiKey": "", + # "sourceIp": "50.129.117.14", + # "accessKey": None, + # "cognitoAuthenticationType": None, + # "cognitoAuthenticationProvider": None, + # "userArn": None, + # "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", + # "user": None + # }, + # "resourcePath": "/", + # "httpMethod": "POST", + # "apiId": "j3azlsj0c4" + # }, + # "body": "payload=%7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22discard%22%2C%22type%22%3A%22select%22%2C%22selected_options%22%3A%5B%7B%22value%22%3A%22U033PFSFB%22%7D%5D%7D%5D%2C%22callback_id%22%3A%22unknown%5C%2Ftest.jpg%22%2C%22team%22%3A%7B%22id%22%3A%22T02HUP4V7%22%2C%22domain%22%3A%22unitt%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22C8HLA7R63%22%2C%22name%22%3A%22whodis%22%7D%2C%22user%22%3A%7B%22id%22%3A%22U033PFSFB%22%2C%22name%22%3A%22svdgraaf%22%7D%2C%22action_ts%22%3A%221513682920.702541%22%2C%22message_ts%22%3A%221513654753.000074%22%2C%22attachment_id%22%3A%221%22%2C%22token%22%3A%22QfG0e3Guj5VqB8FYRu6t6hsG%22%2C%22is_app_unfurl%22%3Afalse%2C%22original_message%22%3A%7B%22text%22%3A%22Whodis%3F%22%2C%22username%22%3A%22Doorman%22%2C%22bot_id%22%3A%22B8GHY4N9G%22%2C%22attachments%22%3A%5B%7B%22fallback%22%3A%22Nope%3F%22%2C%22image_url%22%3A%22https%3A%5C%2F%5C%2Fs3.amazonaws.com%5C%2Fdoorman-faces%5C%2Funknown%5C%2Ftest.jpg%22%2C%22image_width%22%3A720%2C%22image_height%22%3A480%2C%22image_bytes%22%3A85516%2C%22callback_id%22%3A%22unknown%5C%2Ftest.jpg%22%2C%22id%22%3A1%2C%22color%22%3A%223AA3E3%22%2C%22actions%22%3A%5B%7B%22id%22%3A%221%22%2C%22name%22%3A%22username%22%2C%22text%22%3A%22Select+a+username...%22%2C%22type%22%3A%22select%22%2C%22data_source%22%3A%22users%22%7D%5D%7D%5D%2C%22type%22%3A%22message%22%2C%22subtype%22%3A%22bot_message%22%2C%22ts%22%3A%221513654753.000074%22%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT02HUP4V7%5C%2F288116281232%5C%2FqaNuNww7z6pywPrk3jIHHcHF%22%2C%22trigger_id%22%3A%22288701768947.2606786993.b007d1a39a5076df879809512ef1d063%22%7D", + # "isBase64Encoded": False + # }, {})) - # print(guess({'Records': [{'eventVersion': '2.0', 'eventSource': 'aws:s3', 'awsRegion': 'us-east-1', 'eventTime': '2017-12-19T12:04:09.653Z', 'eventName': 'ObjectCreated:Put', 'userIdentity': {'principalId': 'AWS:AROAIHE5RTNGNONLAZCZS:AssumeRoleSession'}, 'requestParameters': {'sourceIPAddress': '63.228.166.237'}, 'responseElements': {'x-amz-request-id': '839246E5C0E2300C', 'x-amz-id-2': 'nZdfLBl9JptArE5YNYgD5vJhHjXSXZHPD8jFKSneIIJ8HWM4wiFvETRixxOVSJGenFGpqCFjckw='}, 's3': {'s3SchemaVersion': '1.0', 'configurationId': 'ca169f58-28da-4b01-a2c2-cdbbad9081c7', 'bucket': {'name': 'doorman-faces', 'ownerIdentity': {'principalId': 'AUAL9TOIHMDDI'}, 'arn': 'arn:aws:s3:::doorman-faces'}, 'object': {'key': 'detected/U033PFSFB/08e80c1689a77cd57818556f963f38c4.jpg', 'size': 81715, 'eTag': '112ba09a09910f3d45508e31398b0517', 'sequencer': '005A39003941D63E04'}}}]} + # print(guess({'Records': [{'eventVersion': '2.0', 'eventSource': 'aws:s3', 'awsRegion': 'us-east-1', 'eventTime': '2017-12-19T12:04:09.653Z', 'eventName': 'ObjectCreated:Put', 'userIdentity': {'principalId': 'AWS:AROAIHE5RTNGNONLAZCZS:AssumeRoleSession'}, 'requestParameters': {'sourceIPAddress': '63.228.166.237'}, 'responseElements': {'x-amz-request-id': '839246E5C0E2300C', 'x-amz-id-2': 'nZdfLBl9JptArE5YNYgD5vJhHjXSXZHPD8jFKSneIIJ8HWM4wiFvETRixxOVSJGenFGpqCFjckw='}, 's3': {'s3SchemaVersion': '1.0', 'configurationId': 'ca169f58-28da-4b01-a2c2-cdbbad9081c7', 'bucket': {'name': 'doorman-faces', 'ownerIdentity': {'principalId': 'AUAL9TOIHMDDI'}, 'arn': 'arn:aws:s3:::doorman-faces'}, 'object': {'key': 'detected/U033PFSFB/ac8b9fe608d6ec59871f5d2e2bcb8edd.jpg', 'size': 81715, 'eTag': '112ba09a09910f3d45508e31398b0517', 'sequencer': '005A39003941D63E04'}}}]} # ,{})) diff --git a/serverless.yml b/serverless.yml index 1300a79..7963460 100644 --- a/serverless.yml +++ b/serverless.yml @@ -92,6 +92,10 @@ functions: path: faces/train method: post + # find person, function for greengrass device + find-person: + handler: find_person.function_handler + # resources: # Resources: # S3BucketDoormanfaces: