def locate(self, geo_image, image, marked_image): '''Find QR codes in image and decode them. Return list of FieldItems representing valid QR codes.''' # Threshold grayscaled image to make white QR codes stands out. #gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #_, mask = cv2.threshold(gray_image, 100, 255, 0) hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) lower_white = np.array([0, 0, 160], np.uint8) upper_white = np.array([179, 65, 255], np.uint8) mask = cv2.inRange(hsv_image, lower_white, upper_white) # Find outer contours (edges) and 'approximate' them to reduce the number of points along nearly straight segments. contours, hierarchy = cv2.findContours(mask.copy(), cv2.cv.CV_RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #contours = [cv2.approxPolyDP(contour, .1, True) for contour in contours] # Create bounding box for each contour. bounding_rectangles = [cv2.minAreaRect(contour) for contour in contours] # Remove any rectangles that couldn't be a QR item based off specified side length. filtered_rectangles = filter_by_size(bounding_rectangles, geo_image.resolution, self.qr_min_size, self.qr_max_size) if ImageWriter.level <= ImageWriter.DEBUG: # Debug save intermediate images thresh_filename = postfix_filename(geo_image.file_name, 'thresh') ImageWriter.save_debug(thresh_filename, mask) # Scan each rectangle with QR reader to remove false positives and also extract data from code. qr_items = [] for rectangle in filtered_rectangles: qr_data = self.scan_image_different_trims_and_threshs(image, rectangle, trims=[0, 3, 8, 12]) scan_successful = len(qr_data) != 0 and len(qr_data[0]) != 0 if scan_successful: qr_code = create_qr_code(qr_data[0]) if qr_code is None: print 'WARNING: Invalid QR data found ' + qr_data[0] else: qr_code.bounding_rect = rectangle qr_items.append(qr_code) elif self.missed_code_finder is not None: # Scan wasn't successful so tag this rectangle for later analysis. self.missed_code_finder.add_possibly_missed_code(rectangle, geo_image) if marked_image is not None: # Show success/failure using colored bounding box success_color = (0, 255, 0) # green if scan_successful and qr_code is not None and qr_code.type == 'RowCode': success_color = (0, 255, 255) # yellow for row codes failure_color = (0, 0, 255) # red item_color = success_color if scan_successful else failure_color draw_rect(marked_image, rectangle, item_color, thickness=2) return qr_items
def locate(self, geo_image, image, marked_image): '''Find possible plant leaves in image and return list of rotated rectangle instances.''' # Convert Blue-Green-Red color space to HSV hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # Threshold the HSV image to get only green colors that correspond to healthy plants. lower_green = np.array([35, 80, 20], np.uint8) upper_green = np.array([90, 255, 255], np.uint8) plant_mask = cv2.inRange(hsv_image, lower_green, upper_green) # Now do the same thing for greenish dead plants. #lower_dead_green = np.array([10, 35, 60], np.uint8) #upper_dead_green = np.array([90, 255, 255], np.uint8) #dead_green_plant_mask = cv2.inRange(hsv_image, lower_dead_green, upper_dead_green) # Now do the same thing for yellowish dead plants. #lower_yellow = np.array([10, 50, 125], np.uint8) #upper_yellow = np.array([40, 255, 255], np.uint8) #dead_yellow_plant_mask = cv2.inRange(hsv_image, lower_yellow, upper_yellow) filtered_rectangles = [] for i, mask in enumerate([plant_mask]): # Open mask (to remove noise) and then dilate it to connect contours. kernel = np.ones((3,3), np.uint8) mask_open = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.dilate(mask_open, kernel, iterations = 1) # Find outer contours (edges) and 'approximate' them to reduce the number of points along nearly straight segments. contours, hierarchy = cv2.findContours(mask.copy(), cv2.cv.CV_RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create bounding box for each contour. bounding_rectangles = [cv2.minAreaRect(contour) for contour in contours] if marked_image is not None and ImageWriter.level <= ImageWriter.DEBUG: for rectangle in bounding_rectangles: # Show rectangles using bounding box. draw_rect(marked_image, rectangle, (0,0,0), thickness=2) # Remove any rectangles that couldn't be a plant based off specified size. filtered_rectangles.extend(filter_by_size(bounding_rectangles, geo_image.resolution, self.min_leaf_size, self.max_leaf_size, enforce_min_on_w_and_h=False)) if ImageWriter.level <= ImageWriter.DEBUG: # Debug save intermediate images mask_filename = postfix_filename(geo_image.file_name, 'mask_{}'.format(i)) ImageWriter.save_debug(mask_filename, mask) if marked_image is not None: for rectangle in filtered_rectangles: # Show rectangles using colored bounding box. purple = (255, 0, 255) draw_rect(marked_image, rectangle, purple, thickness=2) return filtered_rectangles
def debug_draw_plants_in_images_subset(debug_geo_images, possible_plants, actual_plants, image_out_directory): debug_images = [] DebugImage = namedtuple('DebugImage', 'image existed') for geo_image in debug_geo_images: if hasattr(geo_image, 'debug_filepath'): path = geo_image.debug_filepath already_existed = True else: path = geo_image.file_path already_existed = False img = cv2.imread(path, cv2.CV_LOAD_IMAGE_COLOR) if img is None: print "Could not open image {}".format(path) continue debug_images.append(DebugImage(img, already_existed)) for item in possible_plants: from random import randint item_color = (randint(50, 255), randint(50, 100), randint(50, 255)) for k, geo_image in enumerate(debug_geo_images): for ext_item in [item] + item.get('items',[]): image_rect = rect_to_image(ext_item['rect'], geo_image) center, size, theta = image_rect x, y = center if x >= 0 and x < geo_image.width and y >= 0 and y < geo_image.height: draw_rect(debug_images[k].image, image_rect, item_color, thickness=2) for plant in actual_plants: for ref in plant.all_refs: if not ref.parent_image_filename: continue geo_image_filenames = [geo_image.file_name for geo_image in debug_geo_images] try: debug_img_index = geo_image_filenames.index(ref.parent_image_filename) except ValueError: continue debug_img = debug_images[debug_img_index].image if ref.type == 'CreatedPlant': draw_rect(debug_img, ref.bounding_rect, (255, 255, 255), thickness=6) else: draw_rect(debug_img, ref.bounding_rect, (0, 255, 0), thickness=5) debug_filepaths = [os.path.join(image_out_directory, postfix_filename(geo_image.file_name, 'marked')) for geo_image in debug_geo_images] for k, (debug_image, filepath) in enumerate(zip(debug_images, debug_filepaths)): if not debug_image.existed: output_debug_image = debug_image.image # KLM - don't make image smaller or bounding rect coordinates won't be right next time try to draw on image. #output_debug_image = cv2.resize(debug_image.image, (0,0), fx=0.25, fy=0.25) else: output_debug_image = debug_image.image cv2.imwrite(filepath, output_debug_image) debug_geo_images[k].debug_filepath = filepath
def locate(self, geo_image, image, marked_image): '''Find possible blue sticks in image and return list of rotated bounding box instances.''' # Convert Blue-Green-Red color space to HSV hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # Create binary mask image where potential sticks are white. lower_blue = np.array([90, 31, 16], np.uint8) upper_blue = np.array([130, 255, 255], np.uint8) mask = cv2.inRange(hsv_image, lower_blue, upper_blue) # Open mask (to remove noise) and then dilate it to connect contours. kernel = np.ones((3,3), np.uint8) mask_open = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.dilate(mask_open, kernel, iterations = 1) # Find outer contours (edges) contours, hierarchy = cv2.findContours(mask.copy(), cv2.cv.CV_RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create bounding box for each contour. bounding_rectangles = [cv2.minAreaRect(contour) for contour in contours] if marked_image is not None and ImageWriter.level <= ImageWriter.DEBUG: for rectangle in bounding_rectangles: # Show rectangles using bounding box. draw_rect(marked_image, rectangle, (255,255,255), thickness=1) # Remove any rectangles that couldn't be a stick part based off specified size. filtered_rectangles = filter_by_size(bounding_rectangles, geo_image.resolution, self.min_stick_part_size, self.max_stick_part_size, enforce_min_on_w_and_h=True) if ImageWriter.level <= ImageWriter.DEBUG: # Debug save intermediate images mask_filename = postfix_filename(geo_image.file_name, 'blue_thresh') ImageWriter.save_debug(mask_filename, mask) if marked_image is not None: for rectangle in filtered_rectangles: # Show rectangles using colored bounding box. purple = (50, 255, 255) draw_rect(marked_image, rectangle, purple, thickness=2) return filtered_rectangles
def write_out_missed_codes(self, found_codes, missed_code_filename, out_directory): missing_codes = [] for possibly_missed_code in self.possibly_missed_codes: was_actually_found = False for found_code in found_codes: pos_diff = position_difference(possibly_missed_code.position, found_code.position) if pos_diff < 0.12: was_actually_found = True break if not was_actually_found: missing_codes.append(possibly_missed_code) missed_code_filepath = os.path.join(out_directory, missed_code_filename) missed_code_file = open(missed_code_filepath, 'wb') missed_code_csv_writer = csv.writer(missed_code_file) # Create subdirectory to store extracted images in. extracted_img_out_directory = os.path.join(out_directory, 'extracted_images/') if not os.path.exists(extracted_img_out_directory): os.makedirs(extracted_img_out_directory) for k, missed_code in enumerate(missing_codes): parent_img = cv2.imread(missed_code.parent_filepath, cv2.CV_LOAD_IMAGE_COLOR) if parent_img is None: print 'Cannot open image: {}'.format(missed_code.parent_filepath) continue extracted_img = extract_square_image(parent_img, missed_code.rect, 60, rotated=True) extracted_img_filename = '{}_{}'.format(k, postfix_filename(missed_code.parent_filename, '_missed')) extracted_img_filepath = os.path.join(extracted_img_out_directory, extracted_img_filename) cv2.imwrite(extracted_img_filepath, extracted_img) x, y = rectangle_center(missed_code.rect) missed_code_csv_writer.writerow(['add_imaged_code', k, missed_code.parent_filename, int(x), int(y)])