- serialize face cascade to avoid filesystem
[face-privacy-filter.git] / face_privacy_filter / filter_image.py
index 700072f..ce2a55b 100644 (file)
@@ -1,7 +1,7 @@
 #! python
 # -*- coding: utf-8 -*-
 """
-Wrapper for image emotion classification task 
+Wrapper for image emotion classification task
 """
 
 import os.path
@@ -15,26 +15,48 @@ from face_privacy_filter.transform_region import RegionTransform
 from face_privacy_filter._version import MODEL_NAME
 
 
-def model_create_pipeline(transformer, pipeline_type="detect"):
-    #from sklearn.pipeline import Pipeline
-    dependent_modules = [pd, np, 'opencv-python']  # define as dependent libraries
+def model_create_pipeline(transformer):
+    from acumos.session import Requirements
+    from acumos.modeling import Model, List, create_namedtuple
+    import sklearn
+    import cv2
+    from os import path
 
-    # for now, do nothing specific to transformer...
+    # derive the input type from the transformer
+    type_list, type_name = transformer._type_in  # it looked like this {'test': int, 'tag': str}
+    input_type = [(k, List[type_list[k]]) for k in type_list]
+    type_in = create_namedtuple(type_name, input_type)
 
-    return transformer, dependent_modules
+    # derive the output type from the transformer
+    type_list, type_name = transformer._type_out
+    output_type = [(k, List[type_list[k]]) for k in type_list]
+    type_out = create_namedtuple(type_name, output_type)
+
+    def predict_class(val_wrapped: type_in) -> type_out:
+        '''Returns an array of float predictions'''
+        df = pd.DataFrame(list(zip(*val_wrapped)), columns=val_wrapped._fields)
+        # df = pd.DataFrame(np.column_stack(val_wrapped), columns=val_wrapped._fields)  # numpy doesn't like binary
+        tags_df = transformer.predict(df)
+        tags_list = type_out(*(col for col in tags_df.values.T))  # flatten to tag set
+        return tags_list
+
+    # compute path of this package to add it as a dependency
+    package_path = path.dirname(path.realpath(__file__))
+    return Model(transform=predict_class), Requirements(packages=[package_path], reqs=[pd, np, sklearn],
+                                                        req_map={cv2: 'opencv-python'})
 
 
 def main(config={}):
     import argparse
     parser = argparse.ArgumentParser()
     parser.add_argument('-p', '--predict_path', type=str, default='', help="save detections from model (model must be provided via 'dump_model')")
-    parser.add_argument('-i', '--input', type=str, default='',help='absolute path to input data (image or csv, only during prediction / dump)')
+    parser.add_argument('-i', '--input', type=str, default='', help='absolute path to input data (image or csv, only during prediction / dump)')
     parser.add_argument('-c', '--csv_input', dest='csv_input', action='store_true', default=False, help='input as CSV format not an image')
     parser.add_argument('-s', '--suppress_image', dest='suppress_image', action='store_true', default=False, help='do not create an extra row for a returned image')
-    parser.add_argument('-f', '--function', type=str, default='detect',help='which type of model to generate', choices=['detect', 'pixelate'])
+    parser.add_argument('-f', '--function', type=str, default='detect', help='which type of model to generate', choices=['detect', 'pixelate'])
     parser.add_argument('-a', '--push_address', help='server address to push the model (e.g. http://localhost:8887/v2/models)', default='')
     parser.add_argument('-d', '--dump_model', help='dump model to a pickle directory for local running', default='')
-    config.update(vars(parser.parse_args()))     #pargs, unparsed = parser.parse_known_args()
+    config.update(vars(parser.parse_args()))     # pargs, unparsed = parser.parse_known_args()
 
     if not config['predict_path']:
         print("Attempting to create new model for dump or push...")
@@ -47,18 +69,24 @@ def main(config={}):
         else:
             print("Error: Functional mode '{:}' unknown, aborting create".format(config['function']))
         inputDf = transform.generate_in_df()
-        pipeline, EXTRA_DEPS = model_create_pipeline(transform, "detect")
+        pipeline, reqs = model_create_pipeline(transform)
 
         # formulate the pipeline to be used
+        model_name = MODEL_NAME + "_" + config['function']
         if 'push_address' in config and config['push_address']:
-            from cognita_client.push import push_sklearn_model # push_skkeras_hybrid_model (keras?)
+            from acumos.session import AcumosSession
             print("Pushing new model to '{:}'...".format(config['push_address']))
-            push_sklearn_model(pipeline, inputDf, api=config['push_address'], name=MODEL_NAME, extra_deps=EXTRA_DEPS)
+            session = AcumosSession(push_api=config['push_address'], auth_api=config['auth_address'])
+            session.push(pipeline, model_name, reqs)  # creates ./my-iris.zip
 
         if 'dump_model' in config and config['dump_model']:
-            from cognita_client.wrap.dump import dump_sklearn_model # dump_skkeras_hybrid_model (keras?)
+            from acumos.session import AcumosSession
+            from os import makedirs
+            if not os.path.exists(config['dump_model']):
+                makedirs(config['dump_model'])
             print("Dumping new model to '{:}'...".format(config['dump_model']))
-            dump_sklearn_model(pipeline, inputDf, config['dump_model'], name=MODEL_NAME, extra_deps=EXTRA_DEPS)
+            session = AcumosSession()
+            session.dump(pipeline, model_name, config['dump_model'], reqs)  # creates ./my-iris.zip
 
     else:
         if not config['dump_model'] or not os.path.exists(config['dump_model']):
@@ -69,13 +97,22 @@ def main(config={}):
             sys.exit(-1)
 
         print("Attempting predict/transform on input sample...")
-        from cognita_client.wrap.load import load_model
+        from acumos.wrapped import load_model
         model = load_model(config['dump_model'])
         if not config['csv_input']:
             inputDf = FaceDetectTransform.generate_in_df(config['input'])
         else:
-            inputDf = pd.read_csv(config['input'], converters={FaceDetectTransform.COL_IMAGE_DATA:FaceDetectTransform.read_byte_arrays})
-        dfPred = model.transform.from_native(inputDf).as_native()
+            inputDf = pd.read_csv(config['input'], converters={FaceDetectTransform.COL_IMAGE_DATA: FaceDetectTransform.read_byte_arrays})
+
+        type_in = model.transform._input_type
+        transform_in = type_in(*tuple(col for col in inputDf.values.T))
+        transform_out = model.transform.from_wrapped(transform_in).as_wrapped()
+        dfPred = pd.DataFrame(list(zip(*transform_out)), columns=transform_out._fields)
+
+        if not config['csv_input']:
+            dfPred = FaceDetectTransform.suppress_image(dfPred)
+        print("ALMOST DONE")
+        print(dfPred)
 
         if config['predict_path']:
             print("Writing prediction to file '{:}'...".format(config['predict_path']))
@@ -83,11 +120,10 @@ def main(config={}):
                 dfPred.to_csv(config['predict_path'], sep=",", index=False)
             else:
                 FaceDetectTransform.generate_out_image(dfPred, config['predict_path'])
-        if not config['csv_input']:
-            dfPred = FaceDetectTransform.suppress_image(dfPred)
 
         if dfPred is not None:
             print("Predictions:\n{:}".format(dfPred))
 
+
 if __name__ == '__main__':
     main()