11import sensor
22import image
3- import utime
3+ import math
4+ import ml
5+ from ml .utils import NMS
46
57class face_detection :
68 def __init__ (self , config , comms ):
@@ -9,34 +11,63 @@ def __init__(self, config, comms):
911 self .has_face = False
1012 self .face_cascade = image .HaarCascade ("frontalface" , stages = self .config .get ('FaceStages' ))
1113 self .face_object = [0 , 0 , 1 , 1 ]
14+ self .face_angle = 0
15+ self .correct_angle = False
16+ self .ml_model = None
17+ self .min_confidence = 0.4
18+ self .threshold_list = [(math .ceil (self .min_confidence * 255 ), 255 )]
1219
1320 self .extra_fb = sensor .alloc_extra_fb (sensor .width (), sensor .height (), sensor .GRAYSCALE )
1421
15- def detect (self , img ):
22+ def detect (self , img , global_variance ):
1623 if not self .config .get ('TrackFace' ):
1724 self .face_object = [0 , 0 , img .width (), img .height ()]
1825 self .has_face = False
1926 return
2027
21- now = utime .ticks_ms ()
28+ if global_variance >= self .config .get ('TossThreshold' ):
29+ self .face_object = [0 , 0 , img .width (), img .height ()]
30+ self .has_face = False
31+
32+ if self .config .get ('TensorFlow' ):
33+ if self .ml_model == None :
34+ self .ml_model = ml .Model ('fomo_face_detection' )
35+
36+ for i , detection_list in enumerate (self .ml_model .predict ([img ], callback = self .post_process )):
37+ if i == 0 or len (detection_list ) == 0 :
38+ continue
39+
40+ for (x , y , w , h ), score in detection_list :
41+ self .face_object = (x - w , y - h , w * 4 , h * 4 )
42+ self .has_face = True
43+ return
44+
45+ self .face_angle = 0
2246 face_objects = img .find_features (self .face_cascade , threshold = self .config .get ('FaceThreshold' ), scale_factor = self .config .get ('FaceScaleFactor' ))
2347
2448 if len (face_objects ) == 0 and self .config .get ('FaceAngles' ):
2549 for angle in self .config .get ('FaceAngles' ):
2650 self .extra_fb .replace (img )
2751 self .extra_fb .rotation_corr (x_rotation = 0.0 , y_rotation = 0.0 , z_rotation = angle )
52+
2853 face_objects = self .extra_fb .find_features (self .face_cascade , threshold = self .config .get ('FaceThreshold' ), scale_factor = self .config .get ('FaceScaleFactor' ))
2954 if face_objects :
55+ self .face_angle = angle
3056 break
3157
3258 if face_objects :
3359 self .face_object = face_objects [0 ]
3460 self .has_face = True
61+
3562 if self .config .get ('TrackEyes' ):
3663 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 )]
37- else :
38- self .face_object = [0 , 0 , img .width (), img .height ()]
39- self .has_face = False
64+
65+ if self .correct_angle :
66+ eyes_x = self .face_object [0 ] + int (self .face_object [2 ] * 1 / 5 )
67+ eyes_y = self .face_object [1 ] + int (self .face_object [3 ] * 1 / 5 )
68+ eyes_width = self .face_object [2 ] - int (self .face_object [2 ] * 1 / 5 )
69+ eyes_height = int (self .face_object [3 ] * 2 / 5 )
70+ self .face_object = [eyes_x , eyes_y , eyes_width , eyes_height ]
4071
4172 def draw_region (self , img ):
4273 if not self .config .get ('DrawFaceRegion' ):
@@ -45,7 +76,56 @@ def draw_region(self, img):
4576 if self .face_object [2 ] == img .width ():
4677 return
4778
48- if self .config .get ('PixelFormat' ) == 'RGB565' :
49- img .draw_rectangle (self .face_object , color = (70 , 130 , 180 ))
50- else :
51- img .draw_rectangle (self .face_object )
79+ img .draw_rectangle (self .face_object , color = (70 , 130 , 180 ))
80+
81+ if self .correct_angle :
82+ face_x = self .face_object [0 ]
83+ face_y = self .face_object [1 ]
84+ face_width = self .face_object [2 ]
85+ face_height = self .face_object [3 ]
86+
87+ theta = math .radians (self .face_angle * - 1 )
88+ offset = (0 , 0 )
89+
90+ rect = [
91+ (face_x , face_y ),
92+ (face_x , face_y + face_height ),
93+ (face_x + face_width , face_y + face_height ),
94+ (face_x + face_width , face_y )
95+ ]
96+
97+ rotated_rect = [translate (rotate (xy , theta ), offset ) for xy in rect ]
98+
99+ img .draw_line ((rotated_rect [0 ][0 ], rotated_rect [0 ][1 ], rotated_rect [1 ][0 ], rotated_rect [1 ][1 ]), color = (220 , 220 , 0 ))
100+ img .draw_line ((rotated_rect [1 ][0 ], rotated_rect [1 ][1 ], rotated_rect [2 ][0 ], rotated_rect [2 ][1 ]), color = (220 , 220 , 0 ))
101+ img .draw_line ((rotated_rect [2 ][0 ], rotated_rect [2 ][1 ], rotated_rect [3 ][0 ], rotated_rect [3 ][1 ]), color = (220 , 220 , 0 ))
102+ img .draw_line ((rotated_rect [3 ][0 ], rotated_rect [3 ][1 ], rotated_rect [0 ][0 ], rotated_rect [0 ][1 ]), color = (220 , 220 , 0 ))
103+ img .draw_string (face_x , face_y + face_height - 10 , str (self .face_angle ) + "^" , color = (70 , 130 , 180 ), mono_space = False )
104+
105+ def post_process (self , model , inputs , outputs ):
106+ n , oh , ow , oc = model .output_shape [0 ]
107+ nms = NMS (ow , oh , inputs [0 ].roi )
108+ for i in range (oc ):
109+ img = image .Image (outputs [0 ][0 , :, :, i ] * 255 )
110+ blobs = img .find_blobs (
111+ self .threshold_list , x_stride = 1 , area_threshold = 1 , pixels_threshold = 1
112+ )
113+ for b in blobs :
114+ rect = b .rect ()
115+ x , y , w , h = rect
116+ score = (
117+ img .get_statistics (thresholds = self .threshold_list , roi = rect ).l_mean () / 255.0
118+ )
119+ nms .add_bounding_box (x , y , x + w , y + h , score , i )
120+ return nms .get_bounding_boxes ()
121+
122+ def rotate (xy , theta ):
123+ cos_theta , sin_theta = math .cos (theta ), math .sin (theta )
124+
125+ return (
126+ int (xy [0 ] * cos_theta - xy [1 ] * sin_theta ),
127+ int (xy [0 ] * sin_theta + xy [1 ] * cos_theta )
128+ )
129+
130+ def translate (xy , offset ):
131+ return xy [0 ] + offset [0 ], xy [1 ] + offset [1 ]
0 commit comments