-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathperson_classifier_server.py
166 lines (139 loc) · 6 KB
/
person_classifier_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""
Classify persons using tensorflow-gpu served by zerorpc
Should be called from a zerorpc client with ZoneMinder
alarm image metadata from zm-s3-upload.js.
This is part of the smart-zoneminder project.
See https://github.com/goruck/smart-zoneminder
Copyright (c) 2019, 2020 Lindo St. Angel
"""
import numpy as np
import tensorflow as tf
import cv2
import json
import zerorpc
import logging
import gevent
import signal
logging.basicConfig(
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
level=logging.ERROR)
logger = logging.getLogger(__name__)
# Get configuration.
with open('./config.json') as fp:
config = json.load(fp)['personClassifierServer']
# Path to TensorFlow Saved Model.
PATH_TO_MODEL = config['savedModel']
# Model input size.
MODEL_INPUT_SIZE = tuple(config['modelInputSize'])
# Model preprocessor function.
PREPROCESSOR = eval(config['preprocessor'])
# Minimum score for valid TF person detection.
MIN_PROBA = config['minProba']
# Heartbeat interval for zerorpc client in ms.
# This must match the zerorpc client config.
ZRPC_HEARTBEAT = config['zerorpcHeartBeat']
# IPC (or TCP) socket for zerorpc.
# This must match the zerorpc client config.
ZRPC_PIPE = config['zerorpcPipe']
# Get tf label map.
LABEL_MAP = config['labelMap']
# Limit GPU memory growth.
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
try:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
logger.debug(f'{len(gpus)} Physical GPUs, {len(logical_gpus)} Logical GPUs')
except RuntimeError as e:
# Memory growth must be set before GPUs have been initialized
logger.debug(e)
# Load model and prepare for inference.
# See: https://www.tensorflow.org/guide/saved_model
loaded = tf.saved_model.load(PATH_TO_MODEL)
infer = loaded.signatures['serving_default']
logger.debug('Model output info {}:'.format(infer.structured_outputs))
output = list(infer.structured_outputs.keys())[0]
# zerorpc class.
class DetectRPC(object):
def __init__(self):
logger.debug('Starting server for person classification.')
# add optional init statements
def close_server(self):
logger.debug('Closing server for person classification.')
# add optional close statements
def detect_faces(self, test_image_paths):
# List that will hold all images with any person classifications.
objects_classified_persons = []
for obj in test_image_paths:
logger.debug('**********Classify person for {}'.format(obj['image']))
for label in obj['labels']:
# If the object detected is a person then try to identify face.
if label['name'] == 'person':
# Read image from disk.
img = cv2.imread(obj['image'])
if img is None:
# Bad image was read.
logging.error('Bad image was read.')
label['face'] = None
continue
# First bound the roi using the coord info passed in.
# The roi is area around person(s) detected in image.
# (x1, y1) are the top left roi coordinates.
# (x2, y2) are the bottom right roi coordinates.
y2 = int(label['box']['ymin'])
x1 = int(label['box']['xmin'])
y1 = int(label['box']['ymax'])
x2 = int(label['box']['xmax'])
roi = img[y2:y1, x1:x2]
#cv2.imwrite('./roi.jpg', roi)
if roi.size == 0:
# Bad object roi...move on to next image.
logger.error('Bad object roi.')
label['face'] = None
continue
# Format image to what the model expects for input.
# Resize.
roi = cv2.resize(roi, dsize=MODEL_INPUT_SIZE,
interpolation=cv2.INTER_AREA)
# Expand dimensions.
roi = np.expand_dims(roi, axis=0)
# Preprocess.
roi = PREPROCESSOR(roi.astype('float32'))
# Actual predictions per class.
predictions = infer(tf.constant(roi))[output]
# Find most likely prediction.
proba = np.amax(predictions)
j = np.argmax(predictions)
person = LABEL_MAP[j]
logger.debug('person classifier proba {} name {}'
.format(proba, person))
if proba >= MIN_PROBA:
name = person
logger.debug('person classifier says this is {}'
.format(name))
else:
name = None # prob too low to recog face
logger.debug('person classifier cannot recognize person')
# Add face name to label metadata.
label['face'] = name
# Add face confidence to label metadata.
# (First convert NumPy value to native Python type for json serialization.)
label['faceProba'] = proba.item()
# Add processed image to output list.
objects_classified_persons.append(obj)
# Convert json to string and return data.
return(json.dumps(objects_classified_persons))
# Create zerorpc object.
zerorpc_obj = DetectRPC()
# Create and bind zerorpc server.
s = zerorpc.Server(zerorpc_obj, heartbeat=ZRPC_HEARTBEAT)
s.bind(ZRPC_PIPE)
# Register graceful ways to stop server.
gevent.signal(signal.SIGINT, s.stop) # Ctrl-C
gevent.signal(signal.SIGTERM, s.stop) # termination
# Start server.
# This will block until a gevent signal is caught
s.run()
# After server is stopped then close it.
zerorpc_obj.close_server()