- add first pass for testing server, does not act
authorEric Zavesky <ezavesky@research.att.com>
Sun, 15 Oct 2017 10:37:41 +0000 (05:37 -0500)
committerEric Zavesky <ezavesky@research.att.com>
Sun, 15 Oct 2017 10:37:41 +0000 (05:37 -0500)
on images yet

face_privacy_filter/transform_detect.py
testing/.gitignore [new file with mode: 0644]
testing/README.md [new file with mode: 0644]
testing/app.py [new file with mode: 0755]
testing/requirements.txt [new file with mode: 0644]
testing/swagger.yaml [new file with mode: 0644]

index 393d02e..3c3fae9 100644 (file)
@@ -32,12 +32,11 @@ class FaceDetectTransform(BaseEstimator, ClassifierMixin):
         return {'include_image': self.include_image}
 
     @staticmethod
-    def generate_in_df(path_image=""):
+    def generate_in_df(path_image="", bin_stream=b""):
         # munge stream and mimetype into input sample
-        binStream = b""
         if path_image and os.path.exists(path_image):
-            binStream = open(path_image, 'rb').read()
-        return pd.DataFrame([['image/jpeg', binStream]], columns=[FaceDetectTransform.COL_IMAGE_MIME, FaceDetectTransform.COL_IMAGE_DATA])
+            bin_stream = open(path_image, 'rb').read()
+        return pd.DataFrame([['image/jpeg', bin_stream]], columns=[FaceDetectTransform.COL_IMAGE_MIME, FaceDetectTransform.COL_IMAGE_DATA])
 
     def generate_out_dict(self, idx=-1, x=0, y=0, w=0, h=0, image=0):
         return {FaceDetectTransform.COL_FACE_IDX: idx, FaceDetectTransform.COL_FACE_X: x,
diff --git a/testing/.gitignore b/testing/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
diff --git a/testing/README.md b/testing/README.md
new file mode 100644 (file)
index 0000000..7d0e741
--- /dev/null
@@ -0,0 +1,39 @@
+# web_test
+This directory provides a simple web server for demonstrating a face-privacy filter example.
+This web demo will launch an application with a swagger page.
+
+## Example usage
+
+```
+$ python app.py
+usage: app.py [-h] [--port PORT] [--modeldir MODELDIR] [--rich_return]
+
+optional arguments:
+  -h, --help           show this help message and exit
+  --port PORT          port to launch the simple web server
+  --modeldir MODELDIR  model directory to load dumped artifact
+```
+
+### Output formats
+The optional HTTP parameter `rich_output` will generate a more decorated JSON output
+ that is also understood by the included web application.
+
+* standard output - from `DataFrame` version of the transform
+```
+
+
+```
+
+
+* rich output - formatted form of the transform
+```
+```
+
+## Face Privacy Filtering
+
+* For a graphical experience, view the swagger-generated UI at [http://localhost:8884/ui].
+* Additionally, a simple command-line utility could be used to post an image
+and mime type to the main interface.
+```
+curl -F image_binary=@test.jpg -F rich_output="true" -F mime_type="image/jpeg" "http://localhost:8884/transform"
+```
\ No newline at end of file
diff --git a/testing/app.py b/testing/app.py
new file mode 100755 (executable)
index 0000000..6c59982
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+import connexion
+import logging
+
+import argparse
+import json
+import time
+
+from flask import Flask, request, current_app, make_response
+
+import pandas as pd
+import requests
+
+from cognita_client.wrap.load import load_model
+from face_privacy_filter.transform_detect import FaceDetectTransform
+
+
+#def invoke_method(model_method):
+def transform(mime_type, image_binary, rich_output=False):
+    app = current_app
+    time_start = time.clock()
+    image_read = image_binary.stream.read()
+    X = FaceDetectTransform.generate_in_df(bin_stream=image_read)
+
+    pred = app.model.transform.from_native(X).as_native()
+    time_stop = time.clock()
+
+    if rich_output:
+        # NOTE: This response is specially formatted for the webdemo included with this package.
+        #       Alternate forms of a response are viable for any other desired application.
+        retObj = {
+            'regions': pred.to_dict(orient="records"),
+            'clientfile': 'undefined',
+            'info': 'Processed',
+            'processingtime': (time_stop - time_start),
+            'serverfilename': '/dev/null',
+            'status': 'Succeeded'
+        }
+
+        # dump to pretty JSON
+        retStr = json.dumps({'results':retObj}, indent=4)
+    else:
+        retStr = json.dumps(pred.to_dict(orient='records'), indent=4)
+
+    # formulate response
+    resp = make_response((retStr, 200, { } ))
+    # allow 'localhost' from 'file' or other;
+    # NOTE: DO NOT USE IN PRODUCTION!!!
+    resp.headers['Access-Control-Allow-Origin'] = '*'
+    print(type(pred))
+    print(retStr[:min(200,len(retStr))])
+    #print(pred)
+    return resp
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--port", type=int, default=8884, help='port to launch the simple web server')
+    parser.add_argument("--modeldir", type=str, default='../model', help='model directory to load dumped artifact')
+    pargs = parser.parse_args()
+
+    print("Configuring local application... {:}".format(__name__))
+    logging.basicConfig(level=logging.INFO)
+    app = connexion.App(__name__)
+    app.add_api('swagger.yaml')
+    # example usage:
+    #     curl -F image_binary=@test.jpg -F mime_type="image/jpeg" "http://localhost:8885/transform"
+
+    print("Loading model... {:}".format(pargs.modeldir))
+    app.app.model = load_model(pargs.modeldir)  # refers to ./model dir in pwd. generated by helper script also in this dir
+    # # dynamically add handlers depending on model capabilities
+    # for method_name, method in model.methods.items():
+    #     url = "/{}".format(method_name)
+    #     print("Adding route {}".format(url))
+    #     handler = partial(invoke_method, model_method=method)
+    #     app.add_url_rule(url, method_name, handler, methods=['POST'])
+
+    # run our standalone gevent server
+    app.run(port=pargs.port) #, server='gevent')
diff --git a/testing/requirements.txt b/testing/requirements.txt
new file mode 100644 (file)
index 0000000..9151191
--- /dev/null
@@ -0,0 +1 @@
+connexion
\ No newline at end of file
diff --git a/testing/swagger.yaml b/testing/swagger.yaml
new file mode 100644 (file)
index 0000000..be01601
--- /dev/null
@@ -0,0 +1,44 @@
+swagger: '2.0'
+info:
+  title: Face Privacy Filter Example
+  version: "0.1"
+consumes:
+  - application/json
+produces:
+  - application/json
+paths:
+  /transform:
+    post:
+      operationId: app.transform
+      summary: Post an image for processing
+      parameters:
+        - $ref: '#/parameters/mime_type'
+        - $ref: '#/parameters/image_binary'
+        - $ref: '#/parameters/rich_output'
+      responses:
+        200:
+          description: Image processed
+
+parameters:
+  mime_type:
+    name: mime_type
+    description: Image MimeType
+    in: formData
+    type: string
+    required: true
+    default: 'image/jpeg'
+    # pattern: "^[a-zA-Z0-9-]+$"
+  image_binary:
+    name: image_binary
+    description: Binary image blob
+    in: formData
+    type: file
+    required: true
+  rich_output:
+    name: rich_output
+    description: Rich or regular output
+    in: formData
+    type: boolean
+    required: false
+    default: true
+