-
Notifications
You must be signed in to change notification settings - Fork 1
/
utils.py
195 lines (143 loc) · 5.12 KB
/
utils.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import argparse
import cv2 as cv
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
import os
import re
import toolz
import urllib
from collections import namedtuple
from mlhub import utils as mlutils
FACE_CASCADE = cv.CascadeClassifier("haarcascade_frontalface_default.xml")
MARK_COLOR = (0, 255, 0) # Green
MARK_WIDTH = 4
FaceParams = namedtuple('FaceParams', 'scaleFactor minNeighbors minSize')
FACEPARAMS = FaceParams(1.2, 5, 30) # Default face detection parameters
# Face detection parameter command line argument parser
option_parser = argparse.ArgumentParser(add_help=False)
option_parser.add_argument(
'--scaleFactor',
type=float,
default=FACEPARAMS.scaleFactor,
help='scale factor ({} by default, must > 1)'.format(FACEPARAMS.scaleFactor))
option_parser.add_argument(
'--minNeighbors',
type=int,
default=FACEPARAMS.minNeighbors,
help='minimum neighbors ({} by default, integer, must > 1)'.format(FACEPARAMS.minNeighbors))
option_parser.add_argument(
'--minSize',
type=int,
default=FACEPARAMS.minSize,
help='minimum size ({} by default, integer, must > 1)'.format(FACEPARAMS.minSize))
def _plot_image(ax, img, cmap=None, label=''):
"""Plot <img> in <ax>."""
ax.imshow(img, cmap)
ax.tick_params(
axis='both',
which='both',
# bottom='off', # 'off', 'on' is deprecated in matplotlib > 2.2
bottom=False,
# top='off',
top=False,
# left='off',
left=False,
# right='off',
right=False,
# labelleft='off',
labelleft=False,
# labelbottom='off')
labelbottom=False)
ax.set_xlabel(label)
def plot_side_by_side_comparison(
leftimg,
rightimg,
leftlabel='Original Image',
rightlabel='Result',
leftcmap=None,
rightcmap=None):
"""Plot two images side by side."""
# Setup canvas
gs = gridspec.GridSpec(6, 13)
gs.update(hspace=0.1, wspace=0.001)
fig = plt.figure(figsize=(7, 3))
# Plot Left image
ax = fig.add_subplot(gs[:, 0:6])
_plot_image(ax, leftimg, cmap=leftcmap, label=leftlabel)
# Plot right image
ax = fig.add_subplot(gs[:, 7:13])
_plot_image(ax, rightimg, cmap=rightcmap, label=rightlabel)
# Show all of them
plt.show()
def detect_faces(image, face_params=FACEPARAMS):
# Convert image into grey-scale
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# Detect faces in the image
faces = FACE_CASCADE.detectMultiScale(
gray,
scaleFactor=face_params.scaleFactor,
minNeighbors=face_params.minNeighbors,
minSize=(face_params.minSize, face_params.minSize))
return faces
def mark_faces(image, faces, inplace=False):
"""Mark the <faces> in <image>."""
result = image
if not inplace:
result = image.copy()
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv.rectangle(result, (x, y), (x + w, y + h), MARK_COLOR, MARK_WIDTH)
return result
def convert_cv2matplot(*images):
"""Convert color space between OpenCV and Matplotlib.
Because OpenCV and Matplotlib use different color spaces.
"""
if len(images) > 0:
res = []
for image in images:
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
res.append(image)
return res[0] if len(res) == 1 else tuple(res)
else:
return None
def is_url(url):
"""Check if url is a valid URL."""
urlregex = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if re.match(urlregex, url) is not None:
return True
else:
return False
def read_cv_image_from(url):
"""Read an image from url or file as grayscale opencv image."""
return toolz.pipe(
url,
urllib.request.urlopen if is_url(url) else lambda x: open(x, 'rb'),
lambda x: x.read(),
bytearray,
lambda x: np.asarray(x, dtype="uint8"),
lambda x: cv.imdecode(x, cv.IMREAD_COLOR))
def get_faces_frame(cap, face_params=FACEPARAMS):
"""Read one frame from camera and do face detection."""
ret, frame = cap.read() # Capture frame-by-frame
faces = detect_faces(frame, face_params=face_params)
mark_faces(frame, faces, inplace=True)
return convert_cv2matplot(frame)
def get_abspath(path):
"""Return the absolute path of <path>.
Because the working directory of MLHUB model is ~/.mlhub/<model>,
when user run 'ml score facedetect <image-path>', the <image-path> may be a
path relative to the path where 'ml score facedetect' is typed, to cope with
this scenario, mlhub provides mlhub.utils.get_cmd_cwd() to obtain this path.
"""
path = os.path.expanduser(path)
if not os.path.isabs(path):
CMD_CWD = mlutils.get_cmd_cwd()
path = os.path.join(CMD_CWD, path)
return os.path.abspath(path)