def _get_button_images(im): im_mono = extract_color(im, (0, 180), (0, 120), (0, 120)) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = [cv2.approxPolyDP(c, 6, True) for c in contours] midpoint_y = im.shape[1] / 2 contours = [ c for c in contours if len(c) == 4 and cv2.boundingRect(c)[1] > midpoint_y and cv2.isContourConvex(c) ] button_box = sorted(contours, key=cv2.contourArea)[-1] button_box = button_box.reshape((4, 2)) button_im = four_point_transform(im, button_box) button_im_mono = extract_color(button_im, 18, (50, 100), (175, 255)) contours, hierarchy = cv2.findContours(button_im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # There's 4 buttons, which will be the largest 4 contours contours = sorted(contours, key=cv2.contourArea)[-4:] contour_rects = [cv2.boundingRect(c) for c in contours] # Sort from left to right contour_rects = sorted(contour_rects, key=lambda rect: rect[0]) buttons = [] for x, y, w, h in contour_rects: button = four_point_transform( button_im, np.array(((x, y), (x + w, y), (x, y + h), (x + w, y + h))), -6) button = extract_color(button, 18, (50, 100), (175, 255)) buttons.append(button) return buttons
def _get_button_images(im): im_mono = extract_color(im, (0, 180), (0, 120), (0, 120)) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = [cv2.approxPolyDP(c, 6, True) for c in contours] midpoint_y = im.shape[1] / 2 contours = [c for c in contours if len(c) == 4 and cv2.boundingRect(c)[1] > midpoint_y and cv2.isContourConvex(c)] button_box = sorted(contours, key=cv2.contourArea)[-1] button_box = button_box.reshape((4, 2)) button_im = four_point_transform(im, button_box) button_im_mono = extract_color(button_im, 18, (50, 100), (175, 255)) contours, hierarchy = cv2.findContours(button_im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # There's 4 buttons, which will be the largest 4 contours contours = sorted(contours, key=cv2.contourArea)[-4:] contour_rects = [cv2.boundingRect(c) for c in contours] # Sort from left to right contour_rects = sorted(contour_rects, key=lambda rect: rect[0]) buttons = [] for x, y, w, h in contour_rects: button = four_point_transform(button_im, np.array(((x, y), (x + w, y), (x, y + h), (x + w, y + h))), -6) button = extract_color(button, 18, (50, 100), (175, 255)) buttons.append(button) return buttons
def get_screen_content(im, tesseract, debug_idx): # orig_im = im color = 110 sensitivity = 20 lower_bound = np.array([color - sensitivity, 0, 0]) upper_bound = np.array([color + sensitivity, 125, 65]) hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) im_mono = cv2.inRange(hsv, lower_bound, upper_bound) # show(im_mono) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = [cv2.approxPolyDP(c, 0.001 * cv2.contourArea(c), True) for c in contours] contours = sorted([c for c in contours if _is_valid_screen_contour(im, c)], key=cv2.contourArea) # show(get_drawn_contours(im, contours)) contour = contours[-1] contour = contour.reshape((4, 2)) im = four_point_transform(im, contour) im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) retval, im = cv2.threshold(im, 200, 255, cv2.THRESH_BINARY) cv2.imwrite(os.path.join(MODULE_SPECIFIC_DIR, "whos_on_first", "in_game_%i_screen.png" % debug_idx), im) # show(im) # Special case handling for the screen without anything on it. if im.sum() == 0: screen_text = "" else: tesseract.SetImage(Image.fromarray(im)) screen_text = tesseract.GetUTF8Text().upper().strip().replace(" ", "") # print screen_text # show(get_drawn_contours(orig_im, [contour], True)) return screen_text
def extract_maze(im): im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(im_gray, 127, 255, 0) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) best_contour = None for contour in contours: contour = cv2.approxPolyDP(contour, 5, True) if contour.shape[0] == 8 and looks_reasonable(contour, im.shape): if best_contour is None or has_smaller_bounding_box(contour, best_contour): best_contour = contour # show_contours = np.empty(im.shape) # show_contours[:, :] = [0, 0, 0] # cv2.drawContours(show_contours, [best_contour], -1, (0, 255, 0), 1) # for x, y in (tl, tr, br, bl): # if y < show_contours.shape[0] and x < show_contours.shape[1]: # show_contours[y, x] = [255, 255, 255] # show_contours = four_point_transform(show_contours, points) points = get_corners_from_cornerless_rect(best_contour) im = four_point_transform(im, points) # Remove 5% from the edges margin_y = im.shape[0] * 5 / 100 margin_x = im.shape[1] * 5 / 100 return im[margin_y:-margin_y, margin_x:-margin_x]
def _get_letters(text_threshold): # type: (np.array) -> List[np.array] height, width = text_threshold.shape[:2] contours, _ = cv2.findContours(text_threshold.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = [c for c in contours if cv2.boundingRect(c)[3] > height / 3] centers = [get_center_for_contour(c) for c in contours] centers = sorted(centers, key=lambda x: x[0]) distances = [] for idx in range(len(centers) - 1): distances.append(centers[idx + 1][0] - centers[idx][0]) half_avg_dist = sum(distances) / (len(distances) * 2) # contours = [contour_bounding_box_for_contour(c) for c in contours] # show(get_drawn_contours(text_threshold, contours, True)) letters = [] for center in centers: x = center[0] - half_avg_dist y = 0 w = half_avg_dist * 2 h = height contour = np.array([ [x, y], [x + w, y], [x + w, y + h], [x, y + h], ]).reshape((4, 1, 2)) letters.append(four_point_transform(text_threshold, contour)) return letters
def extract_maze(im): im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(im_gray, 127, 255, 0) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) best_contour = None for contour in contours: contour = cv2.approxPolyDP(contour, 5, True) if contour.shape[0] == 8 and looks_reasonable(contour, im.shape): if best_contour is None or has_smaller_bounding_box( contour, best_contour): best_contour = contour # show_contours = np.empty(im.shape) # show_contours[:, :] = [0, 0, 0] # cv2.drawContours(show_contours, [best_contour], -1, (0, 255, 0), 1) # for x, y in (tl, tr, br, bl): # if y < show_contours.shape[0] and x < show_contours.shape[1]: # show_contours[y, x] = [255, 255, 255] # show_contours = four_point_transform(show_contours, points) points = get_corners_from_cornerless_rect(best_contour) im = four_point_transform(im, points) # Remove 5% from the edges margin_y = im.shape[0] * 5 / 100 margin_x = im.shape[1] * 5 / 100 return im[margin_y:-margin_y, margin_x:-margin_x]
def _find_batteries(im): blue = extract_color(im, 216 / 2, (100, 255), (50, 150)) green = extract_color(im, 105 / 2, (200, 255), (200, 255)) color = blue + green structuring_element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)) # structuring_element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) color = cv2.morphologyEx(color, cv2.MORPH_CLOSE, structuring_element1) # color = cv2.morphologyEx(color, cv2.MORPH_OPEN, structuring_element2) # color = scale(color, 0.25) # im = scale(im, 0.25) # show(color) contours, _ = cv2.findContours(color.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # show(get_drawn_contours(color, contours, True)) contours = [contour_bounding_box_for_contour(c) for c in contours] height, width = im.shape[:2] def is_large_enough(contour): x, y, w, h = cv2.boundingRect(contour) return w >= width * 0.1 and h >= height * 0.1 contours = [c for c in contours if is_large_enough(c)] return [four_point_transform(im, c) for c in contours]
def _get_screen_image(im): im_mono = extract_color(im, 76, (50, 150), (50, 150)) # show(im_mono) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) midpoint_y = im.shape[1] * 2 / 3 contours_filtered = [] for contour in contours: # contour = cv2.approxPolyDP(contour, 2, True) x, y, w, h = cv2.boundingRect(contour) # if y + h < midpoint_y and cv2.isContourConvex(contour): if y + h < midpoint_y: contours_filtered.append(contour) contours = sorted(contours_filtered, key=cv2.contourArea)[-4:] boxes = [cv2.boundingRect(c) for c in contours] x1 = min(x for x, y, w, h in boxes) y1 = min(y for x, y, w, h in boxes) x2 = max(x + w for x, y, w, h in boxes) y2 = max(y + h for x, y, w, h in boxes) points = np.array(((x1, y1), (x1, y2), (x2, y1), (x2, y2))) screen = four_point_transform(im, points) screen_mono = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY) _, screen_mono = cv2.threshold(screen_mono, 245, 255, cv2.THRESH_BINARY) return screen_mono
def _get_letters(text_threshold): # type: (np.array) -> List[np.array] height, width = text_threshold.shape[:2] contours, _ = cv2.findContours(text_threshold.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = [c for c in contours if cv2.boundingRect(c)[3] > height / 3] centers = [get_center_for_contour(c) for c in contours] centers = sorted(centers, key=lambda x: x[0]) distances = [] for idx in range(len(centers) - 1): distances.append(centers[idx + 1][0] - centers[idx][0]) half_avg_dist = sum(distances) / (len(distances) * 2) # contours = [contour_bounding_box_for_contour(c) for c in contours] # show(get_drawn_contours(text_threshold, contours, True)) letters = [] for center in centers: x = center[0] - half_avg_dist y = 0 w = half_avg_dist * 2 h = height contour = np.array([ [x, y], [x + w, y], [x + w, y + h], [x, y + h], ]).reshape((4, 1, 2)) letters.append(four_point_transform(text_threshold, contour)) return letters
def _get_screen_image(im): im_mono = extract_color(im, 76, (50, 150), (50, 150)) # show(im_mono) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) midpoint_y = im.shape[1] * 2 / 3 contours_filtered = [] for contour in contours: # contour = cv2.approxPolyDP(contour, 2, True) x, y, w, h = cv2.boundingRect(contour) # if y + h < midpoint_y and cv2.isContourConvex(contour): if y + h < midpoint_y: contours_filtered.append(contour) contours = sorted(contours_filtered, key=cv2.contourArea)[-4:] boxes = [cv2.boundingRect(c) for c in contours] x1 = min(x for x, y, w, h in boxes) y1 = min(y for x, y, w, h in boxes) x2 = max(x + w for x, y, w, h in boxes) y2 = max(y + h for x, y, w, h in boxes) points = np.array(((x1, y1), (x1, y2), (x2, y1), (x2, y2))) screen = four_point_transform(im, points) screen_mono = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY) _, screen_mono = cv2.threshold(screen_mono, 245, 255, cv2.THRESH_BINARY) return screen_mono
def get_letter_images(im): color = 42 sensitivity = 10 lower_bound = np.array([color - sensitivity, 100, 100]) upper_bound = np.array([color + sensitivity, 255, 255]) hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) im_mono = cv2.inRange(hsv, lower_bound, upper_bound) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) assert len( contours) == 1, "Expected single contour, got %s" % len(contours) contour = cv2.approxPolyDP(contours[0], 5, True) assert len(contour) == 4, "Expected 4 point contour, has %s" % len(contour) contour = contour.reshape((4, 2)) im = four_point_transform(im, contour) letter_width = im.shape[1] / 5 letters_raw = [ im[:, i * letter_width:(i + 1) * letter_width] for i in range(5) ] letters_mono = [] for letter in letters_raw: # Take another 5% off the letter boarders margin_height = letter.shape[0] * 5 / 100 margin_width = letter.shape[1] * 5 / 100 letter = letter[margin_height:-margin_height, margin_width:-margin_width] color = 42 sensitivity = 10 lower_bound = np.array([color - sensitivity, 100, 100]) upper_bound = np.array([color + sensitivity, 255, 255]) hsv = cv2.cvtColor(letter, cv2.COLOR_BGR2HSV) letter_mono = cv2.inRange(hsv, lower_bound, upper_bound) letter_mono = 255 - letter_mono # Reduce the letter to its contour bounding box. For some reason I have to pass in a copy of letter_mono # otherwise it won't show properly (unclear if this is an issue with show or with the actual array). # contours, hierarchy = cv2.findContours(np.array(letter_mono), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # flat_contours = reduce(lambda a, b: np.concatenate((a, b)), contours) # x, y, w, h = cv2.boundingRect(flat_contours) # letter_mono = letter_mono[y:y+h, x:x+w] letters_mono.append(letter_mono) return letters_mono
def _extract_side(im, is_bottom): if is_bottom: color = extract_color(im, 32 / 2, (120, 255), (100, 220)) else: color = extract_color(im, 32 / 2, (100, 255), (100, 255)) structuring_element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) structuring_element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) color = cv2.morphologyEx(color, cv2.MORPH_CLOSE, structuring_element1) color = cv2.morphologyEx(color, cv2.MORPH_OPEN, structuring_element2) # show(im) # show(color) height, width = color.shape[:2] contours, _ = cv2.findContours(color.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # contours = [cv2.approxPolyDP(c, 0.03 * cv2.arcLength(c, True), True) for c in contours] contours = [ cv2.approxPolyDP(c, 0.015 * cv2.arcLength(c, True), True) for c in contours ] # show(get_drawn_contours(c2, contours, True)) # for c in contours: # print len(c) # show(get_drawn_contours(c2, [c], True)) contours = [c for c in contours if len(c) == 4] contours = sorted(contours, key=cv2.contourArea, reverse=True)[:2] a, b = contours points = list(a) + list(b) tl = point_closest_to(points, 0, 0) tr = point_closest_to(points, width, 0) bl = point_closest_to(points, 0, height) br = point_closest_to(points, width, height) contour = np.array([tl, tr, br, bl]) contour = contour.reshape((4, 2)) # show(get_drawn_contours(c2, [contour], True)) return four_point_transform(im, contour, margin_percent=5)
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_letter_images(im): color = 42 sensitivity = 10 lower_bound = np.array([color - sensitivity, 100, 100]) upper_bound = np.array([color + sensitivity, 255, 255]) hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) im_mono = cv2.inRange(hsv, lower_bound, upper_bound) contours, hierarchy = cv2.findContours(im_mono, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) assert len(contours) == 1, "Expected single contour, got %s" % len(contours) contour = cv2.approxPolyDP(contours[0], 5, True) assert len(contour) == 4, "Expected 4 point contour, has %s" % len(contour) contour = contour.reshape((4, 2)) im = four_point_transform(im, contour) letter_width = im.shape[1] / 5 letters_raw = [im[:, i * letter_width:(i + 1) * letter_width] for i in range(5)] letters_mono = [] for letter in letters_raw: # Take another 5% off the letter boarders margin_height = letter.shape[0] * 5 / 100 margin_width = letter.shape[1] * 5 / 100 letter = letter[margin_height:-margin_height, margin_width:-margin_width] color = 42 sensitivity = 10 lower_bound = np.array([color - sensitivity, 100, 100]) upper_bound = np.array([color + sensitivity, 255, 255]) hsv = cv2.cvtColor(letter, cv2.COLOR_BGR2HSV) letter_mono = cv2.inRange(hsv, lower_bound, upper_bound) letter_mono = 255 - letter_mono # Reduce the letter to its contour bounding box. For some reason I have to pass in a copy of letter_mono # otherwise it won't show properly (unclear if this is an issue with show or with the actual array). # contours, hierarchy = cv2.findContours(np.array(letter_mono), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # flat_contours = reduce(lambda a, b: np.concatenate((a, b)), contours) # x, y, w, h = cv2.boundingRect(flat_contours) # letter_mono = letter_mono[y:y+h, x:x+w] letters_mono.append(letter_mono) return letters_mono
def _extract_side(im, is_bottom): if is_bottom: color = extract_color(im, 32 / 2, (120, 255), (100, 220)) else: color = extract_color(im, 32 / 2, (100, 255), (100, 255)) structuring_element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) structuring_element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) color = cv2.morphologyEx(color, cv2.MORPH_CLOSE, structuring_element1) color = cv2.morphologyEx(color, cv2.MORPH_OPEN, structuring_element2) # show(im) # show(color) height, width = color.shape[:2] contours, _ = cv2.findContours(color.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # contours = [cv2.approxPolyDP(c, 0.03 * cv2.arcLength(c, True), True) for c in contours] contours = [cv2.approxPolyDP(c, 0.015 * cv2.arcLength(c, True), True) for c in contours] # show(get_drawn_contours(c2, contours, True)) # for c in contours: # print len(c) # show(get_drawn_contours(c2, [c], True)) contours = [c for c in contours if len(c) == 4] contours = sorted(contours, key=cv2.contourArea, reverse=True)[:2] a, b = contours points = list(a) + list(b) tl = point_closest_to(points, 0, 0) tr = point_closest_to(points, width, 0) bl = point_closest_to(points, 0, height) br = point_closest_to(points, width, height) contour = np.array([tl, tr, br, bl]) contour = contour.reshape((4, 2)) # show(get_drawn_contours(c2, [contour], True)) return four_point_transform(im, contour, margin_percent=5)
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_cleaned_up_text_subsection(im): # type: (np.array) -> Optional[np.array] red1 = extract_color(im, (0, 6), (200, 255), (100, 150)) red2 = extract_color(im, (176, 180), (200, 255), (100, 150)) red = red1 + red2 color = extract_color(im, 45 / 2, (20, 50), (200, 255)) # show(red, .25) # show(yellow, .25) # im = scale(im, .25) # color = scale(color, .25) red_contour = _get_box_for_largest_rect_contour(red) text_contour = _get_box_for_largest_rect_contour(color) if red_contour is None or text_contour is None: # if red_contour is not None: # print "RED" # show(get_drawn_contours(red, [red_contour], True)) # show(color) # if text_contour is not None: # print "TEXT" # show(get_drawn_contours(color, [text_contour], True)) # show(red) # if not (red_contour is None and text_contour is None): # show(red) # show(color) # assert red_contour is None and text_contour is None, \ # "Error parsing serial number, didn't find one of the text or its label." return None red_center = get_center_for_contour(red_contour) text_center = get_center_for_contour(text_contour) text_subsection = four_point_transform(im, text_contour) # show(get_drawn_contours(color, text_contour, True), .25) # show(get_drawn_contours(red, red_contour, True), .25) # show(text_subsection) height, width = im.shape[:2] # Rotation logic from http://stackoverflow.com/a/5912847/3000133 if height > width: # Determine if red is left or right of text if text_center[0] < red_center[0]: text_subsection = rotate_image_counter_clockwise(text_subsection) else: # Rotate clockwise 90 text_subsection = rotate_image_clockwise(text_subsection) else: if text_center[1] > red_center[1]: # We're fine pass else: # Rotate 180 text_subsection = rotate_image_180(text_subsection) # show(get_drawn_contours(im, [text_contour], True)) # show(text_subsection) text_subsection_gray = cv2.cvtColor(text_subsection, cv2.COLOR_BGR2GRAY) # show(text_subsection_gray) _, text_threshold = cv2.threshold(text_subsection_gray, 50, 255, 0) text_threshold = 255 - text_threshold # show(text_threshold) height, width = text_threshold.shape[:2] text_threshold[:height / 10, :] = 0 text_threshold[9 * height / 10:, :] = 0 return text_threshold
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
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
def _get_cleaned_up_text_subsection(im): # type: (np.array) -> Optional[np.array] red1 = extract_color(im, (0, 6), (200, 255), (100, 150)) red2 = extract_color(im, (176, 180), (200, 255), (100, 150)) red = red1 + red2 color = extract_color(im, 45 / 2, (20, 50), (200, 255)) # show(red, .25) # show(yellow, .25) # im = scale(im, .25) # color = scale(color, .25) red_contour = _get_box_for_largest_rect_contour(red) text_contour = _get_box_for_largest_rect_contour(color) if red_contour is None or text_contour is None: # if red_contour is not None: # print "RED" # show(get_drawn_contours(red, [red_contour], True)) # show(color) # if text_contour is not None: # print "TEXT" # show(get_drawn_contours(color, [text_contour], True)) # show(red) # if not (red_contour is None and text_contour is None): # show(red) # show(color) # assert red_contour is None and text_contour is None, \ # "Error parsing serial number, didn't find one of the text or its label." return None red_center = get_center_for_contour(red_contour) text_center = get_center_for_contour(text_contour) text_subsection = four_point_transform(im, text_contour) # show(get_drawn_contours(color, text_contour, True), .25) # show(get_drawn_contours(red, red_contour, True), .25) # show(text_subsection) height, width = im.shape[:2] # Rotation logic from http://stackoverflow.com/a/5912847/3000133 if height > width: # Determine if red is left or right of text if text_center[0] < red_center[0]: text_subsection = rotate_image_counter_clockwise(text_subsection) else: # Rotate clockwise 90 text_subsection = rotate_image_clockwise(text_subsection) else: if text_center[1] > red_center[1]: # We're fine pass else: # Rotate 180 text_subsection = rotate_image_180(text_subsection) # show(get_drawn_contours(im, [text_contour], True)) # show(text_subsection) text_subsection_gray = cv2.cvtColor(text_subsection, cv2.COLOR_BGR2GRAY) # show(text_subsection_gray) _, text_threshold = cv2.threshold(text_subsection_gray, 50, 255, 0) text_threshold = 255 - text_threshold # show(text_threshold) height, width = text_threshold.shape[:2] text_threshold[:height / 10, :] = 0 text_threshold[9 * height / 10:, :] = 0 return text_threshold