def binarize(image: np.ndarray, holes_threshold: float = 20) -> np.ndarray: """ Binarize image using Sauvola algorithm. Parameters ---------- image : np.ndarray RGB image to binarize. holes_threshold : float, optional Pixel areas covering less than the given number of pixels are removed in the process, by default 20. Returns ------- binarized : np.ndarray Binarized and filtered image. """ # Extract brightness channel from HSV-converted image image_gray = rgb2hsv(image)[:,:,2] # Enhance contrast image_gray = equalize_adapthist(image_gray, kernel_size=100) # Threshold using Sauvola algorithm thresh_sauvola = threshold_sauvola(image_gray, window_size=51, k=0.25) binary_sauvola = image_gray > thresh_sauvola # Remove small objects binary_cleaned = 1.0 * remove_small_holes(binary_sauvola, area_threshold=holes_threshold) # Remove thick black border (introduced during thresholding) binary_cleaned = flood_fill(binary_cleaned, (0, 0), 0) binary_cleaned = flood_fill(binary_cleaned, (0, 0), 1) return binary_cleaned.astype(np.bool)
def binarize(image: np.ndarray, holes_threshold: float = 20) -> np.ndarray: """ Binarize image using Sauvola algorithm. Parameters ---------- image : np.ndarray RGB image to binarize. holes_threshold : float, optional Pixel areas covering less than the given number of pixels are removed in the process, by default 20. Returns ------- binarized : np.ndarray Binarized and filtered image. """ # Extract brightness channel from HSV-converted image image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # Enhance contrast clahe = cv2.createCLAHE(clipLimit=0.01, tileGridSize=(256, 256)) image_eq = clahe.apply(image_gray) # Threshold using Sauvola algorithm binary_sauvola = cv2.ximgproc.niBlackThreshold(image_eq, 255, k=0.25, blockSize=51, type=cv2.THRESH_BINARY, binarizationMethod=cv2.ximgproc.BINARIZATION_SAUVOLA) # Remove small objects #binary_cleaned = 1.0 * remove_small_holes(binary_sauvola, area_threshold=holes_threshold) # Remove thick black border (introduced during thresholding) binary_sauvola = flood_fill(binary_sauvola, (0, 0), 0) binary_sauvola = flood_fill(binary_sauvola, (0, 0), 1) return binary_sauvola.astype(np.bool)
def get_tumor_region(tumors, list_of_sequence): struct = disk(2) res = np.sum(tumors[..., list_of_sequence], axis=2) min_value = np.min(res) max_value = np.max(res) res = (((res - min_value) / (max_value - min_value)) * 255).astype(np.uint8) thresh = threshold_otsu(res) res_otsu = res > thresh #res_otsu = binary_opening(res_otsu, struct) label_image = label(res_otsu) regions = regionprops(label_image) for region in regions: if region.eccentricity > 0.9 or region.extent < 0.6 or region.area > 3000 or region.area < 150: centroid = tuple(int(x) for x in region.centroid) label_image = flood_fill(label_image, centroid, 0) label_image[label_image > 0] = 255 res[label_image == 0] = 0 l, num = label(label_image, return_num=True) if num < 1: label_image = label(res_otsu) regions = regionprops(label_image) for region in regions: if region.eccentricity > 0.9 or region.extent < 0.5 or region.area > 3000 or region.area < 150: centroid = tuple(int(x) for x in region.centroid) label_image = flood_fill(label_image, centroid, 0) label_image[label_image > 0] = 255 res[label_image == 0] = 0 l, num = label(label_image, return_num=True) regions = regionprops(l, intensity_image=res) mean_intensity = [x.mean_intensity for x in regions] max_intensity = np.max(mean_intensity) for index, region in enumerate(regions): if region.mean_intensity < max_intensity: centroid = tuple(int(x) for x in region.centroid) label_image = flood_fill(label_image, centroid, 0) area = regionprops(label_image)[0].area contour = find_contours(label_image, 0)[0] coords_region = approximate_polygon(contour, tolerance=0) return coords_region, area, label_image
def select_atom_starts(mgrid, G, radius): '''Given a single channel grid and the atomic radius for that type, select initial positions using a weight random selection that treats each disconnected volume of density separately''' per_atom_volume = radius**3 * ((2 * np.pi)**1.5) mask = G.cpu().numpy().copy() #look for islands of density greater than 0.5 (todo: parameterize this threshold?) #label each island in mask THRESHOLD = 0.5 values = G.cpu().numpy() mask[values >= THRESHOLD] = 1.0 mask[values < THRESHOLD] = 0 maxpos = np.unravel_index(mask.argmax(), mask.shape) masks = [] which = -1 while mask[maxpos] > 0: flood_fill(mask, maxpos, which, in_place=True) #identify and mark the connected region maxpos = np.unravel_index(mask.argmax(), mask.shape) which -= 1 for selector in range(-1, which, -1): masks.append(mask == selector) retcoords = [] #print("#masks",len(masks)) for M in masks: maskedG = G.cpu().numpy() maskedG[~M] = 0 flatG = maskedG.flatten() total = float(flatG.sum()) if total < .1 * per_atom_volume: continue #should be very conservative given a 0.5 THRESHOLD cnt = int(np.ceil( total / per_atom_volume)) #pretty sure this can only underestimate #counting this way is especially problematic for large molecules that go to the box edge if cnt == 0: continue flatG[flatG > 1.0] = 1.0 rand = np.random.choice(range(len(flatG)), cnt, False, flatG / flatG.sum()) gcoords = np.array(np.unravel_index(rand, G.shape)).T ccoords = grid_to_xyz(gcoords, mgrid) retcoords += list(ccoords) #print("coords",len(retcoords)) return retcoords
def Highlight(self, e): if e.x() < 0 or e.x() > self.__img.shape[1] or e.y() < 0 or e.y() > self.__img.shape[0]: return self.__mask = flood_fill(self.__mask, (e.y(), e.x()), 255) self.__thirdChannelMask[:, :, 2] = flood_fill(self.__thirdChannelMask[:, :, 2], (e.y(), e.x()), self.__highlightcolor.red()) self.__thirdChannelMask[:, :, 1] = flood_fill(self.__thirdChannelMask[:, :, 1], (e.y(), e.x()), self.__highlightcolor.green()) self.__thirdChannelMask[:, :, 0] = flood_fill(self.__thirdChannelMask[:, :, 0], (e.y(), e.x()), self.__highlightcolor.blue()) img = cv2.addWeighted(self.__img, 1, self.__thirdChannelMask, self.__transparency, 0) marc_img = mark_boundaries(img, self.__mask) self.open_image(marc_img)
def get_tumors(sub): tumors = np.zeros(sub.shape) struct = disk(4) for i in range(sub.shape[2]): if np.count_nonzero(sub[..., i]) == 0: continue test = binary_opening(sub[..., i], struct) label_image = label(test) list_of_data = [] for region in regionprops(label_image): data = [] centroid = tuple(int(x) for x in region.centroid) if region.eccentricity < 0.9 and region.extent > 0.5 and region.area < 3600 and region.area > 50: data.append(centroid) data.append(region.area) list_of_data.append(data) if not list_of_data: continue centroid = max(list_of_data, key=lambda item:item[1])[0] segmented_tumor = flood_fill(label_image, centroid, 255) segmented_tumor[segmented_tumor < 255] = 0 tumors[..., i] = segmented_tumor tumors /= 255 return tumors
def process_image(test_number, disk_size, bit_row, thresh, bit_row_mul, small_objects_size, space_size, small_objects_size_second): struct = disk(disk_size) bit_row = test_number.shape[0] // bit_row bit_col = test_number.shape[1] // 3 space = space_size struct_dilation = disk(1) mask = (test_number[..., 0] < thresh) | (test_number[..., 1] < thresh) | ( test_number[..., 2] < thresh) test_number[mask, :] = 0 mask = np.any(test_number > 0, axis=2) test_number[mask] = 1 test_number = test_number[..., 0].astype(np.bool) test_number[bit_row:bit_row * bit_row_mul, :] = 0 test_number[:, bit_col:bit_col * 2] = 0 test_number = clear_border(test_number, space) test_number_dilated = binary_dilation(test_number, struct).astype(np.bool) test_number_dilated = binary_fill_holes(test_number_dilated).astype( np.bool) test_number_dilated = remove_small_objects(test_number_dilated, small_objects_size) l = label(test_number_dilated) for region in regionprops(l): if region.eccentricity < 0.95 or region.extent < 0.63: centroid = tuple([int(x) for x in region.centroid]) l = flood_fill(l, tuple(region.coords[0, :]), 0) l[l > 0] = 1 test_number[l == 0] = 0 test_number = remove_small_objects(test_number, small_objects_size_second) test_number = binary_dilation(test_number, struct_dilation) return test_number, test_number_dilated
def count_ships(path, seg, date_mask): struct = disk(2) seg = binary_closing(seg, struct).astype(np.bool) im = imread(path) im = rgba2rgb(im) grayscale = rgb2gray(im) thresh = threshold_yen(grayscale) yen = grayscale > thresh eroded = binary_erosion(seg, struct).astype(np.bool) grayscale = grayscale > 0.6 grayscale[(eroded == 0) | (date_mask == 1) | (yen == 0)] = 0 grayscale[date_mask == 1] = 0 grayscale = binary_fill_holes(grayscale) grayscale = clear_border(grayscale) l = label(grayscale) for region in regionprops(l): if region.area > 100 or region.extent < 0.2: grayscale = flood_fill(grayscale, tuple(region.coords[0, :]), 0) label_image, num = label(grayscale, return_num=True) return num, grayscale
def imfill(mask, seed_pt='default'): '''Fill holes within objects in a binary mask. Equivalent to matlab's imfill function. seed_pt needs to be a point in the "background" area. All 0 or False pixels directly contiguous with the seed are defined as background, all other pixels are declared foreground. Thus any "holes" (0 pixels that are not contiguous with background) are filled in. Conceptually, this is like using the "fill" function in classic paint programs to fill the background, and taking all non-background as foreground. Args: mask: ndarray Binary mask of n dimensions. seed_pt: tuple Pixel in mask to use for seeding "background". This is the equi- valent of the point you click when filling in paint. If left as 'default', uses find_background_point to select point. Returns: mask_filled: ndarray Binary mask filled ''' if (seed_pt == 'default'): # Get a random 0-valued background pixel. seed_pt = find_background_point(mask) # Flood background from seed point. mask_flooded = flood_fill(mask, seed_pt, 1) # Set whatever changed (from 0 to 1) from the filling to background; # all else is foreground. mask_filled = np.where(mask == mask_flooded, 1, 0) return mask_filled
def __draw(self, pixmap, color): img = pixmap.toImage() pixels = [ 0 if 0 == img.pixel(x, y) else 1 for y in range(img.height()) for x in range(img.width()) ] pixels = numpy.array(pixels).reshape(img.height(), img.width()) # print(pixels) pixels = flood_fill(pixels, (self.Points[0][0], self.Points[0][1]), 1, connectivity=1) # print(pixels) painter = QtGui.QPainter(pixmap) painter.setCompositionMode(QtGui.QPainter.CompositionMode_Source) painter.fillRect(img.rect(), QtCore.Qt.transparent) painter.drawImage( 0, 0, QtGui.QImage(pixels, img.width(), img.height(), QtGui.QImage.Format_ARGB32)) for y in range(pixmap.height()): for x in range(pixmap.width()): painter.setPen( QtGui.QColor.fromRgba(0 if 0 == pixels[y][x] else self.Color.rgba())) painter.drawPoint(x, y) painter.end()
def get_tumors_post(sub, coords, min_slice, max_slice): struct = disk(4) tumors = np.zeros(sub.shape) for i in range(min_slice - 10, max_slice + 10): if np.count_nonzero(sub[..., i]) == 0: continue test = binary_opening(sub[..., i], struct) label_image = label(test) list_of_data = [] for region in regionprops(label_image): data = [] centroid = tuple(int(x) for x in region.centroid) if (region.eccentricity < 0.95 and region.extent > 0.45 and region.area < 3600 and region.area > 50 and np.sum(points_in_poly(region.coords, coords)) > region.area // 2): data.append(centroid) data.append(region.area) list_of_data.append(data) if not list_of_data: continue centroid = max(list_of_data, key=lambda item:item[1])[0] segmented_tumor = flood_fill(label_image, centroid, 255) segmented_tumor[segmented_tumor < 255] = 0 tumors[..., i] = segmented_tumor tumors /= 255 return tumors
def get_interior(sdf, i_point, qc=False): """ A function to identify interior points given an interior point specified by the user. Parameters ---------- sdf : Function The signed-distance function for the surface i_point : tuple of float The physical position of the interior point qc : bool Display a slice of the segmentation of quality checking purposes """ point_index = get_point_index(i_point, sdf.grid.spacing, sdf.grid.origin) flooded = flood_fill(sdf.data, point_index, -np.amin(sdf.data)) segmented = flooded > -_feps # Show a slice of the segmentation for qc if qc: center = segmented.shape[1]//2 plt.imshow(segmented[:, center].T, origin='lower') plt.colorbar() plt.show() return np.array(segmented)
def flood_fill(self, x, y): x, y = self.convert_to_limits(x, y) tmpmask = np.zeros_like(self.mask().copy()).astype("uint8") tmpmask[self.mask()] = 255 mask = tmpmask.copy() mask = flood_fill(mask, (y, x), 255) self._set_mask(mask == 255) self._update_mask()
def select_atom_starts(mgrid, G, radius): '''Given a single channel grid and the atomic radius for that type, select initial positions using a weight random selection that treats each disconnected volume of density separately''' per_atom_volume = get_per_atom_volume(radius) mask = G.cpu().numpy() mask[mask > 0] = 1.0 maxpos = np.unravel_index( mask.argmax(), mask.shape) #mtr22 - do we want to use the mask here or G? masks = [] while mask[maxpos] > 0: #mtr22 - the only positive values in mask are 1.0 flood_fill(mask, maxpos, -1, in_place=True) #identify and mark the connected region masks.append(mask == -1) # save the boolean selctor for this region mask[mask == -1] = 0 # remove it from the binary mask maxpos = np.unravel_index(mask.argmax(), mask.shape) retcoords = [] for M in masks: maskedG = G.cpu().numpy() maskedG[~M] = 0 flatG = maskedG.flatten() total = flatG.sum() #mtr22 - the next line places at least one atom on each contiguous region, # and for generated densities there can be thousands of these cnt = int(np.round( float(total) / per_atom_volume)) #pretty sure this can only underestimate #counting this way is especially problematic for large molecules that go to the box edge flatG[flatG > 1.0] = 1.0 rand = np.random.choice(range(len(flatG)), cnt, False, flatG / flatG.sum()) gcoords = np.array(np.unravel_index(rand, G.shape)).T ccoords = grid_to_xyz(gcoords, mgrid) #print(total, per_atom_volume, cnt, ccoords.shape) retcoords += list(ccoords) return retcoords
def is_inside(self): """去掉中点不在多边形内和太小的多边形. Returns ------- type Description of returned object. """ if len(self.points) < 30: return False label = np.zeros(self.shape, dtype="uint8") for p in self.points: label[p[0]][p[1]] = 1 flood_fill(label, tuple(self.center), 1, selem=[[0, 1, 0], [1, 1, 1], [0, 1, 0]], in_place=True) if label.sum() > label.size / 2: return False return True
def robust_segment_neuron(self, channel: int = 0, seg: float = 6., step: float = 1., all_chan_clicks: bool = False): """More robust segment routine to attempt to threshold a neuron with the clicks as seed positions. Will iteratively rerun until there are a number of pixels in the mask (tries to avoid an empty map). Does not use a local threshold. Will instead get the max and min intensities and divide that way. Keyword Arguments: channel {int} -- Neuron channel to segement (default: {0}) seg {float} -- Number of segments to divide (default: {6.}) step {float} -- Step to move the segment sizes by (default: {1.}) all_chan_clicks {bool} -- Use the clicks from all channels when flood filling (default: {False}) """ image = self.neurons[channel].copy() limage = np.log10(image) mmin, mmax = np.min(limage), np.max(limage) diff = mmax - mmin good = False while not good: thres = np.max(limage) - (diff / seg) mask = limage < thres image[mask] = 0 image[~mask] = 1 click_chans = [i for i in range(len(self.clicks)) ] if all_chan_clicks else [ channel, ] img_flood = image.copy() for seed_chan in click_chans: for c in self.clicks[seed_chan]: cc = tuple([int(c1) for c1 in c[::-1]]) if img_flood[cc[0], cc[1]] == 1: img_flood = flood_fill(img_flood, cc, 0.5) mask = img_flood == 0.5 img_flood[mask] = 1 img_flood[~mask] = 0 img_flood = binary_dilation(img_flood) img_flood = ndi.binary_fill_holes(img_flood) if np.sum(img_flood) < 20 and seg > 1: seg -= step else: good = True return img_flood
def get_foreground(im): im_processed = cv2.Laplacian(im,cv2.CV_8UC1) im_processed = denoise(im_processed, h=3) #im_processed = cv2.dilate(im_processed,ones((3,3)),iterations=1) im_processed = threshold_local(im_processed) h, w = im.shape[:2] seed = (int(h/2),int(w/2)) #ImageDraw.floodfill(foreground, (h/2-2,w/2-8), 255, border=None, thresh=255) foreground = segmentation.flood_fill(im_processed, seed, 255, connectivity=0, tolerance=0, in_place=False) return foreground
def generate_sd_field(track_wall_pixels, contained_pixel_position): track_pixels = flood_fill(track_wall_pixels, contained_pixel_position, 1, connectivity=1) # fill in the pixels between the track walls occupied_pixels = np.where(track_wall_pixels == 1, 0, 1) # prepare pixels for distance transform by making occupied pixels 0 and uoccupied pixels 1 d_field = np.array(distance_transform_edt(occupied_pixels)) # negate distances outside of the track walls for y, row in enumerate(d_field): for x, distance in enumerate(row): if track_pixels[y][x] == 0: d_field[y][x] = -distance return d_field
def set_nodes(self): ori_img = self._skel_img node_img = [] for i, x in enumerate(self._skel_img): for j, y in enumerate(x): if self._skel_img[i][j] == 0: node_img.append((i, j)) self._skel_img = flood_fill(self._skel_img, (i, j), 127, connectivity=1) self._skel_img = ori_img self._nodes = node_img
def divide_figures(pic): import numpy as np from skimage.segmentation import flood, flood_fill coords = np.array(np.where(pic != 0)) ans = [] while coords.shape[1] != 0: seed_point = tuple(coords[:, 0]) ans.append(flood(pic, seed_point)) pic = flood_fill(pic, seed_point, 0) coords = np.array(np.where(pic != 0)) return ans
def fill_inside_contour(cont_img, left, right, dilate, all_components=False): # foolproof no_good_fill = lambda fill, cont: (fill[0, 0] == 255 or np.sum(fill - cont) < cont.shape[0] * 255) filled_cont_img = flood_fill( cont_img, (len(left) // 2, left[len(left) // 2] + 2 + dilate * 2), 255) # try to find inner points of the ship from the left contour y = 0 while (all_components or no_good_fill(filled_cont_img, cont_img)) and y < cont_img.shape[0]: if not oob(cont_img, left[y] + 2 + dilate * 2, y): filled_cont_img = flood_fill(cont_img, (y, left[y] + 2 + dilate * 2), 255) if all_components and not no_good_fill(filled_cont_img, cont_img): cont_img = filled_cont_img y += 1 # there is a whole in the outline or nothing has been filled at all if filled_cont_img[0, 0] == 255 or np.sum(filled_cont_img - cont_img) < cont_img.shape[0] * 255: # just draw line between left and right as an approximation lines = [ draw.line(y, left[y], y, right[y]) for y in range(cont_img.shape[0]) if right[y] > 0 ] xs = np.concatenate([xs for xs, _ in lines]) ys = np.concatenate([ys for _, ys in lines]) cont_img[xs, ys] = 255 else: cont_img = filled_cont_img return cont_img
def flood_fill(self, event): """ Fills a contour selected by the middle mouse button with the mask. """ if event.widget is self.image_label: for i in range(3): self.mask[:, :, i] = flood_fill(self.mask[:, :, i], seed_point=(event.y, event.x), new_value=255, tolerance=1) self.display_image[:, :, i] = np.where(self.mask[:, :, i] == 255, 255, self.display_image[:, :, i]) self.update_image()
def find_acs(kspace): '''Start at center of kspace and find largest hyper-rectangle. Parameters ---------- kspace : N-D array Assume coil_axis is at -1 and currently unpadded. ''' # import matplotlib.pyplot as plt # Flood fill from center mask = np.abs(kspace[..., 0]) > 0 ctr = tuple([sh // 2 for sh in kspace.shape[:-1]]) if mask[ctr] == 0: raise ValueError('There is no sample at the center!') ACS_val = 2 region = flood_fill(mask.astype(int), seed_point=ctr, new_value=ACS_val, connectivity=0) == ACS_val # plt.imshow(region) # plt.show() if region.ndim == 2: # Find a centered rectangle acs, top, bottom = findRectangle2d(region, mask, ctr) # plt.imshow(acs) # plt.show() nc = kspace.shape[-1] acs = np.tile(acs[..., None], (1, 1, nc)) # I'm not sure what I'm doing wrong yet... for ii in range(3): try: calib = kspace[acs].reshape((bottom - top - ii, -1, nc)) break except ValueError: pass else: raise ValueError() else: raise NotImplementedError() return calib
def fill(lung): struct = disk(5) test = lung.copy() for i in range(lung.shape[2]): test[..., i] = binary_fill_holes(lung[..., i]) label_test, num = label(test[..., i], return_num=True) if num != 2: continue chull = convex_hull_object(test[..., i]) coords = [] for contour in find_contours(chull, 0): coords.append(approximate_polygon(contour, tolerance=0)) if len(coords) < 2: continue inverted = np.invert(test[..., i]) distance = ndi.distance_transform_edt(inverted) peaks = peak_local_max(distance, labels=inverted) left_peaks = points_in_poly(peaks, coords[0]) right_peaks = points_in_poly(peaks, coords[1]) peaks_mask = left_peaks | right_peaks peaks = peaks[peaks_mask] peak_image = np.zeros(lung[..., i].shape) peak_image[peaks[:, 0], peaks[:, 1]] = 1 if len(peaks == 1): peak_image[0, 0] = 1 markers = ndi.label(peak_image)[0] labels = watershed(-distance, markers, mask=inverted) labels[labels == 1] = 0 for region in regionprops(labels): centroid = np.asarray([int(x) for x in region.centroid]).reshape(1, 2) if ((np.sum(points_in_poly(region.coords, coords[0])) < region.area and np.sum(points_in_poly(region.coords, coords[1])) < region.area) or (not points_in_poly(centroid, coords[1]) and not points_in_poly(centroid, coords[1]))): centroid = tuple(int(x) for x in region.centroid) labels = flood_fill(labels, centroid, 0) labels[labels > 0] = 255 test[..., i][labels > 0] = 255 return test
def fill(self, seed_point, fill_value=3): y, x = self.edges.shape mask = np.zeros((y, x)) filled_mask = mask.copy() mask[:, :x - 1] = self.edges[:, 1:x] mask = mask + self.edges mask = mask[1:y - 1, 0:x - 1] k = 0 while int(mask[seed_point]) != 0: j, i = seed_point i += 1 k += 1 if k == 6: raise ValueError filled_mask[1:y - 1, 0:x - 1] = flood_fill(mask, seed_point, fill_value) return filled_mask[filled_mask > 2]
def segment_neuron(self, channel: int = 0, thres: float = 3.25, step: float = 0.5): """Create a binary mask of the segmented regions within a neuron that contain the object of interest. This requires clicking information that is used to isolate specific regions. Keyword Arguments: channel {int} -- The neuron channel to perform segmenting against (default: {0}) thres {float} -- Sigma threshold used for binary masking (default: {3.25}) channel {int} -- Change in sigma threshold when to many pixels clipped (default: {0.5}) """ img = self.neurons[channel].copy() clicks = self.clicks[channel] d = np.log10(img.flatten()) dmean = np.mean(d) dstd = np.std(d) thres = 3.25 mask = np.log10(img) < (dmean + thres * dstd) while np.sum(mask == False) < 10: thres -= 0.5 mask = np.log10(img) < (dmean + thres * dstd) img[mask] = 0 img[~mask] = 1 # Flood fill to find the appropriate segnents with clicks as seeds img_flood = img.copy() for c in clicks: # Clicks were saved in the (x,y) order. For images arrays they are (y, x) cc = tuple([int(c1) for c1 in c[::-1]]) if img_flood[cc[0], cc[1]] == 1: img_flood = flood_fill(img_flood, cc, 0.5) mask = img_flood == 0.5 img_flood[mask] = 1 img_flood[~mask] = 0 img_flood = binary_dilation(img_flood) img_flood = ndi.binary_fill_holes(img_flood) return img_flood
def fill_region(img, loc, val=None, threshold=0.1, dist='rmse', make_copy=False): px = img[loc] if val is None: val = px if dist == 'rmse': diff = np.sqrt(((img - px.reshape(1, 1, -1)) ** 2).sum(-1)) else: diff = (np.abs(img - img[loc].reshape(1, 1, -1))).sum(-1) mask = flood_fill(diff, loc, -1, tolerance=threshold) < 0 if make_copy: img = img.copy() img[mask] = val return img
def flood_outer_contour(img): # use all three channels, flood them separately, AND the results; repeat this for all 4 corners and OR them img = img.astype( int) # so when flooding the value can be set to something unique corners = [(0, 0), (img.shape[0] - 1, img.shape[1] - 1), (0, img.shape[1] - 1), (img.shape[0] - 1, 0)] floods = [ np.bitwise_and.reduce(np.bitwise_and.reduce([ flood_fill(img, corner + (chan, ), -1) == -1 for chan in range(3) ]), axis=-1) for corner in corners ] cont_img = np.invert(np.bitwise_or.reduce(floods)).astype(np.uint8) * 255 # TODO return left, right, top, bottom as in outer_contour return cont_img, None, None, None, None
def remove_blobs(lung): col = lung.shape[1] out_image = np.copy(lung) for i in range(lung.shape[2]): if np.count_nonzero(lung[..., i]) == 0: continue filled = (binary_fill_holes(lung[..., i])).astype(np.uint8) * 255 label_image, num = label(filled, return_num=True) if num == 1: continue max_region_area = 0 max_region = 0 second_max_region_area = 0 second_max_region = 0 j = 0 regions = regionprops(label_image) for index, region in enumerate(regions): if region.area > max_region_area: max_region_area = region.area max_region = region.coords j = index del regions[j] for region in regions: if region.area > second_max_region_area: second_max_region_area = region.area second_max_region = region.coords left = min(np.min(max_region[:, 1]), np.min(second_max_region[:, 1])) right = max(np.max(max_region[:, 1]), np.max(second_max_region[:, 1])) top = min(np.min(max_region[:, 0]), np.min(second_max_region[:, 0])) bottom = max(np.max(max_region[:, 0]), np.max(second_max_region[:, 0])) for region in regionprops(label_image): if ((region.centroid[1] > (col // 2) - (col // 8) and region.centroid[1] < (col // 2) + (col // 8) and region.area < 1500) or region.centroid[0] < top or region.centroid[0] > bottom or region.centroid[1] > right or region.centroid[1] < left) : centroid = tuple(int(x) for x in region.centroid) label_image = flood_fill(label_image, centroid, 0) out_image[..., i][label_image == 0] = 0 return out_image
def get_2d_mask_by_contour(img2d): mask_to_untouch_boarders = np.zeros(img2d.shape, dtype=int) mask_to_untouch_boarders[1:-1, 1:-1] = 1 img2d = img2d * mask_to_untouch_boarders # Find contours at a constant value of 0.8 contours = measure.find_contours(img2d, 0.8) contour = sorted(contours, key=lambda x: len(x))[-1] r_mask = np.zeros_like(img2d, dtype='bool') # Create a contour image by using the contour coordinates rounded to their nearest integer value r_mask[np.round(contour[:, 0]).astype('int'), np.round(contour[:, 1]).astype('int')] = 1 #close contours r_mask = binary_dilation(r_mask) r_mask = r_mask.astype(int) mask = flood_fill(r_mask, seed_point=tuple(np.asarray(r_mask.shape) // 2), new_value=1) return mask