def erase_specular(self, eye_img, debug=False): # Rather arbitrary decision on how large a specularity may be max_specular_contour_area = sum(eye_img.shape[:2]) / 2 # Extract top 50% of intensities eye_img_grey = cv2.cvtColor(eye_img, cv2.COLOR_BGR2GRAY) eye_img_grey_blur = cv2.GaussianBlur(eye_img_grey, (5, 5), 0) # Close to suppress eyelashes morph_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) eye_img_grey_blur = cv2.morphologyEx(eye_img_grey_blur, cv2.MORPH_CLOSE, morph_kernel) if eye_img_grey_blur is None: raise eye_extractor.NoEyesFound() thresh_val = int(np.percentile(eye_img_grey_blur, 50)) _, thresh_img = cv2.threshold(eye_img_grey_blur, thresh_val, 255, cv2.THRESH_BINARY) # Find all contours and throw away the big ones contours, _ = cv2.findContours(np.copy(thresh_img), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) small_contours = filter( lambda x: cv2.contourArea(x) < max_specular_contour_area, contours) small_contours_mask = np.zeros_like(eye_img_grey) cv2.drawContours(small_contours_mask, small_contours, -1, 255, -1) # Dilate the smallest contours found small_contours_mask_dilated = cv2.dilate(small_contours_mask, morph_kernel) removed_specular_img = cv2.inpaint(eye_img, small_contours_mask_dilated, 2, flags=cv2.INPAINT_TELEA) if debug: thresh_hierarchy = cv2.cvtColor(eye_img_grey, cv2.COLOR_GRAY2BGR) cv2.drawContours(thresh_hierarchy, contours, -1, (0, 0, 255), -1) thresh_hierarchy = cv2.add( thresh_hierarchy, cv2.cvtColor(small_contours_mask_dilated, cv2.COLOR_GRAY2BGR)) cv2.drawContours(thresh_hierarchy, small_contours, -1, (255, 0, 0), -1) stacked_imgs = np.concatenate( [eye_img, thresh_hierarchy, removed_specular_img], axis=1) if debug == 1: self.full_debug_img = stacked_imgs elif debug == 2: self.full_debug_img = image_utils.stack_imgs_vertical( [self.full_debug_img, stacked_imgs]) cv2.imshow(winname, self.full_debug_img) elif debug == 3: cv2.imshow(winname, stacked_imgs) return removed_specular_img
def find_pupil( eye_img_bgr, fast_width_grads=25.5, fast_width_iso=80, weight_grads=0.9, weight_iso=0.1, debug_index=False ): eye_img_r = cv2.split(eye_img_bgr)[2] fast_size_grads = (int((fast_width_grads / eye_img_bgr.shape[0]) * eye_img_bgr.shape[1]), int(fast_width_grads)) fast_img_grads = cv2.resize(eye_img_r, fast_size_grads) fast_size_iso = (int(fast_width_iso), int((fast_width_iso / eye_img_r.shape[1]) * eye_img_r.shape[0])) fast_img_iso = cv2.resize(eye_img_r, fast_size_iso) c_map_grads = eye_center_locator_gradients.get_center_map(fast_img_grads) c_map_iso = eye_center_locator_isophote.get_center_map(fast_img_iso) c_map_norm_grads = cv2.normalize(c_map_grads, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) c_map_big_grads = cv2.resize(c_map_norm_grads, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])).astype(np.uint8) c_map_norm_iso = cv2.normalize(c_map_iso, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) c_map_big_iso = cv2.resize(c_map_norm_iso, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])).astype(np.uint8) joint_c_map = cv2.addWeighted(c_map_big_grads, w_grads, c_map_big_iso, w_iso, 1.0) max_val_index = np.argmax(joint_c_map) pupil_y0, pupil_x0 = max_val_index // joint_c_map.shape[1], max_val_index % joint_c_map.shape[1] max_val_index_2 = np.argmax(c_map_big_grads) pupil_y0_2, pupil_x0_2 = max_val_index_2 // joint_c_map.shape[1], max_val_index_2 % joint_c_map.shape[1] max_val_index_3 = np.argmax(c_map_big_iso) pupil_y0_3, pupil_x0_3 = max_val_index_3 // joint_c_map.shape[1], max_val_index_3 % joint_c_map.shape[1] if debug_index: debug_img = eye_img_bgr.copy() joint_c_map = cv2.cvtColor(joint_c_map, cv2.COLOR_GRAY2BGR) c_map_big_iso = cv2.cvtColor(c_map_big_iso, cv2.COLOR_GRAY2BGR) c_map_big_grads = cv2.cvtColor(c_map_big_grads, cv2.COLOR_GRAY2BGR) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 16, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0_3, pupil_y0_3), (255, 0, 255), 8, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0_2, pupil_y0_2), (255, 0, 255), 8, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0, pupil_y0), (255, 0, 0), 16, 2) draw_utils.draw_cross(c_map_big_iso, (pupil_x0_3, pupil_y0_3), (255, 0, 0), 16, 2) draw_utils.draw_cross(c_map_big_grads, (pupil_x0_2, pupil_y0_2), (255, 0, 0), 16, 2) stacked_imgs = image_utils.stack_imgs_horizontal([debug_img, c_map_big_grads, c_map_big_iso, joint_c_map]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical([__debug_imgs[1], __debug_imgs[2]]) cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs) return pupil_x0, pupil_y0
def find_pupil(eye_img_bgr, debug_index=False): eye_img_r = cv2.cvtColor(eye_img_bgr, cv2.COLOR_BGR2GRAY) # Scale to small image for faster computation scale = __fast_width / eye_img_r.shape[1] small_size = (int(__fast_width), int((__fast_width / eye_img_r.shape[1]) * eye_img_r.shape[0])) eye_img_small = cv2.resize(eye_img_r, small_size) eye_img_small = cv2.GaussianBlur(eye_img_small, (3, 3), 0) center_map = get_center_map(eye_img_small) max_val_index = np.argmax(center_map) pupil_y0, pupil_x0 = max_val_index // center_map.shape[ 1], max_val_index % center_map.shape[1] # Scale back to original coordinates pupil_y0, pupil_x0 = int((pupil_y0 + 0.5) / scale), int( (pupil_x0 + 0.5) / scale) if debug_index: eye_img_r_debug = cv2.cvtColor(eye_img_r, cv2.COLOR_GRAY2BGR) debug_img = eye_img_bgr.copy() cmap_norm = cv2.normalize(center_map, 0, 255, norm_type=cv2.NORM_MINMAX).astype(np.uint8) center_map_big = cv2.resize( cmap_norm, (eye_img_r.shape[1], eye_img_r.shape[0])).astype(np.uint8) center_map_big = cv2.cvtColor(center_map_big, cv2.COLOR_GRAY2BGR) overlay_img = cv2.addWeighted(center_map_big, 0.9, eye_img_r_debug, 0.1, 1) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 6) draw_utils.draw_cross(overlay_img, (pupil_x0, pupil_y0), (255, 0, 0), 6) # stacked_small_size = image_utils.stack_imgs_vertical([eye_img_small, cmap_norm]) stacked_imgs = image_utils.stack_imgs_horizontal( [debug_img, overlay_img]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical( [__debug_imgs[1], __debug_imgs[2]]) cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs) return pupil_x0, pupil_y0
def find_eyelids(eye_img, debug_index): u_eyelid = find_upper_eyelid(eye_img, debug_index) l_eyelid = find_lower_eyelid(eye_img, debug_index) if debug_index == 2: debug_img_1 = stack_imgs_horizontal([__debug_imgs_upper[1], __debug_imgs_lower[1]]) debug_img_2 = stack_imgs_horizontal([__debug_imgs_upper[2], __debug_imgs_lower[2]]) full_debug_img = stack_imgs_vertical([debug_img_1, debug_img_2]) cv2.imshow(__winname, full_debug_img) return u_eyelid, l_eyelid
def find_pupil(eye_img_bgr, debug_index=False): """ Estimates the centre of the pupil using image gradients """ eye_img_r = cv2.split(eye_img_bgr)[2] # Extract red channel only # Scale to small image for faster computation scale = __fast_width / eye_img_bgr.shape[0] small_size = (int( (__fast_width / eye_img_bgr.shape[0]) * eye_img_bgr.shape[1]), int(__fast_width)) eye_img_small = cv2.resize(eye_img_r, small_size) center_map = get_center_map(eye_img_small) max_val_index = np.argmax(center_map) pupil_y0, pupil_x0 = max_val_index // center_map.shape[ 1], max_val_index % center_map.shape[1] # Scale back to original coordinates pupil_y0, pupil_x0 = int((pupil_y0 + 0.5) / scale), int( (pupil_x0 + 0.5) / scale) if debug_index: eye_img_r_debug = cv2.cvtColor(eye_img_r, cv2.COLOR_GRAY2BGR) debug_img = eye_img_bgr.copy() cmap_norm = cv2.normalize(center_map, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) center_map_big = cv2.resize( cmap_norm, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])) center_map_big = cv2.cvtColor(center_map_big.astype(np.uint8), cv2.COLOR_GRAY2BGR) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 6) draw_utils.draw_cross(center_map_big, (pupil_x0, pupil_y0), (255, 0, 0), 6) stacked_imgs = image_utils.stack_imgs_horizontal( [debug_img, eye_img_r_debug, center_map_big]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical( [__debug_imgs[1], __debug_imgs[2]]) cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs) return pupil_x0, pupil_y0
def find_limbus_edge_pts(self, eye_roi, debug=False): blurred_eye_roi_img = cv2.GaussianBlur(eye_roi.img, (5, 5), 5) pupil_x0, pupil_y0 = eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2 min_limb_r = int(eye_roi.img.shape[0] * self.limb_r_ratios[0]) max_limb_r = int(eye_roi.img.shape[0] * self.limb_r_ratios[1]) pts_found = set() pts_found = pts_found.union( self.cast_rays_spread(bgr_img=blurred_eye_roi_img, start_pos=(pupil_x0, pupil_y0), angle_mean=0, spread=120, limb_r_range=(min_limb_r, max_limb_r))) pts_found = pts_found.union( self.cast_rays_spread(bgr_img=blurred_eye_roi_img, start_pos=(pupil_x0, pupil_y0), angle_mean=180, spread=120, limb_r_range=(min_limb_r, max_limb_r))) if debug: debug_img = blurred_eye_roi_img.copy() cv2.circle(blurred_eye_roi_img, (eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2), min_limb_r, (255, 255, 0)) cv2.circle(blurred_eye_roi_img, (eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2), max_limb_r, (255, 255, 0)) draw_cross(debug_img, (pupil_x0, pupil_y0), color=(255, 255, 0), width=6) draw_points(debug_img, pts_found, (0, 0, 255), width=1, thickness=2) stacked_imgs = np.concatenate( [eye_roi.img, blurred_eye_roi_img, debug_img], axis=1) if debug == 1: self.full_debug_img = stacked_imgs elif debug == 2: self.full_debug_img = stack_imgs_vertical( [self.full_debug_img, stacked_imgs]) cv2.imshow(winname, self.full_debug_img) elif debug == 3: cv2.imshow(winname, stacked_imgs) return pts_found
def find_eyelids(eye_img, debug_index): u_eyelid = find_upper_eyelid(eye_img, debug_index) l_eyelid = find_lower_eyelid(eye_img, debug_index) if debug_index == 2: debug_img_1 = stack_imgs_horizontal( [__debug_imgs_upper[1], __debug_imgs_lower[1]]) debug_img_2 = stack_imgs_horizontal( [__debug_imgs_upper[2], __debug_imgs_lower[2]]) full_debug_img = stack_imgs_vertical([debug_img_1, debug_img_2]) cv2.imshow(__winname, full_debug_img) return u_eyelid, l_eyelid
def erase_specular(self, eye_img, debug=False): # Rather arbitrary decision on how large a specularity may be max_specular_contour_area = sum(eye_img.shape[:2])/2 # Extract top 50% of intensities eye_img_grey = cv2.cvtColor(eye_img, cv2.COLOR_BGR2GRAY) eye_img_grey_blur = cv2.GaussianBlur(eye_img_grey, (5, 5), 0) # Close to suppress eyelashes morph_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) eye_img_grey_blur = cv2.morphologyEx(eye_img_grey_blur, cv2.MORPH_CLOSE, morph_kernel) if eye_img_grey_blur is None: raise eye_extractor.NoEyesFound() thresh_val = int(np.percentile(eye_img_grey_blur, 50)) _, thresh_img = cv2.threshold(eye_img_grey_blur, thresh_val, 255, cv2.THRESH_BINARY) # Find all contours and throw away the big ones contours, _ = cv2.findContours(np.copy(thresh_img), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) small_contours = filter(lambda x : cv2.contourArea(x) < max_specular_contour_area, contours) small_contours_mask = np.zeros_like(eye_img_grey) cv2.drawContours(small_contours_mask, small_contours, -1, 255, -1) # Dilate the smallest contours found small_contours_mask_dilated = cv2.dilate(small_contours_mask, morph_kernel) removed_specular_img = cv2.inpaint(eye_img, small_contours_mask_dilated, 2, flags=cv2.INPAINT_TELEA) if debug: thresh_hierarchy = cv2.cvtColor(eye_img_grey, cv2.COLOR_GRAY2BGR) cv2.drawContours(thresh_hierarchy, contours, -1, (0, 0, 255), -1) thresh_hierarchy = cv2.add(thresh_hierarchy, cv2.cvtColor(small_contours_mask_dilated, cv2.COLOR_GRAY2BGR)) cv2.drawContours(thresh_hierarchy, small_contours, -1, (255, 0, 0), -1) stacked_imgs = np.concatenate([eye_img, thresh_hierarchy, removed_specular_img],axis=1) if debug == 1: self.full_debug_img = stacked_imgs elif debug == 2: self.full_debug_img = image_utils.stack_imgs_vertical([self.full_debug_img, stacked_imgs]) cv2.imshow(winname, self.full_debug_img) elif debug == 3: cv2.imshow(winname, stacked_imgs); return removed_specular_img
def find_pupil(eye_img_bgr, debug_index=False): eye_img_r = cv2.cvtColor(eye_img_bgr, cv2.COLOR_BGR2GRAY) # Scale to small image for faster computation scale = __fast_width / eye_img_r.shape[1] small_size = (int(__fast_width), int((__fast_width / eye_img_r.shape[1]) * eye_img_r.shape[0])) eye_img_small = cv2.resize(eye_img_r, small_size) eye_img_small = cv2.GaussianBlur(eye_img_small, (3, 3), 0) center_map = get_center_map(eye_img_small) max_val_index = np.argmax(center_map) pupil_y0, pupil_x0 = max_val_index // center_map.shape[1], max_val_index % center_map.shape[1] # Scale back to original coordinates pupil_y0, pupil_x0 = int((pupil_y0 + 0.5) / scale), int((pupil_x0 + 0.5) / scale) if debug_index: eye_img_r_debug = cv2.cvtColor(eye_img_r, cv2.COLOR_GRAY2BGR) debug_img = eye_img_bgr.copy() cmap_norm = cv2.normalize(center_map, 0, 255, norm_type=cv2.NORM_MINMAX).astype(np.uint8) center_map_big = cv2.resize(cmap_norm, (eye_img_r.shape[1], eye_img_r.shape[0])).astype(np.uint8) center_map_big = cv2.cvtColor(center_map_big, cv2.COLOR_GRAY2BGR) overlay_img = cv2.addWeighted(center_map_big, 0.9, eye_img_r_debug, 0.1, 1) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 6) draw_utils.draw_cross(overlay_img, (pupil_x0, pupil_y0), (255, 0, 0), 6) # stacked_small_size = image_utils.stack_imgs_vertical([eye_img_small, cmap_norm]) stacked_imgs = image_utils.stack_imgs_horizontal([debug_img, overlay_img]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical([__debug_imgs[1], __debug_imgs[2]]); cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs); return pupil_x0, pupil_y0
def find_pupil(eye_img_bgr, debug_index=False): """ Estimates the centre of the pupil using image gradients """ eye_img_r = cv2.split(eye_img_bgr)[2] # Extract red channel only # Scale to small image for faster computation scale = __fast_width / eye_img_bgr.shape[0] small_size = (int((__fast_width / eye_img_bgr.shape[0]) * eye_img_bgr.shape[1]), int(__fast_width)) eye_img_small = cv2.resize(eye_img_r, small_size) center_map = get_center_map(eye_img_small) max_val_index = np.argmax(center_map) pupil_y0, pupil_x0 = max_val_index // center_map.shape[1], max_val_index % center_map.shape[1] # Scale back to original coordinates pupil_y0, pupil_x0 = int((pupil_y0 + 0.5) / scale), int((pupil_x0 + 0.5) / scale) if debug_index: eye_img_r_debug = cv2.cvtColor(eye_img_r, cv2.COLOR_GRAY2BGR) debug_img = eye_img_bgr.copy() cmap_norm = cv2.normalize(center_map, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) center_map_big = cv2.resize(cmap_norm, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])) center_map_big = cv2.cvtColor(center_map_big.astype(np.uint8), cv2.COLOR_GRAY2BGR) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 6) draw_utils.draw_cross(center_map_big, (pupil_x0, pupil_y0), (255, 0, 0), 6) stacked_imgs = image_utils.stack_imgs_horizontal([debug_img, eye_img_r_debug, center_map_big]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical([__debug_imgs[1], __debug_imgs[2]]); cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs); return pupil_x0, pupil_y0
def find_limbus_edge_pts(self, eye_roi, debug=False): blurred_eye_roi_img = cv2.GaussianBlur(eye_roi.img, (5, 5), 5) pupil_x0, pupil_y0 = eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2 min_limb_r = int(eye_roi.img.shape[0] * self.limb_r_ratios[0]) max_limb_r = int(eye_roi.img.shape[0] * self.limb_r_ratios[1]) pts_found = set() pts_found = pts_found.union(self.cast_rays_spread(bgr_img=blurred_eye_roi_img, start_pos=(pupil_x0, pupil_y0), angle_mean=0, spread=120, limb_r_range=(min_limb_r, max_limb_r))) pts_found = pts_found.union(self.cast_rays_spread(bgr_img=blurred_eye_roi_img, start_pos=(pupil_x0, pupil_y0), angle_mean=180, spread=120, limb_r_range=(min_limb_r, max_limb_r))) if debug: debug_img = blurred_eye_roi_img.copy() cv2.circle(blurred_eye_roi_img, (eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2), min_limb_r, (255, 255, 0)) cv2.circle(blurred_eye_roi_img, (eye_roi.img.shape[1] / 2, eye_roi.img.shape[0] / 2), max_limb_r, (255, 255, 0)) draw_cross(debug_img, (pupil_x0, pupil_y0), color=(255, 255, 0), width=6) draw_points(debug_img, pts_found, (0, 0, 255), width=1, thickness=2) stacked_imgs = np.concatenate([eye_roi.img, blurred_eye_roi_img, debug_img], axis=1) if debug == 1: self.full_debug_img = stacked_imgs elif debug == 2: self.full_debug_img = stack_imgs_vertical([self.full_debug_img, stacked_imgs]) cv2.imshow(winname, self.full_debug_img) elif debug == 3: cv2.imshow(winname, stacked_imgs); return pts_found
def get_limb_pts(eye_img, phi=20, angle_step=1, debug_index=False): polar_img_w = 360 / angle_step # Polar image has one column per angle of interest phi_range_1 = ((90 - phi) / angle_step, (90 + phi) / angle_step ) # Ranges of angles to be ignored (too close to lids) phi_range_2 = ((270 - phi) / angle_step, (270 + phi) / angle_step) eye_img_grey = cv2.cvtColor(eye_img, cv2.COLOR_BGR2GRAY) # Do BGR-grey eye_img_grey = cv2.medianBlur(eye_img_grey, 5) # Scale to fixed size image for re-using transform matrix scale = eye_img.shape[0] / float(__fixed_width) img_fixed_size = cv2.resize(eye_img_grey, (__fixed_width, __fixed_width)) # Transform image into polar coords and blur img_polar = linpolar(img_fixed_size, trans_w=polar_img_w, trans_h=__fixed_width / 2) img_polar = cv2.GaussianBlur(img_polar, (5, 5), 0) # Take the segment between min & max radii and filter with Gabor kernel img_polar_seg = img_polar[__min_limb_r:__max_limb_r, :] filter_img = cv2.filter2D(img_polar_seg, -1, __gabor_kern) # Black out ignored angles filter_img.T[phi_range_1[0]:phi_range_1[1]] = 0 filter_img.T[phi_range_2[0]:phi_range_2[1]] = 0 # In polar image, x <-> theta, y <-> magnitude pol_ys = np.argmax(filter_img, axis=0) # Take highest filter response as limbus points pol_xs = np.arange(filter_img.shape[1])[pol_ys > 0] mags = (pol_ys + __min_limb_r)[pol_ys > 0] thts = np.radians(pol_xs * angle_step) # Translate each point back into fixed img coords xs, ys = cv2.polarToCart(mags.astype(float), thts) xs = ( xs + __fixed_width / 2 ) * scale # Shift and scale cart. coords back to original eye-ROI coords ys = (ys + __fixed_width / 2) * scale # Points returned in form # [[ x1 y1] # [ x2 y2] # ... # [ xn yn]] pts_cart = np.concatenate([xs, ys], axis=1) # --------------------- Debug Drawing --------------------- if debug_index != False: debug_img = eye_img.copy() debug_polar = cv2.cvtColor(img_polar, cv2.COLOR_GRAY2BGR) cv2.imwrite("polar.jpg", debug_polar) cv2.line(debug_polar, (0, __min_limb_r), (img_polar.shape[1], __min_limb_r), (255, 255, 0)) cv2.line(debug_polar, (0, __max_limb_r), (img_polar.shape[1], __max_limb_r), (255, 255, 0)) cv2.circle(debug_img, (debug_img.shape[1] / 2, debug_img.shape[0] / 2), int(debug_img.shape[0] * __limb_r_ratios[0]), (255, 255, 0)) cv2.circle(debug_img, (debug_img.shape[1] / 2, debug_img.shape[0] / 2), int(debug_img.shape[0] * __limb_r_ratios[1]), (255, 255, 0)) pts_polar = np.squeeze(np.dstack([pol_xs, mags])) draw_points(debug_polar, pts_polar, (0, 0, 255), width=1) draw_points(debug_img, pts_cart, (0, 0, 255), width=1) stacked_imgs_polar = stack_imgs_vertical([debug_polar, filter_img]) stacked_imgs = stack_imgs_horizontal( [debug_img, eye_img_grey, stacked_imgs_polar]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = stack_imgs_vertical( [__debug_imgs[1], __debug_imgs[2]]) cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs) # --------------------- Debug Drawing --------------------- return pts_cart
def get_limb_pts(eye_img, phi=20, angle_step=1, debug_index=False): polar_img_w = 360 / angle_step # Polar image has one column per angle of interest phi_range_1 = ((90 - phi) / angle_step, (90 + phi) / angle_step) # Ranges of angles to be ignored (too close to lids) phi_range_2 = ((270 - phi) / angle_step, (270 + phi) / angle_step) eye_img_grey = cv2.cvtColor(eye_img, cv2.COLOR_BGR2GRAY) # Do BGR-grey eye_img_grey = cv2.medianBlur(eye_img_grey, 5) # Scale to fixed size image for re-using transform matrix scale = eye_img.shape[0] / float(__fixed_width) img_fixed_size = cv2.resize(eye_img_grey, (__fixed_width, __fixed_width)) # Transform image into polar coords and blur img_polar = linpolar(img_fixed_size, trans_w=polar_img_w, trans_h=__fixed_width / 2) img_polar = cv2.GaussianBlur(img_polar, (5, 5), 0) # Take the segment between min & max radii and filter with Gabor kernel img_polar_seg = img_polar[__min_limb_r:__max_limb_r, :] filter_img = cv2.filter2D(img_polar_seg, -1, __gabor_kern) # Black out ignored angles filter_img.T[ phi_range_1[0] : phi_range_1[1] ] = 0 filter_img.T[ phi_range_2[0] : phi_range_2[1] ] = 0 # In polar image, x <-> theta, y <-> magnitude pol_ys = np.argmax(filter_img, axis=0) # Take highest filter response as limbus points pol_xs = np.arange(filter_img.shape[1])[pol_ys > 0] mags = (pol_ys + __min_limb_r)[pol_ys > 0] thts = np.radians(pol_xs * angle_step) # Translate each point back into fixed img coords xs, ys = cv2.polarToCart(mags.astype(float), thts) xs = (xs + __fixed_width / 2) * scale # Shift and scale cart. coords back to original eye-ROI coords ys = (ys + __fixed_width / 2) * scale # Points returned in form # [[ x1 y1] # [ x2 y2] # ... # [ xn yn]] pts_cart = np.concatenate([xs, ys], axis=1) # --------------------- Debug Drawing --------------------- if debug_index != False: debug_img = eye_img.copy() debug_polar = cv2.cvtColor(img_polar, cv2.COLOR_GRAY2BGR) cv2.imwrite("polar.jpg",debug_polar) cv2.line(debug_polar, (0, __min_limb_r), (img_polar.shape[1], __min_limb_r), (255, 255, 0)) cv2.line(debug_polar, (0, __max_limb_r), (img_polar.shape[1], __max_limb_r), (255, 255, 0)) cv2.circle(debug_img, (debug_img.shape[1] / 2, debug_img.shape[0] / 2), int(debug_img.shape[0] * __limb_r_ratios[0]), (255, 255, 0)) cv2.circle(debug_img, (debug_img.shape[1] / 2, debug_img.shape[0] / 2), int(debug_img.shape[0] * __limb_r_ratios[1]), (255, 255, 0)) pts_polar = np.squeeze(np.dstack([pol_xs, mags])) draw_points(debug_polar, pts_polar, (0, 0, 255), width=1) draw_points(debug_img, pts_cart, (0, 0, 255), width=1) stacked_imgs_polar = stack_imgs_vertical([debug_polar, filter_img]) stacked_imgs = stack_imgs_horizontal([debug_img, eye_img_grey, stacked_imgs_polar]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = stack_imgs_vertical([__debug_imgs[1], __debug_imgs[2]]); cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs); # --------------------- Debug Drawing --------------------- return pts_cart
def find_lower_eyelid(eye_img, debug_index): line_y_offset = 0 # Amount to shift eye-lid by after detection img_blue = cv2.split(eye_img)[2] img_w, img_h = eye_img.shape[:2] # Indexes to extract window sub-images w_y1, w_y2 = int(img_h * __l_win_rats_h[0]), int(img_h * sum(__l_win_rats_h[:2])) wl_x1, wl_x2 = int(img_w * __l_win_rats_w_l[0]), int( img_w * sum(__l_win_rats_w_l[:2])) wr_x1, wr_x2 = int(img_w * __l_win_rats_w_r[0]), int( img_w * sum(__l_win_rats_w_r[:2])) # Split image into two halves window_img_l = img_blue[w_y1:w_y2, wl_x1:wl_x2] window_img_r = img_blue[w_y1:w_y2, wr_x1:wr_x2] window_img_l = cv2.GaussianBlur(window_img_l, (5, 5), 20) window_img_r = cv2.GaussianBlur(window_img_r, (5, 5), 20) filter_img_l = cv2.filter2D(window_img_l, -1, __gabor_kern_diag) filter_img_r = cv2.filter2D(window_img_r, -1, cv2.flip(__gabor_kern_diag, 1)) filter_img = np.concatenate([filter_img_l, filter_img_r], axis=1) # In polar image, x <-> theta, y <-> magnitude max_vals = np.max(filter_img, axis=0) ys = np.argmax(filter_img, axis=0) # Take highest filter response as limbus points xs = (np.arange(filter_img.shape[1]) + wl_x1)[max_vals > __min_thresh] ys = (ys + w_y1)[max_vals > __min_thresh] l_lid_pts = np.squeeze(np.dstack([xs, ys]), axis=0) # Only RANSAC fit eyelid if there are enough points if l_lid_pts.size < __min_num_pts_u * 2: eyelid_lower_line = None else: eyelid_lower_line = ransac_line(l_lid_pts) if eyelid_lower_line is not None: a, b = eyelid_lower_line b = b + line_y_offset eyelid_lower_line = a, b if debug_index: debug_img = eye_img.copy() filter_img = cv2.cvtColor(filter_img, cv2.COLOR_GRAY2BGR) if l_lid_pts.size > 2: draw_points(debug_img, l_lid_pts, (0, 0, 255), 1, 2) if eyelid_lower_line is not None: cv2.line(debug_img, (0, int(b)), (img_w, int(a * img_w + b)), (0, 255, 0)) window_img = np.concatenate([window_img_l, window_img_r], axis=1) stacked_windows = stack_imgs_vertical([window_img, filter_img]) stacked_imgs = stack_imgs_horizontal([stacked_windows, debug_img]) __debug_imgs_lower[debug_index] = stacked_imgs if debug_index > 2: cv2.imshow(__winname + repr(debug_index) + "l", stacked_imgs) return eyelid_lower_line
def find_upper_eyelid(eye_img, debug_index): u_2_win_rats_w = [0.0, 1.0, 0.0] # Margins around ROI windows u_2_win_rats_h = [0.0, 0.5, 0.5] # FIXME - using r channel? img_blue = cv2.split(eye_img)[2] img_w, img_h = eye_img.shape[:2] # Indexes to extract window sub-images w_y1, w_y2 = int(img_h * u_2_win_rats_h[0]), int(img_h * sum(u_2_win_rats_h[:2])) w_x1, w_x2 = int(img_w * u_2_win_rats_w[0]), int(img_w * sum(u_2_win_rats_w[:2])) # Split image into two halves window_img = img_blue[w_y1:w_y2, w_x1:w_x2] # Supress eyelashes morph_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) window_img = cv2.morphologyEx(window_img, cv2.MORPH_CLOSE, morph_kernel) # Filter right half with inverse kernel of left half to ignore iris/sclera boundary filter_img_win = cv2.filter2D(window_img, -1, __gabor_kern_horiz) # Copy windows back into correct places in full filter image filter_img = np.zeros(eye_img.shape[:2], dtype=np.uint8) filter_img[w_y1:w_y2, w_x1:w_x2] = filter_img_win # Mask with circles cv2.circle(filter_img, (3 * filter_img.shape[1] / 7, filter_img.shape[0] / 2), filter_img.shape[1] / 4, 0, -1) cv2.circle(filter_img, (4 * filter_img.shape[1] / 7, filter_img.shape[0] / 2), filter_img.shape[1] / 4, 0, -1) ys = np.argmax(filter_img, axis=0) xs = np.arange(filter_img.shape[1])[ys > 0] ys = (ys)[ys > 0] u_lid_pts = [] for i, x in enumerate(xs): col = filter_img.T[x] start_ind, end_ind = ys[i] + 5, min(ys[i] + 100, len(col) - 2) col_window = col[start_ind:end_ind] max_col = np.max(col) max_win = np.max(col_window) if max_col - max_win < 50: new_y = np.argmax(col_window) + ys[i] + 5 u_lid_pts.append((x, new_y)) else: u_lid_pts.append((x, ys[i])) # Only RANSAC fit eyelid if there are enough points if len(u_lid_pts) < __min_num_pts_u * 2: eyelid_upper_parabola = None u_lid_pts = [] else: u_lid_pts_l = [(x, y) for (x, y) in u_lid_pts if x < filter_img.shape[1] / 2] u_lid_pts_r = [(x, y) for (x, y) in u_lid_pts if x > filter_img.shape[1] / 2] # Fit eye_img coord points of sclera-segs to degree 2 polynomial # a(x^2) + b(x) + c eyelid_upper_parabola = ransac_parabola(u_lid_pts_l, u_lid_pts_r, ransac_iters_max=5, refine_iters_max=2, max_err=4) if eyelid_upper_parabola is not None: a, b, c = eyelid_upper_parabola c = c - __parabola_y_offset eyelid_upper_parabola = a, b, c # --------------------- Debug Drawing --------------------- if debug_index: debug_img = eye_img.copy() if eyelid_upper_parabola is not None: lid_xs = np.arange(21) * img_w / 20 lid_ys = a * lid_xs**2 + b * lid_xs + c lid_pts = np.dstack([lid_xs, lid_ys]).astype(int) cv2.polylines(debug_img, lid_pts, False, (0, 255, 0), 1) draw_points(debug_img, u_lid_pts, (0, 0, 255), 1, 2) filter_img = cv2.cvtColor(filter_img, cv2.COLOR_GRAY2BGR) draw_points(filter_img, u_lid_pts, (0, 0, 255), 1, 2) stacked_windows = stack_imgs_vertical([window_img, filter_img]) stacked_imgs = stack_imgs_horizontal([stacked_windows, debug_img]) __debug_imgs_upper[debug_index] = stacked_imgs if debug_index > 2: cv2.imshow(__winname + repr(debug_index) + "u", stacked_imgs) # --------------------- Debug Drawing --------------------- return eyelid_upper_parabola
def find_pupil(eye_img_bgr, fast_width_grads=25.5, fast_width_iso=80, weight_grads=0.9, weight_iso=0.1, debug_index=False): eye_img_r = cv2.split(eye_img_bgr)[2] fast_size_grads = (int( (fast_width_grads / eye_img_bgr.shape[0]) * eye_img_bgr.shape[1]), int(fast_width_grads)) fast_img_grads = cv2.resize(eye_img_r, fast_size_grads) fast_size_iso = (int(fast_width_iso), int((fast_width_iso / eye_img_r.shape[1]) * eye_img_r.shape[0])) fast_img_iso = cv2.resize(eye_img_r, fast_size_iso) c_map_grads = eye_center_locator_gradients.get_center_map(fast_img_grads) c_map_iso = eye_center_locator_isophote.get_center_map(fast_img_iso) c_map_norm_grads = cv2.normalize(c_map_grads, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) c_map_big_grads = cv2.resize( c_map_norm_grads, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])).astype(np.uint8) c_map_norm_iso = cv2.normalize(c_map_iso, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) c_map_big_iso = cv2.resize( c_map_norm_iso, (eye_img_bgr.shape[1], eye_img_bgr.shape[0])).astype(np.uint8) joint_c_map = cv2.addWeighted(c_map_big_grads, w_grads, c_map_big_iso, w_iso, 1.0) max_val_index = np.argmax(joint_c_map) pupil_y0, pupil_x0 = max_val_index // joint_c_map.shape[ 1], max_val_index % joint_c_map.shape[1] max_val_index_2 = np.argmax(c_map_big_grads) pupil_y0_2, pupil_x0_2 = max_val_index_2 // joint_c_map.shape[ 1], max_val_index_2 % joint_c_map.shape[1] max_val_index_3 = np.argmax(c_map_big_iso) pupil_y0_3, pupil_x0_3 = max_val_index_3 // joint_c_map.shape[ 1], max_val_index_3 % joint_c_map.shape[1] if debug_index: debug_img = eye_img_bgr.copy() joint_c_map = cv2.cvtColor(joint_c_map, cv2.COLOR_GRAY2BGR) c_map_big_iso = cv2.cvtColor(c_map_big_iso, cv2.COLOR_GRAY2BGR) c_map_big_grads = cv2.cvtColor(c_map_big_grads, cv2.COLOR_GRAY2BGR) draw_utils.draw_cross(debug_img, (pupil_x0, pupil_y0), (0, 255, 255), 16, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0_3, pupil_y0_3), (255, 0, 255), 8, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0_2, pupil_y0_2), (255, 0, 255), 8, 2) draw_utils.draw_cross(joint_c_map, (pupil_x0, pupil_y0), (255, 0, 0), 16, 2) draw_utils.draw_cross(c_map_big_iso, (pupil_x0_3, pupil_y0_3), (255, 0, 0), 16, 2) draw_utils.draw_cross(c_map_big_grads, (pupil_x0_2, pupil_y0_2), (255, 0, 0), 16, 2) stacked_imgs = image_utils.stack_imgs_horizontal( [debug_img, c_map_big_grads, c_map_big_iso, joint_c_map]) __debug_imgs[debug_index] = stacked_imgs if debug_index == 2: full_debug_img = image_utils.stack_imgs_vertical( [__debug_imgs[1], __debug_imgs[2]]) cv2.imshow(__winname, full_debug_img) elif debug_index > 2: cv2.imshow(__winname, stacked_imgs) return pupil_x0, pupil_y0
def find_lower_eyelid(eye_img, debug_index): line_y_offset = 0 # Amount to shift eye-lid by after detection img_blue = cv2.split(eye_img)[2] img_w, img_h = eye_img.shape[:2] # Indexes to extract window sub-images w_y1, w_y2 = int(img_h * __l_win_rats_h[0]), int(img_h * sum(__l_win_rats_h[:2])) wl_x1, wl_x2 = int(img_w * __l_win_rats_w_l[0]), int(img_w * sum(__l_win_rats_w_l[:2])) wr_x1, wr_x2 = int(img_w * __l_win_rats_w_r[0]), int(img_w * sum(__l_win_rats_w_r[:2])) # Split image into two halves window_img_l = img_blue[w_y1:w_y2, wl_x1:wl_x2] window_img_r = img_blue[w_y1:w_y2, wr_x1:wr_x2] window_img_l = cv2.GaussianBlur(window_img_l, (5, 5), 20) window_img_r = cv2.GaussianBlur(window_img_r, (5, 5), 20) filter_img_l = cv2.filter2D(window_img_l, -1, __gabor_kern_diag) filter_img_r = cv2.filter2D(window_img_r, -1, cv2.flip(__gabor_kern_diag, 1)) filter_img = np.concatenate([filter_img_l, filter_img_r], axis=1) # In polar image, x <-> theta, y <-> magnitude max_vals = np.max(filter_img, axis=0) ys = np.argmax(filter_img, axis=0) # Take highest filter response as limbus points xs = (np.arange(filter_img.shape[1]) + wl_x1)[max_vals > __min_thresh] ys = (ys + w_y1)[max_vals > __min_thresh] l_lid_pts = np.squeeze(np.dstack([xs, ys]), axis=0) # Only RANSAC fit eyelid if there are enough points if l_lid_pts.size < __min_num_pts_u * 2: eyelid_lower_line = None else: eyelid_lower_line = ransac_line(l_lid_pts) if eyelid_lower_line is not None: a, b = eyelid_lower_line b = b + line_y_offset eyelid_lower_line = a, b if debug_index: debug_img = eye_img.copy() filter_img = cv2.cvtColor(filter_img, cv2.COLOR_GRAY2BGR) if l_lid_pts.size > 2: draw_points(debug_img, l_lid_pts, (0, 0, 255), 1, 2) if eyelid_lower_line is not None: cv2.line(debug_img, (0, int(b)), (img_w, int(a * img_w + b)), (0, 255, 0)) window_img = np.concatenate([window_img_l, window_img_r], axis=1) stacked_windows = stack_imgs_vertical([window_img, filter_img]) stacked_imgs = stack_imgs_horizontal([stacked_windows, debug_img]) __debug_imgs_lower[debug_index] = stacked_imgs if debug_index > 2: cv2.imshow(__winname + repr(debug_index) + "l", stacked_imgs) return eyelid_lower_line
def find_upper_eyelid(eye_img, debug_index): u_2_win_rats_w = [0.0, 1.0, 0.0] # Margins around ROI windows u_2_win_rats_h = [0.0, 0.5, 0.5] # FIXME - using r channel? img_blue = cv2.split(eye_img)[2] img_w, img_h = eye_img.shape[:2] # Indexes to extract window sub-images w_y1, w_y2 = int(img_h * u_2_win_rats_h[0]), int(img_h * sum(u_2_win_rats_h[:2])) w_x1, w_x2 = int(img_w * u_2_win_rats_w[0]), int(img_w * sum(u_2_win_rats_w[:2])) # Split image into two halves window_img = img_blue[w_y1:w_y2, w_x1:w_x2] # Supress eyelashes morph_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) window_img = cv2.morphologyEx(window_img, cv2.MORPH_CLOSE, morph_kernel) # Filter right half with inverse kernel of left half to ignore iris/sclera boundary filter_img_win = cv2.filter2D(window_img, -1, __gabor_kern_horiz) # Copy windows back into correct places in full filter image filter_img = np.zeros(eye_img.shape[:2], dtype=np.uint8) filter_img[w_y1:w_y2, w_x1:w_x2] = filter_img_win # Mask with circles cv2.circle(filter_img, (3 * filter_img.shape[1] / 7, filter_img.shape[0] / 2), filter_img.shape[1] / 4, 0, -1) cv2.circle(filter_img, (4 * filter_img.shape[1] / 7, filter_img.shape[0] / 2), filter_img.shape[1] / 4, 0, -1) ys = np.argmax(filter_img, axis=0) xs = np.arange(filter_img.shape[1])[ys > 0] ys = (ys)[ys > 0] u_lid_pts = [] for i, x in enumerate(xs): col = filter_img.T[x] start_ind, end_ind = ys[i] + 5, min(ys[i] + 100, len(col) - 2) col_window = col[start_ind:end_ind] max_col = np.max(col) max_win = np.max(col_window) if max_col - max_win < 50: new_y = np.argmax(col_window) + ys[i] + 5 u_lid_pts.append((x, new_y)) else: u_lid_pts.append((x, ys[i])) # Only RANSAC fit eyelid if there are enough points if len(u_lid_pts) < __min_num_pts_u * 2: eyelid_upper_parabola = None u_lid_pts = [] else: u_lid_pts_l = [(x, y) for (x, y) in u_lid_pts if x < filter_img.shape[1] / 2] u_lid_pts_r = [(x, y) for (x, y) in u_lid_pts if x > filter_img.shape[1] / 2] # Fit eye_img coord points of sclera-segs to degree 2 polynomial # a(x^2) + b(x) + c eyelid_upper_parabola = ransac_parabola( u_lid_pts_l, u_lid_pts_r, ransac_iters_max=5, refine_iters_max=2, max_err=4 ) if eyelid_upper_parabola is not None: a, b, c = eyelid_upper_parabola c = c - __parabola_y_offset eyelid_upper_parabola = a, b, c # --------------------- Debug Drawing --------------------- if debug_index: debug_img = eye_img.copy() if eyelid_upper_parabola is not None: lid_xs = np.arange(21) * img_w / 20 lid_ys = a * lid_xs ** 2 + b * lid_xs + c lid_pts = np.dstack([lid_xs, lid_ys]).astype(int) cv2.polylines(debug_img, lid_pts, False, (0, 255, 0), 1) draw_points(debug_img, u_lid_pts, (0, 0, 255), 1, 2) filter_img = cv2.cvtColor(filter_img, cv2.COLOR_GRAY2BGR) draw_points(filter_img, u_lid_pts, (0, 0, 255), 1, 2) stacked_windows = stack_imgs_vertical([window_img, filter_img]) stacked_imgs = stack_imgs_horizontal([stacked_windows, debug_img]) __debug_imgs_upper[debug_index] = stacked_imgs if debug_index > 2: cv2.imshow(__winname + repr(debug_index) + "u", stacked_imgs) # --------------------- Debug Drawing --------------------- return eyelid_upper_parabola