def flood_rgb(image, starting_coord): image = image.copy() width = len(image) height = len(image[0]) # Sadly, I need to use this 'alternate color' approach because I can't get flood working with an RGB image, so I need to make several calls to flood on each color channel, using # either black or white as an invalidation marker (white if the actual sample colour is black, else black). alternate_col = [1, 1, 1] if (image[starting_coord[0]][starting_coord[1]] == [0, 0, 0]).all() else [0, 0, 0] r_flood = flood(image, (starting_coord[0], starting_coord[1], 0), connectivity=1) for x in range(width): for y in range(height): if not r_flood[x, y, 0]: image[x, y] = alternate_col g_flood = flood(image, (starting_coord[0], starting_coord[1], 1), connectivity=1) for x in range(width): for y in range(height): if not g_flood[x, y, 1]: image[x, y] = alternate_col b_flood = flood(image, (starting_coord[0], starting_coord[1], 2), connectivity=1) all_flood = numpy.empty([width, height], bool) for x in arange(width): for y in arange(height): all_flood[x][y] = r_flood[x, y, 0] and g_flood[x, y, 1] and b_flood[x, y, 2] return all_flood
def difference_area_perimeters(base_propagation, perimeter1_cells, perimeter2_cells, flood_initial_cell): gd1 = np.zeros(base_propagation.prop_data.data.shape) gd2 = np.zeros(base_propagation.prop_data.data.shape) for cell, time in perimeter1_cells.items(): gd1.data[cell] = 1 for cell, time in perimeter2_cells.items(): gd2.data[cell] = 1 flood_initial_cell = (flood_initial_cell[1], base_propagation.prop_data.data.shape[0] - flood_initial_cell[0]) surface1 = flood(gd1, flood_initial_cell, connectivity=1) surface2 = flood(gd2, flood_initial_cell, connectivity=1) area1 = np.count_nonzero( surface1 ) * base_propagation.prop_data.cell_width * base_propagation.prop_data.cell_height area2 = np.count_nonzero( surface2 ) * base_propagation.prop_data.cell_width * base_propagation.prop_data.cell_height difference_surface = flood(gd1, flood_initial_cell, connectivity=1) ^ flood( gd2, flood_initial_cell, connectivity=1) difference_area = np.count_nonzero( difference_surface ) * base_propagation.prop_data.cell_width * base_propagation.prop_data.cell_height return difference_surface, difference_area, area1, area2
def memb_mask(self, frame, roi_size=10): """ Membrane region detection. Outside edge - >= 2sd noise Inside edge - >= cytoplasm mean intensity img - imput z-stack frame; roi_center - list of int [x, y], coordinates of center of the cytoplasmic ROI for cytoplasm mean intensity calculation; roi_size - int, cytoplasmic ROI side size in px (ROI is a square area); noise_size - int, size in px of region for noise sd calculation (square area with start in 0,0 coordinates); sd_low - float, hysteresis algorithm lower threshold for outside cell edge detection, > 2sd of noise (percentage of maximum frame intensity); mean_low - float, hysteresis algorithm lower threshold for inside cell edge detection, > cytoplasmic ROI mean intensity (percentage of maximum frame intensity); gen_high - float, general upper threshold for hysteresis algorithm (percentage of maximum frame intensity); sigma - int, sd for Gaussian filter. """ # THIS IS TEMPORARY METHOD, FOR ONE CELL AT IMAGE ONLY! sd_mask, frame_sd = self.__create_sd_mask(frame) roi_mask, frame_roi = self.__create_roi_mask(frame) logging.info( f'Noise SD={round(frame_sd, 3)}, ROI mean intensity={round(frame_roi, 3)}\n' ) # filling external space and create cytoplasmic mask cytoplasm_mask = roi_mask + segmentation.flood(roi_mask, (0, 0)) if np.all(cytoplasm_mask): logging.fatal( 'Cytoplasm mask is NOT closed, CAN`T create correct membrane mask!' ) membrane_mask = ma.masked_where(~cytoplasm_mask, sd_mask) return [sd_mask, roi_mask, cytoplasm_mask, membrane_mask]
def fit_gaussian_on_histogram_peak(x: "np.ndarray[float]", y: "np.ndarray[float]", peak_idx: int): """ procedure of fitting scaled gaussian with least-squares optimizer. :param peak_idx: index of y near the peak that is being interpolated. """ # get init estimates a_init = y[peak_idx] mu_init = x[peak_idx] gthm = y > (a_init / 2) # gthm -- greater than half maximum gthm = np.where( sks.flood(gthm.astype(int), peak_idx))[0] # for some reason does not work with bools min_hm_x, max_hm_x = x[gthm[[0, -1]]] sigma_init = ( max_hm_x - min_hm_x ) / 2.355 # https://en.wikipedia.org/wiki/Full_width_at_half_maximum init_params = np.array([a_init, mu_init, sigma_init]) # select 2*sigma range idx = np.where((x > mu_init - sigma_init * 2) & (x < mu_init + sigma_init * 2)) x = x[idx] y = y[idx] # fit err = lambda params, x, y: y - gaussian(x, *params) fit_params, _ = sco.leastsq(err, init_params, args=(x, y)) return fit_params
def flood_remove_region(self, position, tolerance): """ Remove from the current mask a flood region at the given position with the given tolerance. Args: position: The position to start the flood fill (x, y) tolerance: The tolerance for pixels to be included Returns: None Postconditions: The _mask will be updated with the new region """ from skimage.segmentation import flood position = int(position[0]), int(position[1]) # If we are still editing the tolerance, we need to go back to the old # mask. if position == self._old_flood_remove_position: self.mask = np.copy(self._old_mask) else: self._old_mask = np.copy(self._mask) remove_mask = flood(self._patch, position, tolerance=tolerance) self._mask[remove_mask] = 0 self._overlay_mask() self._old_flood_remove_tolerance = tolerance self._old_flood_remove_position = position
def seed_fill_img(neuron_img: np.ndarray, clicks: List[tuple]): """Using specified seed points, create a filter with a flood fill style Arguments: neuron_mask {np.ndarray} -- Image of the neuron. Single channel, so shape is (y, x) clicks {List[tuple]} -- All clicks in the form (y, x, sigma) Raises: TypeError: Raised when neuron_img is not two-dimensional (only a single channel ) Returns: np.ndarray -- Combined mask of clicked regions """ if not len(neuron_img.shape) == 2: raise TypeError( f"neuron_img has to be two-dimensional, passed shape is {neuron_img.shape}" ) master_mask = np.zeros_like(neuron_img).astype(np.int) for click in clicks: tmp_mask = neuron_img > click[3] * neuron_img.std() # at the time of writing flood needs floats to be float64 tmp_mask = flood(tmp_mask.astype(np.float64), (int(click[0] + 0.5), int(click[1] + 0.5))) master_mask[tmp_mask] = click[2] return master_mask
def flood(self, e): image = self.pixmap().toImage() b = image.bits() b.setsize(512 * 512 * 4) arr = np.frombuffer(b, np.uint8).reshape((512, 512, 4)) arr = arr.astype(np.int32) arr = np.flip(arr, axis=2) i = self.color_index + 1 arr_test = arr[:,:,i]-((arr[:,:,1]+ arr[:,:,2]+ arr[:,:,3])/3) #arr test is not greyscale i = 2 - self.color_index #painted_arr is BGRA painted_arr = np.zeros_like(arr,dtype=np.uint8) painted_arr[:,:,i][arr_test!=0] = 255 #this makes the drawn images the same as pen color painted_arr[:,:,i] = 255*flood(painted_arr[:,:,i],(e.y(),e.x())) #sets alpha from ith channel painted_arr[:,:,3] = painted_arr[:,:,i] #BGRA qi = QImage(painted_arr.data, painted_arr.shape[1], painted_arr.shape[0], 4*painted_arr.shape[1], QImage.Format_ARGB32_Premultiplied) pixmap = QPixmap(qi) painter = QPainter(self.pixmap()) painter.setOpacity(0.5) painter.drawPixmap(0,0,pixmap) painter.end() self.update() #self.array saves RGB values self.array += np.flip(painted_arr[:,:,:3], axis=2)
def fd_extract(X_train, n_features=3, tolerance=0.2): print('Fourier Descriptor based extraction has been started...') descriptors = [] for x in X_train: img = rgb2gray(x['bbox_img']) w_h, h_h = img.shape[0] // 2, img.shape[1] // 2 res = flood(img, (w_h, h_h), tolerance=tolerance) cont = find_contours(res, 0) if len(cont) > 0: sing_cont = [np.complex(n[0], n[1]) for n in cont[0]] fd = fft(sing_cont) if len(fd) >= n_features + 2: fd_ref = abs(fd[1]) fd_inv = [] for j in range(n_features): fd_inv.append(abs(fd[j + 2]) / fd_ref) descriptors.append(fd_inv) else: descriptors.append([32.1] * n_features) else: descriptors.append([32.1] * n_features) print('Fourier Descriptor based extraction is done') return np.matrix(descriptors)
def get_provinces(province_mask, min_province_pixels, connectivity=1): province_masks = [] province_origins = [] undetermined_mask = numpy.zeros(province_mask.shape, dtype=bool) undetermined_pixels_found = False for x in range(province_mask.shape[0]): for y in range(province_mask.shape[1]): if province_mask[x][y] and ~undetermined_mask[x][y]: flooded_pixels = flood(province_mask, (x, y), connectivity=connectivity) if numpy.count_nonzero(flooded_pixels) < min_province_pixels: undetermined_pixels_found = True # If the discovered area was too small, mark it on the undetermined map (this will be re-evaluated later, allowing for diagonal pixel connections next time). undetermined_mask = numpy.logical_or( undetermined_mask, flooded_pixels) else: # Remove the mask of the discovered province (found via flooding) from the province_mask, preventing it from being re-added by future iterations. province_mask = numpy.logical_and(province_mask, ~flooded_pixels) province_masks.append(flooded_pixels) province_origins.append([x, y]) if not undetermined_pixels_found: undetermined_mask = None return province_masks, province_origins, undetermined_mask
def get_state_mask(state_guide, state_color): # Get a mask of all pixels of the border key color. # Running two_d_array == value produces a 'mask' where only individual pixels are compared, but we want to know about cases where all three pixels are equal. # logical_and does this sort of thing, apparently. border_mask = logical_and.reduce(state_guide == state_color, axis=-1) x_min, y_min, x_max, y_max = find_bounds(border_mask) guide_view = state_guide[y_min:y_max + 1, x_min:x_max + 1] # Crop the border_mask too. border_mask = border_mask[y_min:y_max + 1, x_min:x_max + 1] # Create a copy of the border mask with a new layer of false pixels all around the edge. state_mask = border_mask.copy() state_mask = numpy.insert(state_mask, 0, False, axis=0) state_mask = numpy.insert(state_mask, 0, False, axis=1) state_mask = numpy.insert(state_mask, state_mask.shape[0], False, axis=0) state_mask = numpy.insert(state_mask, state_mask.shape[1], False, axis=1) # Get a mask of all pixels outside the state's borders, by performing a flood from the top-left pixel on the expanded mask we've made. # Then invert the mask. All true values on the mask signify a point on or within the state's borders. state_mask = ~flood(state_mask, (0, 0), connectivity=1) # Crop the state_mask back to the bounds of the relevant pixels. state_mask = numpy.delete(state_mask, state_mask.shape[0] - 1, 0) state_mask = numpy.delete(state_mask, state_mask.shape[1] - 1, 1) state_mask = numpy.delete(state_mask, 0, 0) state_mask = numpy.delete(state_mask, 0, 1) state_mask = state_mask & logical_or.reduce(guide_view != ignore_col, axis=-1) return state_mask, border_mask, x_min, y_min, x_max, y_max
def hystMemb(img, roi_center, roi_size=30, noise_size=20, low_diff=40, gen_high=0.8, sigma=3): """ Function for membrane region detection with hysteresis threshold algorithm. Outdide edge - >= 2sd noise Inside edge - >= cytoplasm mean intensity Require hystLow function for lower hysteresis threshold calculations. img - imput z-stack frame; roi_center - list of int [x, y], coordinates of center of the cytoplasmic ROI for cytoplasm mean intensity calculation; roi_size - int, cutoplasmic ROI side size in px (ROI is a square area); noise_size - int, size in px of region for noise sd calculation (square area witf start in 0,0 coordinates); sd_low - float, hysteresis algorithm lower threshold for outside cell edge detection, > 2sd of noise (percentage of maximum frame intensity); mean_low - float, hysteresis algorithm lower threshold for inside cell edge detection, > cytoplasmic ROI mean intensity (percentage of maximum frame intensity); gen_high - float, general upper threshold for hysteresis algorithm (percentage of maximum frame intensity); sigma - int, sd for gaussian filter. Returts membrane region boolean mask for input frame. """ img = backCon(img, dim=2) img_gauss = filters.gaussian(img, sigma=sigma) noise_sd = np.std(img[:noise_size, :noise_size]) logging.info('Frame noise SD={:.3f}'.format(noise_sd)) roi_mean = np.mean(img[roi_center[0] - roi_size//2:roi_center[0] + roi_size//2, \ roi_center[1] - roi_size//2:roi_center[1] + roi_size//2]) # cutoplasmic ROI mean celculation logging.info('Cytoplasm ROI mean intensity {:.3f}'.format(roi_mean)) low_val = hystLow(img, img_gauss, sd=noise_sd, mean=roi_mean, diff=low_diff, gen_high=gen_high) mask_2sd = filters.apply_hysteresis_threshold( img_gauss, low=low_val['2sd'] * np.max(img_gauss), high=gen_high * np.max(img_gauss)) mask_roi_mean = filters.apply_hysteresis_threshold( img_gauss, low=low_val['mean'] * np.max(img_gauss), high=gen_high * np.max(img_gauss)) # filling external space and create cytoplasmic mask mask_cytoplasm = mask_roi_mean + segmentation.flood(mask_roi_mean, (0, 0)) return mask_2sd, mask_roi_mean, ma.masked_where(~mask_cytoplasm, mask_2sd)
def _make_cell_map(self): #### Get the cell map according to this #### # # Pre: Requires both a Nucleus and Membrane map # Post: Sets a 'cell_map' in the 'segmentation_images' segmentation_images = self.get_data('segmentation_images').set_index( 'segmentation_label') nucid = segmentation_images.loc['Nucleus', 'image_id'] memid = segmentation_images.loc['Membrane', 'image_id'] nuc = self.get_image(nucid) mem = self.get_image(memid) mids = map_image_ids(mem) coords = list(zip(mids['x'], mids['y'])) center = median_id_coordinates(nuc, coords) im = mem.copy() im2 = mem.copy() orig = pd.DataFrame(mem.copy()) for i, cell_index in enumerate(center.index): coord = (center.loc[cell_index]['x'], center.loc[cell_index]['y']) mask = flood(im2, (coord[1], coord[0]), connectivity=1, tolerance=0) if mask.sum().sum() >= 2000: continue im2[mask] = cell_index v = map_image_ids(im2, remove_zero=False) zeros = v.loc[v['id'] == 0] zeros = list(zip(zeros['x'], zeros['y'])) start = v.loc[v['id'] != 0] start = list(zip(start['x'], start['y'])) c1 = map_image_ids(im2).reset_index().rename( columns={'id': 'cell_index_1'}) c2 = map_image_ids(im2).reset_index().rename( columns={'id': 'cell_index_2'}) overlap = c1.merge(c2, on=['x', 'y']).query('cell_index_1!=cell_index_2') if overlap.shape[0] > 0: raise ValueError("need to handle overlap") #print("DONE FILLING IN") cell_map_id = uuid4().hex self._images[cell_map_id] = im2.copy() increment = self.get_data('segmentation_images').index.max() + 1 extra = pd.DataFrame( pd.Series( dict({ 'db_id': increment, 'segmentation_label': 'cell_map', 'image_id': cell_map_id }))).T extra = pd.concat( [self.get_data('segmentation_images'), extra.set_index('db_id')]) self.set_data('segmentation_images', extra)
def make_head_mask(image: "np.ndarray[float]", brain_mask: "np.ndarray[bool]") -> "np.ndarray[bool]": """ applies heuristic (thresholding and flooding) to obtain a mask of an animal head """ x = skf.gaussian(image, sigma=2, truncate=2) x = np.where(x < 0.5, 1, 0) c = center_of_binary_image(brain_mask) mask = sks.flood(x, c) return mask & ~brain_mask
def black_cut_fun(img_rgb): h,w,c = img_rgb.shape corner_pixel = np.mean(img_rgb[-1,-1]).astype(np.int) img_zero = corner_pixel * np.ones((h+80,w+80,c), dtype=img_rgb.dtype) img_zero[40:-40,40:-40,:] = img_rgb img_rgb = img_zero img_gray = (rgb2gray(img_rgb) * 255).astype(np.uint8)*1.5 img_shape = img_gray.shape marker_set = ((5, 5), (5, img_shape[1] - 5), (img_shape[0] - 5, 5), (img_shape[0] - 5, img_shape[1] - 5)) marker_color_set = [] for marker_coords in marker_set: marker_color_set.append(img_gray[(marker_coords[0], marker_coords[1])]) marker_color_set_std = np.std(marker_color_set) marker_color_std_tol = 15 if marker_color_set_std >= marker_color_std_tol: raise Exception("CHANGE BORDER COLOR MODULE: failed to get consistent border color based on four markers.") border_mask = np.zeros(img_gray.shape, np.uint8) tol_min = 10 for marker_coords in marker_set: curr_mask = flood(img_gray, marker_coords, tolerance = 5) border_mask = np.maximum(curr_mask, border_mask) fg_mask = 1 - border_mask fg_label = label(fg_mask) fg_prop = regionprops(fg_label) fg_prop.sort(key=lambda x:x.area,reverse=True) fg_mask = (fg_label == fg_prop[0].label) border_smoothing_size = 5 disk_ele = disk(radius = border_smoothing_size) fg_mask = binary_closing(fg_mask, disk_ele) tmp_mask = 1 - fg_mask kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (50, 50)) center, major_axis_length=_get_center_by_edge(tmp_mask) radius=_get_radius_by_mask_center(tmp_mask,center) h,w = img_shape s_h = max(0,int(center[0] - radius)) s_w = max(0, int(center[1] - radius)) bbox = (s_h, s_w, min(h-s_h,2 * radius), min(w-s_w,2 * radius)) circle_mask=_get_circle_by_center_bbox(img_shape,(h//2,w//2),bbox,radius) if np.sum(fg_mask) > np.sum(circle_mask): result_mask = fg_mask else: result_mask = circle_mask mask = result_mask img = img_rgb * mask[:,:,np.newaxis] img = img[np.ix_(mask.any(1), mask.any(0))] return radius , img
def build_mask(img, tolerance=1e-2): _, h, w = img.shape corners = [(0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1)] gray_image = ttf.rgb_to_grayscale(img).numpy() white_corners = [(x, y) for x, y in corners if gray_image[0, y, x] >= 1 - tolerance] sobel_image = sobel(gray_image)[0] mask = np.full((h, w), False) for x, y in white_corners: if mask[y, x]: continue cfill = flood(sobel_image, (y, x), tolerance=tolerance) mask = mask | cfill return torch.tensor(mask)
def onclick(self, event): """ handle clicking to remove already added stuff """ if event.button == 1: if event.xdata is not None and not self.lasso.active: # transpose x and y bc imshow transposes self.indices = flood( self.class_mask, (np.int(event.ydata), np.int(event.xdata))) self.updateArray() elif event.button == 3: self.panhandler.press(event)
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 flood(self, e): image = self.pixmap().toImage() b = image.bits() b.setsize(self.canvas_size * self.canvas_size * 4) arr = np.frombuffer(b, np.uint8).reshape( (self.canvas_size, self.canvas_size, 4)) OGimage = self.OGpixmap.toImage() b = OGimage.bits() b.setsize(self.canvas_size * self.canvas_size * 4) OGim = np.frombuffer(b, np.uint8).reshape( (self.canvas_size, self.canvas_size, 4)) OGim = np.flip(OGim, axis=2) arr = arr.astype(np.int32) arr = np.flip(arr, axis=2) arr_test = np.zeros_like(arr) arr_test[arr != OGim] = arr[arr != OGim] flat_arr = np.mean(arr_test[:, :, 1:], axis=2) flooded = flood(flat_arr, (e.y(), e.x())) color = self.colors[self.color_index] rgb = [int(color[3:5], 16), int(color[5:7], 16), int(color[7:], 16)] #paint_arr is ARGB paint_arr = np.zeros_like(arr, dtype=np.uint8) paint_arr[:, :, 0] = (flooded) * 255 #sets alpha paint_arr[flooded, 1:] = rgb #fills wil pen colour #BGRA paint_arr = np.flip(paint_arr, axis=2).copy() qi = QImage(paint_arr.data, paint_arr.shape[1], paint_arr.shape[0], 4 * paint_arr.shape[1], QImage.Format_ARGB32_Premultiplied) pixmap = QPixmap(qi) painter = QPainter(self.pixmap()) painter.setOpacity(0.5) painter.drawPixmap(0, 0, pixmap) painter.end() self.update() #self.array saves RGB values self.array += np.flip(paint_arr[:, :, :3], axis=2)
def paint(): """Return the picture with wall color changed.""" file = request.files['image'] # Get image from request coord_x = int(request.form.get('coord_x')) # Get X coordinate from request coord_y = int(request.form.get('coord_y')) # Get Y coordinate from request hex = request.form.get('color') # Get color from request hue, sat, val = hex_to_hsv(hex) # Convert colot to HSV code img = io.imread(file) # Open image img_hsv = color.rgb2hsv(img) # Convert color channels img_gray = color.rgb2gray(img) # Convert color channels # Sobel edge detection edge_sobel = sobel(img_gray) # Felzenszwalb’s image segmentation segments_fz = felzenszwalb(edge_sobel, scale=100, sigma=1, min_size=150) # Create a mask of flooded pixels mask = flood(segments_fz, (coord_y, coord_x), tolerance=0.5) # Set pixels of mask to new value for hue channel img_hsv[mask, 0] = hue # Set pixels of mask to new value for saturation channel img_hsv[mask, 1] = sat # Set pixels of mask to new value for value/brightness channel img_hsv[mask, 2] = val # Convert color channels img_final = color.hsv2rgb(img_hsv) # Create a byte object to save the image file_object = BytesIO() # Save image on the byte object io.imsave(file_object, img_final, plugin='pil') file_object.seek(0) return send_file(file_object, mimetype='image/jpeg')
def check_if_contour_is_closed(self): coords = self.__hook_to_pixel() if not coords: return False object_area = self.__enlarge_window(coords[0], coords[1]) original_num = self.__count_white_pixels_in_given_area(object_area[0], object_area[1], object_area[2], object_area[3], True) self.__img = flood(self.__img, (0, 0), connectivity=0) if self.__count_white_pixels_in_given_area(object_area[0], object_area[1], object_area[2], object_area[3], False) > original_num: return True return False
def get_fiber_track( registered_atlas_path, source_image_path, seed_point, output_path=None, normalising_factor=4, erosion_diameter=5, ): """ gets segmentation image of optical fibers using the background channel and a seed-point, tested on 200um and 400um fibers :param erosion_selem: :param normalising_factor: :param output_path: :param registered_atlas_path: :param source_image_path: :param seed_point: :return: """ brain_mask = brainio.load_any(str(registered_atlas_path)) != 0 brain = brainio.load_any(str(source_image_path)) brain_median_filtered = median(brain) otsu_threshold = threshold_multiotsu(brain_median_filtered)[0] brain_segmentation = (brain_median_filtered < otsu_threshold / normalising_factor) * brain_mask segmentation_eroded = binary_erosion(brain_segmentation, selem=ball(erosion_diameter)) segmentation_eroded_dilated = binary_dilation(segmentation_eroded) fiber_track_image = flood(segmentation_eroded_dilated.astype(np.int16), seed_point) if output_path is not None: save_brain(fiber_track_image, source_image_path, output_path) else: return fiber_track_image
def main(): image_rgb = cv2.imread("1-039.jpg") image = cv2.cvtColor(image_rgb, cv2.COLOR_BGR2GRAY) ''' rows = image.shape[0] cols = image.shape[1] print("length: ", len(image)) print("Dimensions: ", image.shape) print("rows,cols: ", rows, cols) ''' astranaut = data.astronaut() print(astranaut.shape) #image_show(astranaut) astranaut_gray = cv2.cvtColor(astranaut, cv2.COLOR_BGR2GRAY) #image_show(astranaut_gray) seed_point = (255, 255) flood_mask = seg.flood(image, seed_point, tolerance=0.1) fig, ax = image_show(image) ax.imshow(flood_mask, alpha=0.3) ax.plot(255, 255, 'bo') plt.show()
def reconstruction(imagePath, outputPath): image = io.imread(imagePath) if (isGrayscale(imagePath)): image = gray2rgb(image) elif (isNotHsv(imagePath)): image = rgb2hsv(image) img_hsv = image img_hsv_copy = np.copy(img_hsv) imgSize = (image.shape) w = min(imgSize[0], 200) h = min(imgSize[1], 160) # flood function returns a mask of flooded pixels mask = flood(img_hsv[..., 0], (w - 1, h - 1), tolerance=0.016) # Set pixels of mask to new value for hue channel img_hsv[mask, 0] = 0.5 # Post-processing in order to improve the result # Remove white pixels from flag, using saturation channel mask_postprocessed = np.logical_and(mask, img_hsv_copy[..., 1] > 0.4) # Remove thin structures with binary opening # mask_postprocessed = binary_opening(mask_postprocessed, # np.ones((3, 3))) # Fill small holes with binary closing mask_postprocessed = binary_opening(mask_postprocessed, disk(20)) img_hsv_copy[mask_postprocessed, 0] = 0.5 # img_hsv_copy_uint8 = img_as_ubyte(img_hsv_copy) #is_hsv if (len(img_hsv_copy.shape) == 3 and img_hsv_copy.shape[2] == 3): output = hsv2rgb(img_hsv_copy) else: output = img_hsv_copy output_uint8 = img_as_ubyte(output) imsave('' + outputPath, output_uint8) # imsave('/home/marekk/workspace/tmp/1234.png', output_uint8) output_uint8
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 preview: return cracked_blob else: self.updateUsingMask(self.bbox, cracked_blob) return cracked_blob
def process(pts=(10, 10)): filled = np.ones((scr.shape[1], scr.shape[2])) nscr = scr.copy() # scr[line[np.newaxis,:,:].repeat(4,axis=0)<0.75]=-1 for i in range(4): filled_img = flood(scr[i, :, :], pts, tolerance=0.15) filled[~filled_img] = 0 plt.close(2) figi, axi = plt.subplots(figsize=(10, 8)) rnd = np.random.randn(4) * 0.5 for t in range(4): nscr[t][filled[:] == 1] = rnd[t] out = get_recons(nscr) # nimg = img.copy() nimg[filled[:] == 1] = out[filled[:] == 1] nimg[:] = nimg[:] * line[:] io.imsave('saved.png', nimg) axi.imshow(nimg, cmap=plt.cm.gray) axi.set_title('Edited') axi.axis('off') figi.show()
edges = binary_dilation(dog > thr, disk(3)) #rec = dilation(img0, disk(3)) rec = img0.copy() print("Erasing objects...") edge_labels, num_labels = ndi.label(edges) for lbl in tqdm(range(num_labels)): edge_mask = edge_labels == lbl + 1 edge_max = rec[edge_mask].max() edge_min = rec[edge_mask].min() lower = (rec <= edge_min) | edge_mask seed_point = tuple(np.transpose(np.nonzero(edge_mask))[0]) flooded = flood(img_as_ubyte(lower), seed_point) rec[flooded] = edge_max objs = rec > img0 objs = binary_dilation(objs, disk(3)) fig, axes = plt.subplots(nrows=2, ncols=3, sharex=True, sharey=True) axes[0, 0].imshow(img, cmap="gray") axes[0, 1].imshow(dog) axes[0, 2].imshow(dog > thr) rec_ax = axes[1, 0].imshow(rec, cmap="gray") obj_ax = axes[1, 1].imshow(objs, cmap="gray") # axes[1, 2].imshow(, cmap="gray")
############################################################################## # Flood as mask # ------------- # # A sister function, `flood`, is available which returns a mask identifying # the flood rather than modifying the image itself. This is useful for # segmentation purposes and more advanced analysis pipelines. # # Here we segment the nose of a cat. However, multi-channel images are not # supported by flood[_fill]. Instead we Sobel filter the red channel to # enhance edges, then flood the nose with a tolerance. cat = data.chelsea() cat_sobel = filters.sobel(cat[..., 0]) cat_nose = flood(cat_sobel, (240, 265), tolerance=0.03) fig, ax = plt.subplots(nrows=3, figsize=(10, 20)) ax[0].imshow(cat) ax[0].set_title('Original') ax[0].axis('off') ax[1].imshow(cat_sobel) ax[1].set_title('Sobel filtered') ax[1].axis('off') ax[2].imshow(cat) ax[2].imshow(cat_nose, cmap=plt.cm.gray, alpha=0.3) ax[2].plot(265, 240, 'wo') # seed point ax[2].set_title('Nose segmented with `flood`')
np.bincount(skinRegionYCrCb.flat)[1:]) + 1 # get a starting point points_x, points_y = np.where(skinRegionYCrCb == 1) selected_x = points_x[np.argmax(points_x)] selected_y = points_y[np.argmax(points_x)] # flooding image = image.astype('int32') cat_sobel_x = filters.sobel(image, axis=0).astype(np.float32) cat_sobel_y = filters.sobel(image, axis=1).astype(np.float32) cat_sobel = cat_sobel_x * cat_sobel_x + cat_sobel_y * cat_sobel_y cat_sobel *= 255.0 / np.max(cat_sobel) cat_sobel = (cat_sobel[:, :, 0] + cat_sobel[:, :, 1] + cat_sobel[:, :, 2]) / 3.0 cat_nose = flood(cat_sobel, (selected_x - 8, selected_y), tolerance=0.2) # postprocessing cat_nose = cat_nose.astype(np.uint8) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) cat_nose = cv2.erode(cat_nose, kernel, iterations=2) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (4, 4)) cat_nose = cv2.dilate(cat_nose, kernel, iterations=2) # cat_nose = cv2.GaussianBlur(cat_nose, (3, 3), 0) cat_nose = cat_nose.astype(np.int32) cat_nose = cat_nose * 255 # cv2.imwrite( osp.join(OUTPUT_PATH, '{}_{}_flood_mask.jpg'.format(item.split('.')[0],
def label_images(metadata,settings): """ Load satellite images and interactively label different classes (hard-coded) KV WRL 2019 Arguments: ----------- metadata: dict contains all the information about the satellite images that were downloaded settings: dict with the following keys 'cloud_thresh': float value between 0 and 1 indicating the maximum cloud fraction in the cropped image that is accepted 'cloud_mask_issue': boolean True if there is an issue with the cloud mask and sand pixels are erroneously being masked on the images 'labels': dict list of label names (key) and label numbers (value) for each class 'flood_fill': boolean True to use the flood_fill functionality when labelling sand pixels 'tolerance': float tolerance value for flood fill when labelling the sand pixels 'filepath_train': str directory in which to save the labelled data 'inputs': dict input parameters (sitename, filepath, polygon, dates, sat_list) Returns: ----------- Stores the labelled data in the specified directory """ filepath_train = settings['filepath_train'] # initialize figure fig,ax = plt.subplots(1,1,figsize=[17,10], tight_layout=True,sharex=True, sharey=True) mng = plt.get_current_fig_manager() mng.window.showMaximized() # loop through satellites for satname in metadata.keys(): filepath = SDS_tools.get_filepath(settings['inputs'],satname) filenames = metadata[satname]['filenames'] # loop through images for i in range(len(filenames)): # image filename fn = SDS_tools.get_filenames(filenames[i],filepath, satname) # read and preprocess image im_ms, georef, cloud_mask, im_extra, im_QA, im_nodata = SDS_preprocess.preprocess_single(fn, satname, settings['cloud_mask_issue']) # compute cloud_cover percentage (with no data pixels) cloud_cover_combined = np.divide(sum(sum(cloud_mask.astype(int))), (cloud_mask.shape[0]*cloud_mask.shape[1])) if cloud_cover_combined > 0.99: # if 99% of cloudy pixels in image skip continue # remove no data pixels from the cloud mask (for example L7 bands of no data should not be accounted for) cloud_mask_adv = np.logical_xor(cloud_mask, im_nodata) # compute updated cloud cover percentage (without no data pixels) cloud_cover = np.divide(sum(sum(cloud_mask_adv.astype(int))), (sum(sum((~im_nodata).astype(int))))) # skip image if cloud cover is above threshold if cloud_cover > settings['cloud_thresh'] or cloud_cover == 1: continue # get individual RGB image im_RGB = SDS_preprocess.rescale_image_intensity(im_ms[:,:,[2,1,0]], cloud_mask, 99.9) im_NDVI = SDS_tools.nd_index(im_ms[:,:,3], im_ms[:,:,2], cloud_mask) im_NDWI = SDS_tools.nd_index(im_ms[:,:,3], im_ms[:,:,1], cloud_mask) # initialise labels im_viz = im_RGB.copy() im_labels = np.zeros([im_RGB.shape[0],im_RGB.shape[1]]) # show RGB image ax.axis('off') ax.imshow(im_RGB) implot = ax.imshow(im_viz, alpha=0.6) filename = filenames[i][:filenames[i].find('.')][:-4] ax.set_title(filename) ############################################################## # select image to label ############################################################## # set a key event to accept/reject the detections (see https://stackoverflow.com/a/15033071) # this variable needs to be immuatable so we can access it after the keypress event key_event = {} def press(event): # store what key was pressed in the dictionary key_event['pressed'] = event.key # let the user press a key, right arrow to keep the image, left arrow to skip it # to break the loop the user can press 'escape' while True: btn_keep = ax.text(1.1, 0.9, 'keep ⇨', size=12, ha="right", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) btn_skip = ax.text(-0.1, 0.9, '⇦ skip', size=12, ha="left", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) btn_esc = ax.text(0.5, 0, '<esc> to quit', size=12, ha="center", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) fig.canvas.draw_idle() fig.canvas.mpl_connect('key_press_event', press) plt.waitforbuttonpress() # after button is pressed, remove the buttons btn_skip.remove() btn_keep.remove() btn_esc.remove() # keep/skip image according to the pressed key, 'escape' to break the loop if key_event.get('pressed') == 'right': skip_image = False break elif key_event.get('pressed') == 'left': skip_image = True break elif key_event.get('pressed') == 'escape': plt.close() raise StopIteration('User cancelled labelling images') else: plt.waitforbuttonpress() # if user decided to skip show the next image if skip_image: ax.clear() continue # otherwise label this image else: ############################################################## # digitize sandy pixels ############################################################## ax.set_title('Click on SAND pixels (flood fill activated, tolerance = %.2f)\nwhen finished press <Enter>'%settings['tolerance']) # create erase button, if you click there it delets the last selection btn_erase = ax.text(im_ms.shape[1], 0, 'Erase', size=20, ha='right', va='top', bbox=dict(boxstyle="square", ec='k',fc='w')) fig.canvas.draw_idle() color_sand = settings['colors']['sand'] sand_pixels = [] while 1: seed = ginput(n=1, timeout=0, show_clicks=True) # if empty break the loop and go to next label if len(seed) == 0: break else: # round to pixel location seed = np.round(seed[0]).astype(int) # if user clicks on erase, delete the last selection if seed[0] > 0.95*im_ms.shape[1] and seed[1] < 0.05*im_ms.shape[0]: if len(sand_pixels) > 0: im_labels[sand_pixels[-1]] = 0 for k in range(im_viz.shape[2]): im_viz[sand_pixels[-1],k] = im_RGB[sand_pixels[-1],k] implot.set_data(im_viz) fig.canvas.draw_idle() del sand_pixels[-1] # otherwise label the selected sand pixels else: # flood fill the NDVI and the NDWI fill_NDVI = flood(im_NDVI, (seed[1],seed[0]), tolerance=settings['tolerance']) fill_NDWI = flood(im_NDWI, (seed[1],seed[0]), tolerance=settings['tolerance']) # compute the intersection of the two masks fill_sand = np.logical_and(fill_NDVI, fill_NDWI) im_labels[fill_sand] = settings['labels']['sand'] sand_pixels.append(fill_sand) # show the labelled pixels for k in range(im_viz.shape[2]): im_viz[im_labels==settings['labels']['sand'],k] = color_sand[k] implot.set_data(im_viz) fig.canvas.draw_idle() ############################################################## # digitize white-water pixels ############################################################## color_ww = settings['colors']['white-water'] ax.set_title('Click on individual WHITE-WATER pixels (no flood fill)\nwhen finished press <Enter>') fig.canvas.draw_idle() ww_pixels = [] while 1: seed = ginput(n=1, timeout=0, show_clicks=True) # if empty break the loop and go to next label if len(seed) == 0: break else: # round to pixel location seed = np.round(seed[0]).astype(int) # if user clicks on erase, delete the last labelled pixels if seed[0] > 0.95*im_ms.shape[1] and seed[1] < 0.05*im_ms.shape[0]: if len(ww_pixels) > 0: im_labels[ww_pixels[-1][1],ww_pixels[-1][0]] = 0 for k in range(im_viz.shape[2]): im_viz[ww_pixels[-1][1],ww_pixels[-1][0],k] = im_RGB[ww_pixels[-1][1],ww_pixels[-1][0],k] implot.set_data(im_viz) fig.canvas.draw_idle() del ww_pixels[-1] else: im_labels[seed[1],seed[0]] = settings['labels']['white-water'] for k in range(im_viz.shape[2]): im_viz[seed[1],seed[0],k] = color_ww[k] implot.set_data(im_viz) fig.canvas.draw_idle() ww_pixels.append(seed) im_sand_ww = im_viz.copy() btn_erase.set(text='<Esc> to Erase', fontsize=12) ############################################################## # digitize water pixels (with lassos) ############################################################## color_water = settings['colors']['water'] ax.set_title('Click and hold to draw lassos and select WATER pixels\nwhen finished press <Enter>') fig.canvas.draw_idle() selector_water = SelectFromImage(ax, implot, color_water) key_event = {} while True: fig.canvas.draw_idle() fig.canvas.mpl_connect('key_press_event', press) plt.waitforbuttonpress() if key_event.get('pressed') == 'enter': selector_water.disconnect() break elif key_event.get('pressed') == 'escape': selector_water.array = im_sand_ww implot.set_data(selector_water.array) fig.canvas.draw_idle() selector_water.implot = implot selector_water.im_bool = np.zeros((selector_water.array.shape[0], selector_water.array.shape[1])) selector_water.ind=[] # update im_viz and im_labels im_viz = selector_water.array selector_water.im_bool = selector_water.im_bool.astype(bool) im_labels[selector_water.im_bool] = settings['labels']['water'] im_sand_ww_water = im_viz.copy() ############################################################## # digitize land pixels (with lassos) ############################################################## color_land = settings['colors']['other land features'] ax.set_title('Click and hold to draw lassos and select OTHER LAND pixels\nwhen finished press <Enter>') fig.canvas.draw_idle() selector_land = SelectFromImage(ax, implot, color_land) key_event = {} while True: fig.canvas.draw_idle() fig.canvas.mpl_connect('key_press_event', press) plt.waitforbuttonpress() if key_event.get('pressed') == 'enter': selector_land.disconnect() break elif key_event.get('pressed') == 'escape': selector_land.array = im_sand_ww_water implot.set_data(selector_land.array) fig.canvas.draw_idle() selector_land.implot = implot selector_land.im_bool = np.zeros((selector_land.array.shape[0], selector_land.array.shape[1])) selector_land.ind=[] # update im_viz and im_labels im_viz = selector_land.array selector_land.im_bool = selector_land.im_bool.astype(bool) im_labels[selector_land.im_bool] = settings['labels']['other land features'] # save labelled image ax.set_title(filename) fig.canvas.draw_idle() fp = os.path.join(filepath_train,settings['inputs']['sitename']) if not os.path.exists(fp): os.makedirs(fp) fig.savefig(os.path.join(fp,filename+'.jpg'), dpi=150) ax.clear() # save labels and features features = dict([]) for key in settings['labels'].keys(): im_bool = im_labels == settings['labels'][key] features[key] = SDS_shoreline.calculate_features(im_ms, cloud_mask, im_bool) training_data = {'labels':im_labels, 'features':features, 'label_ids':settings['labels']} with open(os.path.join(fp, filename + '.pkl'), 'wb') as f: pickle.dump(training_data,f) # close figure when finished plt.close(fig)
def hystLow(img, img_gauss, sd=0, mean=0, diff=40, init_low=0.05, gen_high=0.8, mode='memb'): """ Lower treshold calculations for hysteresis membrane detection function hystMemb. diff - int, difference (in px number) between hysteresis mask and img without greater values delta_diff - int, tolerance level (in px number) for diff value gen_high, sd, mean - see hystMemb mode - 'cell': only sd treshold calc, 'memb': both tresholds calc """ if mode == 'memb': masks = { '2sd': ma.masked_greater_equal(img, 2 * sd), # values greater then 2 noise sd 'mean': ma.masked_greater(img, mean) } # values greater then mean cytoplasm intensity elif mode == 'cell': masks = {'2sd': ma.masked_greater_equal(img, 2 * sd)} logging.info('masks: {}'.format(masks.keys())) low_val = {} control_diff = False for mask_name in masks: mask_img = masks[mask_name] logging.info( 'Mask {} lower treshold fitting in progress'.format(mask_name)) mask_hyst = filters.apply_hysteresis_threshold( img_gauss, low=init_low * np.max(img_gauss), high=gen_high * np.max(img_gauss)) diff_mask = np.sum(ma.masked_where(~mask_hyst, mask_img) > 0) if diff_mask < diff: raise ValueError('Initial lower threshold is too low!') logging.info('Initial masks difference {}'.format(diff_mask)) low = init_low i = 0 control_diff = 1 while diff_mask >= diff: mask_hyst = filters.apply_hysteresis_threshold( img_gauss, low=low * np.max(img_gauss), high=gen_high * np.max(img_gauss)) diff_mask = np.sum(ma.masked_where(~mask_hyst, mask_img) > 0) low += 0.01 i += 1 # is cytoplasm mean mask at initial lower threshold value closed? prevent infinit cycle if i == 75: logging.fatal( 'Lower treshold for {} mask {:.2f}, control difference {}px' .format(mask_name, low, control_diff)) raise RuntimeError( 'Membrane in mean mask doesn`t detected at initial lower threshold value!' ) # is cytoplasm mask at setted up difference value closed? if mask_name == 'mean': control_diff = np.all((segmentation.flood(mask_hyst, (0, 0)) + mask_hyst)) if control_diff == True: logging.fatal( 'Lower treshold for {} mask {:.2f}, masks difference {}px'. format(mask_name, low, diff_mask)) raise ValueError( 'Membrane in {} mask doesn`t closed, mebrane unlocated at this diff value (too low)!' .format(mask_name)) low_val.update({mask_name: low}) logging.info('Lower tresholds {}\n'.format(low_val)) return low_val