e7d4c1288634299e414a58a3da86198cf6b77bd7
[face-privacy-filter.git] / face_privacy_filter / filter_image.py
1 #! python
2 # -*- coding: utf-8 -*-
3 # ================================================================================
4 # ACUMOS
5 # ================================================================================
6 # Copyright © 2017 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 # ================================================================================
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):
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     return Model(transform=predict_class), Requirements(packages=[package_path], reqs=[pd, np, sklearn],
59                                                         req_map={cv2: 'opencv-python'})
60
61
62 def main(config={}):
63     from face_privacy_filter.transform_detect import FaceDetectTransform
64     from face_privacy_filter.transform_region import RegionTransform
65     from face_privacy_filter._version import MODEL_NAME
66     import argparse
67     parser = argparse.ArgumentParser()
68     parser.add_argument('-p', '--predict_path', type=str, default='', help="save detections from model (model must be provided via 'dump_model')")
69     parser.add_argument('-i', '--input', type=str, default='', help='absolute path to input data (image or csv, only during prediction / dump)')
70     parser.add_argument('-c', '--csv_input', dest='csv_input', action='store_true', default=False, help='input as CSV format not an image')
71     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')
72     parser.add_argument('-f', '--function', type=str, default='detect', help='which type of model to generate', choices=['detect', 'pixelate'])
73     parser.add_argument('-a', '--push_address', help='server address to push the model (e.g. http://localhost:8887/v2/models)', default='')
74     parser.add_argument('-A', '--auth_address', help='server address for login and push of the model (e.g. http://localhost:8887/v2/auth)', default='')
75     parser.add_argument('-d', '--dump_model', help='dump model to a pickle directory for local running', default='')
76     config.update(vars(parser.parse_args()))     # pargs, unparsed = parser.parse_known_args()
77
78     if not config['predict_path']:
79         print("Attempting to create new model for dump or push...")
80
81         # refactor the raw samples from upstream image classifier
82         if config['function'] == "detect":
83             transform = FaceDetectTransform(include_image=not config['suppress_image'])
84         elif config['function'] == "pixelate":
85             transform = RegionTransform()
86         else:
87             print("Error: Functional mode '{:}' unknown, aborting create".format(config['function']))
88         inputDf = transform.generate_in_df()
89         pipeline, reqs = model_create_pipeline(transform)
90
91         # formulate the pipeline to be used
92         model_name = MODEL_NAME + "_" + config['function']
93         if config['push_address']:
94             from acumos.session import AcumosSession
95             print("Pushing new model to '{:}'...".format(config['push_address']))
96             session = AcumosSession(push_api=config['push_address'], auth_api=config['auth_address'])
97             session.push(pipeline, model_name, reqs)  # creates ./my-iris.zip
98
99         if config['dump_model']:
100             from acumos.session import AcumosSession
101             from os import makedirs
102             if not os.path.exists(config['dump_model']):
103                 makedirs(config['dump_model'])
104             print("Dumping new model to '{:}'...".format(config['dump_model']))
105             session = AcumosSession()
106             session.dump(pipeline, model_name, config['dump_model'], reqs)  # creates ./my-iris.zip
107
108     else:
109         if not config['dump_model'] or not os.path.exists(config['dump_model']):
110             print("Attempting to predict from a dumped model, but model not found.".format(config['dump_model']))
111             sys.exit(-1)
112         if not os.path.exists(config['input']):
113             print("Predictino requested but target input '{:}' was not found, please check input arguments.".format(config['input']))
114             sys.exit(-1)
115
116         print("Attempting predict/transform on input sample...")
117         from acumos.wrapped import load_model
118         model = load_model(config['dump_model'])
119         if not config['csv_input']:
120             inputDf = FaceDetectTransform.generate_in_df(config['input'])
121         else:
122             inputDf = pd.read_csv(config['input'], converters={FaceDetectTransform.COL_IMAGE_DATA: FaceDetectTransform.read_byte_arrays})
123
124         type_in = model.transform._input_type
125         transform_in = type_in(*tuple(col for col in inputDf.values.T))
126         transform_out = model.transform.from_wrapped(transform_in).as_wrapped()
127         dfPred = pd.DataFrame(list(zip(*transform_out)), columns=transform_out._fields)
128
129         if not config['csv_input']:
130             dfPred = FaceDetectTransform.suppress_image(dfPred)
131         print("ALMOST DONE")
132         print(dfPred)
133
134         if config['predict_path']:
135             print("Writing prediction to file '{:}'...".format(config['predict_path']))
136             if not config['csv_input']:
137                 dfPred.to_csv(config['predict_path'], sep=",", index=False)
138             else:
139                 FaceDetectTransform.generate_out_image(dfPred, config['predict_path'])
140
141         if dfPred is not None:
142             print("Predictions:\n{:}".format(dfPred))
143
144
145 if __name__ == '__main__':
146     # patch the path to include this object
147     pathRoot = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
148     if pathRoot not in sys.path:
149         sys.path.append(pathRoot)
150     main()