Skip to content

Commit 9e2bed1

Browse files
authored
Merge pull request #179 from lucidcode/feature/camera-sensor-interface
Upgrade to camera sensor interface
2 parents b13ff0e + 75a4dfa commit 9e2bed1

4 files changed

Lines changed: 443 additions & 463 deletions

File tree

release/firmware.bin

65.3 KB
Binary file not shown.

software/face.py

Lines changed: 124 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,124 @@
1-
import sensor
2-
import image
3-
import math
4-
import ml
5-
from ml.utils import NMS
6-
7-
class face_detection:
8-
def __init__(self, config, comms):
9-
self.config = config
10-
self.comms = comms
11-
self.has_face = False
12-
self.face_cascade = image.HaarCascade("/rom/haarcascade_frontalface.cascade", stages=self.config.get('FaceStages'))
13-
self.face_object = [0, 0, 1, 1]
14-
self.face_angle = 0
15-
self.correct_angle = False
16-
self.ml_model = None
17-
self.detector = ""
18-
19-
self.extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.GRAYSCALE)
20-
21-
def detect(self, img, global_variance):
22-
if not self.config.get('TrackFace') and not self.config.get('TensorFlow'):
23-
self.face_object = [0, 0, img.width(), img.height()]
24-
self.has_face = False
25-
return
26-
27-
if global_variance >= self.config.get('TossThreshold'):
28-
self.face_object = [0, 0, img.width(), img.height()]
29-
self.has_face = False
30-
31-
if self.config.get('TensorFlow'):
32-
if self.ml_model == None:
33-
self.ml_model = ml.Model('/rom/fomo_face_detection.tflite')
34-
35-
for i, detection_list in enumerate(self.ml_model.predict([img], callback=self.post_process)):
36-
if i == 0 or len(detection_list) == 0:
37-
continue
38-
39-
for (x, y, w, h), score in detection_list:
40-
self.face_object = (x - w, y - h, w * 4, h * 4)
41-
self.detector = "TensorFlow"
42-
self.has_face = True
43-
return
44-
45-
if not self.config.get('TrackFace'):
46-
return
47-
48-
self.face_angle = 0
49-
face_objects = img.find_features(self.face_cascade, threshold=self.config.get('FaceThreshold'), scale_factor=self.config.get('FaceScaleFactor'))
50-
51-
if len(face_objects) == 0 and self.config.get('FaceAngles'):
52-
for angle in self.config.get('FaceAngles'):
53-
self.extra_fb.replace(img)
54-
self.extra_fb.rotation_corr(x_rotation=0.0, y_rotation=0.0, z_rotation=angle)
55-
56-
face_objects = self.extra_fb.find_features(self.face_cascade, threshold=self.config.get('FaceThreshold'), scale_factor=self.config.get('FaceScaleFactor'))
57-
if face_objects:
58-
self.face_angle = angle
59-
break
60-
61-
if face_objects:
62-
self.face_object = face_objects[0]
63-
self.detector = "HaarCascade"
64-
self.has_face = True
65-
66-
if self.config.get('TrackEyes'):
67-
self.face_object = [self.face_object[0], self.face_object[1] + int(self.face_object[3] * 1/5), self.face_object[2], int(self.face_object[3] * 2/5)]
68-
69-
if self.correct_angle:
70-
eyes_x = self.face_object[0] + int(self.face_object[2] * 1/5)
71-
eyes_y = self.face_object[1] + int(self.face_object[3] * 1/5)
72-
eyes_width = self.face_object[2] - int(self.face_object[2] * 1/5)
73-
eyes_height = int(self.face_object[3] * 2/5)
74-
self.face_object = [eyes_x, eyes_y, eyes_width, eyes_height]
75-
76-
def draw_region(self, img):
77-
if not self.config.get('DrawFaceRegion'):
78-
return
79-
80-
if self.face_object[2] == img.width():
81-
return
82-
83-
if self.detector == "TensorFlow":
84-
img.draw_rectangle(self.face_object, color=(70, 130, 180))
85-
86-
if self.detector == "HaarCascade":
87-
img.draw_rectangle(self.face_object)
88-
89-
if self.correct_angle:
90-
face_x = self.face_object[0]
91-
face_y = self.face_object[1]
92-
face_width = self.face_object[2]
93-
face_height = self.face_object[3]
94-
95-
theta = math.radians(self.face_angle * -1)
96-
offset = (0, 0)
97-
98-
rect = [
99-
(face_x, face_y),
100-
(face_x, face_y + face_height),
101-
(face_x + face_width, face_y + face_height),
102-
(face_x + face_width, face_y)
103-
]
104-
105-
rotated_rect = [translate(rotate(xy, theta), offset) for xy in rect]
106-
107-
img.draw_line((rotated_rect[0][0], rotated_rect[0][1], rotated_rect[1][0], rotated_rect[1][1]), color=(220, 220, 0))
108-
img.draw_line((rotated_rect[1][0], rotated_rect[1][1], rotated_rect[2][0], rotated_rect[2][1]), color=(220, 220, 0))
109-
img.draw_line((rotated_rect[2][0], rotated_rect[2][1], rotated_rect[3][0], rotated_rect[3][1]), color=(220, 220, 0))
110-
img.draw_line((rotated_rect[3][0], rotated_rect[3][1], rotated_rect[0][0], rotated_rect[0][1]), color=(220, 220, 0))
111-
img.draw_string(face_x, face_y + face_height - 10, str(self.face_angle) + "^", color=(70, 130, 180), mono_space=False)
112-
113-
def post_process(self, model, inputs, outputs):
114-
n, oh, ow, oc = model.output_shape[0]
115-
nms = NMS(ow, oh, inputs[0].roi)
116-
threshold_list = [(self.config.get('FaceConfidence'), 255)]
117-
for i in range(oc):
118-
img = image.Image(outputs[0][0, :, :, i] * 255)
119-
blobs = img.find_blobs(
120-
threshold_list, x_stride=1, area_threshold=1, pixels_threshold=1
121-
)
122-
for b in blobs:
123-
rect = b.rect()
124-
x, y, w, h = rect
125-
score = (
126-
img.get_statistics(thresholds=threshold_list, roi=rect).l_mean() / 255.0
127-
)
128-
nms.add_bounding_box(x, y, x + w, y + h, score, i)
129-
return nms.get_bounding_boxes()
130-
131-
def rotate(xy, theta):
132-
cos_theta, sin_theta = math.cos(theta), math.sin(theta)
133-
134-
return (
135-
int(xy[0] * cos_theta - xy[1] * sin_theta),
136-
int(xy[0] * sin_theta + xy[1] * cos_theta)
137-
)
138-
139-
def translate(xy, offset):
140-
return xy[0] + offset[0], xy[1] + offset[1]
1+
import csi
2+
import image
3+
import math
4+
import ml
5+
from ml.postprocessing.edgeimpulse import Fomo
6+
from ml.utils import NMS
7+
8+
class face_detection:
9+
def __init__(self, config, comms, sensor):
10+
self.config = config
11+
self.comms = comms
12+
self.sensor = sensor
13+
self.has_face = False
14+
self.face_cascade = image.HaarCascade("/rom/haarcascade_frontalface.cascade", stages=self.config.get('FaceStages'))
15+
self.face_object = [0, 0, 1, 1]
16+
self.face_angle = 0
17+
self.correct_angle = False
18+
self.ml_model = None
19+
self.detector = ""
20+
21+
self.extra_fb = image.Image(self.sensor.width(), self.sensor.height(), csi.GRAYSCALE)
22+
23+
def detect(self, img, global_variance):
24+
if not self.config.get('TrackFace') and not self.config.get('TensorFlow'):
25+
self.face_object = [0, 0, img.width(), img.height()]
26+
self.has_face = False
27+
return
28+
29+
if global_variance >= self.config.get('TossThreshold'):
30+
self.face_object = [0, 0, img.width(), img.height()]
31+
self.has_face = False
32+
33+
if self.config.get('TensorFlow'):
34+
if self.ml_model == None:
35+
self.ml_model = ml.Model('/rom/fomo_face_detection.tflite', postprocess=Fomo(threshold=self.config.get('FaceConfidence')))
36+
37+
for i, detection_list in enumerate(self.ml_model.predict([img])):
38+
if i == 0 or len(detection_list) == 0:
39+
continue
40+
41+
for (x, y, w, h), score in detection_list:
42+
self.face_object = (x - w, y - h, w * 4, h * 4)
43+
self.detector = "TensorFlow"
44+
self.has_face = True
45+
return
46+
47+
if not self.config.get('TrackFace'):
48+
return
49+
50+
self.face_angle = 0
51+
face_objects = img.find_features(self.face_cascade, threshold=self.config.get('FaceThreshold'), scale_factor=self.config.get('FaceScaleFactor'))
52+
53+
if len(face_objects) == 0 and self.config.get('FaceAngles'):
54+
for angle in self.config.get('FaceAngles'):
55+
self.extra_fb.replace(img)
56+
self.extra_fb.rotation_corr(x_rotation=0.0, y_rotation=0.0, z_rotation=angle)
57+
58+
face_objects = self.extra_fb.find_features(self.face_cascade, threshold=self.config.get('FaceThreshold'), scale_factor=self.config.get('FaceScaleFactor'))
59+
if face_objects:
60+
self.face_angle = angle
61+
break
62+
63+
if face_objects:
64+
self.face_object = face_objects[0]
65+
self.detector = "HaarCascade"
66+
self.has_face = True
67+
68+
if self.config.get('TrackEyes'):
69+
self.face_object = [self.face_object[0], self.face_object[1] + int(self.face_object[3] * 1/5), self.face_object[2], int(self.face_object[3] * 2/5)]
70+
71+
if self.correct_angle:
72+
eyes_x = self.face_object[0] + int(self.face_object[2] * 1/5)
73+
eyes_y = self.face_object[1] + int(self.face_object[3] * 1/5)
74+
eyes_width = self.face_object[2] - int(self.face_object[2] * 1/5)
75+
eyes_height = int(self.face_object[3] * 2/5)
76+
self.face_object = [eyes_x, eyes_y, eyes_width, eyes_height]
77+
78+
def draw_region(self, img):
79+
if not self.config.get('DrawFaceRegion'):
80+
return
81+
82+
if self.face_object[2] == img.width():
83+
return
84+
85+
if self.detector == "TensorFlow":
86+
img.draw_rectangle(self.face_object, color=(70, 130, 180))
87+
88+
if self.detector == "HaarCascade":
89+
img.draw_rectangle(self.face_object)
90+
91+
if self.correct_angle:
92+
face_x = self.face_object[0]
93+
face_y = self.face_object[1]
94+
face_width = self.face_object[2]
95+
face_height = self.face_object[3]
96+
97+
theta = math.radians(self.face_angle * -1)
98+
offset = (0, 0)
99+
100+
rect = [
101+
(face_x, face_y),
102+
(face_x, face_y + face_height),
103+
(face_x + face_width, face_y + face_height),
104+
(face_x + face_width, face_y)
105+
]
106+
107+
rotated_rect = [self.translate(self.rotate(xy, theta), offset) for xy in rect]
108+
109+
img.draw_line((rotated_rect[0][0], rotated_rect[0][1], rotated_rect[1][0], rotated_rect[1][1]), color=(220, 220, 0))
110+
img.draw_line((rotated_rect[1][0], rotated_rect[1][1], rotated_rect[2][0], rotated_rect[2][1]), color=(220, 220, 0))
111+
img.draw_line((rotated_rect[2][0], rotated_rect[2][1], rotated_rect[3][0], rotated_rect[3][1]), color=(220, 220, 0))
112+
img.draw_line((rotated_rect[3][0], rotated_rect[3][1], rotated_rect[0][0], rotated_rect[0][1]), color=(220, 220, 0))
113+
img.draw_string(face_x, face_y + face_height - 10, str(self.face_angle) + "^", color=(70, 130, 180), mono_space=False)
114+
115+
def rotate(self, xy, theta):
116+
cos_theta, sin_theta = math.cos(theta), math.sin(theta)
117+
118+
return (
119+
int(xy[0] * cos_theta - xy[1] * sin_theta),
120+
int(xy[0] * sin_theta + xy[1] * cos_theta)
121+
)
122+
123+
def translate(self, xy, offset):
124+
return xy[0] + offset[0], xy[1] + offset[1]

0 commit comments

Comments
 (0)