def _get_wire_colors_and_positions(im): colors_and_positions = [] for i in range(len(_BOTTOM_X_BOUNDARIES) - 1): wire = get_subset(im, _BOTTOM_X_BOUNDARIES[i:i + 2], _WIRE_Y_BOUNDARIES) wire_colors_and_mats = filter(None, ( _get_wire_color_and_mat_or_none(wire, 354 / 2, (220, 255), (150, 220), WireColor.RED), _get_wire_color_and_mat_or_none(wire, 37 / 2, (0, 50), (200, 255), WireColor.WHITE), _get_wire_color_and_mat_or_none(wire, 229 / 2, (150, 200), (75, 215), WireColor.BLUE), )) if not wire_colors_and_mats: colors_and_positions.append(None) continue wire_colors, mats = zip(*wire_colors_and_mats) w, h = get_dimens(im) left = int((w * _BOTTOM_X_BOUNDARIES[i]) / 100.0) top = int((h * _WIRE_Y_BOUNDARIES[0]) / 100.0) summed_wires = sum(mats) structuring_element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)) summed_wires = cv2.morphologyEx(summed_wires, cv2.MORPH_CLOSE, structuring_element1) contour = max(get_contours(summed_wires, close_and_open=False), key=cv2.contourArea) center = get_center_for_contour(contour) center = apply_offset_to_single_location(center, (left, top)) # show(summed_wires) colors_and_positions.append((wire_colors, center)) return colors_and_positions
def _get_digit_from_image(image): contours = get_contours(image, close_and_open=False) vertical_centers = [] horizontal_centers = [] for contour in contours: _, _, contour_w, contour_h = cv2.boundingRect(contour) center = get_center_for_contour(contour) if contour_w > contour_h: horizontal_centers.append(center) else: vertical_centers.append(center) w, h = get_dimens(image) segments = set() for x, y in vertical_centers: if x < w/2: if y < h/2: segments.add(Segments.TOP_LEFT) else: segments.add(Segments.BOTTOM_LEFT) else: if y < h/2: segments.add(Segments.TOP_RIGHT) else: segments.add(Segments.BOTTOM_RIGHT) for x, y in horizontal_centers: if y < h/3: segments.add(Segments.TOP_CENTER) elif h/3 < y < 2*h/3: segments.add(Segments.MIDDLE) else: segments.add(Segments.BOTTOM_CENTER) return _SEGMENTS_TO_NUMBER[tuple(sorted(segments, key=lambda seg: seg.value))]
def _get_wire_positions(im, wire_color, hue, saturation=(150, 255), value=(100, 255)): color = extract_color(im, hue, saturation, value) contours = get_contours(color, close_and_open=False) return [(get_center_for_contour(c), wire_color) for c in contours]
def get_has_parallel_port_for_side(side): side_w, side_h = get_dimens(side) if side_h > side_w: side = cv2.transpose(side) side_w, side_h = get_dimens(side) color = extract_color(side, 337 / 2, (75, 175), (175, 255)) contours = sorted(get_contours(color), key=cv2.contourArea) if not contours: return False contour = contours[-1] x, y, w, h = cv2.boundingRect(contour) width_percent = float(w) / side_w height_percent = float(h) / side_h return width_percent > _WIDTH_THRESHOLD and height_percent > _HEIGHT_THRESHOLD
def _get_box_for_largest_rect_contour(color): # type: (np.array) -> Optional[np.array] contours = get_contours(color) # print [0.05 * cv2.arcLength(c, True) for c in contours] # show(get_drawn_contours(color, contours, True), .25) contours = [cv2.approxPolyDP(c, 0.05 * cv2.arcLength(c, True), True) for c in contours] height, width = color.shape[:2] total_area = height * width contours = [c for c in contours if len(c) == 4 and cv2.contourArea(c) / total_area > 0.005] if len(contours) == 0: return None contour = sorted(contours, key=cv2.contourArea)[-1] contour = contour_bounding_box_for_contour(contour) return contour
def _get_largest_contour_area_ratio_for_color(im, color): hue_out_of_360 = { LitSquare.YELLOW: 60, LitSquare.BLUE: 193, LitSquare.RED: 19, LitSquare.GREEN: 149, }[color] color_mat = extract_color(im, hue_out_of_360/2, (100, 255), (225, 255)) contour_areas = [cv2.contourArea(c) for c in get_contours(color_mat)] if contour_areas: largest_area = sorted(contour_areas)[-1] else: largest_area = 0 w, h = get_dimens(im) im_area = w * h area_ratio = float(largest_area) / im_area return area_ratio, color
def _get_largest_contour_area_ratio_for_color(im, color): hue_out_of_360 = { LitSquare.YELLOW: 60, LitSquare.BLUE: 193, LitSquare.RED: 19, LitSquare.GREEN: 149, }[color] color_mat = extract_color(im, hue_out_of_360 / 2, (100, 255), (225, 255)) contour_areas = [cv2.contourArea(c) for c in get_contours(color_mat)] if contour_areas: largest_area = sorted(contour_areas)[-1] else: largest_area = 0 w, h = get_dimens(im) im_area = w * h area_ratio = float(largest_area) / im_area return area_ratio, color
def get_clock_time_from_full_screenshot(full_screenshot, current_module_position, screenshot_helper): clock_im = _get_clock_image_from_full_screenshot(full_screenshot, current_module_position, screenshot_helper) assert clock_im is not None, "Unable to find clock" clock_bg = extract_color(clock_im, (0, 180), (0, 255), (0, 50)) contours = [ c for c in simplify_contours(get_contours(clock_bg), 0.001) if len(c) == 4 ] contour = max(contours, key=cv2.contourArea) display = four_point_transform(clock_im, contour) digits_im = extract_color(display, 0, (250, 255), (250, 255)) digit_images = [ get_subset(digits_im, _CLOCK_DIGIT_X_PERCENTS[digit_index:digit_index + 2], (0, 100)) for digit_index in range(len(_CLOCK_DIGIT_X_PERCENTS) - 1) ] # Determine if there is a colon or period separator. is_colon = get_subset(digit_images[2], (0, 100), (0, 50)).any() if is_colon: minutes_images = digit_images[:2] seconds_images = digit_images[-2:] else: minutes_images = [] seconds_images = digit_images[:2] minutes_digits = [_get_digit_from_image(im) for im in minutes_images] seconds_digits = [_get_digit_from_image(im) for im in seconds_images] if not minutes_digits: minutes_digits = [0] return minutes_digits, seconds_digits
def _get_box_for_largest_rect_contour(color): # type: (np.array) -> Optional[np.array] contours = get_contours(color) # print [0.05 * cv2.arcLength(c, True) for c in contours] # show(get_drawn_contours(color, contours, True), .25) contours = [ cv2.approxPolyDP(c, 0.05 * cv2.arcLength(c, True), True) for c in contours ] height, width = color.shape[:2] total_area = height * width contours = [ c for c in contours if len(c) == 4 and cv2.contourArea(c) / total_area > 0.005 ] if len(contours) == 0: return None contour = sorted(contours, key=cv2.contourArea)[-1] contour = contour_bounding_box_for_contour(contour) return contour
def _get_wire_colors_and_positions(im): colors_and_positions = [] for i in range(len(_BOTTOM_X_BOUNDARIES) - 1): wire = get_subset(im, _BOTTOM_X_BOUNDARIES[i:i + 2], _WIRE_Y_BOUNDARIES) wire_colors_and_mats = filter(None, ( _get_wire_color_and_mat_or_none(wire, 354 / 2, (220, 255), (150, 220), WireColor.RED), _get_wire_color_and_mat_or_none(wire, 37 / 2, (0, 50), (200, 255), WireColor.WHITE), _get_wire_color_and_mat_or_none(wire, 229 / 2, (150, 200), (75, 215), WireColor.BLUE), )) if not wire_colors_and_mats: colors_and_positions.append(None) continue wire_colors, mats = zip(*wire_colors_and_mats) w, h = get_dimens(im) left = int((w * _BOTTOM_X_BOUNDARIES[i]) / 100.0) top = int((h * _WIRE_Y_BOUNDARIES[0]) / 100.0) summed_wires = sum(mats) structuring_element1 = cv2.getStructuringElement( cv2.MORPH_RECT, (15, 15)) summed_wires = cv2.morphologyEx(summed_wires, cv2.MORPH_CLOSE, structuring_element1) contour = max(get_contours(summed_wires, close_and_open=False), key=cv2.contourArea) center = get_center_for_contour(contour) center = apply_offset_to_single_location(center, (left, top)) # show(summed_wires) colors_and_positions.append((wire_colors, center)) return colors_and_positions
def get_clock_time_from_full_screenshot(full_screenshot, current_module_position, screenshot_helper): clock_im = _get_clock_image_from_full_screenshot(full_screenshot, current_module_position, screenshot_helper) assert clock_im is not None, "Unable to find clock" clock_bg = extract_color(clock_im, (0, 180), (0, 255), (0, 50)) contours = [c for c in simplify_contours(get_contours(clock_bg), 0.001) if len(c) == 4] contour = max(contours, key=cv2.contourArea) display = four_point_transform(clock_im, contour) digits_im = extract_color(display, 0, (250, 255), (250, 255)) digit_images = [ get_subset(digits_im, _CLOCK_DIGIT_X_PERCENTS[digit_index:digit_index + 2], (0, 100)) for digit_index in range(len(_CLOCK_DIGIT_X_PERCENTS) - 1) ] # Determine if there is a colon or period separator. is_colon = get_subset(digit_images[2], (0, 100), (0, 50)).any() if is_colon: minutes_images = digit_images[:2] seconds_images = digit_images[-2:] else: minutes_images = [] seconds_images = digit_images[:2] minutes_digits = [_get_digit_from_image(im) for im in minutes_images] seconds_digits = [_get_digit_from_image(im) for im in seconds_images] if not minutes_digits: minutes_digits = [0] return minutes_digits, seconds_digits
def _get_digit_from_image(image): contours = get_contours(image, close_and_open=False) vertical_centers = [] horizontal_centers = [] for contour in contours: _, _, contour_w, contour_h = cv2.boundingRect(contour) center = get_center_for_contour(contour) if contour_w > contour_h: horizontal_centers.append(center) else: vertical_centers.append(center) w, h = get_dimens(image) segments = set() for x, y in vertical_centers: if x < w / 2: if y < h / 2: segments.add(Segments.TOP_LEFT) else: segments.add(Segments.BOTTOM_LEFT) else: if y < h / 2: segments.add(Segments.TOP_RIGHT) else: segments.add(Segments.BOTTOM_RIGHT) for x, y in horizontal_centers: if y < h / 3: segments.add(Segments.TOP_CENTER) elif h / 3 < y < 2 * h / 3: segments.add(Segments.MIDDLE) else: segments.add(Segments.BOTTOM_CENTER) return _SEGMENTS_TO_NUMBER[tuple( sorted(segments, key=lambda seg: seg.value))]
def _get_indicator_images_and_light_statuses(im): red = extract_color(im, 0, (50, 200), (50, 200)) # show(red) w_total, h_total = get_dimens(im) w_threshold = int(_INDICATOR_WIDTH_PERCENT_THRESHOLD * w_total) h_threshold = int(_INDICATOR_HEIGHT_PERCENT_THRESHOLD * h_total) def is_indicator_big_enough(contour): _, _, contour_w, contour_h = cv2.boundingRect(contour) return contour_w > w_threshold and contour_h > h_threshold contours = [ contour_bounding_box_for_contour(c) for c in get_contours(red) if is_indicator_big_enough(c) ] indicators = [four_point_transform(im, c) for c in contours] indicators_and_lights = [] for indicator in indicators: w, h = get_dimens(indicator) if w < h: # Rotate 90 degrees so it's horizontal indicator = rotate_image_clockwise(indicator) w, h = get_dimens(indicator) # Check if light is on left or right, flip accordingly light_width_threshold = w * _LIGHT_WIDTH_THRESHOLD light_height_threshold = h * _LIGHT_HEIGHT_THRESHOLD light_on = extract_color(indicator, (0, 180), (0, 0), (255, 255)) light_off = extract_color(indicator, (0, 180), (0, 40), (0, 50)) # show(light_on) # show(light_off) def is_light_big_enough(contour): _, _, contour_w, contour_h = cv2.boundingRect(contour) return contour_w > light_width_threshold and contour_h > light_height_threshold light_on_contours = [ contour_bounding_box_for_contour(c) for c in get_contours(light_on) if is_light_big_enough(c) ] light_off_contours = [ contour_bounding_box_for_contour(c) for c in get_contours(light_off) if is_light_big_enough(c) ] assert len(light_on_contours) + len(light_off_contours) == 1, \ "Expected to find exactly one light on the indicator" if light_on_contours: light_is_on = True light_contour = light_on_contours[0] else: light_is_on = False light_contour = light_off_contours[0] light_x, _ = get_center_for_contour(light_contour) if light_x > (w / 2.0): # Light is on the wrong side, need to flip 180 indicator = rotate_image_180(indicator) indicators_and_lights.append((indicator, light_is_on)) return indicators_and_lights