def _parallel_mask(args): source, sink, idxs, threshold, min_size = args source = io.readData(source) sink = io.readData(sink) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) for i in idxs: print(f'Mask slice {i}') # mask image if threshold: _, img = cv2.threshold(source[..., i], threshold, 255, cv2.THRESH_BINARY) # extract only brain mask nlabels, labels, stats, centroid = cv2.connectedComponentsWithStats( img.astype(np.uint8)) sample_labels = np.where( stats[1:, ..., 4] > min_size)[0] + 1 # get largest shapes img = np.isin(labels, sample_labels).astype(np.uint8) else: img = np.array(source[..., i]).copy() # remove holes in brain mask contours, hier = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contours.sort(key=len) # keep biggest contours img = np.zeros(img.shape) for c in contours[-5:]: img = cv2.drawContours(img, [c], 0, 255, -1) img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) sink[..., i] = img.astype(np.uint8)
def label_props(img, labels, props): """Joins a list of coordiantes from distributed processing based on their IDs into a single list. Also. Handles duplicate ID entries bbased on Arguments: img (np.array): raw image labels (np.array): labeled image props (list): list of strings where each string is the attibute from region_props to export Returns: array: label coordinates (list of tuples), intensities (list), sizes (list) """ img = io.readData(img) labels = io.readData(labels) timer = Timer() log_parameters(props=props) # get label properties regions = region_props(labels, img) # get relavant properties res = [] for prop in props: prop_res = [] for region in regions: method = getattr(region, prop) prop_res.append(method()) res.append(prop_res) timer.log_elapsed() return res
def erode(mask, sink, offset_z, offset_x, processes=1): # offset surface in z mask = io.readData(mask) sink = io.readData(sink) z_idxs = list(range(mask.shape[-1])) z_chunks = [z_idxs[i::processes] for i in range(processes)] z_args = [(mask.filename, sink.filename, z_idxs, offset_x) for z_idxs in z_chunks] x_idxs = list(range(mask.shape[0])) x_chunks = [x_idxs[i::processes] for i in range(processes)] x_args = [(sink.filename, sink.filename, x_idxs, offset_z) for x_idxs in x_chunks] if processes == 1: _parallel_erode_z(*z_args) _parallel_erode_x(*x_args) else: pool = Pool(processes) pool.map(_parallel_erode_z, z_args) pool.map(_parallel_erode_x, x_args) pool.close() return sink
def copyData(source, sink, x=None, y=None, z=None, returnMemmap=False): """Copy a data file from source to sink Arguments: source (str): file name pattern of source sink (str): file name pattern of sink returnMemmap (bool): returns the result as an array Returns: str: file name of the copy """ out_type = io.dataFileNameToType(sink) if out_type == 'TIF': if isinstance(source, np.memmap) and x == y == y == z == None: shutil.copyfile(source.filename, sink) else: Xsize, Ysize, Zsize = io.dataSize(source) # cropped size Xsize = io.toDataSize(Xsize, r=x) Ysize = io.toDataSize(Ysize, r=y) Zsize = io.toDataSize(Zsize, r=z) im = io.readData(source, x=x, y=y, z=z) out = io.writeData(sink, im, returnMemmap=returnMemmap) if returnMemmap: return io.readData(sink) else: return sink else: raise RuntimeError( 'copying from TIF to {} not yet supported.'.format(out_type))
def transformImage(image, reference, transformDirectory, sink=None, invert=False, interpolation='bspline'): """Transform a raw data set to reference using the ANTs alignment results Arguments: source (str or array): image source to be transformed reference (str or array): fixed image from transform transformDirectory (str): Directory containing ANTS transform parameters sink (str or None): image sink to save transformed image to. interpolation (str): ANTS interpolator to use for generating image. Returns: array or str: file name of the transformed data. If sink is None, return array """ log.info('transforming image with ' + transformDirectory) log.info('invert: {}'.format(invert)) # get image and tranform im = ants.from_numpy(io.readData(image).astype('float32')) ref = ants.from_numpy(io.readData(reference).astype('float32')) composite_trans = _compose_transforms(transformDirectory, invert=invert) # apply transforms res = composite_trans.apply_to_image(im, ref, interpolation=interpolation) # output if isinstance(sink, str): return io.writeData(sink, res.numpy()) else: return res.numpy()
def readDataFiles(filename, x=None, y=None, z=None, **args): """Read data from individual images assuming they are the z slices Arguments: filename (str): file name as regular expression x,y,z (tuple): data range specifications Returns: array: image data """ fpath, fl = readFileList(filename) nz = len(fl) #read first image to get data size and type rz = io.toDataRange(nz, r=z) sz = io.toDataSize(nz, r=z) fn = os.path.join(fpath, fl[rz[0]]) img = io.readData(fn, x=x, y=y) nxy = img.shape data = numpy.zeros(nxy + (sz, ), dtype=img.dtype) data[:, :, 0] = img for i in range(rz[0] + 1, rz[1]): fn = os.path.join(fpath, fl[i]) data[:, :, i - rz[0]] = io.readData(fn, x=x, y=y) return data
def overlay_label(dataSource, labelSource, output=None, alpha=False, labelColorMap='jet', x=all, y=all, z=all): """Overlay a gray scale image with colored labeled image Arguments: dataSouce (str or array): volumetric image data labelSource (str or array): labeled image to be overlayed on the image data output (str or None): destination for the overlayed image alpha (float or False): transparency labelColorMap (str or object): color map for the labels x, y, z (all or tuple): sub-range specification Returns: (array or str): figure handle See Also: :func:`overlayPoints` """ label = io.readData(labelSource, x=x, y=y, z=z) image = io.readData(dataSource, x=x, y=y, z=z) lmax = label.max() if lmax <= 1: carray = numpy.array([[1, 0, 0, 1]]) else: cm = mpl.cm.get_cmap(labelColorMap) cNorm = mpl.colors.Normalize(vmin=1, vmax=int(lmax)) carray = mpl.cm.ScalarMappable(norm=cNorm, cmap=cm) carray = carray.to_rgba(numpy.arange(1, int(lmax + 1))) if not alpha: carray = numpy.concatenate(([[0, 0, 0, 1]], carray), axis=0) else: carray = numpy.concatenate(([[1, 1, 1, 1]], carray), axis=0) cm = mpl.colors.ListedColormap(carray) carray = cm(label) carray = carray.take([0, 1, 2], axis=-1) if not alpha: cimage = (label == 0) * image cimage = numpy.repeat(cimage, 3) cimage = cimage.reshape(image.shape + (3, )) cimage = cimage.astype(carray.dtype) cimage += carray else: cimage = numpy.repeat(image, 3) cimage = cimage.reshape(image.shape + (3, )) cimage = cimage.astype(carray.dtype) cimage *= carray return io.writeData(output, cimage)
def _generate_output(self): mask_f = self.temp_dir / 'mask.tif' erode_f = self.temp_dir / 'erode.tif' self.log.info('preparing temp files') tif.tifffile.memmap(mask_f, dtype=np.uint8, shape=self.input.T.shape, bigtiff=True) tif.tifffile.memmap(erode_f, dtype=np.uint8, shape=self.input.T.shape, bigtiff=True) mask = io.readData(mask_f) erode_im = io.readData(erode_f) # extract brain mask z_idxs = list(range(mask.shape[-1])) z_chunks = [z_idxs[i::self.processes] for i in range(self.processes)] args = [(self.input.filename, mask.filename, z_idxs, self.threshold, self.min_size) for z_idxs in z_chunks] if self.processes == 1: _parallel_mask(*args) else: pool = Pool(self.processes) pool.map(_parallel_mask, args) pool.close() erode(mask.filename, erode_im.filename, self.offset_z, self.offset_x, processes=self.processes) # merge erosions and mask args = [(mask.filename, erode_im.filename, z_idxs) for z_idxs in z_chunks] if self.processes == 1: _parallel_merge_mask(*args) else: pool = Pool(self.processes) pool.map(_parallel_merge_mask, args) pool.close() if self.save_mask: self.log.info(f'saving mask to {self.save_mask}') io.writeData(self.save_mask, mask) # mask input # not working self.log.info(f'masking image') for z in range(self.input.shape[2]): im = self.input[:, :, z] im[mask[:, :, z] == 0] = 0 self.input[:, :, z] = im return self.input
def _parallel_merge_mask(args): mask, erosion, idxs = args bkg_mask = io.readData(mask) erosion = io.readData(erosion) for z in idxs: print(f'Generating output for slice {z}') merge = np.full(bkg_mask.shape[:2], 255, dtype=np.uint8) merge[bkg_mask[:, :, z] == 0] = 0 merge[erosion[:, :, z] != 0] = 0 bkg_mask[:, :, z] = merge
def _parallel_erode_x(args): source, sink, idxs, offset = args source = io.readData(source) sink = io.readData(sink) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) for i in idxs: print(f'Erode slice {i} along x') img = source[i].astype(np.uint8) sink[i] = cv2.erode(img, kernel, iterations=offset)
def alignData(fixedImage, movingImage, resultDirectory=None, type_of_transform='SyNRA', **kwargs): """Align images using elastix, estimates a transformation :math:`T:` fixed image :math:`\\rightarrow` moving image. Arguments: fixedImage (str): image source of the fixed image (typically the reference image) movingImage (str): image source of the moving image (typically the image to be registered) resultDirectory (str or None): result directory for transform parameters. None saves to bq3ddefault temp file transform: (str): type of transform to apply as defined in 'ants.registration' type_of_transform **kwargs: additional arguments to pass to 'ants.registration' Returns: str: path to elastix result directory """ log_parameters(fixedImage=fixedImage, movingImage=movingImage, resultDirectory=resultDirectory, type_of_transform=type_of_transform) # setup input mi = ants.from_numpy(io.readData(movingImage).astype('float32')) fi = ants.from_numpy(io.readData(fixedImage).astype('float32')) # setup output directory if not resultDirectory: tmp_folder = os.path.join(config.temp_dir, 'ANTs') resultDirectory = tmp_folder resultDirectory = resultDirectory + '/' if not resultDirectory.endswith( '/') else resultDirectory #make sure ends with '/' os.makedirs(resultDirectory, exist_ok=True) # run result = ants.registration(fi, mi, type_of_transform=type_of_transform, outprefix=resultDirectory, verbose=True, **kwargs) # save output io.writeData(os.path.join(resultDirectory, 'result.tif'), result['warpedmovout'].numpy()) # cleanup# if not resultDirectory: shutil.rmtree(tmp_folder) return resultDirectory
def grays_to_rand_rgb(source, output): ftype = io.dataFileNameToType(source) if ftype == 'TIF': log.info(f'Generating random RGB imgage for {ftype}') data = io.readData(source).astype(int) max_label = int(np.max(data)) # create lut lut_r = np.array(random.sample(list(range(0, max_label)), max_label)) lut_g = np.array(random.sample(list(range(0, max_label)), max_label)) lut_b = np.array(random.sample(list(range(0, max_label)), max_label)) # downsample to 8bit lut_r = ((255 / max_label) * lut_r).astype(int) lut_g = ((255 / max_label) * lut_g).astype(int) lut_b = ((255 / max_label) * lut_b).astype(int) # add 0 value lut_r = np.insert(lut_r, 0, 0) lut_g = np.insert(lut_g, 0, 0) lut_b = np.insert(lut_b, 0, 0) rgb_output = np.zeros(data.shape + (3, ), dtype='uint8') rgb_output[..., 0] = lut_r[data] rgb_output[..., 1] = lut_g[data] rgb_output[..., 2] = lut_b[data] io.writeData(output, rgb_output, rgb=True) else: raise RuntimeError( f'Conversion to random RGB not supported for {ftype}') return output
def labels_to_coords(label_im: np.array): """ Converts each label in an image to a set of coordinates Args: label_im (np.array): labeled image Returns: (dict) dict of coordinated in format { 'label_id': [[x,y,z],...] } """ im = io.readData(label_im) region_coords = {} for z in range(im.shape[2]): print(z) regions = regionprops(im[..., z]) for reg in regions: coord = list(np.insert(reg.coords(), 2, z, 1)) if reg.label in region_coords: region_coords[reg.label].extend(coord) else: region_coords[reg.label] = coord for id in region_coords: region_coords[id] = np.array(region_coords[id]) return region_coords
def resampleXY(source, dataSizeSink, zList=[0], sink=None, interpolation='linear'): """Resample an image stack along the a 2d slice This routine is used for resampling a large stack in parallel in xy or xz direction. Arguments: source (str or array): 2d image source dataSizeSink (tuple): size of the resmapled image zList (int): z planes to crop sink (str or None): location for the resmapled image interpolation (int): CV2 interpolation method to use Returns: array or str: resampled data or file name """ for i in zList: data = io.readData(source, z=i) log.verbose( f'resample XY: Resampling plane {i} to size: ({dataSizeSink[0]}, ' f'{dataSizeSink[1]})') # note: cv2.resize reverses x-Y axes sink[i] = cv2.resize(data, (dataSizeSink[2], dataSizeSink[1]), interpolation=interpolation) return sink
def _populate_region_coordiantes(self, labels): """ adds coordinate info to regions under region.voxels. Will also generate self.volume. Arguments: labels (array or str): labeled image with values corresponding to region id """ image = io.readData(labels, returnMemmap = False).astype(int) # to facilitate pooling and speed up log.verbose(f'calculating region info from {labels}') properties = region_props(image) # add voxels for i,prop in enumerate(properties): log.verbose(f'retrieving coordinates and densities for region {i}') region = self.get_region_by_id(prop.label) region.volume = int(prop.area()) for c in prop.coords(): c = tuple(c) region.add_voxel(c) self.regions_by_coord[c] = region # label value 0 is ignored by region_props self.get_region_by_id(0).add_volume(int(np.sum(image == 0))) if self.COLLAPSE: for region in PostOrderIter(self.tree_root): region.collapse_volume()
def getDataType(filename): """gets dtype of data in file Arguments: filename (str): file name Returns: dtype: data type """ return io.readData(filename).dtype
def writePoints(filename, points, labelImage = None): """Write point data to vtk file Arguments: filename (str): file name points (array): point data labelImage (str, array or None): optional label image to determine point label Returns: str: file name """ x = points[:,0] y = points[:,1] z = points[:,2] nPoint = x.size pointLabels = numpy.ones(nPoint) if not labelImage is None: if isinstance(labelImage, str): labelImage = io.readData(labelImage) dsize = labelImage.shape for i in range(nPoint): if 0 <= x[i] < dsize[0] and 0 <= y[i] < dsize[1] and 0 <= z[i] < dsize[2]: pointLabels[i] = labelImage[x[i], y[i], z[i]] #write VTK file vtkFile = file(filename, 'w') vtkFile.write('# vtk DataFile Version 2.0\n') vtkFile.write('Unstructured Grid Example\n') vtkFile.write('ASCII\n') vtkFile.write('DATASET UNSTRUCTURED_GRID\n') vtkFile.write("POINTS " + str(nPoint) + " float\n") for iPoint in range(nPoint): vtkFile.write(str(x[iPoint]).format('%05.20f') + " " + str(y[iPoint]).format('%05.20f') + " " + str(z[iPoint]).format('%05.20f') + "\n") vtkFile.write("CELLS " + str(nPoint) + " " + str(nPoint * 2) + "\n") for iPoint in range(nPoint): vtkFile.write("1 " + str(iPoint) + "\n") vtkFile.write("CELL_TYPES " + str(nPoint) + "\n") for iPoint in range(0, nPoint): vtkFile.write("1 \n") vtkFile.write("POINT_DATA " + str(nPoint) + "\n") vtkFile.write('SCALARS scalars float 1\n') vtkFile.write("LOOKUP_TABLE default\n") for iLabel in pointLabels: vtkFile.write(str(int(iLabel)) + " ") vtkFile.write("\n") vtkFile.close() return filename
def _parallelCopyToTif(args): """copies FileList to Tif in parallel""" sources, idxs, sink, Xrng, Yrng = args output = io.readData(sink) for i, idx in enumerate(idxs): file = sources[i] log.debug(f'copyData: copying {file} to {sink}') im = tif.imread(file) if not Xrng == Yrng == None: output[idx] = io.dataToRange(im, x=Xrng, y=Yrng) else: output[idx] = im
def readData(filename, **args): """Read image stack from single or multiple images Arguments: filename (str): file name as regular expression x,y,z (tuple): data range specifications Returns: array: image data """ if os.path.exists(filename): return io.readData(filename, **args) else: return readDataFiles(filename, **args)
def _resampleXYParallel(arg): """Resampling helper function to use for parallel resampling of image slices""" fileSource = arg[0] fileSink = arg[1] dataSizeSink = arg[2] interpolation = arg[3] zChunks = arg[4] sink = io.readData(fileSink) resampleXY(fileSource, zList=zChunks, sink=sink, dataSizeSink=dataSizeSink, interpolation=interpolation)
def readDataGroup(filenames, combine=True, **args): """Turn a list of filenames for data into a np stack""" # check if stack already an array if isinstance(filenames, np.ndarray): return filenames #read the individual files group = [] for f in filenames: data = io.readData(f, **args) data = np.reshape(data, (1, ) + data.shape) group.append(data) if combine: return np.vstack(group) else: return group
def readData(filename, **args): """Read nrrd file image data Arguments: filename (str): file name as regular expression x,y,z (tuple): data range specifications Returns: array: image data """ with open(filename,'rb') as filehandle: header = readHeader(filehandle) data = _read_data(header, filehandle, filename) #return (data, header) #return data.transpose([1,0,2]) data = io.readData(data, **args) return data
def filter_image(filter, input, output=None, temp_dir_root=None, **kwargs): """ Passes an image through the specified image filter. Arguments: filter (str): filter to use. string should match filter class name. source (str or array): Image to filter kwargs (dict): additional arguments to pass to the filter as 'argument': value. These will be parsed into the filters attributes. key string must match a filters attribute name. Returns: (array): filtered image """ input = io.readData(input) kwargs['input'] = input kwargs['output'] = input im_filter = set_filter(filter, kwargs) if temp_dir_root: im_filter.set_temp_dir(root=temp_dir_root) return im_filter.run()
def deformationDistance(deformationField, sink=None, scale=None): """Compute the distance field from a deformation vector field Arguments: deformationField (str or array): source of the deformation field determined by :func:`deformationField` 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: array or str: array or file name of the transformed data """ deformationField = io.readData(deformationField) df = np.square(deformationField) if not scale is None: for i in range(3): df[:, :, :, i] = df[:, :, :, i] * (scale[i] * scale[i]) return io.writeData(sink, np.sqrt(np.sum(df, axis=3)))
def _generate_output(self): if self.background is None: raise RuntimeError('background not defined') else: self.background = io.readData(self.background) if self.shift_z != 0: shifted = tif.writeData(self.temp_dir / f'{uuid.uuid4()}.tif', self.background, returnMemmap=True) for z in range(self.background.shape[0]): try: shifted[z + self.shift_z, ] = self.background[z] except: continue # if out of bounds self.background = shifted img = self.input orig_shape = img.shape if len(orig_shape) < 3: img = img[np.newaxis, ...] if self.method == 'mean': img_mean = img.mean() bkg_mean = self.background.mean() ratio = img_mean / bkg_mean for z in range(img.shape[0]): bkg = (self.background[z] * ratio).astype(img.dtype) sub = img[z].astype(np.int32) - bkg sub[sub < 0] = 0 img[z] = sub else: raise ValueError(f'Method {self.method} not recongnized') img.shape = orig_shape return self.input
def _generate_output(self): # extract brain mask method = self.method.lower() if self.mask: mask = io.readData(self.mask) if method == 'max': sink = np.zeros(self.input.shape[1:], dtype=self.input.dtype) if self.mask: for z in range(self.input.shape[0]): slice = np.array(self.input[z]) mask_s = np.array(mask[z]) slice[mask_s == 0] = 0 sink = cv2.max(sink, slice) else: for z in range(self.input.shape[0]): sink = cv2.max(sink, self.input[z]) return sink if method == 'min': max_v = np.iinfo(self.input.dtype).max sink = np.full(self.input.shape[1:], max_v, dtype=self.input.dtype) if self.mask: for z in range(self.input.shape[0]): slice = np.array(self.input[z]) mask_s = np.array(mask[z]) slice[mask_s == 0] = max_v sink = cv2.min(sink, slice) else: for z in range(self.input.shape[0]): sink = cv2.min(sink, self.input[z]) sink = sink sink[sink == max_v] = 0 return sink
def _generate_output(self): self._initialize_Ilastik() # create temp npy input_fn = str((self.temp_dir / Path(self.input.filename).stem).with_suffix('.npy')) io.writeData(input_fn, self.input) output_fn = str(self.temp_dir / 'out_prelim.npy') ilinp = self._filename_to_input_arg(input_fn) ilout = self._filename_to_output_arg(output_fn) cmd_args = f'--project="{self.project}" {ilout} {ilinp}' self.run_headless(cmd_args) output = io.readData(output_fn) output_chan = self.temp_dir / 'out.npy' # transpose to restore input dimensionality output_chan = io.writeData(output_chan, output[..., self.output_channel], returnMemmap=True) return output_chan
def _generate_output(self): # label return count if self.sigmas: if self.input.ndim != len(self.sigmas): raise ValueError( 'Sigmas must have same length as image dimensions.') # Smooth image self.log.verbose('Smoothing image.') self.input[:] = gaussian_filter(self.input, sigma=self.sigmas) raw_img = self.input if self.mode == 3: # Pad image by 1 pixel in each dimension self.log.debug('Padding image.') padded_img = tif.tifffile.memmap( os.path.join(self.temp_dir, 'temp_padded_img.tif'), dtype=raw_img.dtype, shape=(tuple(x + 2 for x in raw_img.shape))) if raw_img.ndim == 3: padded_img[1:-1, 1:-1, 1:-1] = raw_img if raw_img.ndim == 2: padded_img[1:-1, 1:-1] = raw_img raw_img = padded_img bin_img = tif.tifffile.memmap(os.path.join(self.temp_dir, 'temp_bin_img.tif'), dtype=np.uint8, shape=raw_img.shape) labeled_1_img = tif.tifffile.memmap(os.path.join( self.temp_dir, 'temp_labeled_1_img.tif'), dtype=np.int32, shape=raw_img.shape) # Binarize image self.log.debug('Thresholding') threshold(raw_img, self.high_threshold, bin_img) # Label image self.log.debug('Labeling') connect(bin_img, labeled_1_img) # Filter labeled regions by size (1st pass) # Mode 1: Stop after this self.log.debug('Size filtering') _, counts = size_filter(labeled_1_img, self.min_size, self.max_size, labeled_1_img) if len(counts) == 0: if self.mode == 3: if self.input.ndim == 2: labeled_1_img = labeled_1_img[1:-1, 1:-1] else: labeled_1_img = labeled_1_img[1:-1, 1:-1, 1:-1] out = io.empty(os.path.join(self.temp_dir, 'output.tif'), shape=labeled_1_img.shape, dtype=labeled_1_img.dtype) out[:] = 0 self.log.debug('No components found in first threshold.') return out if self.mode == 1: return io.readData(labeled_1_img.filename) # Mode 2 two serial thresholding>label> filter runs elif self.mode == 2: labeled_2_img = tif.tifffile.memmap(os.path.join( self.temp_dir, 'temp_labeled_2_img.tif'), dtype=np.int32, shape=raw_img.shape) self.log.debug('Low thresholding') threshold(raw_img, self.low_threshold, bin_img) self.log.debug('Labeling.') _ = connect(bin_img, labeled_2_img) self.log.debug('Comparing overlap.') overlap(labeled_1_img, labeled_2_img, labeled_2_img) self.log.debug('Running final size filter.') _, counts = size_filter(labeled_2_img, self.min_size2, self.max_size2, labeled_2_img) if len(counts) == 0: self.log.debug('No components found in second threshold.') return io.readData(labeled_2_img.filename) return io.readData(labeled_1_img.filename) # Mode 3 two serial thresholds with identity preservation elif self.mode == 3: # Low threshold Image self.log.debug('Low thresholding.') threshold(raw_img, self.low_threshold, bin_img) ############################## Watershed ############################## # Get coordinates of all nonzero values in labeled/size-filtered image self.log.debug('Getting label coordinates.') marker_locations_filename = os.path.join(self.temp_dir, 'marker_locations.mmap') marker_locations = nonzero_coords(labeled_1_img, marker_locations_filename) connectivity, offset = structure_element_binary(raw_img.ndim, connectivity=1, offset=None) flat_neighborhood = _offsets_to_raveled_neighbors(raw_img.shape, connectivity, center=offset) image_strides = np.array(raw_img.strides, dtype=np.intp) // raw_img.itemsize self.log.debug('Running watershed.') watershed( raw_img, marker_locations, flat_neighborhood, bin_img, image_strides, labeled_1_img, # <-- Output True) # <-- Inverted watershed ####################################################################### # Final size filter self.log.debug('Running final size filter.') _, counts = size_filter(labeled_1_img, self.min_size2, self.max_size2, labeled_1_img) if len(counts) == 0: self.log.debug( 'No components found in after inverse watershed.') if self.input.ndim == 2: labeled_1_img = labeled_1_img[1:-1, 1:-1] else: labeled_1_img = labeled_1_img[1:-1, 1:-1, 1:-1] out = io.empty(os.path.join(self.temp_dir, 'output.tif'), shape=labeled_1_img.shape, dtype=labeled_1_img.dtype) out[:] = labeled_1_img return out
def overlay_points(pointSource, dataSource, output=None, overlay=True, x=all, y=all, z=all): """Overlay points on 3D data and return as color image Arguments: pointSource (str or array): point data to be overlayed on the image data dataSource (str or array): volumetric image data. if None just output points as an image overlay (bool): if False will not overlay and just output points as an image. x, y, z (all or tuple): sub-range specification Returns: (str or array): image overlayed with points See Also: :func:`overlayLabel` """ points = (io.readPoints(pointSource, x=x, y=y, z=z, shift=True)).astype(int) if overlay: X, Y, Z = io.dataSize(dataSource) datatype = io.readData(dataSource, x=x, y=y, z=0).dtype if io.isMappable(output): output = tif.tifffile.memmap(output, dtype=datatype, shape=(Z, 2, Y, X), imagej=True) # TZCYXS FIJI elif io.isFileExpression(output): output = [ tif.tifffile.memmap(output, dtype=datatype, shape=(2, Y, X), imagej=True) for z in range(Z) ] # sequence of memmap files elif output is None: output = numpy.zeros((Z, 2, Y, X), dtype=datatype) else: RuntimeError('output format not compatable with overlayPoints: ' + output) for z in range(Z): print(('Overlaying {}...'.format(z))) output[z][0][:] = io.readData(dataSource, x=x, y=y, z=z).squeeze().T z_points = points[points[:, -1] == z][:, :2] output[z][1][[*z_points[:, ::-1].T]] = 1 return output else: shape = io.dataSize(dataSource) cimage = vox.voxelize( points, shape, output=output, method='Pixel', weights=65535 ) # TODO: weight should depend on bit depth of dataSource return cimage
def writeData(filename, data, rgb=False, substack=None, returnMemmap=True): """Write image data to tif file Arguments: filename (str): file name data (array): image data in x,y,z rgb (bool): if true will save RGB image. channels should be last array axis returnMemmap (bool): returns array rather than file name Returns: str or np.array: output file name or memory mapped array """ fn, fe = os.path.splitext( filename ) # initial write to partial filename to prevent creating incomplete files d = len(data.shape) # fiji wants 'TZCYXS' dtype = data.flat[0].dtype if substack: sub = range_to_slices(substack) data_map = io.readData(filename) data_map[sub] = data else: if not rgb: if d == 2: # XY data_map = tif.tifffile.memmap(fn, dtype=dtype, shape=data.shape) elif d == 3: # XYZ data_map = tif.tifffile.memmap( fn, dtype=dtype, shape=data.shape, bigtiff=True) #imageJ = true not work for int32 elif d == 4: #XYZC data_map = tif.tifffile.memmap(fn, dtype=dtype, shape=data.shape, imagej=True) else: raise RuntimeError( 'writing {} dimensional data to tif not supported!'.format( len(data.shape))) else: if d == 3: # XYC data_map = tif.tifffile.memmap( fn, dtype=dtype, shape=data.shape, imagej=True) #imageJ = true not work for int32 elif d == 4: # XYZS data_map = tif.tifffile.memmap(fn, dtype=dtype, shape=data.shape, imagej=True) else: raise RuntimeError( 'writing {} dimensional data to tif not supported!'.format( len(data.shape))) data_map[:] = data shutil.move(fn, filename) if returnMemmap: return readData(filename) else: return filename