def optic_disk_detect_3(img): ''' Method that seems to work well with DIARETDB1 database. ''' hsi = rgb_to_hsi(img) intensity = hsi[:, :, 2].copy() #plt.axis('off'); show_image(intensity) #i_sp = add_salt_and_pepper(intensity, 0.005) i_med = mh.median_filter(intensity) # use Wiener filter instead? i_clahe = skimage.exposure.equalize_adapthist(i_med) #plt.axis('off'); show_image(i_clahe) seeds = (i_clahe > 0.85) seeds = skimage.morphology.remove_small_objects(seeds, min_size=300, connectivity=2) #plt.axis('off'); show_image(seeds) optic_disk_map = region_growing_1(i_clahe, radius=3, tol1=0.1, tol2=0.2, tol3=0.2, seeds=seeds) optic_disk_map += 1 #plt.axis('off'); show_image(optic_disk_map) _cl_areas = cl_areas(optic_disk_map) print _cl_areas optic_disk_map = leave_segments_by_mask(optic_disk_map, (8000 < _cl_areas) & (_cl_areas < 30000)) #optic_disk_map = skimage.morphology.remove_small_objects(optic_disk_map, min_size=500, connectivity=2) optic_disk_map = mh.close_holes(mh.close(optic_disk_map, Bc=np.ones((10, 10)))) #optic_disk_map = skimage.morphology.remove_small_objects(optic_disk_map, min_size=5000, connectivity=2) if np.all(optic_disk_map == 0): print 'Disk not found' return optic_disk_map
def predict(self, filenames_list, scale=0.4, n_clusters=4): if not isinstance(filenames_list, list): raise Exception('Input list of files is not a list actually') rectangles = [] for filename in filenames_list: img_orig = gaussian(rgb2lab(imread(filename))[:, :, 1], 3) h_orig, w_orig = img_orig.shape h_small, w_small = int(h_orig * scale), int(w_orig * scale) thumb = resize(img_orig, (h_small, w_small)) model = KMeans(n_clusters) segments_flatten = model.fit_predict(thumb.reshape(-1, 1)) segments = segments_flatten.reshape(h_small, w_small) seg_means = [ np.mean(thumb[segments == n]) for n in range(np.max(segments) + 1) ] brightest_seg = np.argmax(seg_means) mask = segments == brightest_seg thumb_closed = mahotas.close( mask, np.ones((int(30 * scale), int(30 * scale)))) label_objects, nb_labels = ndi.label(thumb_closed) sizes = np.bincount(label_objects.ravel()) mask_sizes = sizes == np.sort(sizes)[-2] img_cleaned = mask_sizes[label_objects] img_final = resize(img_cleaned, (h_orig, w_orig)).astype(int) rectangles.append(blob.bound_rect(img_final)) return rectangles
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 optic_disk_detect_2(img): hsi = rgb_to_hsi(img) intensity = hsi[:, :, 2].copy() i_sp = add_salt_and_pepper(intensity, 0.005) i_med = mh.median_filter(i_sp) i_clahe = skimage.exposure.equalize_adapthist(i_med) optic_disk_map = (i_clahe > 0.6) & (hsi[:, :, 1] < 0.3) #show_image(optic_disk_map) optic_disk_map = skimage.morphology.remove_small_objects(optic_disk_map, min_size=500, connectivity=2) optic_disk_map = mh.close_holes(mh.close(optic_disk_map, Bc=np.ones((30, 30)))) optic_disk_map = skimage.morphology.remove_small_objects(optic_disk_map, min_size=10000, connectivity=2) if np.all(optic_disk_map == 0): print 'Disk not found' return optic_disk_map
def predict(self, filenames_list, threshold=0.75): if not isinstance(filenames_list, list): raise Exception('Input list of files is not a list actually') rectangles = [] for filename in filenames_list: img = imread(filename) img_a = color.scaler(gaussian(rgb2lab(img)[:,:,1], 3)) h,w = img_a.shape mask = img_a > threshold img_closed = mahotas.close(mask, disk(h/15)) label_objects, nb_labels = ndi.label(img_closed) sizes = np.bincount(label_objects.ravel()) mask_sizes = sizes == np.sort(sizes)[-2] img_cleaned = mask_sizes[label_objects] rectangles.append(blob.bound_rect(img_cleaned)) return rectangles
if len(img.shape) == 3: #color image #convert to grayscale img = mh.colors.rgb2gray(img, dtype=np.uint8) # thresholding T_otsu = mh.otsu(img) # finds a numeric threshold img = (img > T_otsu) # make image binary # invert the image (just because the test image is stored the other way) img = ~img # close single-pixel holes. Also makes the skeletonization much more well-behaved, # with less tiny branches close to terminals. # # This can create loops if two branches are separated by < 3 px of background img = mh.close(img) print('Thinning...') # skeletonization from scikit image. # Zhang-Suen algorithm (apparently with staircase removal) skel = morphology.skeletonize(img) # Try mahotas skeletonization. Makes many spikes. # works better after mh.close(), but still splits many tips in two. # Also gives staircases in the skeleton. #skel = mh.thin(img) #skel = morphology.skeletonize(skel) # one pass of the other skeletonization to remove staircases ################
import numpy as np import mahotas as mh image = mh.imread('../1400OS_10_01.jpeg') image = mh.colors.rgb2gray(image, dtype=np.uint8) thresh = mh.thresholding.otsu(image) print(thresh) otsubin = (image > thresh) mh.imsave('otsu-threshold.jpeg', otsubin.astype(np.uint8) * 255) otsubin = ~mh.close(~otsubin, np.ones((15, 15))) mh.imsave('otsu-closed.jpeg', otsubin.astype(np.uint8) * 255) thresh = mh.thresholding.rc(image) print(thresh) mh.imsave('rc-threshold.jpeg', (image > thresh).astype(np.uint8) * 255)
if len(img.shape) == 3: #color image #convert to grayscale img = mh.colors.rgb2gray(img, dtype=np.uint8) # thresholding T_otsu = mh.otsu(img) # finds a numeric threshold img = (img > T_otsu) # make image binary # invert the image (just because the test image is stored the other way) img = ~img # close single-pixel holes. Also makes the skeletonization much more well-behaved, # with less tiny branches close to terminals. # # This can create loops if two branches are separated by < 3 px of background img = mh.close(img) print('Thinning...') # skeletonization from scikit image. # Zhang-Suen algorithm (apparently with staircase removal) skel = morphology.skeletonize(img) # Try mahotas skeletonization. Makes many spikes. # works better after mh.close(), but still splits many tips in two. # Also gives staircases in the skeleton. #skel = mh.thin(img) #skel = morphology.skeletonize(skel) # one pass of the other skeletonization to remove staircases ################ # find terminals and junctions. t and j are in the format [(x1, y1), (x2, y2), ...]
# This code is supporting material for the book # Building Machine Learning Systems with Python # by Willi Richert and Luis Pedro Coelho # published by PACKT Publishing # # It is made available under the MIT License import numpy as np import mahotas as mh image = mh.imread('../SimpleImageDataset/building05.jpg') image = mh.colors.rgb2gray(image, dtype=np.uint8) thresh = mh.thresholding.otsu(image) print(thresh) otsubin = (image > thresh) mh.imsave('otsu-threshold.jpeg', otsubin.astype(np.uint8) * 255) otsubin = ~ mh.close(~otsubin, np.ones((15, 15))) mh.imsave('otsu-closed.jpeg', otsubin.astype(np.uint8) * 255) thresh = mh.thresholding.rc(image) print(thresh) mh.imsave('rc-threshold.jpeg', (image > thresh).astype(np.uint8) * 255)
def findClosestEdge(G, p): emin = None dmin = 1e100 for e in G.edges(): d = distPointLine(e[0], e[1], p) if d < dmin: dmin = d emin = e return emin #@profile #def my_func(): ################################################################################### # Main code starts here # # take the file name from the command line, if given """ if len(sys.argv) > 1: filename = sys.argv[1] else: filename = 'img/f.png' """ filename = '1-1.png' # ============================================================================= # # read database of leaves # leaves = {} # # fileHandle = None # try: # leavesFile = getScriptPath() + '/leaves.json' # print('Database file: ' + leavesFile) # fileHandle = open(leavesFile, 'r') # except: # print('Could not load the leaf data base') # # if fileHandle != None: # # if the file exists, but the JSON is not correct, this will fail # # This is on purpose, since if we continue and save the database at the end, # # the data in it will be overwritten # leaves = json.load(fileHandle) # # # path_basename = os.path.splitext(filename)[0] # print('path_basename', path_basename) # basename = os.path.splitext(os.path.basename(filename))[0] # # ## Read from .json # leafData = {} # try: # #find the current leaf, based on file name # leafData = leaves[basename] # except: # print('%s was not found in the leaf data base'%basename) # # add an entry for this leaf to the ditionary # leaves[basename] = leafData # # print('Data for this leaf: ' + str(leafData)) # # px_mm = None # root = None # # try: # px_mm = leafData['px_mm'] # except: # print('No resolution found for leaf %s'%basename) # # try: # root = tuple(leafData['root']) # except: # print('No root found for leaf %s'%basename) # # print('Resolution: ' + str(px_mm) + ' px/mm') # print('Root: ' + str(root)) # print() # # print('Reading image %s'%filename) # ============================================================================= root = None img = mh.imread(filename) # color image handling - works if the background is brighter than the object if len(img.shape) == 3: #color image #convert to grayscale img = mh.colors.rgb2gray(img, dtype=np.uint8) # thresholding T_otsu = mh.otsu(img) # finds a numeric threshold img = (img > T_otsu) # make image binary # invert the image (just because the test image is stored the other way) img = ~img # close single-pixel holes. Also makes the skeletonization much more well-behaved, # with less tiny branches close to terminals. # # This can create loops if two branches are separated by < 3 px of background img = mh.close(img) # print('Thinning...') # skeletonization from scikit image. # Zhang-Suen algorithm (apparently with staircase removal) skel = morphology.skeletonize(img) # Try mahotas skeletonization. Makes many spikes. # works better after mh.close(), but still splits many tips in two. # Also gives staircases in the skeleton. #skel = mh.thin(img) #skel = morphology.skeletonize(skel) # one pass of the other skeletonization to remove staircases ################ # find terminals and junctions. t and j are in the format [(x1, y1), (x2, y2), ...] # print('Features...') t, j = terminals(skel) if root == None: # unpack the tuples to separate lists of x:s and y:s, for plotting and root selection #tx = [x[0] for x in t] #ty = [x[1] for x in t] dist_terminal = [] for x in t: _dist = distPointLine([0, 316], [img.shape[1], 316], list(x)) dist_terminal.append(_dist) # find the index of the lowest terminal. Use that as the root, for now. iroot = dist_terminal.index(min(dist_terminal)) # find the lowest node, to use as root root = t[iroot] else: if root not in t + j: newroot = findClosest(t + j, root) print('Moving the root to a node in the tree. New root is', str(newroot), 'old root was', str(root), 'distance', dist(newroot, root)) root = newroot # print('Plotting') # make the skeleton image's background transparent skel = np.ma.masked_where(skel == 0, skel) #visited = np.ma.masked_where (visited==0, visited) # fig = plt.figure() # Plot images. Inversion here is just for nicer coloring # interpolation=nearest is to turn smoothing off. width = skel.shape[1] height = skel.shape[0] # plt.axis((0,width,height,0)) # plt.imshow(~img, cmap=leaf_colors, interpolation="nearest") #plt.imshow(np.sqrt(dmap), cmap=mpl.cm.jet_r, interpolation="nearest") # plt.imshow(skel, cmap=skel_colors, interpolation="nearest") #plt.imshow(visited, cmap=mpl.cm.cool, interpolation="nearest") # update measures that depend on graph structure or root placement def updateMeasures(G, root): #print(' Strahler order...') StrahlerOrder(G, root) #print(' apical distances...') measureApicalDist(G) #print(' branch angles...') measureAngles(G) def buildGraph(img, skel): #print('Building tree...') G = nx.Graph() visited = np.zeros_like( skel) # a new array of zeros, same dimensions as skel #print(' distance transform...') dmap = mh.distance(img) #print(' constructing tree...') buildTree(skel, visited, dmap, root, j, t, G) # measure node diameters measureDia(G, dmap) # automatically remove bad nodes and branches, e.g. too small ones removed = cleanup(G, root) # show (automatically) removed nodes for x, y in removed: plt.gca().add_patch(plt.Circle((x, y), radius=4, alpha=.4)) updateMeasures(G, root) #print('Done.') return G # read in the graph from a previous run, if it exists path_basename = os.path.splitext(filename)[0] try: G = nx.read_gpickle(path_basename + '_graph.pkl') print('Loaded graph from ' + path_basename + '_graph.pkl') except: # could not read the graph. Constructing it now G = buildGraph(img, skel) # handles to plot elements nodes = None edges = None node_labels = None edge_labels = None rad = [] # plot_graph(G) # semi-transparent circles on the nodes # for p,r in zip(G.nodes(), rad): # plt.gca().add_patch(plt.Circle(p, radius=r, alpha=terminal_disk_alpha, color=terminal_disk_color)) # root_patch = plt.Circle(root, radius=40, alpha=root_disk_alpha, color=root_disk_color) # plt.gca().add_patch(root_patch) # ============================================================================= # def setRoot(root): # root_patch.center = root; # # # save the new root in database # # convert to int from numpy type, for JSON to work later # leafData['root'] = (int(root[0]), int(root[1])) # # # # a function called when the user clicks a node # ============================================================================= def onpick(event): global root # make the clicked node the new root # for some reason it's difficult to get the coordinates of the clicked node # so we use mouse coordinates and search for the closest node. root = findClosest(G.nodes(), (event.mouseevent.xdata, event.mouseevent.ydata)) print('New root: ' + str(root)) #setRoot(root) updateMeasures(G, root) ##report(G) # plot_graph(G) # fig.canvas.draw() undo_stack = [] # a function called on keypress events def keypress(event): global nodes, edges, node_labels, G, root #print('press', event.key) sys.stdout.flush() if event.key == 'd': # delete closest node p = findClosest(G.nodes(), (event.xdata, event.ydata)) print('closest node is (%5.1f, %5.1f)' % p) if p == root: print('Cannot remove the root.') return undo_stack.append((G.copy(), root)) G.remove_node(p) updateMeasures(G, root) # plot_graph(G) # fig.canvas.draw() if event.key == 'x': # delete closest branch e = findClosestEdge(G, (event.xdata, event.ydata)) undo_stack.append((G.copy(), root)) G.remove_edge(*e) updateMeasures(G, root) # plot_graph(G) # fig.canvas.draw() if event.key == 'u': # undo if len(undo_stack) > 0: print('Undo') G, root = undo_stack.pop() #setRoot(root) updateMeasures(G, root) # plot_graph(G) # fig.canvas.draw() else: print('No further undo') if event.key == 'r': #re-build the graph from skeleton print('Rebuilding tree') undo_stack.append((G.copy(), root)) G = buildGraph(img, skel) # plot_graph(G) # fig.canvas.draw() # register the event callback functions # fig.canvas.mpl_connect('pick_event', onpick) # fig.canvas.mpl_connect('key_press_event', keypress) # plot disks for the apical distance, just for testing #for n in G.nodes(): # if G.node[n]['level'] == 1: # r = G.node[n]['apicaldist'] # plt.gca().add_patch(plt.Circle(n, radius=r, alpha=.4)) # print(G.node[n]) # report(G) # plt.tight_layout() # show the plot - program pauses here for as long as the window is open # plt.show() # # saveTreeText(G, path_basename+'_branches.txt', path_basename+'_nodes.txt'); # nx.write_gpickle(G, path_basename+'_graph.pkl') def saveReport(reportFile, report_text, header_text): # if the file does not exist already, write the header present = os.path.isfile(reportFile) print('Saving leaf report in file ' + reportFile) try: f = open(reportFile, 'at') if not present: f.write(header_text + '\n') f.write(report_text + '\n') except: print('Error when saving the report')
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 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)