def segment_glomeruli2d(input_file, tissue_mask_file, output_file, voxel_xy): kmask = io.imread(tissue_mask_file) if kmask.max() == 0: tifffile.imsave(output_file, kmask, compress=5) return # normalize image img = io.imread(input_file) img = ndimage.median_filter(img, 3) img = img * 255. / img.max() # remove all intensity variations larger than maximum radius of a glomerulus d = mahotas.disk(int(float(glomeruli_maxrad) / voxel_xy)) img = img - mahotas.open(img.astype(np.uint8), d) img = img * 255. / img.max() ch = img[np.where(kmask > 0)] # segment glomeruli by otsu thresholding only if this threshold is higher than the 75-th percentile in the kidney mask t = mahotas.otsu(img.astype(np.uint8)) cells = None if t > np.percentile(ch, 75) * 1.5: cells = img > t cells[np.where(kmask == 0)] = 0 cells = mahotas.open( cells, mahotas.disk(int(float(glomeruli_minrad) / 2. / voxel_xy))) else: cells = np.zeros_like(img) tifffile.imsave(output_file, img_as_ubyte(cells), compress=5)
def generate_correct(image, prob, segmentation): hist = Util.get_histogram(segmentation.astype(np.uint64)) labels = range(1, len(hist)) # do not include zeros np.random.shuffle(labels) for l in labels: binary_mask = Util.threshold(segmentation, l) borders = mh.labeled.borders(binary_mask, Bc=mh.disk(2)) labeled_borders = skimage.measure.label(borders) labeled_borders[borders == 0] = 0 relabeled_borders, no_relabeled_borders = mh.labeled.relabel( labeled_borders.astype(np.uint16)) for border in range(1, no_relabeled_borders + 1): isolated_border = Util.threshold(relabeled_borders, border) patches = Patch.analyze_border(image, prob, binary_mask, binary_mask.invert(), isolated_border) for s in patches: yield s yield Patch.fliplr(s) yield Patch.flipud(s) yield Patch.rotate(s, 90) yield Patch.rotate(s, 180) yield Patch.rotate(s, 270)
def extract(self): '''Extracts neighbour features. Returns ------- pandas.DataFrame extracted feature values for each object in `label_image` ''' # Create an empty dataset in case no objects were detected logger.info('extract neighbour features') features = list() for obj in self.object_ids: pad = max(self.neighbour_distance, self.touching_distance) object_image = self._get_bbox_containing_neighbours(obj, pad) # dilate the current object object_image_dilate = mh.dilate(object_image == obj, Bc=mh.disk( self.neighbour_distance)) # mask the corresponding region of the label image object_image_mask = np.copy(object_image) object_image_mask[object_image_dilate == 0] = 0 object_image_mask[object_image == obj] = 0 neighbour_ids = np.unique(object_image_mask) unique_values = neighbour_ids[np.nonzero(neighbour_ids)].tolist() neighbour_count = len(unique_values) # save these unique values as a string if neighbour_count == 0: neighbour_string = '.' else: neighbour_string = '.'.join(str(x) for x in unique_values) # create an inverted image of the surrounding cells neighbours = np.zeros_like(object_image) for n in unique_values: neighbours += mh.dilate(object_image == n) # calculate the distance from each pixel of object to neighbours dist = ndi.morphology.distance_transform_edt( np.invert(neighbours > 0)) # select perimeter pixels whose distance to neighbours is # less than threshold touching distance perimeter_image = mh.bwperim(object_image == obj) dist[perimeter_image == 0] = 0 dist[dist > self.touching_distance] = 0 fraction_touching = np.count_nonzero(dist) / float( np.count_nonzero(perimeter_image)) values = [neighbour_count, neighbour_string, fraction_touching] features.append(values) return pd.DataFrame(features, columns=self.names, index=self.object_ids)
def segment_layer(filename, params): ''' Segment one layer in a stack ''' #extract pixel size in xy and z xsize, zsize = extract_zoom(params.folder) #load image img = tifffile.imread(params.inputfolder + params.folder + filename) #normalize image img = ndimage.median_filter(img, 3) per_low = np.percentile(img, 5) img[img < per_low] = per_low img = img - img.min() per_high = np.percentile(img, 99) img[img > per_high] = per_high img = img*255./img.max() imgf = ndimage.gaussian_filter(img*1., 30./xsize).astype(np.uint8) kmask = (imgf > mahotas.otsu(imgf.astype(np.uint8)))*255. sizefactor = 10 small = ndimage.interpolation.zoom(kmask, 1./sizefactor) #scale the image to a smaller size rad = int(300./xsize) small_ext = np.zeros([small.shape[0] + 4*rad, small.shape[1] + 4*rad]) small_ext[2*rad : 2*rad + small.shape[0], 2*rad : 2*rad + small.shape[1]] = small small_ext = mahotas.close(small_ext.astype(np.uint8), mahotas.disk(rad)) small = small_ext[2*rad : 2*rad + small.shape[0], 2*rad : 2*rad + small.shape[1]] small = mahotas.close_holes(small)*1. small = small*255./small.max() kmask = ndimage.interpolation.zoom(small, sizefactor) #scale back to normal size kmask = normalize(kmask) kmask = (kmask > mahotas.otsu(kmask.astype(np.uint8)))*255. #remove artifacts of interpolation if np.median(imgf[np.where(kmask > 0)]) < (np.median(imgf[np.where(kmask == 0)]) + 1)*3: kmask = np.zeros_like(kmask) #save indices of the kidney mask # ind = np.where(kmask > 0) # ind = np.array(ind) # np.save(params.inputfolder + '../segmented/masks/' + params.folder + filename[:-4] + '.npy', ind) #save outlines im = np.zeros([img.shape[0], img.shape[1], 3]) img = tifffile.imread(params.inputfolder + params.folder + filename) im[:,:,0] = im[:,:,1] = im[:,:,2] = np.array(img) output = overlay(kmask, im, (255,0,0), borders = True) tifffile.imsave(params.inputfolder + '../segmented/outlines/' + params.folder + filename[:-4] + '.tif', (output).astype(np.uint8))
def test_eccentricity(): D = mh.disk(32, 2) ecc = mahotas.features.shape.eccentricity(D) assert 0 <= ecc < .01 Index = np.indices((33,33)).astype(float) Index -= 15 X,Y = Index ellipse = ((X**2+2*Y**2) < 12**2) assert 0 < mahotas.features.shape.eccentricity(ellipse) < 1
def test_eccentricity(): D = mh.disk(32, 2) ecc = mahotas.features.shape.eccentricity(D) assert 0 <= ecc < .01 Index = np.indices((33, 33)).astype(float) Index -= 15 X, Y = Index ellipse = ((X**2 + 2 * Y**2) < 12**2) assert 0 < mahotas.features.shape.eccentricity(ellipse) < 1
def segment_tissue2d(input_file, output_file, voxel_xy): # load image img = io.imread(input_file) # normalize image img = ndimage.median_filter(img, 3) img = img * 255. / img.max() ##segment kidney tissue sizefactor = 10. small = ndimage.interpolation.zoom( img, 1. / sizefactor) # scale the image to a smaller size imgf = ndimage.gaussian_filter(small, 3. / voxel_xy) # Gaussian filter median = np.percentile(imgf, 40) # 40-th percentile for thresholding kmask = imgf > median * 1.5 # thresholding kmask = mahotas.dilate(kmask, mahotas.disk(5)) kmask = mahotas.close_holes(kmask) # closing holes kmask = mahotas.erode(kmask, mahotas.disk(5)) * 255 # remove objects that are darker than 2*percentile l, n = ndimage.label(kmask) llist = np.unique(l) if len(llist) > 2: means = ndimage.mean(imgf, l, llist) bv = llist[np.where(means < median * 2)] ix = np.in1d(l.ravel(), bv).reshape(l.shape) kmask[ix] = 0 kmask = ndimage.interpolation.zoom(kmask, sizefactor) # scale back to normal size kmask = normalize(kmask) kmask = (kmask > mahotas.otsu(kmask.astype(np.uint8)) ) # remove artifacts of interpolation tifffile.imsave(output_file, img_as_ubyte(kmask), compress=5)
def random_watershed(array, speed_image, border_seeds=False, erode=False): ''' ''' copy_array = np.array(array, dtype=np.bool) if erode: for i in range(10): copy_array = mh.erode(copy_array) seed_array = np.array(copy_array) if border_seeds: seed_array = mh.labeled.border(copy_array, 1, 0, Bc=mh.disk(7)) coords = zip(*np.where(seed_array==1)) if len(coords) == 0: # print 'err' return np.zeros(array.shape) seed1_ = None seed2_ = None max_distance = -np.inf for i in range(10): seed1 = random.choice(coords) seed2 = random.choice(coords) d = distance.euclidean(seed1, seed2) if max_distance < d: max_distance = d seed1_ = seed1 seed2_ = seed2 seeds = np.zeros(array.shape, dtype=np.uint8) seeds[seed1_[0], seed1_[1]] = 1 seeds[seed2_[0], seed2_[1]] = 2 for i in range(8): seeds = mh.dilate(seeds) # Util.view(seeds,large=True) # print speed_image.shape, seeds.shape ws = mh.cwatershed(speed_image, seeds) ws[array == 0] = 0 return ws
def mahotas_clean_up_seg(input_jacobian, frame_num): import mahotas as mh dsk = mh.disk(7) thresh_r = 0.1 thresh_g = 1 size_cutoff = 200 thresholded_jacobian = (np.int32(np.log(1+input_jacobian[frame_num][0])>thresh_r)+\ np.int32(np.log(1+input_jacobian[frame_num][1])>thresh_g))>0 thresholded_jacobian = mh.close_holes(thresholded_jacobian) thresholded_jacobian = mh.erode(thresholded_jacobian, dsk) labeled = mh.label(thresholded_jacobian)[0] sizes = mh.labeled.labeled_size(labeled) too_small = np.where(sizes < size_cutoff) labeled = mh.labeled.remove_regions(labeled, too_small) thresholded_jacobian = labeled > 0 thresholded_jacobian = mh.dilate(thresholded_jacobian, dsk) return thresholded_jacobian
def show_segmentation_boundaries(input_image,seg_mask,frame_num,sizeX,sizeY): import mahotas as mh cmap = plt.get_cmap('gray') #outlines dsk = mh.disk(7) boundaries = mh.dilate(seg_mask,dsk)-seg_mask boundaries=boundaries>0 cmap_2_use = truncate_colormap(cmap,0.9,1) border2overlay = np.ma.masked_where(boundaries ==0, boundaries) x =mh.as_rgb(input_image[frame_num][0,:,:],input_image[frame_num][1,:,:],np.zeros((sizeY,sizeX))) x[:,:,1][x[:,:,1]>240]=240 x[:,:,0][x[:,:,0]>240]=240 slice2show = [slice(0,sizeY),slice(0,sizeX)] plt.imshow(x[slice2show]/np.array([240.,240.,1]).reshape(1,1,3)) plt.imshow(border2overlay[slice2show],cmap_2_use,alpha=0.8),plt.axis('off')
def mahotas_clean_up_seg(input_jacobian,frame_num): import mahotas as mh dsk = mh.disk(7) thresh_r = 0.1 thresh_g = 1 size_cutoff = 200 thresholded_jacobian = (np.int32(np.log(1+input_jacobian[frame_num][0])>thresh_r)+\ np.int32(np.log(1+input_jacobian[frame_num][1])>thresh_g))>0 thresholded_jacobian = mh.close_holes(thresholded_jacobian) thresholded_jacobian = mh.erode(thresholded_jacobian,dsk) labeled = mh.label(thresholded_jacobian)[0] sizes = mh.labeled.labeled_size(labeled) too_small = np.where(sizes < size_cutoff) labeled = mh.labeled.remove_regions(labeled, too_small) thresholded_jacobian = labeled>0 thresholded_jacobian = mh.dilate(thresholded_jacobian,dsk) return thresholded_jacobian
def generate_correct(image, prob, label): ''' ''' labels = np.arange(len(Util.get_histogram(label)))[1:] # no zeros np.random.shuffle(labels) for l in labels: # neighbors = PG.grab_neighbors(label, l) # for n in neighbors: # patches = PG.analyze_border(image, prob, label, l, n, sample_rate=10) # for s in patches: # yield s # yield PG.fliplr(s) # yield PG.flipud(s) # yield PG.rotate(s, 90) # yield PG.rotate(s, 180) # yield PG.rotate(s, 270) binary_mask = Util.threshold(label, l) borders = mh.labeled.borders(binary_mask,Bc=mh.disk(2)) labeled_borders = skimage.measure.label(borders) labeled_borders[borders==0] = 0 relabeled_borders, no_relabeled_borders = mh.labeled.relabel(labeled_borders.astype(np.uint16)) for border in range(1,no_relabeled_borders+1): isolated_border = Util.threshold(relabeled_borders, border) patches = PGNew.analyze_border(image, prob, binary_mask, isolated_border) for s in patches: yield s yield PGNew.fliplr(s) yield PGNew.flipud(s) yield PGNew.rotate(s, 90) yield PGNew.rotate(s, 180) yield PGNew.rotate(s, 270)
def show_segmentation_boundaries(input_image, seg_mask, frame_num, sizeX, sizeY): import mahotas as mh cmap = plt.get_cmap('gray') #outlines dsk = mh.disk(7) boundaries = mh.dilate(seg_mask, dsk) - seg_mask boundaries = boundaries > 0 cmap_2_use = truncate_colormap(cmap, 0.9, 1) border2overlay = np.ma.masked_where(boundaries == 0, boundaries) x = mh.as_rgb(input_image[frame_num][0, :, :], input_image[frame_num][1, :, :], np.zeros((sizeY, sizeX))) x[:, :, 1][x[:, :, 1] > 240] = 240 x[:, :, 0][x[:, :, 0] > 240] = 240 slice2show = [slice(0, sizeY), slice(0, sizeX)] plt.imshow(x[slice2show] / np.array([240., 240., 1]).reshape(1, 1, 3)) plt.imshow(border2overlay[slice2show], cmap_2_use, alpha=0.8), plt.axis('off')
def fix_single_merge(cnn, cropped_image, cropped_prob, cropped_binary, N=10, invert=True, dilate=True, border_seeds=True, erode=False, debug=False, before_merge_error=None, real_border=np.zeros((1,1)), oversampling=False, crop=True): ''' invert: True/False for invert or gradient image ''' bbox = mh.bbox(cropped_binary) orig_cropped_image = np.array(cropped_image) orig_cropped_prob = np.array(cropped_prob) orig_cropped_binary = np.array(cropped_binary) speed_image = None if invert: speed_image = Legacy.invert(cropped_image, smooth=True, sigma=2.5) else: speed_image = Legacy.gradient(cropped_image) Util.view(speed_image, large=False, color=False) dilated_binary = np.array(cropped_binary, dtype=np.bool) if dilate: for i in range(20): dilated_binary = mh.dilate(dilated_binary) Util.view(dilated_binary, large=False, color=False) borders = np.zeros(cropped_binary.shape) best_border_prediction = np.inf best_border_image = np.zeros(cropped_binary.shape) original_border = mh.labeled.border(cropped_binary, 1, 0, Bc=mh.disk(3)) results_no_border = [] predictions = [] borders = [] results = [] for n in range(N): ws = Legacy.random_watershed(dilated_binary, speed_image, border_seeds=border_seeds, erode=erode) if ws.max() == 0: continue ws_label1 = ws.max() ws_label2 = ws.max()-1 border = mh.labeled.border(ws, ws_label1, ws_label2) # Util.view(ws, large=True) # Util.view(border, large=True) # print i, len(border[border==True]) # # remove parts of the border which overlap with the original border # ws[cropped_binary == 0] = 0 # Util.view(ws, large=False, color=False) ws_label1_array = Util.threshold(ws, ws_label1) ws_label2_array = Util.threshold(ws, ws_label2) eroded_ws1 = np.array(ws_label1_array, dtype=np.bool) eroded_ws2 = np.array(ws_label2_array, dtype=np.bool) if erode: for i in range(5): eroded_ws1 = mh.erode(eroded_ws1) # Util.view(eroded_ws, large=True, color=False) dilated_ws1 = np.array(eroded_ws1) for i in range(5): dilated_ws1 = mh.dilate(dilated_ws1) for i in range(5): eroded_ws2 = mh.erode(eroded_ws2) # Util.view(eroded_ws, large=True, color=False) dilated_ws2 = np.array(eroded_ws2) for i in range(5): dilated_ws2 = mh.dilate(dilated_ws2) new_ws = np.zeros(ws.shape, dtype=np.uint8) new_ws[dilated_ws1 == 1] = ws_label1 new_ws[dilated_ws2 == 1] = ws_label2 ws = new_ws # Util.view(new_ws, large=True, color=True) # ws[original_border == 1] = 0 prediction = Patch.grab_group_test_and_unify(cnn, cropped_image, cropped_prob, ws, ws_label1, ws_label2, oversampling=oversampling) if prediction == -1 or prediction >= .5: # invalid continue # here we have for one border # the border # the prediction # borders.append(border) # predictions.append(prediction) results.append((prediction, border)) return results
def test_perimeter(): for r in (40, 80, 160): disk = mh.disk(r, 2) p = mahotas.labeled.perimeter(disk) p_exact = r * np.pi * 2 assert .9 < (p / p_exact) < 1.1
def test_perimeter(): for r in (40,80,160): disk = mh.disk(r, 2) p = mahotas.labeled.perimeter(disk) p_exact = r*np.pi*2 assert .9 < (p/p_exact) < 1.1
def segment_layer(filename, params): ''' Segment one layer in a stack ''' start = time.time() #extract pixel size in xy and z xsize, zsize = extract_zoom(params.folder) #load image img = tifffile.imread(params.inputfolder + params.folder + filename) #normalize image img = ndimage.median_filter(img, 3) img = img * 255. / img.max() ##segment kidney tissue sizefactor = 10. small = ndimage.interpolation.zoom( img, 1. / sizefactor) #scale the image to a smaller size imgf = ndimage.gaussian_filter(small, 3. / xsize) #Gaussian filter median = np.percentile(imgf, 40) #40-th percentile for thresholding kmask = imgf > median * 1.5 #thresholding kmask = mahotas.dilate(kmask, mahotas.disk(5)) kmask = mahotas.close_holes(kmask) #closing holes kmask = mahotas.erode(kmask, mahotas.disk(5)) * 255 #remove objects that are darker than 2*percentile l, n = ndimage.label(kmask) llist = np.unique(l) if len(llist) > 2: means = ndimage.mean(imgf, l, llist) bv = llist[np.where(means < median * 2)] ix = np.in1d(l.ravel(), bv).reshape(l.shape) kmask[ix] = 0 kmask = ndimage.interpolation.zoom(kmask, sizefactor) #scale back to normal size kmask = normalize(kmask) kmask = (kmask > mahotas.otsu(kmask.astype( np.uint8))) * 255. #remove artifacts of interpolation #save indices of the kidney mask ind = np.where(kmask > 0) ind = np.array(ind) np.save( params.inputfolder + '../segmented/masks/kidney/' + params.folder + filename[:-4] + '.npy', ind) #segment glomeruli, if there is a kidney tissue if kmask.max() > 0: #remove all intensity variations larger than maximum radius of a glomerulus d = mahotas.disk(int(float(params.maxrad) / xsize)) img = img - mahotas.open(img.astype(np.uint8), d) img = img * 255. / img.max() ch = img[np.where(kmask > 0)] #segment glomeruli by otsu thresholding only if this threshold is higher than the 75-th percentile in the kidney mask t = mahotas.otsu(img.astype(np.uint8)) if t > np.percentile(ch, 75) * 1.5: cells = img > t cells[np.where(kmask == 0)] = 0 cells = mahotas.open( cells, mahotas.disk(int(float(params.minrad) / 2. / xsize))) else: cells = np.zeros_like(img) else: cells = np.zeros_like(img) #save indices of the glomeruli mask ind = np.where(cells > 0) ind = np.array(ind) np.save( params.inputfolder + '../segmented/masks/glomeruli/' + params.folder + filename[:-4] + '.npy', ind)
def fix_single_merge(cnn, cropped_image, cropped_prob, cropped_binary, N=10, invert=True, dilate=True, border_seeds=True, erode=False, debug=False, before_merge_error=None, real_border=np.zeros((1, 1)), oversampling=False, crop=True): ''' invert: True/False for invert or gradient image ''' bbox = mh.bbox(cropped_binary) orig_cropped_image = np.array(cropped_image) orig_cropped_prob = np.array(cropped_prob) orig_cropped_binary = np.array(cropped_binary) speed_image = None if invert: speed_image = Util.invert(cropped_image, smooth=True, sigma=2.5) else: speed_image = Util.gradient(cropped_image) dilated_binary = np.array(cropped_binary, dtype=np.bool) if dilate: for i in range(20): dilated_binary = mh.dilate(dilated_binary) # Util.view(dilated_binary, large=True) borders = np.zeros(cropped_binary.shape) best_border_prediction = np.inf best_border_image = np.zeros(cropped_binary.shape) original_border = mh.labeled.border(cropped_binary, 1, 0, Bc=mh.disk(3)) results_no_border = [] predictions = [] for n in range(N): ws = Util.random_watershed(dilated_binary, speed_image, border_seeds=border_seeds, erode=erode) if ws.max() == 0: continue ws_label1 = ws.max() ws_label2 = ws.max() - 1 border = mh.labeled.border(ws, ws_label1, ws_label2) # Util.view(ws, large=True) # Util.view(border, large=True) # print i, len(border[border==True]) # # remove parts of the border which overlap with the original border # ws[cropped_binary == 0] = 0 # Util.view(ws, large=False, color=False) ws_label1_array = Util.threshold(ws, ws_label1) ws_label2_array = Util.threshold(ws, ws_label2) eroded_ws1 = np.array(ws_label1_array, dtype=np.bool) eroded_ws2 = np.array(ws_label2_array, dtype=np.bool) if erode: for i in range(5): eroded_ws1 = mh.erode(eroded_ws1) # Util.view(eroded_ws, large=True, color=False) dilated_ws1 = np.array(eroded_ws1) for i in range(5): dilated_ws1 = mh.dilate(dilated_ws1) for i in range(5): eroded_ws2 = mh.erode(eroded_ws2) # Util.view(eroded_ws, large=True, color=False) dilated_ws2 = np.array(eroded_ws2) for i in range(5): dilated_ws2 = mh.dilate(dilated_ws2) new_ws = np.zeros(ws.shape, dtype=np.uint8) new_ws[dilated_ws1 == 1] = ws_label1 new_ws[dilated_ws2 == 1] = ws_label2 ws = new_ws # Util.view(new_ws, large=True, color=True) # ws[original_border == 1] = 0 prediction = Patch.grab_group_test_and_unify( cnn, cropped_image, cropped_prob, ws, ws_label1, ws_label2, oversampling=oversampling) if prediction == -1: # invalid continue # if (prediction < best_border_prediction): # best_border_prediction = prediction # best_border_image = border # print 'new best', n, prediction best_border_image = border borders += (border * prediction) result = np.array(cropped_binary) best_border_image[result == 0] = 0 result[best_border_image == 1] = 2 result = skimage.measure.label(result) result_no_border = np.array(result) result_no_border[best_border_image == 1] = 0 predictions.append(prediction) results_no_border.append(result_no_border) # result = np.array(cropped_binary) # best_border_image[result==0] = 0 # result[best_border_image==1] = 2 # result = skimage.measure.label(result) # result_no_border = np.array(result) # result_no_border[best_border_image==1] = 0 # result_no_border = mh.croptobbox(result_no_border) # if before_merge_error == None: # continue # print result_no_border.shape, before_merge_error.shape # if before_merge_error.shape[0] != result_no_border.shape[0] or before_merge_error.shape[1] != result_no_border.shape[1]: # result_no_border = np.resize(result_no_border, before_merge_error.shape) # print 'vi', Util.vi(before_merge_error.astype(np.uint8), result_no_border.astype(np.uint8)) # if debug: # Util.view(ws, text=str(i) + ' ' + str(prediction)) result = np.array(cropped_binary) best_border_image[result == 0] = 0 result[best_border_image == 1] = 2 result = skimage.measure.label(result) result_no_border = np.array(result) result_no_border[best_border_image == 1] = 0 return borders, best_border_image, result, result_no_border, results_no_border, predictions
def main(image, mask, threshold=150, bead_size=2, superpixel_size=4, close_surface=False, close_disc_size=8, plot=False): '''Converts an image stack with labelled cell surface to a cell `volume` image Parameters ---------- image: numpy.ndarray[Union[numpy.uint8, numpy.uint16]] grayscale image in which beads should be detected (3D) mask: numpy.ndarray[Union[numpy.int32, numpy.bool]] binary or labeled image of cell segmentation (2D) threshold: int, optional intensity of bead (default: ``150``) bead_size: int, optional minimal size of bead (default: ``2``) superpixel_size: int, optional size of superpixels for searching the 3D position of a bead close_surface: bool, optional whether the interpolated surface should be morphologically closed close_disc_size: int, optional size in pixels of the disc used to morphologically close the interpolated surface plot: bool, optional whether a plot should be generated (default: ``False``) Returns ------- jtmodules.generate_volume_image.Output ''' n_slices = image.shape[-1] logger.debug('input image has size %d in last dimension', n_slices) logger.debug('mask beads inside cell') beads_outside_cell = np.copy(image) for iz in range(n_slices): beads_outside_cell[mask > 0, iz] = 0 logger.debug('search for 3D position of beads outside cell') slide = np.argmax(beads_outside_cell, axis=2) slide[slide > np.percentile(slide[mask == 0], 20)] = 0 logger.debug('determine surface of slide') slide_coordinates = array_to_coordinate_list(slide) bottom_surface = fit_plane( subsample_coordinate_list(slide_coordinates, 2000)) logger.debug('detect_beads in 2D') mip = np.max(image, axis=-1) try: # TODO: use LOG filter??? beads, beads_centroids = detect_blobs(image=mip, mask=np.invert(mask > 0), threshold=threshold, min_area=bead_size) except: logger.warn('detect_blobs failed, returning empty volume image') volume_image = np.zeros(shape=mask.shape, dtype=image.dtype) figure = str() return Output(volume_image, figure) n_beads = np.count_nonzero(beads_centroids) logger.info('found %d beads on cells', n_beads) if n_beads == 0: logger.warn('empty volume image') volume_image = np.zeros(shape=mask.shape, dtype=image.dtype) else: logger.debug('locate beads in 3D') beads_coords_3D = locate_in_3D(image=image, mask=beads_centroids, bin_size=superpixel_size) logger.info('interpolate cell surface') volume_image = interpolate_surface(coords=beads_coords_3D, output_shape=np.shape(image[:, :, 1]), method='linear') volume_image = volume_image.astype(image.dtype) if (close_surface is True): import mahotas as mh logger.info('morphological closing of cell surface') volume_image = mh.close(volume_image, Bc=mh.disk(close_disc_size)) volume_image[mask == 0] = 0 if plot: logger.debug('convert bottom surface plane to image for plotting') bottom_surface_image = np.zeros(slide.shape, dtype=np.uint8) for ix in range(slide.shape[0]): for iy in range(slide.shape[1]): bottom_surface_image[ix, iy] = plane(ix, iy, bottom_surface.x) logger.info('create plot') from jtlib import plotting plots = [ plotting.create_intensity_image_plot(mip, 'ul', clip=True), plotting.create_intensity_image_plot(bottom_surface_image, 'll', clip=True), plotting.create_intensity_image_plot(volume_image, 'ur', clip=True) ] figure = plotting.create_figure(plots, title='Convert stack to volume image') else: figure = str() return Output(volume_image, figure)
def split_new(image, binary): ''' ''' bbox = mh.bbox(binary) sub_image = np.array(image[bbox[0]:bbox[1], bbox[2]:bbox[3]]) sub_binary = np.array(binary[bbox[0]:bbox[1], bbox[2]:bbox[3]]) sub_binary_border = mh.labeled.borders(sub_binary, Bc=mh.disk(3)) sub_binary = mh.erode(sub_binary.astype(np.bool)) for e in range(5): sub_binary = mh.erode(sub_binary) # sub_binary = mh.erode(sub_binary) if sub_image.shape[0] < 2 or sub_image.shape[1] < 2: return np.zeros(binary.shape, dtype=np.bool), np.zeros(binary.shape, dtype=np.bool) # # smooth the image # sub_image = mh.gaussian_filter(sub_image, 3.5) grad_x = np.gradient(sub_image)[0] grad_y = np.gradient(sub_image)[1] grad = np.add(np.abs(grad_x), np.abs(grad_y)) grad -= grad.min() grad /= grad.max() grad *= 255 grad = grad.astype(np.uint8) coords = zip(*np.where(sub_binary==1)) if len(coords) < 2: print 'STRAAAAANGE' return np.zeros(binary.shape, dtype=np.bool), np.zeros(binary.shape, dtype=np.bool) seed1 = random.choice(coords) seed2 = random.choice(coords) seeds = np.zeros(sub_binary.shape, dtype=np.uint64) seeds[seed1] = 1 seeds[seed2] = 2 for i in range(10): seeds = mh.dilate(seeds) ws = mh.cwatershed(grad, seeds) ws[sub_binary==0] = 0 # ws_relabeled = skimage.measure.label(ws.astype(np.uint8)) # ws_relabeled[sub_binary==0] = 0 # max_label = ws_relabeled.max() # plt.figure() # imshow(ws) binary_mask = Util.threshold(ws, ws.max()) border = mh.labeled.border(ws, ws.max(), ws.max()-1, Bc=mh.disk(2)) # border[sub_binary_border == 1] = 0 # remove any "real" border pixels # plt.figure() # imshow(binary_mask) # plt.figure() # imshow(border) large_label = np.zeros(binary.shape, dtype=np.bool) large_border = np.zeros(binary.shape, dtype=np.bool) large_label[bbox[0]:bbox[1], bbox[2]:bbox[3]] = binary_mask large_border[bbox[0]:bbox[1], bbox[2]:bbox[3]] = border return large_label, large_border
def split_label(image, binary): bbox = mh.bbox(binary) sub_image = np.array(image[bbox[0]:bbox[1], bbox[2]:bbox[3]]) sub_binary = np.array(binary[bbox[0]:bbox[1], bbox[2]:bbox[3]]) sub_binary_border = mh.labeled.borders(sub_binary, Bc=mh.disk(3)) sub_binary = mh.erode(sub_binary.astype(np.bool)) for e in range(15): sub_binary = mh.erode(sub_binary) # # sub_binary = mh.erode(sub_binary) if sub_binary.shape[0] < 2 or sub_binary.shape[1] < 2: return np.zeros(binary.shape, dtype=np.bool), np.zeros(binary.shape, dtype=np.bool) # # smooth the image # sub_image = mh.gaussian_filter(sub_image, 3.5) grad_x = np.gradient(sub_image)[0] grad_y = np.gradient(sub_image)[1] grad = np.add(np.abs(grad_x), np.abs(grad_y)) grad -= grad.min() grad /= grad.max() grad *= 255 grad = grad.astype(np.uint8) coords = zip(*np.where(sub_binary == 1)) if len(coords) < 2: # print 'STRAAAAANGE' return np.zeros(binary.shape, dtype=np.bool), np.zeros(binary.shape, dtype=np.bool) seed1 = random.choice(coords) seed2 = random.choice(coords) seeds = np.zeros(sub_binary.shape, dtype=np.uint64) seeds[seed1] = 1 seeds[seed2] = 2 for i in range(10): seeds = mh.dilate(seeds) ws = mh.cwatershed(grad, seeds) ws[sub_binary == 0] = 0 # ws_relabeled = skimage.measure.label(ws.astype(np.uint8)) # ws_relabeled[sub_binary==0] = 0 # max_label = ws_relabeled.max() # plt.figure() # imshow(ws) binary_mask = Util.threshold(ws, ws.max()) border = mh.labeled.border(ws, ws.max(), ws.max() - 1, Bc=mh.disk(2)) border[sub_binary_border == 1] = 0 # remove any "real" border pixels # plt.figure() # imshow(binary_mask) # plt.figure() # imshow(border) # at this point, there can be multiple borders and labels labeled_border = skimage.measure.label(border) labeled_binary_mask = skimage.measure.label(binary_mask) # .. and we are going to select only the largest largest_border_label = Util.get_largest_label( labeled_border.astype(np.uint16), True) largest_binary_mask_label = Util.get_largest_label( labeled_binary_mask.astype(np.uint16), True) # .. filter out everything else border[labeled_border != largest_border_label] = 0 binary_mask[labeled_binary_mask != largest_binary_mask_label] = 0 large_label = np.zeros(binary.shape, dtype=np.bool) large_border = np.zeros(binary.shape, dtype=np.bool) large_label[bbox[0]:bbox[1], bbox[2]:bbox[3]] = binary_mask large_border[bbox[0]:bbox[1], bbox[2]:bbox[3]] = border return large_label, large_border
def main(image, mask, threshold=150, bead_size=2, superpixel_size=4, close_surface=False, close_disc_size=8, plot=False): '''Converts an image stack with labelled cell surface to a cell `volume` image Parameters ---------- image: numpy.ndarray[Union[numpy.uint8, numpy.uint16]] grayscale image in which beads should be detected (3D) mask: numpy.ndarray[Union[numpy.int32, numpy.bool]] binary or labeled image of cell segmentation (2D) threshold: int, optional intensity of bead (default: ``150``) bead_size: int, optional minimal size of bead (default: ``2``) superpixel_size: int, optional size of superpixels for searching the 3D position of a bead close_surface: bool, optional whether the interpolated surface should be morphologically closed close_disc_size: int, optional size in pixels of the disc used to morphologically close the interpolated surface plot: bool, optional whether a plot should be generated (default: ``False``) Returns ------- jtmodules.generate_volume_image.Output ''' n_slices = image.shape[-1] logger.debug('input image has size %d in last dimension', n_slices) logger.debug('mask beads inside cell') beads_outside_cell = np.copy(image) for iz in range(n_slices): beads_outside_cell[mask > 0, iz] = 0 logger.debug('search for 3D position of beads outside cell') slide = np.argmax(beads_outside_cell, axis=2) slide[slide > np.percentile(slide[mask == 0], 20)] = 0 logger.debug('determine surface of slide') slide_coordinates = array_to_coordinate_list(slide) bottom_surface = fit_plane(subsample_coordinate_list( slide_coordinates, 2000) ) logger.debug('detect_beads in 2D') mip = np.max(image, axis=-1) try: # TODO: use LOG filter??? beads, beads_centroids = detect_blobs( image=mip, mask=np.invert(mask > 0), threshold=threshold, min_area=bead_size ) except: logger.warn('detect_blobs failed, returning empty volume image') volume_image = np.zeros(shape=mask.shape, dtype=image.dtype) figure = str() return Output(volume_image, figure) n_beads = np.count_nonzero(beads_centroids) logger.info('found %d beads on cells', n_beads) if n_beads == 0: logger.warn('empty volume image') volume_image = np.zeros(shape=mask.shape, dtype=image.dtype) else: logger.debug('locate beads in 3D') beads_coords_3D = locate_in_3D( image=image, mask=beads_centroids, bin_size=superpixel_size ) logger.info('interpolate cell surface') volume_image = interpolate_surface( coords=beads_coords_3D, output_shape=np.shape(image[:, :, 1]), method='linear' ) volume_image = volume_image.astype(image.dtype) if (close_surface is True): import mahotas as mh logger.info('morphological closing of cell surface') volume_image = mh.close(volume_image, Bc=mh.disk(close_disc_size)) volume_image[mask == 0] = 0 if plot: logger.debug('convert bottom surface plane to image for plotting') bottom_surface_image = np.zeros(slide.shape, dtype=np.uint8) for ix in range(slide.shape[0]): for iy in range(slide.shape[1]): bottom_surface_image[ix, iy] = plane( ix, iy, bottom_surface.x) logger.info('create plot') from jtlib import plotting plots = [ plotting.create_intensity_image_plot( mip, 'ul', clip=True ), plotting.create_intensity_image_plot( bottom_surface_image, 'll', clip=True ), plotting.create_intensity_image_plot( volume_image, 'ur', clip=True ) ] figure = plotting.create_figure( plots, title='Convert stack to volume image' ) else: figure = str() return Output(volume_image, figure)