def setSource(self, source, index = all): #initialize sources and axis settings if index is all: if isinstance(source, tuple): source = list(source); if not isinstance(source, list): source = [source]; if self.nsources != len(source): raise RuntimeError('Number of sources does not match!'); source = [io.as_source(s) for s in source]; index = range(self.nsources); else: s = self.sources; s[index] = io.as_source(source); source = s; index = [index]; for i in index: s = source[i]; if s.shape != self.source_shape: raise RuntimeError('Shape of sources does not match!'); if s.dtype == bool: self.sources[i] = s.view('uint8'); if s.ndim == 2: s.shape = s.shape + (1,); if s.ndim != 3: raise RuntimeError('Sources dont have dimensions 2 or 3 but %d in source %d!' % (s.ndim, i)); self.image_items[i].updateImage(s[self.source_slice[:s.ndims]]); self.sources = source;
def find_intensity(source, label, max_label=None, method='sum', verbose=False): """Find integrated intensity given object shapes as labled image. Arguments --------- source : array, str, or Source Source to measure intensities from. label : array, str, or Source Labeled image with a separate label for each object. max_label : int or None Maximal label to include. If None use all. method : {'sum', 'mean', 'max', 'min'} Method to use to measure the intensities in each object's area. verbose : bool If True, print progress information. Returns ------- intensities : array Measured intensities. """ if verbose: timer = tmr.Timer() hdict.pprint(head='Intensity detection:', max_label=max_label, method=method) source = io.as_source(source).array label = io.as_source(label) if max_label is None: max_label = label.max() if method.lower() == 'sum': measure = scipy.ndimage.measurements.sum elif method.lower() == 'mean': measure = scipy.ndimage.measurements.mean elif method.lower() == 'max': measure = scipy.ndimage.measurements.maximum elif method.lower() == 'min': measure = scipy.ndimage.measurements.minimum else: raise RuntimeError('Unkown method %r!' % (method, )) intensities = measure(label, labels=label, index=np.arange(1, max_label + 1)) if verbose: timer.print_elapsed_time(head='Intensity detection') return intensities
def flatfield_from_line(line, shape, axis = 0, dtype = float): """Creates a 2d flat field image from a 1d line of estimated intensities. Arguments --------- line : array Array of intensities along the specified axis. shape : tuple Shape of the resulting image. axis : int Axis of the flat field line estimate. Returns ------- flatfield : array Full 2d flat field. """ line = io.as_source(line); if isinstance(shape, int): shape = (line.shape[0], shape) if axis == 0 else (shape, line.shape[0]); if shape[axis] != line.shape[0]: raise ValueError('Line shape %d does not match image shape %d!' % (line.shape[0], shape[axis])); shape = shape[axis]; flatfield = np.array([line.array] * shape, dtype=dtype) if axis == 1: flatfield = flatfield.T; return flatfield;
def filter_cells(source, sink, thresholds): """Filter a array of detected cells according to the thresholds. Arguments --------- source : str, array or Source The source for the cell data. sink : str, array or Source The sink for the results. thresholds : dict Dictionary of the form {name : threshold} where name refers to the column in the cell data and threshold can be None, a float indicating a minimal threshold or a tuple (min,max) where min,max can be None or a minimal and maximal threshold value. Returns ------- sink : str, array or Source The thresholded cell data. """ source = io.as_source(source) ids = np.ones(source.shape[0], dtype=bool) for k, t in thresholds.items(): if t: if not isinstance(t, (tuple, list)): t = (t, None) if t[0] is not None: ids = np.logical_and(ids, t[0] <= source[k]) if t[1] is not None: ids = np.logical_and(ids, t[1] > source[k]) cells_filtered = source[ids] return io.write(sink, cells_filtered)
def read_group(sources, combine=True, **args): """Turn a list of sources for data into a numpy stack. Arguments --------- sources : list of str or sources The sources to combine. combine : bool If true combine the sources to ndarray, oterhwise return a list. Returns ------- group : array or list The gorup data. """ #check if stack already: if isinstance(sources, np.ndarray): return sources #read the individual files group = [] for f in sources: data = io.as_source(f, **args).array data = np.reshape(data, (1, ) + data.shape) group.append(data) if combine: return np.vstack(group) else: return group
def write_header_from_source(source, filename=None, header=None): """Create a mhd header file for a source file. Arguments --------- source : Source specification Source file or class to create a mhd header file for. filename : str or None Filename of the mhd file. If None, the source location with extension 'mhd' is used. header : dict or None Optional additional entries for the header file. Returns ------- filename : str The filename of the mhd header. """ import ClearMap.IO.IO as io source = io.as_source(source) mhd_header = header_from_source(source, header=header) if filename is None: filename = source.location + '.mhd' return write_header(filename, mhd_header)
def as_memory_block(self): source = io.as_source(self.as_memory()) return Block(source=source, slicing=slice(None), valid_slicing=self.valid.slicing, index=self.index, neighbours=self.neighbours)
def find_size(label, max_label=None, verbose=False): """Find size given object shapes as a labled image Arguments --------- label : array, str or Source Labeled image in which each object has its own label. max_label : int or None Maximal label to include, if None use all label. verbose : bool Print progress info. Returns ------- sizes : array Measured intensities """ if verbose: timer = tmr.Timer() hdict.pprint(head='Size detection:', max_label=max_label) label = io.as_source(label) if max_label is None: max_label = int(label.max()) sizes = scipy.ndimage.measurements.sum(np.ones(label.shape, dtype=bool), labels=label, index=np.arange(1, max_label + 1)) if verbose: timer.print_elapsed_time(head='Size detection') return sizes
def block_axes(source, axes=None): """Determine the axes for block processing from source order. Arguments --------- source : array or Source The source on which the block processing is used. axes : list or None The axes over which to split the block processing. Returns ------- axes : list or None The axes over which to split the block processing. """ if axes is all: axes = [d for d in range(source.ndim)] if axes is not None: if np.max(axes) >= source.ndim or np.min(axes) < 0: raise ValueError( 'Axes specification %r for source with dimnesion %d not valid!' % (axes, source.ndim)) return axes source = io.as_source(source) if source.order == 'F': axes = [source.ndim - 1] else: axes = [0] return axes
def create_debug(self, ftype, slicing, debug = None, **kwargs): if debug is None: debug = self.debug; if debug is None: debug = 'debug'; self.debug = None; source = io.as_source(self.filename(ftype, **kwargs)); self.debug = debug; return io.write(self.filename(ftype, **kwargs), np.asarray(source[slicing], order='F'));
def detect_shape(source, seeds, threshold=None, verbose=False): """Detect object shapes by generatng a labeled image from seeds. Arguments --------- source : array, str or Source Source image. seeds : array, str or Source Cell centers as point coordinates. threshold : float or None Threshold to determine mask for watershed, pixel below this are treated as background. If None, the seeds are expanded indefinately. verbose :bool If True, print progress info. Returns ------- shapes : array Labeled image, where each label indicates a object. """ if verbose: timer = tmr.Timer() hdict.pprint(head='Shape detection', threshold=threshold) source = io.as_source(source).array seeds = io.as_source(seeds) if threshold is None: mask = None else: mask = source > threshold peaks = vox.voxelize(seeds, shape=source.shape, weights=np.arange(1, seeds.shape[0] + 1)).array shapes = skimage.morphology.watershed(-source, peaks, mask=mask) #shapes = watershed_ift(-source.astype('uint16'), peaks); #shapes[numpy.logical_not(mask)] = 0; if verbose: timer.print_elapsed_time('Shape detection') return shapes
def initializeSources(self, source, scale=None, axis=None, update=True): #initialize sources and axis settings if isinstance(source, tuple): source = list(source) if not isinstance(source, list): source = [source] self.nsources = len(source) self.sources = [io.as_source(s) for s in source] # avoid bools for i, s in enumerate(self.sources): if s.dtype == bool: self.sources[i] = s.view('uint8') # # ensure 3d images # for i,s in enumerate(self.sources): # if s.ndim == 2: # s = s.view(); # s.shape = s.shape + (1,); # self.sources[i] = s; # if s.ndim != 3: # raise RuntimeError('Sources dont have dimensions 2 or 3 but %d in source %d!' % (s.ndim, i)); # source shapes self.source_shape = self.shape3d(self.sources[0].shape) for s in self.sources: if s.ndim > 3: raise RuntimeError('Source has %d > 3 dimensions: %r!' % (s.ndim, s)) if self.shape3d(s.shape) != self.source_shape: raise RuntimeError('Sources shape %r vs %r in source %r!' % (self.source_shape, s.shape, s)) self.source_shape2 = np.array( np.array(self.source_shape, dtype=float) / 2, dtype=int) # for i,s in enumerate(self.sources): # # slicing if axis is None: axis = 2 self.source_axis = axis self.source_index = self.source_shape2 # scaling if scale is None: scale = np.ones(3) else: scale = np.array(scale) scale = np.hstack([scale, [1] * 3])[:3] self.source_scale = scale #print(self.source_shape, self.source_scale) self.updateSourceRange() self.updateSourceSlice()
def plot_3d(source, colormap=None, view=None, title=None, center_view=True, **kwargs): """Plot 3d volume. Arguments --------- source : array The 3d volume. title : str or None Window title. view : view or None Add plot to this view. if given. Returns ------- view : view The view of the plot. """ #visual #VolumePlot3D = vispy.scene.visuals.create_visual_node(vispy.visuals.VolumeVisual) VolumePlot3D = vispy.scene.visuals.create_visual_node(vvi.VolumeVisual) #view title = title if title is not None else 'plot_3d' #center = (np.array(source.shape) // 2); view = initialize_view(view, title=title, fov=0, depth_value=10**8) #, center=center) #style style = dict(cmap=grays_alpha(), method='translucent', relative_step_size=0.5) style.update(**kwargs) #source source = io.as_source(source)[:] if source.dtype == bool: source = source.view(dtype='uint8') #orient source = source.transpose([2, 1, 0]) #plot p = VolumePlot3D(source, parent=view.scene, **style) #view.camera.set_range(); if center_view: _center_view(view) return p
def _initialize_source(source, as_source = False, as_1d = False, return_shape = False, return_strides = False): """Initialize a source for parallel array processing. Arguments --------- source : source specification The source to initialize. as_source : bool If True, return source as Source class. If false, a buffer compatible with cython memory views is returned. return_shape : bool If True, also return shape of the source. return_strides : bool If True, also return the element strides of the source. Returns ------- source : Source or buffer The intialized source. shape : tuple of int Shape of the source. return_Strides : tuple of int Element strides of the source. """ source = io.as_source(source) if return_shape: shape = source.shape; if return_strides: strides = source.element_strides; if not as_source: #make sure the source is a buffer compatible with cython memoryviews source = source.as_buffer(); if source.dtype == bool: source = source.view('uint8'); if as_1d: source = source.reshape(-1, order = 'A'); result = (source,); if return_shape: result += (shape,); if return_strides: result += (strides,); if len(result) == 1: return result[0]; else: return result;
def _test(): import numpy as np #analysis:ok import ClearMap.ParallelProcessing.Block as blk import ClearMap.IO.IO as io source = io.as_source(np.asarray(np.random.rand(50, 100, 200), order='F')) block = blk.Block(source=source, index=(1, 2, 3), blocks_shape=(10, 20, 30)) print(block.n_iterations) print(block.iteration) print(block.iteration_info())
def binary_statistics(source): """Counts the binarization types. Arguments --------- source : array The status array of the binarization process. Returns ------- statistics : dict A dict with entires {description : count}. """ status, counts = np.unique(io.as_source(source)[:], return_counts = True); return {status_to_description(s) : c for s,c in zip(status, counts)}
def _test(): import numpy as np import ClearMap.IO.IO as io import ClearMap.Visualization.Plot3d as p3d import ClearMap.Tests.Files as tsf import ClearMap.ImageProcessing.Skeletonization.PK12 as PK12 from importlib import reload reload(PK12) #Lookup tables #lut = PK12.generate_lookup_table(); #np.save(PK12.filename, lut); #lut.sum() #lutnr = PK12.generate_lookup_table(function=PK12.match_non_removable, verbose = True); #np.save(PK12.filename_non_removable, lutnr); #lutnr.sum() #Skeletonization reload(PK12) binary = tsf.skeleton_binary binary_array = np.array(io.as_source(binary)) #default version skeleton = PK12.skeletonize(binary_array.copy(), delete_border=True, verbose=True) p3d.plot([[binary_array, skeleton]]) #fast index version skeleton = PK12.skeletonize_index(binary_array.copy(), delete_border=True, verbose=True) p3d.plot([[binary_array, skeleton]]) # plotting import ClearMap.Visualization.Plot3d as p3d p3d.plot_3d(binary_array[:150, :150, :150], cmap=p3d.grays_alpha(0.05)) p3d.plot_3d(skeleton[:150, :150, :150], cmap=p3d.single_color_colormap('red', alpha=0.8)) #save for figure import scipy.io as sio sio.savemat('binary.mat', {'binary': binary_array[:100, :100, :100]}) sio.savemat('binary_skeleton.mat', {'skeleton': skeleton[:100, :100, :100]})
def process(function, source, sink=None, axes=None, size_max=None, size_min=None, overlap=None, optimization=True, optimization_fix='all', neighbours=False, function_type=None, as_memory=False, return_result=False, return_blocks=False, processes=None, verbose=False, **kwargs): """Create blocks and process a function on them in parallel. Arguments --------- function : function The main data processing script. source : str, Source, or list The source or list of sources to apply a function to sink : str, Source, list, or None The sink or list of sinks to write the result to. If None, return single array. axes : int, list of ints, or None Axes along which to split the source. If None, the splitting is determined automaticlly from the order of the array. size_max : int, list of ints or None Maximal size of a block along the axes. If None, :const:`default_size_max` is used. size_min : int or list of ints Minial size of a block along the axes. If None, :const:`default_size_min` is used. overlap : int, list of ints or None Minimal overlap between blocks along the axes. If None, :const:`default_overlap` is used. optimization : bool or list of bools If True, optimize block sizes to best fit number of processes. optimization_fix : 'increase', 'decrease', 'all' or None or list Increase, decrease or optimally change the block size when optimization is active. neighbours : bool If True, also include information about the neighbourhood in the blocks. function_type : 'array', 'source', 'block' or None The function type passed. If None, 'array' is used. * 'array' Reading and writing the valid slices from the blocks is automatic and the function gets passed numpy arrays. * 'source' Reading and writing the valid slices from the blocks is automatic and the function gets passed Source classes as inputs. * 'block' The function is assumed to act on and update blocks itself. as_memory : bool If True, load full blocks into memory before applying the function. Can be useful to reduce frequent reading and writing operations of memmaps. return_result : bool If True, return the results of the proceessing functions. return_blocks : bool If True, return the block information used to distribute the processing. processes : int The number of parallel processes, if 'serial', use serial processing. verbose : bool Print information on sub-stack generation. Returns ------- sink : str, Source, list or array The results of the processing. Note ---- This implementation only supports processing into sinks with the same shape as the source. """ #sources and sinks if isinstance(source, list): sources = source else: sources = [source] sources = [io.as_source(s).as_virtual() for s in sources] #if sink is None: # sink = sma.Source(shape=sources[0].shape, dtype=sources[0].dtype, order=sources[0].order); if isinstance(sink, list): sinks = sink elif sink is None: sinks = [] else: sinks = [sink] sinks = [io.initialize(s, hint=sources[0]) for s in sinks] sinks = [io.as_source(s).as_virtual() for s in sinks] axes = block_axes(sources[0], axes=axes) split = ft.partial(split_into_blocks, processes=processes, axes=axes, size_max=size_max, size_min=size_min, overlap=overlap, optimization=optimization, optimization_fix=optimization_fix, neighbours=neighbours, verbose=False) source_blocks = [split(s) for s in sources] sink_blocks = [split(s) for s in sinks] n_blocks = len(source_blocks[0]) source_blocks = [[blocks[i] for blocks in source_blocks] for i in range(n_blocks)] sink_blocks = [[blocks[i] for blocks in sink_blocks] for i in range(n_blocks)] if function_type is None: function_type = 'array' if function_type == 'block': func = ft.partial(process_block_block, function=function, as_memory=as_memory, return_result=return_result, verbose=verbose, **kwargs) elif function_type == 'source': func = ft.partial(process_block_source, function=function, as_memory=as_memory, as_array=False, verbose=verbose, **kwargs) elif function_type == 'array': func = ft.partial(process_block_source, function=function, as_memory=as_memory, as_array=True, verbose=verbose, **kwargs) else: raise ValueError( "function type %r not 'array', 'source', 'block' or None!") if not isinstance(processes, int) and processes != "serial": processes = mp.cpu_count() if verbose: timer = tmr.Timer() print("Processing %d blocks with function %r." % (n_blocks, function.__name__)) if isinstance(processes, int): #from bounded_pool_executor import BoundedProcessPoolExecutor with cf.ProcessPoolExecutor(max_workers=processes) as executor: #with BoundedProcessPoolExecutor(max_workers=processes) as executor: futures = [ executor.submit(func, *args) for args in zip(source_blocks, sink_blocks) ] result = [f.result() for f in futures] #executor.map(function, source_blocks, sink_blocks) else: result = [func(*args) for args in zip(source_blocks, sink_blocks)] #analysis:ignore if verbose: timer.print_elapsed_time("Processed %d blocks with function %r" % (n_blocks, function.__name__)) #gc.collect(); if return_result: ret = result else: ret = sink if return_blocks: ret = (ret, [source_blocks, sink_blocks]) return ret
def transform(source, sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None): """Transform a raw data set to reference using the elastix alignment results. Arguments --------- source : str or array Image source to be transformed. sink : str, [] or None Image sink to save transformed image to. If [] return the default name of the data file generated by transformix. transform_parameter_file : str or None Parameter file for the primary transformation. If None, the file is determined from the transform_directory. transform_directory : str or None Result directory of elastix alignment. If None the transform_parameter_file has to be given. result_directory : str or None The directorty for the transformix results. Returns ------- transformed : array or st Array or file name of the transformed data. Note ---- If the map determined by elastix is :math:`T: \\mathrm{fixed} \\rightarrow \\mathrm{moving}`, transformix on data works as :math:`T^{-1}(\\mathrm{data})`. """ check_elastix_initialized(); # image source = io.as_source(source); if isinstance(source, io.tif.Source): imgname = source.location; delete_image = None; else: imgname = os.path.join(tempfile.gettempdir(), 'elastix_input.tif'); io.write(source, imgname); delete_image = imgname; # result directory delete_result_directory = None; if result_directory == None: resultdirname = os.path.join(tempfile.gettempdir(), 'elastix_output'); delete_result_directory = resultdirname; else: resultdirname = result_directory; if not os.path.exists(resultdirname): os.makedirs(resultdirname); # tranformation parameter transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); set_path_transform_files(transform_parameter_dir); #transformix -in inputImage.ext -out outputDirectory -tp TransformParameters.txx cmd = '%s -in %s -out %s -tp %s' % (transformix_binary, imgname, resultdirname, transform_parameter_file); res = os.system(cmd); if res != 0: raise RuntimeError('transform_data: failed executing: ' + cmd); # read data and clean up if delete_image is not None: os.remove(delete_image); if sink == []: return result_data_file(resultdirname); elif sink is None: resultfile = result_data_file(resultdirname); result = io.read(resultfile); elif isinstance(sink, str): resultfile = result_data_file(resultdirname); result = io.convert(resultfile, sink); else: raise RuntimeError('transform_data: sink not valid!'); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return result;
def resample_points(source, sink = None, resample_source = None, resample_sink = None, orientation = None, source_shape = None, sink_shape = None, source_resolution = None, sink_resolution = None, **args): """Resample points from original coordiantes to resampled ones. Arguments --------- source : str or array Points to be resampled. sink : str or None Sink for the resmapled point coordinates. orientation : tuple Orientation as specified in :func:`resample`. resample_source : str, array or None Optional source as in :func:`resample`. resample_sink: str, array or None Optional sink used in :func:`resample`. source_shape : tuple or None Optional value of source_shape as in :func:`resample`. source_resolution : tuple or None Optional value of source_resolution as in :func:`resample`. sink_resolution : tuple or None Optional value of sink_resolution as in :func:`resample`. Returns ------- resmapled : array or str Sink for the resampled point coordinates. Notes ----- * The resampling of points here corresponds to he resampling of an image in :func:`resample`. * The arguments should be passed exactly as in :func:`resample` except soure and sink that point to the point sources. Use resample_source and resmaple_sink to pass the source and sink values used in :func:`resample`. """ #orientation orientation = format_orientation(orientation); #original source info if source_shape is None: if source_resolution is None and resample_source is None: raise ValueError('Either source_shape, source_resolution or resample_source must to be given!') if resample_source is not None: source_shape = io.shape(resample_source); #original sink info if sink_shape is None and sink_resolution is None: if resample_sink is None: sink_shape = io.shape(source); else: sink_shape = io.shape(resample_sink); source_shape, sink_shape, source_resolution, sink_resolution = \ resample_shape(source_shape=source_shape, sink_shape=sink_shape, source_resolution=source_resolution, sink_resolution=sink_resolution, orientation=orientation); sink_shape_in_source_orientation = orient_shape(sink_shape, orientation, inverse=True); resample_factor = [float(t)/float(s) for s,t in zip(source_shape, sink_shape_in_source_orientation)]; points = io.as_source(source); resampled = points[:] * resample_factor; # reorient points if orientation is not None: #permute per = orientation_to_permuation(orientation); resampled = resampled.transpose(per); #reverse axes reslice = False; slicing = [slice(None)] * len(source_shape); for d,o in enumerate(orientation): if o < 0: slicing[d] = slice(None, None, -1); reslice = True; if reslice: resampled = resampled[slicing]; return io.write(sink, resampled);
def resample_inverse(source, sink = None, resample_source = None, resample_sink = None, orientation = None, source_shape = None, source_resolution = None, sink_shape = None, sink_resolution = None, axes_order = None, method = 'memmap', interpolation = 'linear', processes = None, verbose = True, **args): """Resample data inversely to :func:`resample` routine. Arguments --------- source : str, array Source to be inversly resampled (e.g. sink in :func:`resample`). sink : str or None Sink to write the inversly resampled image to. resample_source : str, array or None Optional source in :func:`resample`. resmaple_sink: str, array or None Optional sink used in :func:`resample`. orientation : tuple Orientation as specified as in :func:`resample`. source_shape : tuple or None Optional value of source_shape as in :func:`resample`. source_resolution : tuple or None Optional value of source_resolution as in :func:`resample`. sink_resolution : tuple or None Optional value of sink_resolution as in :func:`resample`. processing_directory : str or None Optional directory in which to perform resmapling in parallel. If None, a temporary directry will be created. axis_order : list of tuples of int or None The axes pairs along which to resample the data as in :func:`resample`. method : 'shared' or 'memmap' Method to handle intermediate resampling results. If 'shared' use shared memory, otherwise use a memory map on disk. interpolation : str Method to use for interpolating to the resmapled image. processes int or None Number of processes to use for parallel resampling. verbose : bool If True, print progress information. Returns ------- resampled : array or str Data or file name of inversly resampled image. Notes ----- * All arguments, except source and sink should be passed as :func:`resample` to invert the resmapling. """ source = io.as_source(source); ndim = source.ndim; dtype = source.dtype; #orientation orientation = format_orientation(orientation); orientation_inverse = inverse_orientation(orientation); #original source info if source_shape is None: if source_resolution is None and resample_source is None: raise ValueError('Either source_shape, source_resolution or resample_source must to be given!') if resample_source is not None: source_shape = io.shape(resample_source); #original sink info if sink_shape is None and sink_resolution is None: if resample_sink is None: sink_shape = io.shape(source); else: sink_shape = io.shape(resample_sink); source_shape, sink_shape, source_resolution, sink_resolution = \ resample_shape(source_shape=source_shape, sink_shape=sink_shape, source_resolution=source_resolution, sink_resolution=sink_resolution, orientation=orientation); sink_shape_in_source_orientation = orient_shape(sink_shape, orientation, inverse=True); axes_order, shape_order = _axes_order(axes_order, source, source_shape, sink_shape_in_source_orientation); interpolation = _interpolation_to_cv2(interpolation); if processes is None or not processes == 'serial': processes = io.mp.cpu_count(); #reversed orientation if not orientation is None: #reverse axes slicing = [slice(None)] * ndim; reslice = False; for d,o in enumerate(orientation): if o < 0: slicing[d] = slice(None, None, -1); reslice = True; if reslice: source = source[slicing]; #re-orient per = orientation_to_permuation(orientation_inverse); source = io.read(source); source = source.transpose(per); source = io.sma.as_shared(source); #reverse resampling steps axes_order = axes_order[::-1]; shape_order = shape_order[:-1]; shape_order = shape_order[::-1]; shape_order = shape_order + [source_shape] #print(axes_order, shape_order) #reverse resampling n_steps = len(axes_order); last_source = source; delete_files = []; #print(last_source) for step, axes, shape in zip(range(n_steps), axes_order, shape_order): if step == n_steps-1: resampled = io.initialize(source=sink, shape=shape, dtype=dtype, memory='shared', as_source=True); else: if method == 'shared': resampled = io.sma.create(shape, dtype=dtype, order='C', as_source=True); else: location = tempfile.mktemp() + '.npy'; resampled = io.mmp.create(location, shape=shape, dtype=dtype, order='C', as_source=True); delete_files.append(location); #indices for non-resampled axes indices = tuple([range(s) for d,s in enumerate(shape) if d not in axes]); indices = [i for i in itertools.product(*indices)]; n_indices = len(indices); #resample step last_source_virtual = last_source.as_virtual(); resampled_virtual = resampled.as_virtual(); _resample = ft.partial(_resample_2d, source=last_source_virtual, sink=resampled_virtual, axes=axes, shape=shape, interpolation=interpolation, n_indices=n_indices, verbose=verbose) if processes == 'serial': for index in indices: _resample(index=index); else: with concurrent.futures.ProcessPoolExecutor(processes) as executor: executor.map(_resample, indices); last_source = resampled; for f in delete_files: io.delete_file(f); sink = resampled.as_real(); return sink;
def resample(source, sink = None, orientation = None, sink_shape = None, source_resolution = None, sink_resolution = None, interpolation = 'linear', axes_order = None, method = 'shared', processes = None, verbose = True): """Resample data of source in new shape/resolution and orientation. Arguments --------- source : str or array The source to be resampled. sink : str or None The sink for the resampled image. orientation : tuple or None: The orientation specified by permuation and change in sign of (1,2,3). sink_shape : tuple or None The target shape of the resampled sink. source_resolution : tuple or None The resolution of the source (in length per pixel). sink_resolution : tuple or None The resolution of the resampled source (in length per pixel). interpolation : str The method to use for interpolating to the resmapled array. axis_order : str, list of tuples of int or None The axes pairs along which to resample the data at each step. If None, this is detertmined automatically. For a FileList source, setting the first tuple should point to axis not indicating files. If 'size' the axis order is determined automatically to maximally reduce the size of the array in each resmapling step. If 'order' the axis order is chosed automatically to optimize io speed. method : 'shared' or 'memmap' Method to handle intermediate resampling results. If 'shared' use shared memory, otherwise use a memory map on disk. processes : int, None or 'serial' Number of processes to use for parallel resampling, if None use maximal processes avaialable, if 'serial' process in serial. verbose : bool If True, display progress information. Returns ------- sink : array or str The data or filename of resampled sink. Notes ----- * Resolutions are assumed to be given for the axes of the intrinsic orientation of the data and reference (as when viewed by ImageJ). * Orientation: permuation of 1,2,3 with potential sign, indicating which axes map onto the reference axes, a negative sign indicates reversal of that particular axes. * Only a minimal set of information to determine the resampling parameter has to be given, e.g. source_shape and sink_shape. * The resampling is done by iterating two dimensional resampling steps. """ #TODO: write full nd resampling routine extending cv2 lib. if verbose: timer = tmr.Timer(); source = io.as_source(source); source_shape = source.shape; ndim = len(source_shape); dtype = source.dtype; order = source.order; orientation = format_orientation(orientation); source_shape, sink_shape, source_resolution, sink_resolution = \ resample_shape(source_shape=source_shape, sink_shape=sink_shape, source_resolution=source_resolution, sink_resolution=sink_resolution, orientation=orientation); sink_shape_in_source_orientation = orient_shape(sink_shape, orientation, inverse=True); interpolation = _interpolation_to_cv2(interpolation); if not isinstance(processes, int) and processes != 'serial': processes = io.mp.cpu_count(); #detemine order of resampling axes_order, shape_order = _axes_order(axes_order, source, sink_shape_in_source_orientation, order=order); #print(axes_order, shape_order) if len(axes_order) == 0: if verbose: print('resampling: no resampling necessary, source has same size as sink!'); if sink != source: return io.write(sink, source); else: return source; #resample n_steps = len(axes_order); last_source = source; delete_files = []; for step, axes, shape in zip(range(n_steps), axes_order, shape_order): if step == n_steps-1 and orientation is None: resampled = io.initialize(source=sink, shape=sink_shape, dtype=dtype, as_source=True); else: if method == 'shared': resampled = io.sma.create(shape, dtype=dtype, order=order, as_source=True); else: location = tempfile.mktemp() + '.npy'; resampled = io.mmp.create(location, shape=shape, dtype=dtype, order=order, as_source=True); delete_files.append(location); #print(resampled) #indices for non-resampled axes indices = tuple([range(s) for d,s in enumerate(shape) if d not in axes]); indices = [i for i in itertools.product(*indices)]; n_indices = len(indices); #resample step last_source_virtual = last_source.as_virtual(); resampled_virtual = resampled.as_virtual(); _resample = ft.partial(_resample_2d, source=last_source_virtual, sink=resampled_virtual, axes=axes, shape=shape, interpolation=interpolation, n_indices=n_indices, verbose=verbose) if processes == 'serial': for index in indices: _resample(index=index); else: #print(processes); with concurrent.futures.ProcessPoolExecutor(processes) as executor: executor.map(_resample, indices); last_source = resampled; #fix orientation if not orientation is None: #permute per = orientation_to_permuation(orientation); resampled = resampled.transpose(per); #reverse axes reslice = False; slicing = [slice(None)] * ndim; for d,o in enumerate(orientation): if o < 0: slicing[d] = slice(None, None, -1); reslice = True; if reslice: resampled = resampled[slicing]; if verbose: print("resample: re-oriented shape %r!" % (resampled.shape,)) sink = io.write(sink, resampled); else: sink = resampled; for f in delete_files: io.delete_file(f); if verbose: timer.print_elapsed_time('Resampling') return sink;
def smooth_by_configuration(source, sink = None, iterations = 1, processing_parameter = None, processes = None, verbose = False): """Smooth a binary source using the local configuration around each pixel. Arguments --------- source : array or Source The binary source to smooth. sink : array, Source or None The sink to write result of smoothing. If None, return array. iterations : int Number of smoothing iterations. processing_parameter : None or dict The parameter passed to :func:`ClearMap.ParallelProcessing.BlockProcessing.process`. processes : int or None number of processes to use. verbose : bool If True, print progress information. Returns ------- smoothed : array or Source Thre smoothed binary array. Note ---- The algorithm is based on a topological smoothing operation defined by adding or removing forground pixels based on the local topology of the binary array. """ if verbose: print('Binary smoothing: initialized!'); timer = tmr.Timer(); #smoothing function smooth = functools.partial(smooth_by_configuration_block, iterations=iterations, verbose=False); smooth.__name__ = 'smooth_by_configuration' #initialize sources and sinks source = io.as_source(source); sink = io.initialize(sink, shape=source.shape, dtype=bool, order=source.order); #block processing parameter block_processing_parameter = dict(axes = bp.block_axes(source), as_memory=True, overlap=None, function_type='source', processes=processes, verbose=verbose); if processing_parameter is not None: block_processing_parameter.update(processing_parameter); if not 'overlap' in block_processing_parameter or block_processing_parameter['overlap'] is None: block_processing_parameter['overlap'] = 2 + 2 * iterations; if not 'size_min' in block_processing_parameter or block_processing_parameter['size_min'] is None: block_processing_parameter['size_min'] = 2 + 2 * iterations + 1; if not 'axes' in block_processing_parameter or block_processing_parameter['axes'] is None: block_processing_parameter['axes'] = bp.block_axes(source); #print(block_processing_parameter) #block process bp.process(smooth, source, sink, **block_processing_parameter); if verbose: timer.print_elapsed_time('Binary smoothing: done'); return sink;
def measure_expression(source, points, search_radius, method='max', sink=None, processes=None, verbose=False): """Measures the expression around a list of points in a source. Arguments --------- source : array Source for measurement. points : array List of indices to measure radis for. search_radius : int or array List of search radii to use around each point. If int use this radius for all points. Array should be of length of points. method : 'max' or 'min', 'mean' Measurement type. processes : int or None Number of processes to use. verbose : bool If True, print progress info. """ source = io.as_source(source) ndim = source.ndim if verbose: timer = tmr.Timer() print('Measuring expression of %d points in array of shape %r.' % (points.shape[0], source.shape)) if not hasattr(search_radius, '__len__'): search_radius = search_radius * np.ones(points.shape[0]) if len(search_radius) != len(points): raise ValueError('The search_radius is not valid!') indices, radii_indices = search_indices(search_radius, ndim) if method == 'max': expression = mpl.measure_max(source, points, indices, radii_indices, sink=sink, processes=processes, verbose=verbose) elif method == 'min': expression = mpl.measure_min(source, points, indices, radii_indices, sink=sink, processes=processes, verbose=verbose) elif method == 'mean': expression = mpl.measure_mean(source, points, indices, radii_indices, sink=sink, processes=processes, verbose=verbose) elif method == 'sum': expression = mpl.measure_sum(source, points, indices, radii_indices, sink=sink, processes=processes, verbose=verbose) else: raise ValueError("Method %r not in 'max', 'min', 'mean'" % method) if verbose: timer.print_elapsed_time('Measuring expression done') return expression
def detect_cells_block(source, parameter=default_cell_detection_parameter): """Detect cells in a Block.""" #initialize parameter and slicings verbose = parameter.get('verbose', False) if verbose: prefix = 'Block %s: ' % (source.info(), ) total_time = tmr.Timer(prefix) base_slicing = source.valid.base_slicing valid_slicing = source.valid.slicing valid_lower = source.valid.lower valid_upper = source.valid.upper lower = source.lower parameter_intensity = parameter.get('intensity_detection', None) measure_to_array = dict() if parameter_intensity: parameter_intensity = parameter_intensity.copy() measure = parameter_intensity.pop('measure', []) if measure is None: measure = [] for m in measure: measure_to_array[m] = None if 'source' in measure_to_array: measure_to_array['source'] = source # correct illumination parameter_illumination = parameter.get('illumination_correction', None) if parameter_illumination: parameter_illumination = parameter_illumination.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_illumination, head=prefix + 'Illumination correction') save = parameter_illumination.pop('save', None) corrected = ic.correct_illumination(source, **parameter_illumination) if save: save = io.as_source(save) save[base_slicing] = corrected[valid_slicing] if verbose: timer.print_elapsed_time('Illumination correction') else: corrected = np.array(source.array) if 'illumination' in measure_to_array: measure_to_array['illumination'] = corrected #background subtraction parameter_background = parameter.get('background_correction', None) if parameter_background: parameter_background = parameter_background.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_background, head=prefix + 'Background removal') save = parameter_background.pop('save', None) background = remove_background(corrected, **parameter_background) if save: save = io.as_source(save) save[base_slicing] = corrected[valid_slicing] if verbose: timer.print_elapsed_time('Illumination correction') else: background = corrected del corrected if 'background' in measure_to_array: measure_to_array['background'] = background # equalize parameter_equalize = parameter.get('equalization', None) if parameter_equalize: parameter_equalize = parameter_equalize.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_equalize, head=prefix + 'Equalization:') save = parameter_equalize.pop('save', None) equalized = equalize(background, mask=None, **parameter_equalize) if save: save = io.as_source(save) save[base_slicing] = equalized[valid_slicing] if verbose: timer.print_elapsed_time('Equalization') else: equalized = background del background if 'equalized' in measure_to_array: measure_to_array['equalized'] = equalized #DoG filter parameter_dog_filter = parameter.get('dog_filter', None) if parameter_dog_filter: parameter_dog_filter = parameter_dog_filter.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_dog_filter, head=prefix + 'DoG filter:') save = parameter_dog_filter.pop('save', None) dog = dog_filter(equalized, **parameter_dog_filter) if save: save = io.as_source(save) save[base_slicing] = dog[valid_slicing] if verbose: timer.print_elapsed_time('DoG filter') else: dog = equalized del equalized if 'dog' in measure_to_array: measure_to_array['dog'] = dog #Maxima detection parameter_maxima = parameter.get('maxima_detection', None) parameter_shape = parameter.get('shape_detection', None) if parameter_shape or parameter_intensity: if not parameter_maxima: print( prefix + 'Warning: maxima detection needed for shape and intensity detection!' ) parameter_maxima = dict() if parameter_maxima: parameter_maxima = parameter_maxima.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_maxima, head=prefix + 'Maxima detection:') save = parameter_maxima.pop('save', None) valid = parameter_maxima.pop('valid', None) # extended maxima maxima = md.find_maxima(source.array, **parameter_maxima, verbose=verbose) if save: save = io.as_source(save) save[base_slicing] = maxima[valid_slicing] #center of maxima if parameter_maxima['h_max']: centers = md.find_center_of_maxima(source, maxima=maxima, verbose=verbose) else: centers = ap.where(maxima).array if verbose: timer.print_elapsed_time('Maxima detection') #correct for valid region if valid: ids = np.ones(len(centers), dtype=bool) for c, l, u in zip(centers.T, valid_lower, valid_upper): ids = np.logical_and(ids, np.logical_and(l <= c, c < u)) centers = centers[ids] del ids del dog, maxima results = (centers, ) #cell shape detection if parameter_shape: parameter_shape = parameter_shape.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_shape, head=prefix + 'Shape detection:') save = parameter_shape.pop('save', None) # shape detection shape = sd.detect_shape(source, centers, **parameter_shape, verbose=verbose) if save: save = io.as_source(save) save[base_slicing] = shape[valid_slicing] #size detection max_label = centers.shape[0] sizes = sd.find_size(shape, max_label=max_label) valid = sizes > 0 if verbose: timer.print_elapsed_time('Shape detection') results += (sizes, ) else: valid = None shape = None #cell intensity detection if parameter_intensity: parameter_intensity = parameter_intensity.copy() if verbose: timer = tmr.Timer(prefix) hdict.pprint(parameter_intensity, head=prefix + 'Intensity detection:') if not shape is None: r = parameter_intensity.pop('shape', 3) if isinstance(r, tuple): r = r[0] for m in measure: if shape is not None: intensity = sd.find_intensity(measure_to_array[m], label=shape, max_label=max_label, **parameter_intensity) else: intensity = me.measure_expression(measure_to_array[m], centers, search_radius=r, **parameter_intensity, processes=1, verbose=False) results += (intensity, ) if verbose: timer.print_elapsed_time('Shape detection') if valid is not None: results = tuple(r[valid] for r in results) #correct coordinate offsets of blocks results = (results[0] + lower, ) + results[1:] #correct shapes for merging results = tuple(r[:, None] if r.ndim == 1 else r for r in results) if verbose: total_time.print_elapsed_time('Cell detection') gc.collect() return results
def prepare_annotation_files(slicing=None, orientation=None, directory=None, postfix=None, annotation_file=None, reference_file=None, distance_to_surface_file=None, overwrite=False, verbose=False): """Crop the annotation, reference and distance files to match the data. Arguments --------- slicing : tuple or None The slice specification after reorienting. orientation : tuple, str or None. The orientation specification. Strings can be 'left' or 'right', for the two hemispheres. directory : str or None The target directory. If None, use ClearMap resources folder. postfix : str or None Use this postfix for the cropped annotation file. If None and automatic label is choosen. annotation_file : str or None The annotation file to use. reference_file : str or None The reference file to use. distance_to_surface_file : str or None The distance file to use. overwrite : bool If True, overwrite exisitng files. Returns ------- annotation_file : str The cropped annotation file. reference_file : str The cropped reference file. distance_to_surface_file : str The distance cropped file. """ if annotation_file is None: annotation_file = default_annotation_file if reference_file is None: reference_file = default_reference_file if distance_to_surface_file is None: distance_to_surface_file = default_distance_to_surface_file files = [annotation_file, reference_file, distance_to_surface_file] results = [] for f in files: if f is not None: fn = format_annotation_filename(f, orientation=orientation, slicing=slicing, postfix=postfix, directory=directory) if verbose: print('Preparing: %r' % fn) if not overwrite and io.is_file(fn): results.append(fn) continue if not io.is_file(f): raise ValueError('Cannot find annotation file: %s' % f) s = io.as_source(f) if verbose: print('Preparing: from source %r' % s) data = np.array(s.array) if not orientation is None: #permute per = res.orientation_to_permuation(orientation) data = data.transpose(per) #reverse axes reslice = False sl = [slice(None)] * data.ndim for d, o in enumerate(orientation): if o < 0: sl[d] = slice(None, None, -1) reslice = True if reslice: data = data[tuple(sl)] if slicing is not None: data = data[slicing] io.write(fn, data) results.append(fn) else: results.append(None) return results
def _test(): import numpy as np import ClearMap.IO.IO as io import ClearMap.ParallelProcessing.BlockProcessing as bp source = io.as_source(np.asarray(np.random.rand(50, 100, 200), order='F')) blocks = bp.split_into_blocks(source, processes=10, axes=[2], size_min=30, size_max=50, overlap=20) print(blocks) b = blocks[0] print(b.valid.base_shape) print(b.valid.base_slicing) print(blocks[5].iteration) blocks = bp.split_into_blocks(source, processes=10, axes=[1, 2], size_min=30, size_max=50, overlap=20, neighbours=True) b = blocks[0] print(b.valid.base_shape) print(b.valid.base_slicing) blocks = bp.split_into_blocks(source, processes=10, axes=[1, 2], size_min='fixed', size_max=50, overlap=20, neighbours=True) b = blocks[0] print(b.valid.base_shape) print(b.valid.base_slicing) shape = (2, 3, 20) source = io.npy.Source(array=np.random.rand(*shape)) sink = io.npy.Source(array=np.zeros(shape)) def process_image(source, sink=None): if sink is None: sink = np.zeros(source.shape) sink[:] = 100 * source[:] return sink bp.process(process_image, source, sink, processes='serial', size_max=4, size_min=1, overlap=0, axes=[2], optimization=True, verbose=True) print(np.all(sink[:] == process_image(source))) bp.process(process_image, source, sink, processes=None, size_max=10, size_min=6, overlap=3, axes=all, optimization=True, verbose=True) assert (np.all(sink[:] == process_image(source))) result, blocks = bp.process(process_image, source, sink, size_max=15, size_min=4, overlap=3, axes=[2], optimization=True, return_blocks=True, processes=None, verbose=True) #memmaps loading source = io.mmp.create(location='source.npy', shape=shape) source[:] = np.random.rand(*shape) sink = io.mmp.create(location='sink.npy', shape=shape) bp.process(process_image, source, sink, size_max=10, size_min=6, overlap=3, axes=[2], optimization=True, as_memory=True, verbose=True, processes=None) assert (np.all(sink[:] == process_image(source))) io.delete_file(source.location) io.delete_file(sink.location) #multiple sources and sinks shape = (2, 50, 30) source1 = io.sma.Source(array=np.random.rand(*shape)) source2 = io.sma.Source(array=np.random.rand(*shape)) sink1 = io.sma.Source(array=np.zeros(shape)) sink2 = io.sma.Source(array=np.zeros(shape)) def sum_and_difference(source1, source2, sink1=None, sink2=None): if sink1 is None: sink1 = np.zeros(source1.shape) if sink2 is None: sink2 = np.zeros(source2.shape) sink1[:] = source1[:] + source2[:] sink2[:] = source1[:] - source2[:] return sink1, sink2 bp.process(sum_and_difference, [source1, source2], [sink1, sink2], processes='!serial', size_max=10, size_min=5, overlap=3, axes=[1, 2], optimization=True, verbose=True) s, d = sum_and_difference(source1, source2) assert (np.all(sink1[:] == s)) assert (np.all(sink2[:] == d)) #trace backs shape = (3, 4) source = io.sma.Source(array=np.random.rand(*shape)) sink = io.sma.Source(array=np.zeros(shape)) def raise_error(source, sink=None): raise RuntimeError('test') bp.process(raise_error, source, sink, processes='!serial', size_max=10, size_min=5, overlap=0, axes=[2], optimization=True, verbose=True)
def postprocess(source, sink = None, postprocessing_parameter = default_postprocessing_parameter, processing_parameter = default_postprocessing_processing_parameter, processes = None, verbose = True): """Postprocess a binarized image. Arguments --------- source : source specification The binary source. sink : sink specification or None The sink to write the postprocesses result to. If None, an array is returned. postprocessing_parameter : dict Parameter for the postprocessing. processing_parameter : dict Parameter for the parallel processing. verbose : bool If True, print progress output. Returns ------- sink : Source The result of the binarization. Notes ----- * The postporcessing pipeline is composed of several steps. The parameters for each step are passed as sub-dictionaries to the postprocessing_parameter dictionary. * If None is passed for one of the steps the step is skipped. Smoothing --------- smooth : dict or None Smoothing step parameter. See :func:`ClearMap.ImageProcessing.Binary.Smoothing.smooth_by_configuration` iterations : int Number of smoothing iterations. For the vasculature a typical value is 6. Filling ------- fill : bool or None If True, fill holes in the binary data. """ source = io.as_source(source); sink = ap.initialize_sink(sink, shape=source.shape, dtype=source.dtype, order=source.order, return_buffer=False); if verbose: timer = tmr.Timer(); print('Binary post processing: initialized.'); postprocessing_parameter = postprocessing_parameter.copy(); parameter_smooth = postprocessing_parameter.pop('smooth', None); parameter_fill = postprocessing_parameter.pop('fill', None); #print(parameter_smooth, parameter_fill) #smoothing save = None; if parameter_smooth: #intialize temporary files if needed if parameter_fill: save = parameter_smooth.pop('save', None); temporary_filename = save; if temporary_filename is None: temporary_filename = postprocessing_parameter['temporary_filename']; if temporary_filename is None: temporary_filename = tmpf.mktemp(prefix='TubeMap_Vasculature_postprocessing', suffix='.npy'); sink_smooth = ap.initialize_sink(temporary_filename, shape=source.shape, dtype=source.dtype, order=source.order, return_buffer=False); else: sink_smooth = sink; #run smoothing source_fill = bs.smooth_by_configuration(source, sink=sink_smooth, processing_parameter=processing_parameter, processes=processes, verbose=verbose, **parameter_smooth); else: source_fill = source; if parameter_fill: sink = bf.fill(source_fill, sink=sink, processes=processes, verbose=verbose); if parameter_smooth and save is None: io.delete_file(temporary_filename); else: sink = source_fill; if verbose: timer.print_elapsed_time('Binary post processing'); gc.collect() return None;
def binarize_block(source, sink, parameter = default_binarization_parameter): """Binarize a Block.""" #initialize parameter and slicings verbose = parameter.get('verbose', False); if verbose: prefix = 'Block %s: ' % (source.info(),); total_time = tmr.Timer(prefix); max_bin = parameter.get('max_bin', MAX_BIN); base_slicing = sink.valid.base_slicing; valid_slicing = source.valid.slicing; #initialize binary status for inspection binary_status = parameter.get('binary_status', None); if binary_status: binary_status = io.as_source(binary_status); binary_status = binary_status[base_slicing]; #clipping parameter_clip = parameter.get('clip', None); if parameter_clip: parameter_clip = parameter_clip.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_clip, head = prefix + 'Clipping:') parameter_clip.update(norm=max_bin, dtype=DTYPE); save = parameter_clip.pop('save', None); clipped, mask, high, low = clip(source, **parameter_clip); not_low = np.logical_not(low); if save: save = io.as_source(save); save[base_slicing] = clipped[valid_slicing]; if binary_status is not None: binary_status[high[valid_slicing]] += BINARY_STATUS['High'] else: sink[valid_slicing] = high[valid_slicing]; del high, low if verbose: timer.print_elapsed_time('Clipping'); else: clipped = source mask = not_low = np.ones(source.shape, dtype=bool); #high = low = np.zeros(source.shape, dtype=bool); #low = np.zeros(source.shape, dtype=bool); #not_low = np.logical_not(low); #active arrays: clipped, mask, not_low #lightsheet correction parameter_lightsheet = parameter.get('lightsheet', None); if parameter_lightsheet: parameter_lightsheet = parameter_lightsheet.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_lightsheet, head = prefix + 'Lightsheet:') #parameter_lightsheet.update(max_bin=max_bin); save = parameter_lightsheet.pop('save', None); corrected = lc.correct_lightsheet(clipped, mask=mask, max_bin=max_bin, **parameter_lightsheet); if save: save = io.as_source(save); save[base_slicing] = corrected[valid_slicing]; if verbose: timer.print_elapsed_time('Lightsheet'); else: corrected = clipped; del clipped #active arrays: corrected, mask, not_low #median filter parameter_median = parameter.get('median', None); if parameter_median: parameter_median = parameter_median.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_median, head = prefix + 'Median:') save = parameter_median.pop('save', None); median = rnk.median(corrected, max_bin=max_bin, mask=not_low, **parameter_median); if save: save = io.as_source(save); save[base_slicing] = median[valid_slicing]; if verbose: timer.print_elapsed_time('Median'); else: median = corrected; del corrected, not_low; #active arrays: median, mask #pseudo deconvolution parameter_deconvolution = parameter.get('deconvolve', None); if parameter_deconvolution: parameter_deconvolution = parameter_deconvolution.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_deconvolution, head = prefix + 'Deconvolution:') save = parameter_deconvolution.pop('save', None); threshold = parameter_deconvolution.pop('threshold', None); if binary_status is not None: binarized = binary_status > 0; else: binarized = sink[:]; deconvolved = deconvolve(median, binarized[:], **parameter_deconvolution) del binarized if save: save = io.as_source(save); save[base_slicing] = deconvolved[valid_slicing]; if verbose: timer.print_elapsed_time('Deconvolution'); if threshold: binary_deconvolved = deconvolved > threshold; if binary_status is not None: binary_status[binary_deconvolved[valid_slicing]] += BINARY_STATUS['Deconvolved']; else: sink[valid_slicing] += binary_deconvolved[valid_slicing]; del binary_deconvolved if verbose: timer.print_elapsed_time('Deconvolution: binarization'); else: deconvolved = median; #active arrays: median, mask, deconvolved #adaptive parameter_adaptive = parameter.get('adaptive', None); if parameter_adaptive: parameter_adaptive = parameter_adaptive.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_adaptive, head = prefix + 'Adaptive:') save = parameter_adaptive.pop('save', None); adaptive = threshold_adaptive(deconvolved, **parameter_adaptive) if save: save = io.as_source(save); save[base_slicing] = adaptive[valid_slicing]; binary_adaptive = deconvolved > adaptive; if binary_status is not None: binary_status[binary_adaptive[valid_slicing]] += BINARY_STATUS['Adaptive']; else: sink[valid_slicing] += binary_adaptive[valid_slicing]; del binary_adaptive, adaptive; if verbose: timer.print_elapsed_time('Adaptive'); del deconvolved #active arrays: median, mask # equalize parameter_equalize = parameter.get('equalize', None); if parameter_equalize: parameter_equalize = parameter_equalize.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_equalize, head = prefix + 'Equalization:') save = parameter_equalize.pop('save', None); threshold = parameter_equalize.pop('threshold', None); equalized = equalize(median, mask=mask, **parameter_equalize); if save: save = io.as_source(save); save[base_slicing] = equalized[valid_slicing]; if verbose: timer.print_elapsed_time('Equalization'); if threshold: binary_equalized = equalized > threshold if binary_status is not None: binary_status[binary_equalized[valid_slicing]] += BINARY_STATUS['Equalized']; else: sink[valid_slicing] += binary_equalized[valid_slicing]; #prepare equalized for use in vesselization parameter_vesselization = parameter.get('vesselize', None); if parameter_vesselization and parameter_vesselization.get('background', None): equalized[binary_equalized] = threshold; equalized = float(max_bin-1) / threshold * equalized; del binary_equalized if verbose: timer.print_elapsed_time('Equalization: binarization'); else: equalized = median; del median #active arrays: mask, equalized # smaller vessels /capilarries parameter_vesselization = parameter.get('vesselize', None); if parameter_vesselization: parameter_vesselization = parameter_vesselization.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_vesselization, head = prefix + 'Vesselization:') parameter_background = parameter_vesselization.get('background', None) parameter_background = parameter_background.copy(); if parameter_background: save = parameter_background.pop('save', None); equalized = np.array(equalized, dtype = 'uint16'); background = rnk.percentile(equalized, max_bin=max_bin, mask=mask, **parameter_background); tubeness = equalized - np.minimum(equalized, background); del background if save: save = io.as_source(save); save[base_slicing] = tubeness[valid_slicing]; else: tubeness = equalized; parameter_tubeness = parameter_vesselization.get('tubeness', {}) tubeness = tubify(tubeness, **parameter_tubeness); save = parameter_vesselization.get('save', None); if save: save = io.as_source(save); save[base_slicing] = tubeness[valid_slicing]; if verbose: timer.print_elapsed_time('Vesselization'); threshold = parameter_vesselization.get('threshold', None); if threshold: binary_vesselized = tubeness > threshold; if binary_status is not None: binary_status[binary_vesselized[valid_slicing]] += BINARY_STATUS['Tube']; else: sink[valid_slicing] += binary_vesselized[valid_slicing]; del binary_vesselized if verbose: timer.print_elapsed_time('Vesselization: binarization'); del tubeness del equalized, mask #active arrays: None #fill holes parameter_fill = parameter.get('fill', None); if parameter_fill: parameter_fill = parameter_fill.copy(); if verbose: timer = tmr.Timer(prefix); #hdict.pprint(parameter_fill, head = 'Filling:') if binary_status is not None: foreground = binary_status > 0; filled = ndi.morphology.binary_fill_holes(foreground); binary_status[np.logical_and(filled, np.logical_not(foreground))] += BINARY_STATUS['Fill']; del foreground, filled else: filled = ndi.morphology.binary_fill_holes(sink[:]); sink[valid_slicing] += filled[valid_slicing]; del filled if verbose: timer.print_elapsed_time('Filling'); if binary_status is not None: sink[valid_slicing] = binary_status[valid_slicing] > 0; #smooth binary parameter_smooth = parameter.get('smooth', None); if parameter_smooth: parameter_smooth = parameter_smooth.copy(); if verbose: timer = tmr.Timer(prefix); hdict.pprint(parameter_smooth, head = prefix + 'Smoothing:') smoothed = bs.smooth_by_configuration(sink, sink=None, processes=1, **parameter_smooth); sink[valid_slicing] = smoothed[valid_slicing]; del smoothed; if verbose: timer.print_elapsed_time('Smoothing'); if verbose: total_time.print_elapsed_time('Binarization') gc.collect() return None;
def threshold_adaptive(source, function = threshold_isodata, selem = (100,100,3), spacing = (25,25,3), interpolate = 1, mask = None, step = None): source = io.as_source(source)[:]; threshold = ls.apply_local_function(source, function=function, mask=mask, dtype=float, selem=selem, spacing=spacing, interpolate=interpolate, step = step); return threshold