def draw(self,img, color=red): line = draw.line(self.y1, self.x1, self.y2, self.x2) draw.set_color(img, line, color) if self.cx and self.cy: centre = draw.circle(self.cy, self.cx, 3) draw.set_color(img, centre, color)
def test_line_aa_horizontal(): img = np.zeros((10, 10)) rr, cc, val = line_aa(0, 0, 0, 9) set_color(img, (rr, cc), 1, alpha=val) img_ = np.zeros((10, 10)) img_[0, :] = 1 assert_array_equal(img, img_)
def test_set_color(): img = np.zeros((10, 10)) rr, cc = line(0, 0, 0, 30) set_color(img, (rr, cc), 1) img_ = np.zeros((10, 10)) img_[0, :] = 1 assert_array_equal(img, img_)
def visualize_result(image, edge_image, shape, closest_points): output_image = 0.5 * (image + edge_image[:, :, np.newaxis]) points = closest_points[:, [1, 0]] perimeter = draw.polygon_perimeter(points[:, 0], points[:, 1]) draw.set_color(output_image, (perimeter[0].astype(np.int), perimeter[1].astype(np.int)), [0, 1, 0]) points = shape[:, [1, 0]] perimeter = draw.polygon_perimeter(points[:, 0], points[:, 1]) draw.set_color(output_image, (perimeter[0].astype(np.int), perimeter[1].astype(np.int)), [0, 0, 1]) return output_image
def test_set_color_with_alpha(): img = np.zeros((10, 10)) rr, cc, alpha = line_aa(0, 0, 0, 30) set_color(img, (rr, cc), 1, alpha=alpha) # Wrong dimensionality color assert_raises(ValueError, set_color, img, (rr, cc), (255, 0, 0), alpha=alpha) img = np.zeros((10, 10, 3)) rr, cc, alpha = line_aa(0, 0, 0, 30) set_color(img, (rr, cc), (1, 0, 0), alpha=alpha)
def draw_boxes_v3(rawImage, boxes, cls_thresh): image = copy.copy(rawImage) for box in boxes: print(str(box.get_score()) + ', ' + str(box.classes[0]) + '\n') image = copy.copy(image) rr, cc = polygon_perimeter((box.ymin, box.ymin, box.ymax, box.ymax), (box.xmin, box.xmax, box.xmax, box.xmin), shape=image.shape) set_color(image, (rr, cc), (255, 0, 0)) imageObject = Image.fromarray(image, mode='RGB') imageDraw = ImageDraw.Draw(imageObject) imageDraw.text((box.xmin, box.ymin - 13), str(box.get_score()) + ', ' + str(box.classes[0]), (255, 0, 0)) image = np.asarray(imageObject) return image
def create_for_contours(file_name, field, boxes, labels, size_labels, RGB_tuples=None): if RGB_tuples is None: RGB_tuples = np.array([(0, 0, 255), (0, 255, 0), (255, 0, 0)]) output_field = grey2rgb(field.copy()) for (x1, y1, x2, y2), label in list(zip(boxes, labels)): # use the label to index into the size ordering, to index into the colors. set_color( output_field, circle(abs(x2 + x1) / 2.0, abs(y2 + y1) / 2.0, radius=(abs(y2 - y1) + 1.0) / 2.0), RGB_tuples[size_labels[label]]) #imsave(file_name + "_for_contour.png", output_field) return output_field
def draw_wpoints(points, data, weights, clrmap): ''' Draw 2D points for a batch of images. points -- 2D points, array shape (Nx2xM) where N is the number of images in the batch 2 is the number of point dimensions (x, y) M is the number of points data -- batch of images to draw to weights -- array shape (NxM), one weight per point, for visualization clrmap -- OpenCV color map for visualizing weights ''' # create explicit color map color_map = np.arange(256).astype('u1') color_map = cv2.applyColorMap(color_map, clrmap) color_map = color_map[:,:,::-1] # BGR to RGB n = points.shape[0] # number of images m = points.shape[2] # number of points for i in range (0, n): s_idx = weights[i].sort(descending=False)[1] # draw low weight points first weights[i] = weights[i] / weights[i].max() # normalize weights for visualization for j in range(0, m): idx = int(s_idx[j]) # convert weight to color clr_idx = float(min(1, weights[i,idx])) clr_idx = int(clr_idx * 255) clr = color_map[clr_idx, 0] / 255 # draw point r = int(points[i, 0, idx] * opt.imagesize) c = int(points[i, 1, idx] * opt.imagesize) rr, cc = circle(r, c, 2) set_color(data[i], (rr, cc), clr) return data
def draw_shape(self, image, shape, dims, color): """Draws a shape from the given specs.""" # Get the center x, y and the size s x, y, s = dims if shape == 'square': r = np.array([x - s, x - s, x + s, x + s]) c = np.array([y - s, y + s, y + s, y - s]) rr, cc = draw.polygon(r, c, shape=image.shape[0:2]) elif shape == "circle": rr, cc = draw.circle(x, y, s) elif shape == "triangle": r = np.array([ x, x - s / math.sin(math.radians(60)), x + s / math.sin(math.radians(60)) ]) c = np.array([y - s, y + s, y + s]) rr, cc = draw.polygon(r, c, shape=image.shape[0:2]) draw.set_color(image, [rr, cc], color) return image
def image_contours(image, level=0.8, show=True): contours = measure.find_contours(image, level=level, fully_connected='high', positive_orientation='low') # Display the image and plot all contours found if show: fig, ax = plt.subplots() ax.imshow(image, cmap=plt.gray()) for n, contour in enumerate(contours): draw.set_color(image, draw.polygon(contour[:, 0], contour[:, 1]), True) ax.plot(contour[:, 1], contour[:, 0], linewidth=2) ax.set_title("Contours") ax.axis_order('image') ax.set_xticks([]) ax.set_yticks([]) plt.show() return image
def draw_count(self, info, width, height): image = np.zeros((width, height, 3)) red = (255, 0, 0) bonus = info["Bonus"] max_bonus = info["Max_Bonus"] cts_image = info["CTS_State"] cts_pg = info["Pixel_PG"] if max_bonus == 0: max_bonus = 0.000001 # Bar bar_height = int(height * bonus / max_bonus) bonus_rect_coords = [(0, 0), (0, bar_height), (3, bar_height), (3, 0)] rect_row = [r[0] for r in bonus_rect_coords] rect_col = [r[1] for r in bonus_rect_coords] rect_array_coords = draw.polygon(rect_row, rect_col) draw.set_color(image, rect_array_coords, red) # PG per pixel cts_gray = np.concatenate([cts_image for _ in range(3)], axis=2) cts_pg_image = np.empty_like(cts_gray) for x in range(cts_image.shape[0]): for y in range(cts_image.shape[1]): pg = cts_pg[x, y] if pg < 0: # Blue pg = max(-1, pg) cts_pg_image[x, y, :] = (0, 0, int(-pg * 255)) else: # Red pg = min(pg, 1) cts_pg_image[x, y, :] = (int(pg * 255), 0, 0) cts_alpha = np.stack( [np.abs(np.clip(cts_pg, -1, 1)) for _ in range(3)], axis=2) cts_colour_image = cts_alpha * cts_pg_image + (1 - cts_alpha) * cts_gray image[4:, -cts_image.shape[1]:, :] = np.fliplr( np.swapaxes(cts_colour_image, 0, 1)) return np.fliplr(image)
def get_moving_actors(sample, box_type, N_actors): moving_actors = sample[box_type] N_moving_actors = len(moving_actors) rasterized_image = np.zeros([len(moving_actors), 500, 500, 3]) ego_centers = np.zeros([len(moving_actors), 36, 2]) - 1 translation = np.zeros([len(moving_actors), 3]) rotation = np.zeros([len(moving_actors), 3, 3]) moving_index = np.zeros([N_actors, len(moving_actors)]) for i in range(len(moving_actors)): moving_actor = moving_actors[i] rasterized_image[i] = np.load(moving_actor['rasterized_image']) ego_centers[i] = np.load( moving_actor['ego_centers'])[:, 0, :] # [36, 2] translation[i] = np.array(moving_actor['translation']) rotation[i] = np.reshape( np.array( Quaternion(moving_actor['rotation']).rotation_matrix), [3, 3]) if moving_actor['ref_coord_id'] < N_actors: moving_index[int(moving_actor['ref_coord_id']), i] = 1 gt_traj = np.array(np.reshape(ego_centers[i], [-1, 2]), dtype=np.int32) # draw gt in blue color blue = np.array([0, 0, 255], dtype=np.int32) draw.set_color(rasterized_image[i], [gt_traj[:, 0], gt_traj[:, 1]], blue) bgr_rasterized_image = rasterized_image[i, :, :, ::-1] cv2.imwrite( os.path.join( '/DATA5_DB8/data/yhu/Nuscenes/data/nuscenes/sample_visualization', '{}_{}_{}_{}.png'.format( sample['scene_id'], sample['sweep_id'], box_type, moving_actor['ref_coord_id'])), bgr_rasterized_image) return ego_centers, translation, rotation, moving_index
def fillHoles(mask, thresh=40): # Find contours contours = measure.find_contours(mask, 0.8) # Display the image and plot all contours found for contour in contours: # Get polygon poly = draw.polygon(contour[:, 0], contour[:, 1]) area = ( (poly[0].max() - poly[0].min() + 1) * (poly[1].max() - poly[1].min() + 1) ) # Filter by size if area < thresh: draw.set_color( mask, poly, True ) return mask
def draw_lines_on_img(img, point_ver, point_hor, point_class): line_list = [[0, 1], [1, 2], [3, 4], [4, 5], [6, 7], [7, 8], [9, 10], [10, 11], [12, 13], [13, 6], [13, 9], [13, 0], [13, 3]] # key point class: 1:visible, 2: not visible, 3: not marked for start_point_id in range(len(point_class)): if point_class[start_point_id] == 3: continue for end_point_id in range(len(point_class)): if point_class[end_point_id] == 3: continue if [start_point_id, end_point_id] in line_list: rr, cc = draw.line(int(point_ver[start_point_id]), int(point_hor[start_point_id]), int(point_ver[end_point_id]), int(point_hor[end_point_id])) draw.set_color(img, [rr, cc], [255, 0, 0]) return img
def area(): sp = image.shape # obtain the image shape sz1 = sp[0] # height(rows) of image sz2 = sp[1] # width(colums) of image x = sz1 / 2 y = sz2 / 2 rule = 2 Y = np.array([a, b, a, b]) X = np.array([c, c, d, d]) rr, cc = draw.polygon(Y, X) draw.set_color(img, [rr, cc], [255, 0, 0]) #画出规则矩形 #面积的计算 area = (b - a) * (d - c) total_area = a * y distance = (area / total_area) * rule cropImg = image[a:b, c:d] # crop the image cv2.imwrite(dest, cropImg) # write in destination path return distance
def draw_circle(image, coordinate, color=None, radius=10): """ Draw a circle at the [y_height, x_width] coordinate of an image. Requires scikit-image, http://scikit-image.org If it is not available this function is a no-op. """ if circle_perimeter_aa is not None and set_color is not None: image_shape_len = len(image.shape) if image_shape_len == 4: batch, y_height, x_width, channels = image.shape elif image_shape_len == 3 and image.shape[0] == 1: batch, y_height, x_width = image.shape channels = 1 elif image_shape_len == 3 and image.shape[2] == 1: y_height, x_width, channels = image.shape batch = 1 elif image_shape_len == 2: y_height, x_width = image.shape batch = 1 channels = 1 if color is None: if channels == 1: color = [255] else: color = [0, 255, 255] image = np.squeeze(image) y, x = np.array(coordinate, dtype=np.int32) # please note that skimage uses a funky coordinate system: # origin in the top left with coordinates ordered # (y, x) where # +y is down from the top left corner and # +x is right from the top left corner rr, cc, aa = circle_perimeter_aa(y, x, radius, shape=image.shape) set_color(image, (rr, cc), color, alpha=aa) # axs.imshow(np.squeeze(frame)) if image_shape_len > len(image.shape): # return the image to the shape that was provided image = np.expand_dims(image, axis=0) return image
def imageDrawWallDepth(data, polygon, wall): size = (data.shape[1], data.shape[0]) polyx = np.array([p[0] for p in polygon]) polyy = np.array([p[1] for p in polygon]) posy, posx = draw.polygon(polyy, polyx) for i in range(len(posy)): coords = utils.pos2coords((posx[i], posy[i]), size) vec = utils.coords2xyz(coords, 1) point = utils.vectorPlaneHit(vec, wall.planeEquation) depth = 0 if point is None else utils.pointsDistance((0, 0, 0), point) color = (depth, depth, depth) preDepth = data[posy[i], posx[i], 0] if preDepth == 0: draw.set_color(data, [posy[i], posx[i]], list(color)) elif preDepth > depth: draw.set_color(data, [posy[i], posx[i]], list(color))
def color_circle(c, r, image): x_ls, y_ls = circle_perimeter(int(c[0]), int(c[1]), r) set_color(image, (y_ls, x_ls), color=(0, 77, 253)) x_ls, y_ls = circle_perimeter(int(c[0]), int(c[1]), r - 1) set_color(image, (y_ls, x_ls), color=(0, 77, 253)) x_ls, y_ls = circle_perimeter(int(c[0]), int(c[1]), r - 2) set_color(image, (y_ls, x_ls), color=(0, 77, 253))
def draw_rect(img, rect, color): left = int(rect[0]) right = int(rect[2]) top = int(rect[1]) bottom = int(rect[3]) r1, c1 = draw.line(left, top, right, top) r2, c2 = draw.line(left, top, left, bottom) r3, c3 = draw.line(right, bottom, right, top) r4, c4 = draw.line(right, bottom, left, bottom) draw.set_color(img, [c1, r1], color) draw.set_color(img, [c2, r2], color) draw.set_color(img, [c3, r3], color) draw.set_color(img, [c4, r4], color)
def plotFind(self, **kwargs): """ Locate the pattern from the scene image """ # set some default parameters # kwargs.setdefault('edgecolor', 'k') # kwargs.setdefault('facecolor', 'g') # kwargs.setdefault('linewidth', 2) # kwargs.setdefault('alpha', 0.5) fig, ax = plt.subplots(1, 1, figsize=(8, 8)) # plot scene image # ax.imshow(fun.bgr2rgb(self.scene), cmap=plt.cm.gray) # draw rectangle # width = self.dst[2, 0, 0] - self.dst[0, 0, 0] # height = self.dst[2, 0, 1] - self.dst[0, 0, 1] # rect = plt.Rectangle((self.dst[0, 0, 0], self.dst[0, 0, 1]), width, height, **kwargs) # ax.add_patch(rect) # draw detected object img = fun.bgr2rgb(self.scene) Y = np.array([self.dst[0, 0, 1],self.dst[1, 0, 1],self.dst[2, 0, 1],self.dst[3, 0, 1]]) X = np.array([self.dst[0, 0, 0],self.dst[1, 0, 0],self.dst[2, 0, 0],self.dst[3, 0, 0]]) rr, cc = dr.polygon(Y, X) dr.set_color(img, [rr, cc], [0,255,0], alpha=0.6) ax.imshow(img, plt.cm.gray) # draw center point ax.plot(self.computeCenter()[0], self.computeCenter()[1], 'k+', alpha=0.8) plt.title('Finded pattern in the scene [center:(%d, %d)]' % (self.computeCenter()[0], self.computeCenter()[1])) ax.axis('off') plt.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.9, hspace=0.1, wspace=0.05)
def tensor_imshow(inp, title=None, bbox=None, img_name=None, size=None, vis_dir=None, **kwargs): """Imshow for Tensor.""" inp = inp.numpy().transpose((1, 2, 0)) # Mean and std for ImageNet mean = np.array([0.485, 0.456, 0.406]) std = np.array([0.229, 0.224, 0.225]) inp = std * inp + mean inp = np.clip(inp, 0, 1) if bbox is None: plt.imshow(inp, **kwargs) if title is not None: plt.title(title) else: rr, cc = rectangle_perimeter(bbox[:2], bbox[2:], shape=inp.shape) # plt.figure(figsize=(10, 5)) # plt.axis('off') # plt.imshow(inp,**kwargs) # if title is not None: # plt.title(title) save_path = os.path.join(vis_dir, 'vis_%s' % img_name[0]) # print(save_path) rescaled = np.uint8(inp * 255) color = np.array([255, 0, 0]) set_color(rescaled, (rr, cc), color) # print(size) new_size = (480, 480) img = Image.fromarray(rescaled).convert('RGB') img = img.resize(new_size) img.save(save_path)
def get_canvas(self): frames = 0 while frames < self.n_frames: while not all([ self.game.players[player].updated for player in self.game.players.keys() ]): continue for player in self.game.players.keys(): self.game.players[player].updated = False frames += 1 self._send_message(self.BOT_READY) board = self.trails.copy() width = np.ceil(1.2 * self.scale) for player in self.game.players.keys(): draw_x, draw_y = self.game.players[player].draw_position rr, cc = draw.circle(draw_y, draw_x, width / 2, shape=board.shape) color = hex_to_rgb(self.game.players[player].color) draw.set_color(board, (rr, cc), color) return board
def plot_img(img, region): img_region = img.copy() min_x = region["min_x"] min_y = region["min_y"] max_x = region["max_x"] max_y = region["max_y"] rr, cc = draw.line(min_y, min_x, max_y + 1, min_x) draw.set_color(img_region, [rr, cc], [255, 0, 0]) rr, cc = draw.line(min_y, max_x + 1, max_y + 1, max_x + 1) draw.set_color(img_region, [rr, cc], [255, 0, 0]) rr, cc = draw.line(min_y, min_x, min_y, max_x + 1) draw.set_color(img_region, [rr, cc], [255, 0, 0]) rr, cc = draw.line(max_y + 1, min_x, max_y + 1, max_x + 1) draw.set_color(img_region, [rr, cc], [255, 0, 0]) return img_region
def create_colorful_section(): textfile = "20160823_Gs_NDVI_1000ft_2-148_1_modified.tif_2758_6810_3058_7110.txt" dataset_name = '20160823_Gs_NDVI_1000ft_2-148_1/' #dataset_name = '20160816_Gs_Wk33_NDVI_1000ft_Shippea_Hill_211-362' image_path = '../AirSurf/Jennifer Manual Counts/ground_truth/Processed for Batch Analysis/' + dataset_name ground_truth_path = '../AirSurf/Jennifer Manual Counts/ground_truth/' + dataset_name names = [] train_X = [] position_Y = [] image_Y = ground_truth_path image = image_path for txt in os.path.splitext(os.path.basename(textfile))[:-1]: image += txt image_Y += txt image += '.txt_sub_img.tif' img = fix_noise(cv2.cvtColor(cv2.imread(image), cv2.COLOR_BGR2RGB)) img = rgb2grey(img) name = "./CONVERTED/" + os.path.basename(textfile) + ".tif" img_y = imread(image_Y + ".tif") img = resize(img, (img_y.shape[0], img_y.shape[1], 1)) positions = construct_ground_truth(img_y) print(img.shape) im = grey2rgb(np.squeeze(img)) print(im.shape) plt.imshow(im) plt.show() for (x, y, w) in positions: color = [[255, 0, 0], [0, 255, 0], [0, 0, 255]][np.random.choice([0, 1, 2])] print(x, y, w) set_color(im, circle(x, y, radius=w), color) plt.imshow(im) plt.show()
def draw_boxes(rawImage, boxes, labels, cls_thresh): image = copy.copy(rawImage) for box in boxes: label_str = '' label = -1 for i in range(len(labels)): if box.classes[i] > 0.1: #cls_thresh: #? label_str += labels[i] label = i print(labels[i] + ': ' + str(box.classes[i] * 100) + '%') print(label_str \ + ' ' + str(box.get_score()) \ + ' ' + str(box.objness)\ + ' (' + str(box.anchor[0]) \ + ',' + str(box.anchor[1]) \ + ') ' + str(entropy(box.classes)) + '\n') if label >= 0: #print(label_str, box.objness, box.classes) image = copy.copy(image) rr, cc = polygon_perimeter( (box.ymin, box.ymin, box.ymax, box.ymax), (box.xmin, box.xmax, box.xmax, box.xmin), shape=image.shape) set_color(image, (rr, cc), (0, 255, 0)) imageObject = Image.fromarray(image, mode='RGB') imageDraw = ImageDraw.Draw(imageObject) imageDraw.text((box.xmin, box.ymin - 13), label_str \ + ' ' + str(box.get_score()) \ + ' ' + str(box.objness) \ + ' (' + str(box.anchor[0]) \ + ',' + str(box.anchor[1]) \ + ') ' + str(entropy(box.classes)), (0,0,255)) image = np.asarray(imageObject) return image
def draw(self, width, *_): img = np.zeros((width, width, 3)) for detection in self.sensor_values: distance = detection.distance if self._normalize: distance *= self._max_range distance *= width / (2 * self._max_range) pos_x = int(width / 2 - distance * math.cos(-detection.angle)) pos_y = int(width / 2 - distance * math.sin(-detection.angle)) rr, cc = line(int(width / 2), int(width / 2), pos_x, pos_y) set_color(img, (rr, cc), (0.3, 0.1, 0.5)) rr, cc = disk((pos_x, pos_y), 2) color = [c / 255 for c in detection.entity.base_color] set_color(img, (rr, cc), color) return img
def vis_bbox(bbox, img, color=(0, 0, 0), modify=False): im_h, im_w = img.shape[0:2] x1, y1, x2, y2 = bbox x1 = max(0, min(x1, im_w - 1)) x2 = max(x1, min(x2, im_w - 1)) y1 = max(0, min(y1, im_h - 1)) y2 = max(y1, min(y2, im_h - 1)) r = [y1, y1, y2, y2] c = [x1, x2, x2, x1] if modify: img_ = img else: img_ = np.copy(img) rr, cc = skdraw.polygon(r, c, img.shape[:2]) skdraw.set_color(img_, (rr, cc), color, alpha=0.2) rr, cc = skdraw.polygon_perimeter(r, c, img.shape[:2]) for k in range(3): img_[rr, cc, k] = color[k] return img_
def dcm2png(parse_path, cube_size=96, **kwargs): """Dicom image of nodules image generation.""" ds_array = sitk.ReadImage(parse_path) img_array = sitk.GetArrayFromImage(ds_array) shape = img_array.shape img_array = np.reshape(img_array, (shape[1], shape[2])) img_array = normalize(img_array) high = np.max(img_array) low = np.min(img_array) lungwin = np.array([low * 1.0, high * 1.0]) newimg = (img_array - lungwin[0]) / (lungwin[1] - lungwin[0]) # 归一化 newimg = (newimg * 255).astype('uint8') # 将像素值扩展到[0,255] lesion = kwargs.get("lesion") y = int(lesion.get("coordY")) x = int(lesion.get("coordX")) start_z, start_y, start_x = coord_conversion(shape, cube_size, x, y) cube = newimg[start_y:start_y + cube_size, start_x:start_x + cube_size] img_rgb = np.array(Image.fromarray(newimg).convert("RGB")) # 框选位置的半径(取框选结节x,y轴偏移量的最大值) d = max(lesion.get('diameter_x'), lesion.get("diameter_y")) rr, cc = draw.circle_perimeter(y, x, int(d) // 2 + 4) draw.set_color(img_rgb, [rr, cc], [0, 0, 255], alpha=1.0) return img_rgb, cube
def draw_state(frame, val, M): if isinstance(val, ContourType): if M is not None: val = val.transform(M) # contours are drawn in red cv2.circle(frame, (int(val.x),int(val.y)),2,(255,0,255),1) cv2.drawContours(frame, [val.pts], 0, (255,0,255), 1) elif isinstance(val, (TrackedObjectType, DetectedObjectType)): if M is not None: val = val.transform(M) # objects in green if getattr(val, "unit", UNIT_PIXELS) == UNIT_PIXELS: cv2.circle(frame, (int(val.x),int(val.y)),3,(0,255,0),2) elif isinstance(val, PointArrayType): if M is not None: val = val.transform(M) if set_color is not None: r,c = val.y, val.x if is_color(frame): # draw in yellow set_color(frame, (r,c), (0,255,255)) else: set_color(frame, (r,c), 255)
def color_out_image(regions, image, multi=True, min=0, scale=2.5): for reg in regions: temp_set = {(1, 0)} y_ls = [] x_ls = [] for pt in reg.coords: y_ls.append(pt[0]) x_ls.append(pt[1]) #z = pt[2] #temp_set.add((x,y)) if multi: r = random.randint(0, 200) g = random.randint(0, 255) b = random.randint(0, 255) else: #r,g,b = 183,255,15 r, g, b = 23, 162, 25 scale = float(scale) if (scale * scale * len(reg.coords) > (float(min))): set_color(image, (array(y_ls), array(x_ls)), color=(r, g, b)) return image
def draw_models(self, labels, data=None, correct=None): """ Draw lines for a batch of images. :param labels: line parameters, array shape (Nx2) where N is the number of images in the batch 2 is the number of line parameters (intercept, slope) :param data: batch of images to draw to, if empty a new batch wil be created according to the shape of labels and lines will be green, lines will be blue otherwise :param correct: array of shape (N) indicating whether a line estimate is correct """ n = labels.shape[0] if data is None: data = np.zeros((n, self.imgH, self.imgW, 3), dtype=np.float32) data.fill(self.bg_clr) clr = (0, 1, 0) else: clr = (0, 0, 1) for i in range(0, n): lY1 = int(labels[i, 0] * self.imgH) lY2 = int(labels[i, 1] * self.imgW + labels[i, 0] * self.imgH) self.draw_line(data[i], 0, lY1, self.imgW, lY2, clr) if correct is not None: # draw border green if estiamte is correct, red otherwise if correct[i]: borderclr = (0, 1, 0) else: borderclr = (1, 0, 0) set_color(data[i], line(0, 0, 0, self.imgW - 1), borderclr) set_color(data[i], line(0, 0, self.imgH - 1, 0), borderclr) set_color(data[i], line(self.imgH - 1, 0, self.imgH - 1, self.imgW - 1), borderclr) set_color(data[i], line(0, self.imgW - 1, self.imgH - 1, self.imgW - 1), borderclr) return data
def draw_models(self, labels, data=None, correct=None): ''' Draw circles for a batch of images. labels -- circle parameters, array shape (Nx3) where N is the number of images in the batch 3 is the number of circles parameters (center x, center y, radius) data -- batch of images to draw to, if empty a new batch wil be created according to the shape of labels and circles will be green, circles will be blue otherwise correct -- array of shape (N) indicating whether a circle estimate is correct ''' n = labels.shape[0] if data is None: data = np.zeros((n, self.imgH, self.imgW, 3), dtype=np.float32) data.fill(self.bg_clr) clr = (0, 1, 0) else: clr = (0, 0, 1) for i in range(0, n): self.draw_circle(data[i], labels[i, 0], labels[i, 1], labels[i, 2], clr) if correct is not None: # draw border green if estiamte is correct, red otherwise if correct[i]: borderclr = (0, 1, 0) else: borderclr = (1, 0, 0) set_color(data[i], line(0, 0, 0, self.imgW - 1), borderclr) set_color(data[i], line(0, 0, self.imgH - 1, 0), borderclr) set_color(data[i], line(self.imgH - 1, 0, self.imgH - 1, self.imgW - 1), borderclr) set_color(data[i], line(0, self.imgW - 1, self.imgH - 1, self.imgW - 1), borderclr) return data
def test_set_color_with_alpha(): img = np.zeros((10, 10)) rr, cc, alpha = line_aa(0, 0, 0, 30) set_color(img, (rr, cc), 1, alpha=alpha) # Wrong dimensionality color with testing.raises(ValueError): set_color(img, (rr, cc), (255, 0, 0), alpha=alpha) img = np.zeros((10, 10, 3)) rr, cc, alpha = line_aa(0, 0, 0, 30) set_color(img, (rr, cc), (1, 0, 0), alpha=alpha)
def create_grid(crsave,result_output,path,gridH,gridC,station): filetime,mid_path,generateTime,image = empty_grid(crsave,result_output,path,gridH,gridC,station) for cr,result in zip(crsave,result_output): r = cr[0] c = cr[1] X=np.array([147.5+18*r,147.5+18*r,165.5+18*r,165.5+18*r]) Y=np.array([517.5+16*c,533.5+16*c,533.5+16*c,517.5+16*c]) rr, cc=draw.polygon(X,Y) if int(result) == 0: draw.set_color(image,[rr,cc],[255,255,255],0.6) elif int(result) == 1: draw.set_color(image,[rr,cc],[0,160,248],0.6) elif int(result) == 2: draw.set_color(image,[rr,cc],[255,0,0],0.6) elif int(result) == 3: draw.set_color(image,[rr,cc],[150,0,180],0.6) try: os.makedirs("E:/product/"+generateTime+"/"+station+"/"+"picture") except: print('文件夹已经存在,无需新建') os.chdir("E:/product/"+generateTime+"/"+station+"/"+"picture") title='Nowcasting_FindStorm'+'_'+filetime+'_'+station io.imsave(title+'.png',image) return filetime,generateTime,title
def draw(self, width, *_): img = np.zeros((width, width, 3)) for detection in self.sensor_values: distance = detection.distance if self._normalize: distance *= self._max_range distance *= width / (2 * self._max_range) pos_x_1 = int( width / 2 - distance * math.cos(-detection.angle - self._fov / self.number_cones / 2)) pos_y_1 = int( width / 2 - distance * math.sin(-detection.angle - self._fov / self.number_cones / 2)) pos_x_2 = int( width / 2 - distance * math.cos(-detection.angle + self._fov / self.number_cones / 2)) pos_y_2 = int( width / 2 - distance * math.sin(-detection.angle + self._fov / self.number_cones / 2)) # pylint: disable=no-member rr, cc = line(int(width / 2), int(width / 2), pos_x_1, pos_y_1) set_color(img, (rr, cc), (0.3, 0.1, 0.5)) rr, cc = line(pos_x_1, pos_y_1, pos_x_2, pos_y_2) set_color(img, (rr, cc), (0.3, 0.1, 0.5)) rr, cc = line(pos_x_2, pos_y_2, int(width / 2), int(width / 2)) set_color(img, (rr, cc), (0.3, 0.1, 0.5)) return img
def performDetect(imagePath="data/dog.jpg", thresh= 0.25, configPath = "./cfg/yolov3.cfg", weightPath = "yolov3.weights", metaPath= "./data/coco.data", showImage= True, makeImageOnly = False, initOnly= False): """ Convenience function to handle the detection and returns of objects. Displaying bounding boxes requires libraries scikit-image and numpy Parameters ---------------- imagePath: str Path to the image to evaluate. Raises ValueError if not found thresh: float (default= 0.25) The detection threshold configPath: str Path to the configuration file. Raises ValueError if not found weightPath: str Path to the weights file. Raises ValueError if not found metaPath: str Path to the data file. Raises ValueError if not found showImage: bool (default= True) Compute (and show) bounding boxes. Changes return. makeImageOnly: bool (default= False) If showImage is True, this won't actually *show* the image, but will create the array and return it. initOnly: bool (default= False) Only initialize globals. Don't actually run a prediction. Returns ---------------------- When showImage is False, list of tuples like ('obj_label', confidence, (bounding_box_x_px, bounding_box_y_px, bounding_box_width_px, bounding_box_height_px)) The X and Y coordinates are from the center of the bounding box. Subtract half the width or height to get the lower corner. Otherwise, a dict with { "detections": as above "image": a numpy array representing an image, compatible with scikit-image "caption": an image caption } """ # Import the global variables. This lets us instance Darknet once, then just call performDetect() again without instancing again global metaMain, netMain, altNames #pylint: disable=W0603 assert 0 < thresh < 1, "Threshold should be a float between zero and one (non-inclusive)" if not os.path.exists(configPath): raise ValueError("Invalid config path `"+os.path.abspath(configPath)+"`") if not os.path.exists(weightPath): raise ValueError("Invalid weight path `"+os.path.abspath(weightPath)+"`") if not os.path.exists(metaPath): raise ValueError("Invalid data file path `"+os.path.abspath(metaPath)+"`") if netMain is None: netMain = load_net_custom(configPath.encode("ascii"), weightPath.encode("ascii"), 0, 1) # batch size = 1 if metaMain is None: metaMain = load_meta(metaPath.encode("ascii")) if altNames is None: # In Python 3, the metafile default access craps out on Windows (but not Linux) # Read the names file and create a list to feed to detect try: with open(metaPath) as metaFH: metaContents = metaFH.read() import re match = re.search("names *= *(.*)$", metaContents, re.IGNORECASE | re.MULTILINE) if match: result = match.group(1) else: result = None try: if os.path.exists(result): with open(result) as namesFH: namesList = namesFH.read().strip().split("\n") altNames = [x.strip() for x in namesList] except TypeError: pass except Exception: pass if initOnly: print("Initialized detector") return None if not os.path.exists(imagePath): raise ValueError("Invalid image path `"+os.path.abspath(imagePath)+"`") # Do the detection detections = detect(netMain, metaMain, imagePath.encode("ascii"), thresh) if showImage: try: from skimage import io, draw import numpy as np image = io.imread(imagePath) print("*** "+str(len(detections))+" Results, color coded by confidence ***") imcaption = [] for detection in detections: label = detection[0] confidence = detection[1] pstring = label+": "+str(np.rint(100 * confidence))+"%" imcaption.append(pstring) print(pstring) bounds = detection[2] shape = image.shape # x = shape[1] # xExtent = int(x * bounds[2] / 100) # y = shape[0] # yExtent = int(y * bounds[3] / 100) yExtent = int(bounds[3]) xEntent = int(bounds[2]) # Coordinates are around the center xCoord = int(bounds[0] - bounds[2]/2) yCoord = int(bounds[1] - bounds[3]/2) boundingBox = [ [xCoord, yCoord], [xCoord, yCoord + yExtent], [xCoord + xEntent, yCoord + yExtent], [xCoord + xEntent, yCoord] ] # Wiggle it around to make a 3px border rr, cc = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] for x in boundingBox], shape= shape) rr2, cc2 = draw.polygon_perimeter([x[1] + 1 for x in boundingBox], [x[0] for x in boundingBox], shape= shape) rr3, cc3 = draw.polygon_perimeter([x[1] - 1 for x in boundingBox], [x[0] for x in boundingBox], shape= shape) rr4, cc4 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] + 1 for x in boundingBox], shape= shape) rr5, cc5 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] - 1 for x in boundingBox], shape= shape) boxColor = (int(255 * (1 - (confidence ** 2))), int(255 * (confidence ** 2)), 0) draw.set_color(image, (rr, cc), boxColor, alpha= 0.8) draw.set_color(image, (rr2, cc2), boxColor, alpha= 0.8) draw.set_color(image, (rr3, cc3), boxColor, alpha= 0.8) draw.set_color(image, (rr4, cc4), boxColor, alpha= 0.8) draw.set_color(image, (rr5, cc5), boxColor, alpha= 0.8) if not makeImageOnly: io.imshow(image) io.show() detections = { "detections": detections, "image": image, "caption": "\n<br/>".join(imcaption) } except Exception as e: print("Unable to show image: "+str(e)) return detections
d_prob = d/np.sum(d) max_idx = np.argmax(d_prob) max_prob = d_prob.max() j = max_idx + 1 while j < d.shape[0] and d_prob[j] > 0.5*max_prob: j += 1 i = max_idx - 1 while i >= 0 and d_prob[i] > 0.5*max_prob: i -= 1 x1,y1,x2,y2 = symmetry.line_coords(img_in, sym, d, angle_bins) line = draw.line(y1, x1, y2, x2) draw.set_color(img_in, line, (1,0,0)) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') xr = np.arange(0,sym.shape[1]) yr = np.arange(0,sym.shape[0]) xx, yy = np.meshgrid(xr, yr) print(xx.shape, yy.shape, sym.shape) ax.plot_surface(xx, yy, sym, cstride=1, rstride=1, linewidth=0, antialiased=False, cmap=cm.coolwarm) plt.show() input()
def daisy(img, step=4, radius=15, rings=3, histograms=8, orientations=8, normalization='l1', sigmas=None, ring_radii=None, visualize=False): '''Extract DAISY feature descriptors densely for the given image. DAISY is a feature descriptor similar to SIFT formulated in a way that allows for fast dense extraction. Typically, this is practical for bag-of-features image representations. The implementation follows Tola et al. [1]_ but deviate on the following points: * Histogram bin contribution are smoothed with a circular Gaussian window over the tonal range (the angular range). * The sigma values of the spatial Gaussian smoothing in this code do not match the sigma values in the original code by Tola et al. [2]_. In their code, spatial smoothing is applied to both the input image and the center histogram. However, this smoothing is not documented in [1]_ and, therefore, it is omitted. Parameters ---------- img : (M, N) array Input image (greyscale). step : int, optional Distance between descriptor sampling points. radius : int, optional Radius (in pixels) of the outermost ring. rings : int, optional Number of rings. histograms : int, optional Number of histograms sampled per ring. orientations : int, optional Number of orientations (bins) per histogram. normalization : [ 'l1' | 'l2' | 'daisy' | 'off' ], optional How to normalize the descriptors * 'l1': L1-normalization of each descriptor. * 'l2': L2-normalization of each descriptor. * 'daisy': L2-normalization of individual histograms. * 'off': Disable normalization. sigmas : 1D array of float, optional Standard deviation of spatial Gaussian smoothing for the center histogram and for each ring of histograms. The array of sigmas should be sorted from the center and out. I.e. the first sigma value defines the spatial smoothing of the center histogram and the last sigma value defines the spatial smoothing of the outermost ring. Specifying sigmas overrides the following parameter. ``rings = len(sigmas)-1`` ring_radii : 1D array of int, optional Radius (in pixels) for each ring. Specifying ring_radii overrides the following two parameters. | ``rings = len(ring_radii)`` | ``radius = ring_radii[-1]`` If both sigmas and ring_radii are given, they must satisfy the following predicate since no radius is needed for the center histogram. ``len(ring_radii) == len(sigmas)+1`` visualize : bool, optional Generate a visualization of the DAISY descriptors Returns ------- descs : array Grid of DAISY descriptors for the given image as an array dimensionality (P, Q, R) where | ``P = ceil((M-radius*2)/step)`` | ``Q = ceil((N-radius*2)/step)`` | ``R = (rings*histograms + 1)*orientations`` descs_img : (M, N, 3) array (only if visualize==True) Visualization of the DAISY descriptors. References ---------- .. [1] Tola et al. "Daisy: An efficient dense descriptor applied to wide- baseline stereo." Pattern Analysis and Machine Intelligence, IEEE Transactions on 32.5 (2010): 815-830. .. [2] http://cvlab.epfl.ch/alumni/tola/daisy.html ''' # Validate image format. if img.ndim > 2: raise ValueError('Only grey-level images are supported.') if img.dtype.kind != 'f': img = img_as_float(img) # Validate parameters. if sigmas is not None and ring_radii is not None \ and len(sigmas) - 1 != len(ring_radii): raise ValueError('len(sigmas)-1 != len(ring_radii)') if ring_radii is not None: rings = len(ring_radii) radius = ring_radii[-1] if sigmas is not None: rings = len(sigmas) - 1 if sigmas is None: sigmas = [radius * (i + 1) / float(2 * rings) for i in range(rings)] if ring_radii is None: ring_radii = [radius * (i + 1) / float(rings) for i in range(rings)] if normalization not in ['l1', 'l2', 'daisy', 'off']: raise ValueError('Invalid normalization method.') # Compute image derivatives. dx = np.zeros(img.shape) dy = np.zeros(img.shape) dx[:, :-1] = np.diff(img, n=1, axis=1) dy[:-1, :] = np.diff(img, n=1, axis=0) # Compute gradient orientation and magnitude and their contribution # to the histograms. grad_mag = sqrt(dx ** 2 + dy ** 2) grad_ori = arctan2(dy, dx) orientation_kappa = orientations / pi orientation_angles = [2 * o * pi / orientations - pi for o in range(orientations)] hist = np.empty((orientations,) + img.shape, dtype=float) for i, o in enumerate(orientation_angles): # Weigh bin contribution by the circular normal distribution hist[i, :, :] = exp(orientation_kappa * cos(grad_ori - o)) # Weigh bin contribution by the gradient magnitude hist[i, :, :] = np.multiply(hist[i, :, :], grad_mag) # Smooth orientation histograms for the center and all rings. sigmas = [sigmas[0]] + sigmas hist_smooth = np.empty((rings + 1,) + hist.shape, dtype=float) for i in range(rings + 1): for j in range(orientations): hist_smooth[i, j, :, :] = gaussian_filter(hist[j, :, :], sigma=sigmas[i]) # Assemble descriptor grid. theta = [2 * pi * j / histograms for j in range(histograms)] desc_dims = (rings * histograms + 1) * orientations descs = np.empty((desc_dims, img.shape[0] - 2 * radius, img.shape[1] - 2 * radius)) descs[:orientations, :, :] = hist_smooth[0, :, radius:-radius, radius:-radius] idx = orientations for i in range(rings): for j in range(histograms): y_min = radius + int(round(ring_radii[i] * sin(theta[j]))) y_max = descs.shape[1] + y_min x_min = radius + int(round(ring_radii[i] * cos(theta[j]))) x_max = descs.shape[2] + x_min descs[idx:idx + orientations, :, :] = hist_smooth[i + 1, :, y_min:y_max, x_min:x_max] idx += orientations descs = descs[:, ::step, ::step] descs = descs.swapaxes(0, 1).swapaxes(1, 2) # Normalize descriptors. if normalization != 'off': descs += 1e-10 if normalization == 'l1': descs /= np.sum(descs, axis=2)[:, :, np.newaxis] elif normalization == 'l2': descs /= sqrt(np.sum(descs ** 2, axis=2))[:, :, np.newaxis] elif normalization == 'daisy': for i in range(0, desc_dims, orientations): norms = sqrt(np.sum(descs[:, :, i:i + orientations] ** 2, axis=2)) descs[:, :, i:i + orientations] /= norms[:, :, np.newaxis] if visualize: descs_img = skimage.color.gray2rgb(img) for i in range(descs.shape[0]): for j in range(descs.shape[1]): # Draw center histogram sigma color = (1, 0, 0) desc_y = i * step + radius desc_x = j * step + radius coords = draw.circle_perimeter(desc_y, desc_x, sigmas[0]) draw.set_color(descs_img, coords, color) max_bin = np.max(descs[i, j, :]) for o_num, o in enumerate(orientation_angles): # Draw center histogram bins bin_size = descs[i, j, o_num] / max_bin dy = sigmas[0] * bin_size * sin(o) dx = sigmas[0] * bin_size * cos(o) coords = draw.line(desc_y, desc_x, desc_y + dy, desc_x + dx) draw.set_color(descs_img, coords, color) for r_num, r in enumerate(ring_radii): color_offset = float(1 + r_num) / rings color = (1 - color_offset, 1, color_offset) for t_num, t in enumerate(theta): # Draw ring histogram sigmas hist_y = desc_y + int(round(r * sin(t))) hist_x = desc_x + int(round(r * cos(t))) coords = draw.circle_perimeter(hist_y, hist_x, sigmas[r_num + 1]) draw.set_color(descs_img, coords, color) for o_num, o in enumerate(orientation_angles): # Draw histogram bins bin_size = descs[i, j, orientations + r_num * histograms * orientations + t_num * orientations + o_num] bin_size /= max_bin dy = sigmas[r_num + 1] * bin_size * sin(o) dx = sigmas[r_num + 1] * bin_size * cos(o) coords = draw.line(hist_y, hist_x, hist_y + dy, hist_x + dx) draw.set_color(descs_img, coords, color) return descs, descs_img else: return descs
def performDetect(imagePath="dog.jpg", thresh=0.25, configPath="./yolov3.cfg", weightPath="yolov3.weights", metaPath="./cfg/coco.data", showImage=True, makeImageOnly=False, initOnly=False): """ Convenience function to handle the detection and returns of objects. Displaying bounding boxes requires libraries scikit-image and numpy Parameters ---------------- imagePath: str Path to the image to evaluate. Raises ValueError if not found thresh: float (default= 0.25) The detection threshold configPath: str Path to the configuration file. Raises ValueError if not found weightPath: str Path to the weights file. Raises ValueError if not found metaPath: str Path to the data file. Raises ValueError if not found showImage: bool (default= True) Compute (and show) bounding boxes. Changes return. makeImageOnly: bool (default= False) If showImage is True, this won't actually *show* the image, but will create the array and return it. initOnly: bool (default= False) Only initialize globals. Don't actually run a prediction. Returns ---------------------- When showImage is False, list of tuples like ('obj_label', confidence, (bounding_box_x_px, bounding_box_y_px, bounding_box_width_px, bounding_box_height_px)) The X and Y coordinates are from the center of the bounding box. Subtract half the width or height to get the lower corner. Otherwise, a dict with { "detections": as above "image": a numpy array representing an image, compatible with scikit-image "caption": an image caption } """ # Import the global variables. This lets us instance Darknet once, then just call performDetect() again without instancing again global metaMain, netMain, altNames #pylint: disable=W0603 assert 0 < thresh < 1, "Threshold should be a float between zero and one (non-inclusive)" if not os.path.exists(configPath): raise ValueError("Invalid config path `" + os.path.abspath(configPath) + "`") if not os.path.exists(weightPath): raise ValueError("Invalid weight path `" + os.path.abspath(weightPath) + "`") if not os.path.exists(metaPath): raise ValueError("Invalid data file path `" + os.path.abspath(metaPath) + "`") if netMain is None: netMain = load_net_custom(configPath.encode("ascii"), weightPath.encode("ascii"), 0, 1) # batch size = 1 if metaMain is None: metaMain = load_meta(metaPath.encode("ascii")) if altNames is None: # In Python 3, the metafile default access craps out on Windows (but not Linux) # Read the names file and create a list to feed to detect try: with open(metaPath) as metaFH: metaContents = metaFH.read() import re match = re.search("names *= *(.*)$", metaContents, re.IGNORECASE | re.MULTILINE) if match: result = match.group(1) else: result = None try: if os.path.exists(result): with open(result) as namesFH: namesList = namesFH.read().strip().split("\n") altNames = [x.strip() for x in namesList] except TypeError: pass except Exception: pass if initOnly: print("Initialized detector") return None if not os.path.exists(imagePath): raise ValueError("Invalid image path `" + os.path.abspath(imagePath) + "`") # Do the detection #detections = detect(netMain, metaMain, imagePath, thresh) # if is used cv2.imread(image) detections = detect(netMain, metaMain, imagePath.encode("ascii"), thresh) if showImage: try: from skimage import io, draw import numpy as np image = io.imread(imagePath) print("*** " + str(len(detections)) + " Results, color coded by confidence ***") imcaption = [] for detection in detections: label = detection[0] confidence = detection[1] pstring = label + ": " + str(np.rint(100 * confidence)) + "%" imcaption.append(pstring) print(pstring) bounds = detection[2] shape = image.shape # x = shape[1] # xExtent = int(x * bounds[2] / 100) # y = shape[0] # yExtent = int(y * bounds[3] / 100) yExtent = int(bounds[3]) xEntent = int(bounds[2]) # Coordinates are around the center xCoord = int(bounds[0] - bounds[2] / 2) yCoord = int(bounds[1] - bounds[3] / 2) boundingBox = [[xCoord, yCoord], [xCoord, yCoord + yExtent], [xCoord + xEntent, yCoord + yExtent], [xCoord + xEntent, yCoord]] # Wiggle it around to make a 3px border rr, cc = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] for x in boundingBox], shape=shape) rr2, cc2 = draw.polygon_perimeter( [x[1] + 1 for x in boundingBox], [x[0] for x in boundingBox], shape=shape) rr3, cc3 = draw.polygon_perimeter( [x[1] - 1 for x in boundingBox], [x[0] for x in boundingBox], shape=shape) rr4, cc4 = draw.polygon_perimeter( [x[1] for x in boundingBox], [x[0] + 1 for x in boundingBox], shape=shape) rr5, cc5 = draw.polygon_perimeter( [x[1] for x in boundingBox], [x[0] - 1 for x in boundingBox], shape=shape) boxColor = (int(255 * (1 - (confidence**2))), int(255 * (confidence**2)), 0) draw.set_color(image, (rr, cc), boxColor, alpha=0.8) draw.set_color(image, (rr2, cc2), boxColor, alpha=0.8) draw.set_color(image, (rr3, cc3), boxColor, alpha=0.8) draw.set_color(image, (rr4, cc4), boxColor, alpha=0.8) draw.set_color(image, (rr5, cc5), boxColor, alpha=0.8) if not makeImageOnly: io.imshow(image) io.show() detections = { "detections": detections, "image": image, "caption": "\n<br/>".join(imcaption) } except Exception as e: print("Unable to show image: " + str(e)) return detections
aligned_shapes = procrustes.generalized_procrustes(shapes) shape_model = subspace_shape.learn(aligned_shapes, K=8) wings_image = get_test_image('wing_area', 'cropped', 'unlabelled', '7.png') # write_image('wings.png', wings_image) edges = canny(wings_image[:, :, 1], 3) saliency = saliency_dragonfly(wings_image) thresh = threshold(saliency) background = threshold(scipy.ndimage.distance_transform_edt(~thresh)) contours = find_contours(thresh, level=0.5) outline = max(contours, key=attrgetter('size')).astype(np.int) outline_image = np.zeros_like(edges) draw.set_color(outline_image, (outline[:, 0], outline[:, 1]), True) edges = skeletonize(edges) gaps = scipy.ndimage.filters.convolve(1 * edges, np.ones((3, 3)), mode='constant', cval=False) edges[(gaps == 2) & ~edges] = True edges = skeletonize(edges) # write_image('wing_edge.png', edges) distance = scipy.ndimage.distance_transform_edt(~edges) labels = label(edges) num_labels = np.max(labels) edge_distance = np.zeros(num_labels + 1) for i in range(num_labels + 1): other_distance = scipy.ndimage.distance_transform_edt(~((labels > 0) & (labels != (i)))) edge_distance[i] = np.median(other_distance[labels == (i)])