def get_blob_crops_from_scale_space_imgs(img, scale_space, t_key, min_radius, max_radius, response_threshold=100): blob_arrs = [] t_range = get_t_range(min_radius, max_radius) start_index = -1 for i in range(0, len(scale_space)): if t_key[i] > t_range[0] and t_key[i] < t_range[1]: if start_index == -1: start_index = i append_img = KernelMath.convolute_array(scale_space[i], BLOB_KERNEL) arr = numpy.array(append_img) arr = numpy.abs(arr) * t_key[i] blob_arrs.append(arr) scale_fit_blob_arrs(blob_arrs, scale_space) maxes = get_rel_maxes(blob_arrs, response_threshold, t_key[start_index:]) #draw_maxes_to_img(img, blob_arrs, maxes).show() blob_rects = get_blob_rectangles(img.size, blob_arrs, maxes) copy_img = img.convert('RGB') copy_image = copy_img.load() for i in range(0, len(blob_rects)): blob_rects[i].draw(copy_img, copy_image, (255, 0, 0)) copy_img.show() blob_rect_clusters = cluster_blob_rects(blob_rects) biggest_cluster = sorted(blob_rect_clusters, key=lambda cluster: len(cluster), reverse=True) bounded_clustered_blob_rects = get_cluster_bounding_boxes( blob_rect_clusters) img_copy = img.convert('RGB') image_copy = img_copy.load() for i in range(0, len(bounded_clustered_blob_rects)): bounded_clustered_blob_rects[i].draw(img_copy, image_copy, (0, 0, 255)) img_copy.show() crops = [] for i in range(0, len(bounded_clustered_blob_rects)): crops.append( Crop.get_img_cropped_to_bounds(img, bounded_clustered_blob_rects[i])) return crops
def get_target_crops_from_img(img, geo_stamps, ppsi): '''takes the full image, geostamps, and ppsi and finds the targets, crops them, and instantiates a list of TargetCrop objects and returns them''' '''notes: ppsi of the image should be used in picking a reasonable max crop size''' img_downsized = Scale.get_img_scaled_to_one_bound(img, DOWNSCALE_CONSTRAINT).convert('RGB') #img.show() #img = Image.fromarray(cv2.GaussianBlur(numpy.array(img), (5,5), 3)) #img = Image.fromarray(cv2.GaussianBlur(numpy.array(img), (7,7), 2)) #img = img.resize((img.size[0]//5, img.size[1]//5)) img_downsized = Image.fromarray(cv2.bilateralFilter(numpy.array(img_downsized), 15, 40, 40)) #img_downsized.show() img_downsized.show() #img.show() image = numpy.array(img_downsized) kernel_size = 3 kernel_margin = (kernel_size - 1)//2 variance_map = numpy.zeros(img.size) var_img = numpy.zeros((image.shape[0], image.shape[1])) for x in range(kernel_margin, var_img.shape[0]-kernel_margin): for y in range(kernel_margin, var_img.shape[1]-kernel_margin): sub_arr = image[x-kernel_margin:x+kernel_margin+1, y-kernel_margin:y+kernel_margin+1] mean, std_dev = (cv2.meanStdDev(sub_arr)) '''for some reason porting to this hasn't been the same as it functioned before''' var_img[x,y] = numpy.linalg.norm(std_dev)**2 Image.fromarray(255*var_img/numpy.amax(var_img)).show() #pil_var_img = Image.fromarray(255*var_img/numpy.amax(var_img)) #pil_var_img.show() #threshold_img = ImageMath.get_binary_bw_img(pil_var_img, pil_var_img.load(), 40) #threshold_img.show() var_img = var_img.T threshold_img = Image.new('L', (var_img.shape[0], var_img.shape[1])) threshold_image = threshold_img.load() for x in range(0, threshold_img.size[0]): for y in range(0, threshold_img.size[1]): if var_img[x,y] > VARIANCE_THRESHOLD: threshold_image[x,y] = 255 threshold_img.show() threshold_connected_components_map = ImageMath.get_bw_connected_components_map(threshold_img, threshold_img.load()) threshold_connected_components = ImageMath.convert_connected_component_map_into_clusters(threshold_connected_components_map) resize_ratio = float(img.size[0])/float(img_downsized.size[0]) min_crop_size = (int((1.0/resize_ratio) * MAX_SQUARE_CROP_BOUNDS), int((1.0/resize_ratio) * MAX_SQUARE_CROP_BOUNDS)) min_cluster_size = 38 max_cluster_size = 200 crops = [] for i in range(0, len(threshold_connected_components)): if len(threshold_connected_components[i]) > min_cluster_size: crop_img = ImageMath.get_connected_component_mask(threshold_img.size, threshold_connected_components[i]) crops.append(crop_img) color_crops = [] crop_margin = 5 for i in range(0, len(crops)): crop_img = crops[i] #crop_img.show() crop_img_mean_pixel = ImageMath.get_bw_img_mean_pixel(crop_img, crop_img.load()) #crop_img.show() bilat_crop_img = img_downsized.crop((crop_img_mean_pixel[0]-(100 * (1.0/resize_ratio)), crop_img_mean_pixel[1]-(100 * (1.0/resize_ratio)), crop_img_mean_pixel[0]+(100 * (1.0/resize_ratio)), crop_img_mean_pixel[1]+ (100 * (1.0/resize_ratio)))).convert('L') bilat_crop_canny_img = Image.fromarray(cv2.Canny(numpy.array(bilat_crop_img), 40, 80)) #bilat_crop_canny_img.show() bilat_start_x = crop_img_mean_pixel[0]-(100 * (1.0/resize_ratio)) bilat_start_y = crop_img_mean_pixel[1]-(100 * (1.0/resize_ratio)) bilat_bounding_rect = Crop.get_bw_img_bounds(bilat_crop_canny_img, bilat_crop_canny_img.load()) bounding_rect = Crop.get_bw_img_bounds(crop_img, crop_img.load()) bounding_rect.set_x(int(bounding_rect.get_x() * resize_ratio) - crop_margin) bounding_rect.set_y(int(bounding_rect.get_y() * resize_ratio) - crop_margin) bounding_rect.set_width(int(bounding_rect.get_width() * resize_ratio) + 3*crop_margin) bounding_rect.set_height(int(bounding_rect.get_height() * resize_ratio) + 3*crop_margin) append_img = Crop.get_img_cropped_to_bounds(img, bounding_rect) color_crops.append((numpy.asarray(crop_img_mean_pixel), append_img)) '''kills crops in the same list whose center are very close to each other (sometimes the same target pops up twice)''' min_dist_threshold = 15 min_area = 1600 max_area = 48400*3 i = 0 while i < len(color_crops): sorted_crops = sorted(color_crops, key = lambda crop : numpy.linalg.norm(crop[0]-color_crops[i][0])) '''sorted_crops[1][0] so that it excludes a measurement to itself''' if numpy.linalg.norm(sorted_crops[1][0] - color_crops[i][0]) < min_dist_threshold: if not (color_crops[i][1].size[0]*color_crops[i][1].size[1] > sorted_crops[1][1].size[0]*sorted_crops[1][1].size[1]): del color_crops[i] else: i+=1 else: i+=1 i = 0 while i < len(color_crops): area = color_crops[i][1].size[0]*color_crops[i][1].size[1] if area < min_area or area > max_area: del color_crops[i] else: i += 1 i = 0 KMEANS_RUN_TIMES = 10 MIN_TOTAL_CLUSTER_DISTANCE_SUM = 370 '''maybe, instead of summing the distance, use the area of the triangle that the three points make and threshold that''' while i < len(color_crops): #print('color crops[i][1]: ', color_crops[i][1]) colors = numpy.array(color_crops[i][1].convert('RGB')).reshape((-1, 3)) colors = numpy.float32(colors) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, KMEANS_RUN_TIMES, 1.0) ret, labels, color_clusters = cv2.kmeans(colors, 3, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) #print("color clusters: ", color_clusters) dist_sum = 0 dist_sum += numpy.linalg.norm(color_clusters[1]-color_clusters[0]) dist_sum += numpy.linalg.norm(color_clusters[2]-color_clusters[1]) dist_sum += numpy.linalg.norm(color_clusters[0]-color_clusters[2]) print("dist sum is: ", dist_sum) if not dist_sum > MIN_TOTAL_CLUSTER_DISTANCE_SUM: del color_crops[i] else: i += 1 target_crops = [] for i in range(0, len(color_crops)): target_crops.append(TargetCrop(img, color_crops[i][1], geo_stamps, color_crops[i][0], ppsi)) return target_crops
connected_components_map = connected_components_map[1] <<<<<<< HEAD ======= #print("THRESHOLDED IMG & CONNECTED COMPONENTS COMPLETED") >>>>>>> committed so I don't lose everything during a rebase connected_components = ImageMath.convert_connected_component_map_into_clusters(connected_components_map) crop_masks = [] for i in range(0, len(connected_components)): if len(connected_components[i]) > CONNECTED_COMPONENT_SIZE_THRESHOLDS[0] and len(connected_components[i]) < CONNECTED_COMPONENT_SIZE_THRESHOLDS[1]: crop_masks.append(ImageMath.get_connected_component_mask(thresholded_local_variance_img.size, connected_components[i])) #new_img = parent_img.crop((100,100,200,200)) color_target_rects = [] pixel_crop_margin = int(float(CROP_MARGIN_INCHES) * sqrt(ppsi))+1 for i in range(0, len(crop_masks)): mask_bounding_rect = Crop.get_bw_img_bounds(crop_masks[i], crop_masks[i].load()) '''for some reason adding a crop margin to the right and bottom edge to the crop requires more than just 2* the crop margin. The *3 is to mitigate this, and seems to work''' parent_img_bounding_rect = Rectangle(int(upscale_multiplier * mask_bounding_rect.get_x()) - pixel_crop_margin, int(upscale_multiplier * mask_bounding_rect.get_y()) - pixel_crop_margin, int(upscale_multiplier * mask_bounding_rect.get_width()) + (3 * pixel_crop_margin), int(upscale_multiplier * mask_bounding_rect.get_height()) + (3 * pixel_crop_margin)) #append_img = Crop.get_img_cropped_to_bounds(parent_img, parent_img_bounding_rect) crop_midpoint = parent_img_bounding_rect.get_center() color_target_rects.append((crop_midpoint, parent_img_bounding_rect)) removal_start_time = timeit.default_timer() print("len before first remove step: ", len(color_target_rects)) remove_duplicate_imgs_by_proximity(color_target_rects, ppsi) print("len before second remove step: ", len(color_target_rects)) remove_crops_by_area(color_target_rects, ppsi) color_target_crops = [] for i in range(0, len(color_target_rects)):
def resize_img_to_PCA_specs(img, img_dim): crop_img = img crop_img = Crop.get_bw_img_cropped_to_bounds(img.convert('L'), img.convert('L').load()) crop_img = crop_img.resize(img_dim) return crop_img
def get_target_crops_from_img(parent_img, geo_stamps, ppsi, get_centers=False): downsized_parent_image = numpy.array( Scale.get_img_scaled_to_one_bound(parent_img, DOWNSCALE_CONSTRAINT).convert('RGB')) downscale_multiplier = float(DOWNSCALE_CONSTRAINT) / float( parent_img.size[0]) upscale_multiplier = 1.0 / downscale_multiplier img = Image.fromarray(downsized_parent_image) downsized_parent_image = cv2.bilateralFilter(downsized_parent_image, BILAT_FILTER_THRESHOLDS[0], BILAT_FILTER_THRESHOLDS[1], BILAT_FILTER_THRESHOLDS[2]) for i in range(0, MEDIAN_BLUR_RUN_TIMES): downsized_parent_image = cv2.medianBlur(downsized_parent_image, 3) start_time = timeit.default_timer() #var_image = create_local_variance_image(cv2.cvtColor(downsized_parent_image, cv2.COLOR_RGB2GRAY)) #Image.fromarray(255*var_image/numpy.amax(var_image)).show() #print("var image shape: ", var_image.shape) print("time elapsed creating var image: ", timeit.default_timer() - start_time) start_time = timeit.default_timer() gaussian_blurred_downsized_image = cv2.GaussianBlur( downsized_parent_image, (5, 5), 3.0) thresh_var_img = Image.fromarray( cv2.Canny(gaussian_blurred_downsized_image, 100, 150, apertureSize=3) ) #create_thresholded_local_variance_img(var_image)#Image.fromarray(cv2.Canny(gaussian_blurred_downsized_image, 20, 40, apertureSize = 3)) #thresh_var_img.show() print("time elapsed thresholding variance img: ", timeit.default_timer() - start_time) downsized_canny_image = numpy.uint8(numpy.array(thresh_var_img)) contour_image, contours, hierarchy = cv2.findContours( downsized_canny_image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) print("contours shape: ", len(contours), ", hierarchy shape: ", hierarchy.shape) drawn_contour_image = numpy.array( Image.new('RGB', tuple(contour_image.T.shape))) good_contours = [] for i in range(0, hierarchy.shape[1]): if hierarchy[0][i][2] >= 0: # and hierarchy[0][i][0] >= 0: good_contours.append(contours[i]) print("good contours shape: ", len(good_contours)) drawn_contour_image = cv2.drawContours(drawn_contour_image, good_contours, -1, (0, 255, 0), 1) '''print("hierarchy is: ", hierarchy) for i in range(0, hierarchy.shape[0]): print("hierarchy [i,0,2] is: ", hierarchy[i][0][2]) if hierarchy[i][0][2] == -1: drawn_contour_image = cv2.drawContours(drawn_contour_image, contours[i], -1, (0,255,0), 3) #drawn_contour_image = cv2.drawContours(numpy.array(Image.new('RGB', tuple(contour_image.T.shape))), contours, -1, (0,255,0), 3)''' crop_masks = [] for i in range(0, len(good_contours)): iter_contour_image = numpy.zeros( (downsized_parent_image.shape[0], downsized_parent_image.shape[1])) iter_contour_image = cv2.drawContours(iter_contour_image, good_contours, i, 255, 3) iter_contour_img = Image.fromarray(iter_contour_image) crop_masks.append(iter_contour_img) color_target_rects = [] pixel_crop_margin = int(float(CROP_MARGIN_INCHES) * sqrt(ppsi)) + 1 for i in range(0, len(crop_masks)): mask_bounding_rect = Crop.get_bw_img_bounds(crop_masks[i], crop_masks[i].load()) '''for some reason adding a crop margin to the right and bottom edge to the crop requires more than just 2* the crop margin. The *3 is to mitigate this, and seems to work''' parent_img_bounding_rect = Rectangle( int(upscale_multiplier * mask_bounding_rect.get_x()) - pixel_crop_margin, int(upscale_multiplier * mask_bounding_rect.get_y()) - pixel_crop_margin, int(upscale_multiplier * mask_bounding_rect.get_width()) + (2 * pixel_crop_margin), int(upscale_multiplier * mask_bounding_rect.get_height()) + (2 * pixel_crop_margin)) crop_midpoint = parent_img_bounding_rect.get_center() color_target_rects.append((crop_midpoint, parent_img_bounding_rect)) print("len before first remove step: ", len(color_target_rects)) remove_duplicate_imgs_by_proximity(color_target_rects, ppsi) print("len before second remove step: ", len(color_target_rects)) remove_crops_by_area(color_target_rects, ppsi) color_target_crops = [] for i in range(0, len(color_target_rects)): color_target_crops.append( (color_target_rects[i][0], Crop.get_img_cropped_to_bounds( parent_img, color_target_rects[i][1], min_size=(sqrt(ppsi) * MIN_CROP_SIZE_INCHES, sqrt(ppsi) * MIN_CROP_SIZE_INCHES)))) print("len before third remove step: ", len(color_target_crops)) '''needs to remove bad crops through kmeans. Run kmeans on the image and find where the clusetres are. Two of these cluseters should be near valid target and letter colors''' '''crop removal with false crop catcher needs a more aggressive threshold to be set. Rather than only training off of random crops selected from images as false positives, instead use actual false positives that were found in the images it was run on.''' #remove_crops_with_false_crop_catcher(color_target_crops) remove_crops_with_width_length_ratio(color_target_crops) ''' TO KILL REMAINDER OF FALSE POSITIVES: Train a decision tree or random forest Vector would be an assemblage of responses to various shape algorithm responses i.e. number harris corners, circle response score, etc. Possibly train a model that uses the colors present in the image as well''' print("final len: ", len(color_target_crops)) centers = [] for i in range(0, len(color_target_crops)): centers.append(color_target_rects[i][1].get_center()) target_crops = [] for i in range(0, len(color_target_crops)): target_crops.append( TargetCrop(parent_img, color_target_crops[i][1], image_timestamp, geo_stamps, color_target_crops[i][0], ppsi)) '''target_crop_index = 0 KMEANS_RUN_TIMES = 4 MIN_DIST_TO_TARGET_COLOR = 20 MIN_DIST_TO_ELIMINATE_CLUSTER_COLOR = 20 target_colors = TargetColorReader.target_colors while target_crop_index < len(target_crops): num_valid_colors = 0 crop_colors = numpy.array(target_crops[target_crop_index].crop_img).reshape((-1, 3)) crop_colors = numpy.float32(crop_colors) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, KMEANS_RUN_TIMES, 1.0) ret, labels, color_clusters = cv2.kmeans(crop_colors, 3, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) i = 0 new_color_clusters = [] while i < len(color_clusters): smallest_dist = numpy.linalg.norm(color_clusters[i] - color_clusters[0]) for j in range(1, len(color_clusters)): if i != j: dist_to_color = numpy.linalg.norm(color_clusters[i] - color_clusters[j]) if dist_to_color < smallest_dist: dist_to_color = smallest_dist if not smallest_dist < MIN_DIST_TO_ELIMINATE_CLUSTER_COLOR: new_color_clusters.append(color_clusters[i]) i+=1 color_clusters = numpy.asarray(new_color_clusters) for i in range(0, len(color_clusters)): closest_color = sorted(target_colors, key = lambda target_color: numpy.linalg.norm(numpy.array(target_color) - color_clusters[i]))[0] dist_to_closest_color = numpy.linalg.norm(color_clusters[i] - numpy.array(closest_color)) if dist_to_closest_color < MIN_DIST_TO_TARGET_COLOR: num_valid_colors += 1 if num_valid_colors < 2: del target_crops[target_crop_index] else: target_crop_index += 1''' if get_centers: return target_crops, centers return target_crops
def get_img_resized_to_PCA_specs(self, img_in): img = ImageOps.invert(img_in) image = img.load() img = Crop.get_bw_img_cropped_to_bounds(img, image, margin=0) img = img.resize(LetterGenerator.PCA_DIM) return img
def get_letter_img_resized_to_PCA_dims(self, letter_img): out_img = Crop.get_bw_img_cropped_to_bounds(letter_img, letter_img.load(), margin=0) out_img = out_img.resize(TargetTwo.PCA_LETTER_DIM) return out_img