def morph_voxelization(vert, face, sampleN=1000000, grid_dim=128, selem_size=6): """ Morphological voxelization. Given arbitrary triangle soup, return the watertight voxelization of it. First sample cloud from mesh, voxelize the cloud, dilate, floodfill, erose. Note that dilate+erose=closing """ vmin, vmax = np.abs(vert).min(), np.abs(vert).max() if vmax > 1.: print( f"Warning: Mesh should be fallen into [-1,1]^3 bounding box! vmin:{vmin} vmax:{vmax}" ) samples = sampleMesh(vert, face, sampleN) voxel, coords = ptutil.ths2nps( ptutil.point2voxel(samples[None, ...], grid_dim=grid_dim, ret_coords=True)) voxel, coords = voxel[0], coords[0] if selem_size == 0: water_tight_voxel = 1 - morphology.flood(voxel, (0, 0, 0)) else: selem = morphology.ball(selem_size) dilated = morphology.binary_dilation(voxel, selem) mask = 1 - morphology.flood(dilated, (0, 0, 0)) erosed = morphology.binary_erosion(mask, selem) water_tight_voxel = erosed return water_tight_voxel, coords
def test_empty_input(): # Test shortcut output = flood_fill(np.empty(0), (), 2) assert output.size == 0 # Boolean output type assert flood(np.empty(0), ()).dtype == np.bool # Maintain shape, even with zero size present assert flood(np.empty((20, 0, 4)), ()).shape == (20, 0, 4)
def fill(im_sd, t=0.05): from skimage.morphology import flood im_sd[0, 0] = 0 im_sd[0, -1] = 0 im_sd[-1, 0] = 0 im_sd[-1, -1] = 0 mask = flood(im_sd, (0, 0), tolerance=t) | \ flood(im_sd, (im_sd.shape[0] - 1, 0), tolerance=t) | \ flood(im_sd, (0, im_sd.shape[1] - 1), tolerance=t) | \ flood(im_sd, (im_sd.shape[0] - 1, im_sd.shape[1] - 1), tolerance=t) return mask
def apply_flood_fill(image, starting_coordinates, tolerance): upper_bound = np.max(image) img_as_float = image / upper_bound mask = flood(img_as_float, starting_coordinates, tolerance=tolerance) mask = mask.astype('uint16') return mask
def global_both_line(img, r, c, color): img = img.reshape((img.shape + (1, ))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:, :, i], (r, c), connectivity=2) dilation = binary_dilation(msk, np.ones((3, 3))) dilation ^= msk img[dilation] = color
def global_in_fill(img, r, c, color): img = img.reshape((img.shape + (1, ))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:, :, i], (r, c), connectivity=2) filled = binary_fill_holes(msk) filled ^= msk img[filled] = color
def test_f_order(tolerance): image = np.array([ [0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], ], order="F") expected = np.array([ [0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], ], dtype=bool) mask = flood(image, seed_point=(1, 0), tolerance=tolerance) np.testing.assert_array_equal(expected, mask) mask = flood(image, seed_point=(2, 1), tolerance=tolerance) np.testing.assert_array_equal(expected, mask)
def global_in_line(img, r, c, color): img = img.reshape((img.shape + (1, ))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:, :, i], (r, c), connectivity=2) inarea = binary_fill_holes(msk) inarea ^= msk inarea ^= binary_erosion(inarea, np.ones((3, 3))) img[inarea] = color
def mouse_down(self, ips, x, y, btn, **key): ips.snapshot() img, color = ips.img, ColorManager.get_front() connectivity=(self.para['con']=='8-connect')+1 img = ips.img.reshape((ips.img.shape+(1,))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:,:,i], (int(y),int(x)), connectivity=connectivity, tolerance=self.para['tor']) img[msk] = np.mean(color) if img.shape[2]==1 else color ips.update()
def mouse_down(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==1 or btn==3: if ips.roi!= None: self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return if ips.roi == None: connectivity=(self.para['con']=='8-connect')+1 img = ips.img.reshape((ips.img.shape+(1,))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:,:,i], (int(y),int(x)), connectivity=connectivity, tolerance=self.para['tor']) conts = find_contours(msk, 0, 'high') ips.roi = shape2roi(polygonize(conts, btn==3)) elif hasattr(ips.roi, 'topolygon'): shp = roi2shape(ips.roi.topolygon()) oper = '' if key['shift']: oper = '+' elif key['ctrl']: oper = '-' elif self.curobj: return else: ips.roi=None connectivity=(self.para['con']=='8-connect')+1 img = ips.img.reshape((ips.img.shape+(1,))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:,:,i], (int(y),int(x)), connectivity=connectivity, tolerance=self.para['tor']) conts = find_contours(msk, 0, 'high') cur = polygonize(conts, btn==3) if oper == '+': ips.roi = shape2roi(shp.union(cur)) elif oper == '-': ips.roi = shape2roi(shp.difference(cur)) else: ips.roi = shape2roi(cur) else: ips.roi = None ips.update()
def global_out_fill(img, r, c, color): img = img.reshape((img.shape + (1, ))[:3]) ori = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): ori &= flood(img[:, :, i], (r, c), connectivity=2) filled = binary_fill_holes(ori) dilation = binary_dilation(ori) dilation ^= filled rs, cs = np.where(dilation) if len(rs) == 0: return msk = ((img == img[r, c]).min(axis=2)).astype(np.uint8) flood_fill(msk, (rs[0], cs[0]), 2, connectivity=2, inplace=True) img[msk == 2] = color
def action_trim_pixels(self, label, frame, x_location, y_location): ''' get rid of any stray pixels of selected label; pixels of value label that are not connected to the cell selected will be removed from annotation in that frame ''' img_ann = self.tracked[frame,:,:,0] contig_cell = flood(image = img_ann, seed_point = (int(y_location/self.scale_factor), int(x_location/self.scale_factor))) img_trimmed = np.where(np.logical_and(np.invert(contig_cell), img_ann == label), 0, img_ann) comparison = np.where(img_trimmed != img_ann) self.frames_changed = np.any(comparison) self.tracked[frame,:,:,0] = img_trimmed
def region_growing(img, seed_point, tolerance=20): """ Args: img (ndarray): image seed_point (tuple): define the seed point. tolerance: a float or int value to define the maximal difference of grayvalues in the region. According to the documentation in scikit-image, if tolerance is provided, adjacent points with values within plus or minus tolerance from the seed point are filled (inclusive). Returns: mask (ndarray): output binary image """ mask = flood(img, seed_point, tolerance=tolerance) mask = binary_dilation(mask, np.ones((3, 3))) return mask
def mouse_move(self, ips, x, y, btn, **key): if self.status == None: return img, color = ips.img, (255, 255, 0) rs, cs = line(*[int(round(i)) for i in self.oldp + (y, x)]) np.clip(rs, 0, img.shape[0] - 1, out=rs) np.clip(cs, 0, img.shape[1] - 1, out=cs) color = (np.mean(color), color)[img.ndim == 3] w = self.para['win'] for r, c in zip(rs, cs): sr = (max(0, r - w), min(img.shape[0], r + w)) sc = (max(0, c - w), min(img.shape[1], c + w)) r, c = min(r, w), min(c, w) clip = img[slice(*sr), slice(*sc)] if (clip[r, c] - color).sum() == 0: continue lab = felzenszwalb(clip, 1, 0, self.para['ms']) clip[flood(lab, (r, c), connectivity=2)] = color self.oldp = (y, x) ips.update()
def get_houses(target_image: np.array) -> list: """Cut out houses from target data Parameters: target_image: np.array 2 D Matrix with target values with values either 0 or 1 Returns: coordiantes_list: list Each element represents a bounding box around a house (xmin, ymin, xmax, ymax) """ # Clip target_image to values 0, 1 target_image = np.clip(target_image, a_min=0, a_max=1) coordinates_list = [] # Starting from the top left, search the image for a house for i in range(0, len(target_image)): for j in range(0, len(target_image[0])): if target_image[i][j] == 1: # Flood and overlay selected house seed_point = (i, j) mask = flood(target_image, seed_point) mask_int = mask.astype(int) # Find X,Y coordinates of all pixels with value 1 coordinates = np.argwhere(mask_int == 1) # print('Number of Pixels: ', len(coordinates)) # Find min and max values for x and y y_min, x_min = np.min(coordinates, axis=0) y_max, x_max = np.max(coordinates, axis=0) # Remove flood filled building from original image target_image[mask] = 0 # Only select larger houses if len(coordinates) > 50: coordinates_list.append([(x_min, y_min), (x_max, y_max)]) return coordinates_list
def mouse_down(self, ips, x, y, btn, **key): if btn != 1: return msk = flood(ips.img, (int(y), int(x)), connectivity=0, tolerance=0) conts = find_contours(msk, 0.5, 'high') conts = conts[0][:, ::-1] ips.mark = Mark(conts) trans = ips.img.mat jw = np.dot(trans[:, 1:], conts.T).T + trans[:, 0] osrprj = osr.SpatialReference() osrprj.ImportFromWkt(ips.img.crs) osrgeo = osr.SpatialReference() osrgeo.ImportFromEPSG(3857) ct = osr.CoordinateTransformation(osrprj, osrgeo) xy = ct.TransformPoints(jw) polygon = Polygon(xy) c = polygon.centroid IPy.set_info('At N:%.4f, E:%.4f Area:%.4f' % (c.x, c.y, polygon.area)) ips.update()
def action_trim_pixels(self, label, x_location, y_location): """ Remove any pixels with value label that are not connected to the selected cell in the given frame. Args: label (int): label to trim x_location (int): x position of seed remove label that is not connect to this seed y_location (int): y position of seed """ img_ann = self.frame[..., self.feature] seed_point = (int(y_location), int(x_location)) contig_cell = flood(image=img_ann, seed_point=seed_point) stray_pixels = np.logical_and(np.invert(contig_cell), img_ann == label) img_trimmed = np.where(stray_pixels, 0, img_ann) self.y_changed = np.any(np.where(img_trimmed != img_ann)) self.frame[..., self.feature] = img_trimmed
def createCrack(self, blob, input_arr, x, y, tolerance, preview=True): """ Given a inner blob point (x,y), the function use it as a seed for a paint butcket tool and create a correspondent blob hole """ box = blob.bbox x_crop = x - box[1] y_crop = y - box[0] input_arr = gaussian(input_arr, 2) # input_arr = segmentation.inverse_gaussian_gradient(input_arr, alpha=1, sigma=1) blob_mask = blob.getMask() crack_mask = flood(input_arr, (int(y_crop), int(x_crop)), tolerance=tolerance).astype(int) cracked_blob = np.logical_and((blob_mask > 0), (crack_mask < 1)) cracked_blob = cracked_blob.astype(int) if preview: return cracked_blob regions = measure.regionprops(measure.label(cracked_blob)) area_th = 1000 created_blobs = [] for region in regions: if region.area > area_th: id = len(self.seg_blobs) b = Blob(region, box[1], box[0], self.progressive_id) self.progressive_id += 1 b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def createCrack(self, input_arr, x, y, tolerance, preview=True): """ Given a inner blob point (x,y), the function use it as a seed for a paint butcket tool and create a correspondent blob hole """ x_crop = x - self.bbox[1] y_crop = y - self.bbox[0] input_arr = gaussian(input_arr, 2) # input_arr = segmentation.inverse_gaussian_gradient(input_arr, alpha=1, sigma=1) blob_mask = self.getMask() crack_mask = flood(input_arr, (int(y_crop), int(x_crop)), tolerance=tolerance).astype(int) cracked_blob = np.logical_and((blob_mask > 0), (crack_mask < 1)) cracked_blob = cracked_blob.astype(int) if not preview: self.updateUsingMask(self.bbox, cracked_blob) return cracked_blob
def local_brush(img, back, r, c, color, sigma, msize): lab = felzenszwalb(back, 1, sigma, msize) msk = flood(lab, (r, c), connectivity=2) img[msk] = color
from functions import * from skimage.morphology import flood from matplotlib import pyplot as plt from scipy import ndimage allpost, allpre, basenames = load("DATA") for image, name in zip(allpre, basenames): # load ground truth ground_truth = gt(name) x, y = ground_truth[0][1:3] mask = flood(image, (y, x), connectivity=5, tolerance=5) for n, x, y in ground_truth[1:]: mask_temp = flood(image, (y, x), connectivity=5, tolerance=5) mask = np.logical_or(mask_temp, mask) fig, ax = plt.subplots(1, 1, figsize=(9, 3), sharex=True, sharey=True) title = 'Laplacian of Gaussian' ax.set_title(title) ax.imshow(mask) for n, x, y in ground_truth: c = plt.Circle((x, y), 20, color='red', linewidth=2, fill=False) ax.add_patch(c) plt.show()
if resize: iris_prediction = cv2.resize( iris_prediction, (original_shape[1], original_shape[0]), interpolation=cv2.INTER_LINEAR) else: iris_prediction = iris_prediction[pads[0]:pads[0] + transformed_shape[0], pads[1]:pads[1] + transformed_shape[1]] iris_prediction = cv2.resize( iris_prediction, (original_shape[1], original_shape[0]), interpolation=cv2.INTER_LINEAR) _, iris_prediction = cv2.threshold(iris_prediction, 0.5, 1, 0) iris_prediction = keep_large_area(iris_prediction, top_n_large=1).astype(np.uint8) outer_prediction = (1 - flood(iris_prediction, (0, 0))).astype(np.uint8) inner_prediction = (outer_prediction - iris_prediction).astype( np.uint8) if args.ellipse: inner_prediction = fit_Ellipse(inner_prediction).astype(np.uint8) outer_prediction = fit_Ellipse(outer_prediction).astype(np.uint8) Dice = compute_dice(outer_prediction - inner_prediction, local_outer - local_inner) HD = hd(outer_prediction - inner_prediction, local_outer - local_inner) print(i, img_name, Dice, HD) name_all.append(path) Dice_all.append(Dice) HD_all.append(HD) inner_save = np.zeros(inner_prediction.shape) contours, _ = cv2.findContours(inner_prediction, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
def fill_normal(img, r, c, color, con, tor): img = img.reshape((img.shape + (1, ))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) for i in range(img.shape[2]): msk &= flood(img[:, :, i], (r, c), connectivity=con, tolerance=tor) img[msk] = color
def action_active_contour(self, label, min_pixels=20, iterations=100): label_img = np.copy(self.frame[..., self.feature]) # get centroid of selected label props = regionprops(np.where(label_img == label, label, 0))[0] # make bounding box size to encompass some background box_height = props['bbox'][2] - props['bbox'][0] y1 = max(0, props['bbox'][0] - box_height // 2) y2 = min(self.project.height, props['bbox'][2] + box_height // 2) box_width = props['bbox'][3] - props['bbox'][1] x1 = max(0, props['bbox'][1] - box_width // 2) x2 = min(self.project.width, props['bbox'][3] + box_width // 2) # relevant region of label image to work on label_img = label_img[y1:y2, x1:x2] # use existing label as initial level set for contour calculations level_set = np.where(label_img == label, 1, 0) # normalize input 2D frame data values to range [0.0, 1.0] adjusted_raw_frame = Normalize()(self.raw_frame[..., self.channel]) predict_area = adjusted_raw_frame[y1:y2, x1:x2] # returns 1 where label is predicted to be based on contouring, 0 background contoured = morphological_chan_vese( predict_area, iterations, init_level_set=level_set ) # contoured area should get original label value contoured_label = contoured * label # contours tend to fit very tightly, a small expansion here works well contoured_label = dilation(contoured_label, disk(3)) # don't want to leave the original (un-contoured) label in the image # never overwrite other labels with new contoured label cond = np.logical_or(label_img == label, label_img == 0) safe_overlay = np.where(cond, contoured_label, label_img) # label must be present in safe_overlay for this to be a valid contour result # very few pixels of contoured label indicate contour prediction not worth keeping pixel_count = np.count_nonzero(safe_overlay == label) if pixel_count < min_pixels: safe_overlay = np.copy(self.frame[y1:y2, x1:x2, self.feature]) # put it back in the full image so can use centroid coords for post-contour cleanup full_frame = np.copy(self.frame[..., self.feature]) full_frame[y1:y2, x1:x2] = safe_overlay # avoid automated label cleanup if centroid (flood seed point) is not the right label if full_frame[int(props['centroid'][0]), int(props['centroid'][1])] != label: img_trimmed = full_frame else: # morphology and logic used by pixel-trimming action, with object centroid as seed contig_cell = flood( image=full_frame, seed_point=(int(props['centroid'][0]), int(props['centroid'][1])), ) # any pixels in img_ann that have value 'label' and are NOT connected to # hole_fill_seed get changed to 0, all other pixels retain their original value img_trimmed = np.where( np.logical_and(np.invert(contig_cell), full_frame == label), 0, full_frame, ) # update image; cell_info should never change as a result of this self.frame[y1:y2, x1:x2, self.feature] = img_trimmed[y1:y2, x1:x2] self.y_changed = True