def place_cross(self, source: np.ndarray, point: tuple, color: tuple) -> None: try: source[to_int(point[1] - 3):to_int(point[1] + 4), to_int(point[0])] = color source[to_int(point[1]), to_int(point[0] - 3):to_int(point[0] + 4)] = color except: pass
def cr_artifacts(self, cr_processor, offsetx: int, offsety: int, pupil_area) -> None: """ Computes pupillary overlaps and acts to remove these artifacts. """ cr_center, cr_width, cr_height, cr_angle, cr_dimensions_int = cr_processor.ellipse.parameters( ) cr_center_int = tuple_int(cr_center) larger_width, larger_height = larger_radius = tuple( int(1.2 * element) for element in cr_dimensions_int) cr_width_norm = larger_width * self.norm cr_height_norm = larger_height * self.norm dimensional_product = larger_width * larger_height arc = [ dimensional_product / np.sqrt((cr_width_norm * anglesteps_cos[i])**2 + (cr_width_norm * anglesteps_sin[i])**2) for i in angular_range ] cos_sin_arc = [(to_int(anglesteps_cos[i] * arc[i]), to_int(anglesteps_sin[i] * arc[i])) for i in angular_range] hit_list = zeros.copy() for i, arc_element in enumerate(cos_sin_arc): cos, sin = arc_element x = cr_center_int[0] + offsetx + cos y = cr_center_int[1] + offsety + sin n = 1 while n < self.norm_cr_artefact: n += 1 try: if pupil_area[y, x] != 0: hit_list[i] = i + 1 break else: x += cos y += sin except: break if np.any(hit_list): delta = np.count_nonzero(number_row - hit_list) if delta < self.norm_cr_artefact: cv2.ellipse(self.pupil_source, cr_center_int, larger_radius, cr_angle, 0, 360, 0, -1) else: for element in hit_list: if element != 0: cos, sin = cos_sin_arc[element - 1] x = cr_center_int[0] + cos y = cr_center_int[1] + sin cv2.ellipse(self.pupil_source, cr_center_int, larger_radius, cr_angle, element * 40 - 40, element * 40, 0, 4) # normalize qqqq
def track(self, last: bool = False): try: # FIXME: while the hasattr is fixing the exception, it is not the # most elegant solution (nor probably the right one) if config.engine.blink_i == 1 and hasattr(self, 'standard_corners'): self.corners = self.standard_corners.copy() self.walkout_offset = 0 self.refresh_source(self.source) contours, hierarchy = cv2.findContours(self.area, 1, 2) if len(contours) > 0: dists = np.zeros(len(contours)) for i, cnt in enumerate(contours): M = cv2.moments(cnt) try: cx = int(M['m10'] / M['m00']) cy = int(M['m01'] / M['m00']) if self.type == 1: dists[i] = np.mean(self.source[cy - 2:cy + 2, cx - 2:cx + 2]) else: dists[i] = np.sqrt((cx - self.center[0])**2 + (cy - self.center[1])**2) except: dists[i] = 255 from operator import itemgetter # todo: score these based on 1) color and 2) distance to center M = cv2.moments(contours[min(enumerate(dists), key=itemgetter(1))[0]]) try: cx = round(M['m10'] / M['m00']) cy = round(M['m01'] / M['m00']) except: return False # print(cx,cy) self.center = (cx, cy) # self.source[cy,cx]=250 # cv2.imshow("JJ", self.source) # cv2.waitKey(0) # center has not been initialized yet if self.center == -1: return False # center has not been initialized yet if self.center == -1: return False center = [ self.center[0] - self.corners[0][0], self.center[1] - self.corners[0][1] ] walkout = self.walkout walkout.reset(center) fit_product = 1 if walkout.walkout(): fit_product = self.fit_model if fit_product.fit(walkout.rx, walkout.ry): ellipse = fit_product else: ellipse = 0 else: ellipse = 0 except Exception as e: import traceback traceback.print_exc() return False if ellipse == fit_product: center, width, height = ellipse.center, ellipse.width, ellipse.height if width * height > 4: # if self.type == 2: # if distance(np.array(center), np.array(self.center)) > 6: #normalize qqqq # self.center = self.original_center[self.center_index] # self.corners = self.standard_corners.copy() # return False self.ellipse = ellipse self.center = center # "walkout_offset" defines the walkout offset for the next frame. # The multiplication factor, here .4, returns slightly below the average. self.walkout_offset = int(.4 * (width + height)) self.margin = np.amax(ellipse.dimensions_int) * 2 center_int = tuple_int(center) self.corners[0] = (max(center_int[0] - self.margin, 0), max(center_int[1] - self.margin, 0)) self.corners[1] = (min(center_int[0] + self.margin, config.engine.width), min(center_int[1] + self.margin, config.engine.height)) self.center_index = 0 margin = to_int(np.amax(ellipse.dimensions_int) * 1) # self.bbox = (max(center_int[0] - margin, 0), max(center_int[1] - margin, 0), margin * 2, margin * 2) # try: # if self.bbox: # (success, box) = self.tracker.update(self.source) # if success: # box=np.array(box, dtype=int) # x,y,w,h = box # # cv2.rectangle(self.source,(x,y),(x+w,y+h),(0,255,0),1) # #cv2.rectangle(self.source, box, (0,255,0),1) # cv2.imshow("Kk", self.source) # #cv2.waitKey(0) # #print(box) # except Exception as e: # print(e) # pass return True if last: return False # Shape detection failed, try contour detection self.corners = self.standard_corners.copy() self.walkout_offset = 0 self.refresh_source(self.source) contours, hierarchy = cv2.findContours(self.area, 1, 2) if len(contours) > 0: dists = np.zeros(len(contours)) for i, cnt in enumerate(contours): M = cv2.moments(cnt) try: cx = int(M['m10'] / M['m00']) cy = int(M['m01'] / M['m00']) if self.type == 1: dists[i] = np.mean(self.source[cy - 2:cy + 2, cx - 2:cx + 2]) else: dists[i] = np.sqrt((cx - self.center[0])**2 + (cy - self.center[1])**2) except: dists[i] = 255 from operator import itemgetter # todo: score these based on 1) color and 2) distance to center M = cv2.moments(contours[min(enumerate(dists), key=itemgetter(1))[0]]) try: cx = round(M['m10'] / M['m00']) cy = round(M['m01'] / M['m00']) except: return False # print(cx,cy) self.center = (cx, cy) return self.track(True) return False
def walkout(self, total_n=0): """ Points are iteratively translated centrifugally to a limit, and in an equally distributed manner (θ=360/n) from a center. Notably, n is offset based on the width and height of the previously detected ellipsoid. Similar to what was described by Sakatani and Isa (2004). """ x = point_source.copy() y = point_source.copy() step_list = step_list_source.copy() offset = self.processor.walkout_offset b = 0 for i, cos_sin in enumerate(cos_sin_steps): cos, sin = cos_sin x[i] = self.center[0] + cos * offset y[i] = self.center[1] + sin * offset insidemark = False for _ in limit: b += 1 # If walkout coordinate hits out-of-contour area, break out of the loop. try: pixel = self.processor.area[to_int(y[i]), to_int(x[i])] if pixel != 255: if pixel == 100: # inside mark if insidemark == False: lastcoord = x[i], y[i] insidemark = True else: if insidemark: x[i], y[i] = lastcoord break else: insidemark = False except: # Walkout hit outside of Processor area. Go back to previous in-bounds point, then break. x[i] -= cos y[i] -= sin step_list[i] -= 1 break x[i] += cos y[i] += sin step_list[i] += 1 coord_length = len(x) if b <= len(x) + 2: return False if coord_length < 5: return False x, y, coord_length = self.filter(x, y, coord_length, step_list) if coord_length < 4: return False self.rx, self.ry = x[(0 != x)], y[(0 != y)] return True
def update_track(self, blink: int) -> None: frame_preview = cv2.cvtColor(config.engine.source, cv2.COLOR_GRAY2BGR) frame_source = frame_preview.copy() cr_width = pupil_width = -1 Processor = self.pupil_processor if blink == 0: self.rplace_markers(frame_preview) for index, cr_processor in enumerate(config.engine.cr_processors): if cr_processor.active: cr_corners = cr_processor.corners cr_center, cr_width, cr_height, cr_angle, cr_dimensions_int = cr_processor.ellipse.parameters( ) if self._state == "adjustment": if cr_processor == self.current_cr_processor: color = bluish else: color = green try: cv2.ellipse(frame_preview, tuple_int(cr_center), cr_dimensions_int, cr_angle, 0, 360, color, 1) self.place_cross(frame_preview, cr_center, color) cv2.rectangle(frame_preview, cr_corners[0], cr_corners[1], color) cv2.putText( frame_preview, "{}".format(index + 2), (int((cr_corners[1][0] + cr_corners[0][0]) * .5 - 3), cr_corners[0][1] - 3), font, .7, color, 0, cv2.LINE_4) except Exception as e: cr_processor.active = False else: self.place_cross(frame_preview, cr_center, bluish) try: pupil_corners = Processor.corners pupil_center, pupil_width, pupil_height, pupil_angle, pupil_dimensions_int = Processor.ellipse.parameters( ) cv2.ellipse(frame_preview, tuple_int(pupil_center), pupil_dimensions_int, pupil_angle, 0, 360, red, 1) self.place_cross(frame_preview, pupil_center, red) cv2.rectangle(frame_preview, pupil_corners[0], pupil_corners[1], red) except: pass if self._state == "adjustment": stock_P = self.PStock.copy() stock_CR = self.CRStock.copy() if cr_width != -1: cr_area = self.current_cr_processor.area offset_y = int((self.binary_height - cr_area.shape[0]) / 2) offset_x = int((self.binary_width - cr_area.shape[1]) / 2) stock_CR[offset_y:min(offset_y + cr_area.shape[0], self.binary_height), offset_x:min(offset_x + cr_area.shape[1], self. binary_width)] = cr_area stock_CR[0:20, 0:self.binary_width] = self.crstock_txt_selected else: stock_CR[0:20, 0:self.binary_width] = self.crstock_txt if pupil_width != -1: # stock_P[pcorners[0][1]:pcorners[0][1]+Processor.area.shape[0], # pcorners[0][0]:pcorners[0][0]+Processor.area.shape[1]] = Processor.area stock_P[0:20, 0:self.binary_width] = self.pstock_txt_selected pupil_area = Processor.area offset_y = int((self.binary_height - pupil_area.shape[0]) / 2) offset_x = int((self.binary_width - pupil_area.shape[1]) / 2) stock_P[offset_y:min(offset_y + pupil_area.shape[0], self.binary_height), offset_x:min(offset_x + pupil_area.shape[1], self. binary_width)] = pupil_area else: stock_P[0:20, 0:self.binary_width] = self.pstock_txt cv2.putText( stock_P, "{} || {}".format(round(Processor.binarythreshold, 1), Processor.blur), (10, self.binary_height - 10), font, .7, 255, 0, cv2.LINE_4) cv2.putText( stock_CR, "{} || {}".format( round(self.current_cr_processor.binarythreshold, 1), self.current_cr_processor.blur), (10, self.binary_height - 10), font, .7, 255, 0, cv2.LINE_4) frame_source[0:20, 0:self.width] = self.src_txt frame_preview[0:20, 0:self.width] = self.prev_txt frame_preview[0:self.height, 0:1] = 0 cv2.putText(frame_source, "#" + str(config.importer.frame), (to_int(self.width / 2), 12), font, .7, (255, 255, 255), 0, cv2.LINE_4) i = 0 while i < 5: frame_source[to_int(self.height * i / 5) - 1:to_int(self.height * i / 5) + 1, 0:self.width] = (100, 100, 100) i += 1 cv2.imshow("CONFIGURATION", np.hstack( (frame_source, frame_preview))) cv2.imshow("BINARY", np.vstack((stock_P, stock_CR))) self.out.write(frame_preview) self.key = cv2.waitKey(50) if self.key == ord("-"): cv2.imwrite( "screen_cap_fr{}.jpg".format(config.importer.frame), frame_preview) self.key_listener(self.key) else: # real tracking self.out.write(frame_preview) cv2.imshow("TRACKING", frame_preview) key = cv2.waitKey(1) if key == ord("q"): config.engine.release()