c30b0c23746dc123a47f69d8dd8e9ffd6417cd7a
[face-privacy-filter.git] / face_privacy_filter / filter_image.py
1 #! python
2 # -*- coding: utf-8 -*-
3 # ===============LICENSE_START=======================================================
4 # Acumos Apache-2.0
5 # ===================================================================================
6 # Copyright (C) 2017-2018 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
7 # ===================================================================================
8 # This Acumos software file is distributed by AT&T and Tech Mahindra
9 # under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # This file is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 # ===============LICENSE_END=========================================================
20 """
21 Wrapper for face privacy transform task
22 """
23
24 import os.path
25 import sys
26
27 import numpy as np
28 import pandas as pd
29
30
31 def model_create_pipeline(transformer, funcName):
32     from acumos.session import Requirements
33     from acumos.modeling import Model, List, create_namedtuple
34     import sklearn
35     import cv2
36     from os import path
37
38     # derive the input type from the transformer
39     type_list, type_name = transformer._type_in  # it looked like this {'test': int, 'tag': str}
40     input_type = [(k, List[type_list[k]]) for k in type_list]
41     type_in = create_namedtuple(type_name, input_type)
42
43     # derive the output type from the transformer
44     type_list, type_name = transformer._type_out
45     output_type = [(k, List[type_list[k]]) for k in type_list]
46     type_out = create_namedtuple(type_name, output_type)
47
48     def predict_class(val_wrapped: type_in) -> type_out:
49         '''Returns an array of float predictions'''
50         df = pd.DataFrame(list(zip(*val_wrapped)), columns=val_wrapped._fields)
51         # df = pd.DataFrame(np.column_stack(val_wrapped), columns=val_wrapped._fields)  # numpy doesn't like binary
52         tags_df = transformer.predict(df)
53         tags_list = type_out(*(col for col in tags_df.values.T))  # flatten to tag set
54         return tags_list
55
56     # compute path of this package to add it as a dependency
57     package_path = path.dirname(path.realpath(__file__))
58     objModelDeclare = {}
59     objModelDeclare[funcName] = predict_class
60     # add the model dependency manually because of the way we constructed the package;
61     # the opencv-python/cv2 dependency is not picked up automagically
62     return Model(**objModelDeclare), Requirements(packages=[package_path], reqs=[pd, np, sklearn, 'opencv-python'],
63                                                   req_map={cv2: 'opencv-python'})
64
65
66 def main(config={}):
67     from face_privacy_filter.transform_detect import FaceDetectTransform
68     from face_privacy_filter.transform_region import RegionTransform
69     from face_privacy_filter._version import MODEL_NAME
70     import argparse
71     parser = argparse.ArgumentParser()
72     parser.add_argument('-p', '--predict_path', type=str, default='', help="save detections from model (model must be provided via 'dump_model')")
73     parser.add_argument('-i', '--input', type=str, default='', help='absolute path to input data (image or csv, only during prediction / dump)')
74     parser.add_argument('-c', '--csv_input', dest='csv_input', action='store_true', default=False, help='input as CSV format not an image')
75     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')
76     parser.add_argument('-f', '--function', type=str, default='detect', help='which type of model to generate', choices=['detect', 'pixelate'])
77     parser.add_argument('-a', '--push_address', help='server address to push the model (e.g. http://localhost:8887/v2/models)', default='')
78     parser.add_argument('-A', '--auth_address', help='server address for login and push of the model (e.g. http://localhost:8887/v2/auth)', default='')
79     parser.add_argument('-d', '--dump_model', help='dump model to a pickle directory for local running', default='')
80     config.update(vars(parser.parse_args()))     # pargs, unparsed = parser.parse_known_args()
81
82     if not config['predict_path']:
83         print("Attempting to create new model for dump or push...")
84
85         # refactor the raw samples from upstream image classifier
86         if config['function'] == "detect":
87             transform = FaceDetectTransform(include_image=not config['suppress_image'])
88         elif config['function'] == "pixelate":
89             transform = RegionTransform()
90         else:
91             print("Error: Functional mode '{:}' unknown, aborting create".format(config['function']))
92         inputDf = transform.generate_in_df()
93         pipeline, reqs = model_create_pipeline(transform, config['function'])
94
95         # formulate the pipeline to be used
96         model_name = MODEL_NAME + "_" + config['function']
97         if config['push_address']:
98             from acumos.session import AcumosSession
99             print("Pushing new model to '{:}'...".format(config['push_address']))
100             session = AcumosSession(push_api=config['push_address'], auth_api=config['auth_address'])
101             session.push(pipeline, model_name, reqs)  # creates ./my-iris.zip
102
103         if config['dump_model']:
104             from acumos.session import AcumosSession
105             from os import makedirs
106             if not os.path.exists(config['dump_model']):
107                 makedirs(config['dump_model'])
108             print("Dumping new model to '{:}'...".format(config['dump_model']))
109             session = AcumosSession()
110             session.dump(pipeline, model_name, config['dump_model'], reqs)  # creates ./my-iris.zip
111
112     else:
113         if not config['dump_model'] or not os.path.exists(config['dump_model']):
114             print("Attempting to predict from a dumped model, but model not found.".format(config['dump_model']))
115             sys.exit(-1)
116         if not os.path.exists(config['input']):
117             print("Predictino requested but target input '{:}' was not found, please check input arguments.".format(config['input']))
118             sys.exit(-1)
119
120         print("Attempting predict/transform on input sample...")
121         from acumos.wrapped import load_model
122         model = load_model(config['dump_model'])
123         if not config['csv_input']:
124             inputDf = FaceDetectTransform.generate_in_df(config['input'])
125         else:
126             inputDf = pd.read_csv(config['input'], converters={FaceDetectTransform.COL_IMAGE_DATA: FaceDetectTransform.read_byte_arrays})
127
128         type_in = model.transform._input_type
129         transform_in = type_in(*tuple(col for col in inputDf.values.T))
130         transform_out = model.transform.from_wrapped(transform_in).as_wrapped()
131         dfPred = pd.DataFrame(list(zip(*transform_out)), columns=transform_out._fields)
132
133         if not config['csv_input']:
134             dfPred = FaceDetectTransform.suppress_image(dfPred)
135
136         if config['predict_path']:
137             print("Writing prediction to file '{:}'...".format(config['predict_path']))
138             if not config['csv_input']:
139                 dfPred.to_csv(config['predict_path'], sep=",", index=False)
140             else:
141                 FaceDetectTransform.generate_out_image(dfPred, config['predict_path'])
142
143         if dfPred is not None:
144             print("Predictions:\n{:}".format(dfPred))
145
146
147 if __name__ == '__main__':
148     # patch the path to include this object
149     pathRoot = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
150     if pathRoot not in sys.path:
151         sys.path.append(pathRoot)
152     main()