def deformation_distance(deformation_field, sink = None, scale = None): """Compute the distance field from a deformation vector field. Arguments --------- deformation_field : str or array Source of the deformation field determined by :func:`deformation_field`. sink : str or None Image sink to save the deformation field to. scale : tuple or None Scale factor for each dimension, if None = (1,1,1). Returns ------- deformation_distannce : array or st Array or file name of the deformation distance data. """ deformation_field = io.read(deformation_field); df = np.square(deformation_field); if not scale is None: for i in range(3): df[:,:,:,i] = df[:,:,:,i] * (scale[i] * scale[i]); df = np.sqrt(np.sum(df, axis = 3)); return io.write(sink, df);
def write_color_annotation(filename, annotation_file=None): """Creates a rgb image from the atlas color data. Arguments --------- filename : str The name of the color palette file. annotation_file : str File name of the atals annotation. Returns ------- filename : str The name of the file to which the color atlas was written. """ #load atlas and convert to order if annotation_file is None: annotation_file = annotation.annotation_file atlas = np.array(io.read(annotation_file), dtype=int) atlas = convert_label(atlas, key='id', value='order', method='map') #apply color map cm = color_map(alpha=False, as_int=True) atlas = cm[atlas] return io.write(filename, atlas)
def label_points(points, annotation_file=None, invalid=0, key='order', level=None): """Label points according to the annotation in the labeled image file. Arguments --------- points : array Array of nxdim point coordinates to be labeled. annotation_file : str File name of the atals annotation. invalid : int Label for invalid points. key : str The key of the label, by default the order of the labels. Returns ------- label : array Label of the points corresponding to the given key. """ n_points = points.shape[0] n_dim = points.shape[1] if annotation_file is None: annotation_file = annotation.annotation_file atlas = io.read(annotation_file) atlas = np.array(atlas, dtype=int) atlas_shape = atlas.shape label = np.full(n_points, invalid, dtype=int) points_int = np.asarray(points, dtype=int) for d in range(n_dim): if d == 0: valid = np.logical_and(points_int[:, d] >= 0, points_int[:, d] < atlas_shape[d]) else: valid = np.logical_and( valid, np.logical_and(points_int[:, d] >= 0, points_int[:, d] < atlas_shape[d])) indices = [points_int[valid, d] for d in range(n_dim)] label[valid] = atlas[indices] if key != 'id' or level is not None: label[valid] = convert_label(label[valid], key='id', value=key, level=level) return label
def write_points(filename, points, indices = False, binary = True): """Write points as elastix/transformix point file Arguments --------- filename : str File name of the elastix point file. points : array or str Source of the points. indices : bool Write as pixel indices or physical coordiantes. Returns ------- filename: str File name of the elastix point file. """ points = io.read(points); if binary: with open(filename, 'wb') as pointfile: if indices: np.array(1, dtype = np.int64).tofile(pointfile) else: np.array(0, dtype = np.int64).tofile(pointfile) num_points = np.array(len(points), dtype = np.int64); num_points.tofile(pointfile); points = np.asarray(points, dtype = np.double); points.tofile(pointfile); pointfile.close(); else: with open(filename, 'w') as pointfile: if indices: pointfile.write('index\n') else: pointfile.write('point\n') pointfile.write(str(points.shape[0]) + '\n'); np.savetxt(pointfile, points, delimiter = ' ', newline = '\n', fmt = '%.5e') pointfile.close(); return filename;
def average(source, sink=None, shape=None, dtype=None, weights=None, indices=None, kernel=None, return_counts=False, processes=None, verbose=False): """Averages a list of points into an volumetric image array. Arguments --------- source : str, array or Source Source of point of nxd coordinates. sink : str, array or None The sink for the devolved image, if None return array. shape : tuple, str or None Shape of the final devolved data. If None, determine from points. If str, determine shape from the source at the specified location. dtype : dtype or None Optional data type of the sink. weights : array or None Weight array of length n for each point. If None, use uniform weights. method : str Method for voxelization: 'sphere', 'rectangle' or 'pixel'. indices : array The relative indices to the center to devolve over as nxd array. kernel : array Optional kernel weights for each index in indices. processes : int or None Number of processes to use. verbose : bool If True, print progress info. Returns ------- sink : str, array Volumetric data of devolved point data. """ processes, timer = ap.initialize_processing(processes=processes, verbose=verbose, function='devolve') #points, points_buffer = ap.initialize_source(points); points_buffer = io.read(source) if points_buffer.ndim == 1: points_buffer = points_buffer[:, None] if sink is None and shape is None: if points_buffer.ndim > 1: shape = tuple( int(math.ceil(points_buffer[:, d].max())) for d in range(points_buffer.shape[1])) else: shape = (int(math.ceil(points_buffer[:].max())), ) elif isinstance(shape, str): shape = io.shape(shape) if sink is None and dtype is None: if weights is not None: dtype = io.dtype(weights) elif kernel is not None: kernel = np.asarray(kernel) dtype = kernel.dtype else: dtype = int sink, sink_buffer, sink_shape, sink_strides = ap.initialize_sink( sink=sink, shape=shape, dtype=dtype, return_shape=True, return_strides=True, as_1d=True) #TODO: initialize properly counts = np.zeros(sink_shape, dtype=int, order=sink.order) counts_buffer = counts.reshape(-1, order='A') #print(counts.shape, counts_buffer.shape) if indices is None: return sink indices = np.asarray(indices, dtype=int) if indices.ndim == 1: indices = indices[:, None] if kernel is not None: kernel = np.asarray(kernel, dtype=float) #print(kernel); #print(weights) #return; code.average(points_buffer, weights, indices, sink_buffer, sink_shape, sink_strides, counts_buffer, processes) # if weights is None: # if kernel is None: # code.devolve_uniform(points_buffer, indices, sink_buffer, sink_shape, sink_strides, processes); # else: # code.devolve_uniform_kernel(points_buffer, indices, kernel, sink_buffer, sink_shape, sink_strides, processes); # else: # if kernel is None: # code.devolve_weights(points_buffer, weights, indices, sink_buffer, sink_shape, sink_strides, processes); # else: # code.devolve_weights_kernel(points_buffer, weights, indices, kernel, sink_buffer, sink_shape, sink_strides, processes); #TODO: move to code good = counts_buffer > 0 sink_buffer[good] /= counts_buffer[good] ap.finalize_processing(verbose=verbose, function='devolve', timer=timer) if return_counts: return sink, counts else: return sink
def transform_points(source, sink = None, transform_parameter_file = None, transform_directory = None, indices = False, result_directory = None, temp_file = None, binary = True): """Transform coordinates math:`x` via elastix estimated transformation to :math:`T(x)`. Arguments --------- source : str Source of the points. sink : str or None Sink for transformed points. 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. indices : bool If True use points as pixel coordinates otherwise spatial coordinates. result_directory : str or None Elastic result directory. temp_file : str or None Optional file name for the elastix point file. Returns ------- points : array or st Array or file name of transformed points. Note ---- The transformation is from the fixed image coorindates to the moving image coordiantes. """ check_elastix_initialized(); # input point file if temp_file == None: if binary: temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.bin'); else: temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.txt'); delete_point_file = None; if isinstance(source, str): if len(source) > 3 and source[-3:] in ['txt', 'bin']: if source[-3:] == 'txt': binary = False; if source[-3] == 'bin': binary = True; pointfile = source; else: points = io.read(source); pointfile = temp_file; delete_point_file = temp_file; write_points(pointfile, points, indices = indices, binary = binary); elif isinstance(source, np.ndarray): pointfile = temp_file; delete_point_file = temp_file; write_points(pointfile, source, indices = indices, binary = binary); else: raise RuntimeError('transform_points: source not string or array!'); #print(pointfile) # result directory if result_directory == None: outdirname = os.path.join(tempfile.gettempdir(), 'elastix_output'); delete_result_directory = outdirname; else: outdirname = result_directory; delete_result_directory = None; if not os.path.exists(outdirname): os.makedirs(outdirname); #transform 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); #run transformix cmd = '%s -def %s -out %s -tp %s' % (transformix_binary, pointfile, outdirname, transform_parameter_file); print(cmd) res = os.system(cmd); if res != 0: raise RuntimeError('failed executing ' + cmd); # read data and clean up if delete_point_file is not None: os.remove(delete_point_file); #read data / file if sink == []: # return sink as file name if binary: return os.path.join(outdirname, 'outputpoints.bin') else: return os.path.join(outdirname, 'outputpoints.txt') else: if binary: transpoints = read_points(os.path.join(outdirname, 'outputpoints.bin'), indices = indices, binary = True); else: transpoints = read_points(os.path.join(outdirname, 'outputpoints.txt'), indices = indices, binary = False); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return io.write(sink, transpoints);
def deformation_field(sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None): """Create the deformation field T(x) - x. Arguments --------- sink : str, [] or None Image sink to save the transformation field; 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 ------- deformation_field : array or str Array or file name of the deformation field data. Note ---- The map determined by elastix is :math:`T \\mathrm{fixed} \\rightarrow \\mathrm{moving}`. """ check_elastix_initialized(); # 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); # setup transformation 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.txt cmd = '%s -def all -out %s -tp %s' % (transformix_binary, resultdirname, transform_parameter_file) res = os.system(cmd); if res != 0: raise RuntimeError('deformation_field: failed executing: ' + cmd); # read result and clean up 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('deformation_field: sink not valid!'); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return result;
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_inverse(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 inversely. sink : str or None Sink for the inversly resmapled points. resample_source : str, array or None Optional source as in :func:`resample`. resample_sink: str, array or None Optional sink used in :func:`resample`. orientation : tuple Orientation as specified 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 inversly resampled point coordinates. Notes ----- * The resampling of points here corresponds to the inverse resampling of an image in :func:`resample`, i.e. to func:`resample_inverse` * The arguments should be passed exactly as in :func:`resample` except source 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.read(source); # reorient points if orientation is not None: #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: points = points[slicing]; #permute per = orientation_to_permuation(orientation); points = points.transpose(per); points = points[:] / resample_factor; return io.write(sink, points);
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 voxelize(source, sink=None, shape=None, dtype=None, weights=None, method='sphere', radius=(1, 1, 1), kernel=None, processes=None, verbose=False): """Converts a list of points into an volumetric image array Arguments --------- source : str, array or Source Source of point of nxd coordinates. sink : str, array or None The sink for the voxelized image, if None return array. shape : tuple or None Shape of the final voxelized data. If None, deterimine from points. dtype : dtype or None Optional data type of the sink. weights : array or None Weight array of length n for each point. If None, use uniform weights. method : str Method for voxelization: 'sphere', 'rectangle' or 'pixel'. radius : tuple Radius of the voxel region to integrate over. kernel : function Optional function of distance to set weights in the voxelization. processes : int or None Number of processes to use. verbose : bool If True, print progress info. Returns ------- sink : str, array Volumetric data of voxelied point data. """ points = io.read(source) points_shape = points.shape if len(points_shape) > 1: ndim = points_shape[1] else: ndim = 1 if not hasattr(radius, '__len__'): radius = [radius] * ndim if len(radius) != ndim: raise ValueError( 'Radius %r and points with shape %r do not match in dimension!' % (radius, points_shape)) if method == 'sphere': indices, kernel = search_indices_sphere(radius, kernel) elif method == 'rectangle': indices, kernel = search_indices_rectangle(radius, kernel) elif method == 'pixel': indices = np.array(0, dtype=int) if kernel is not None: kernel = np.array([kernel(0)]) else: raise ValueError( "method not 'sphere', 'rectangle', or 'pixel', but %r!" % method) return dpl.devolve(points, sink=sink, shape=shape, dtype=dtype, weights=weights, indices=indices, kernel=kernel, processes=processes, verbose=verbose)