def border_indices(source): """Returns the flat indices of the border pixels in source""" ndim = source.ndim shape = source.shape strides = io.element_strides(source) border = [] for d in range(ndim): offsets = tuple(0 if i > d else 1 for i in range(ndim)) for c in [0, shape[d] - 1]: sl = tuple( slice(o, None if o == 0 else -o) if i != d else c for i, o in enumerate(offsets)) where = np.where(np.logical_not(source[sl])) n = len(where[0]) if n > 0: indices = np.zeros(n, dtype=int) l = 0 for k in range(ndim): if k == d: indices += strides[k] * c else: indices += strides[k] * (where[l] + offsets[k]) l += 1 border.append(indices) return np.concatenate(border)
def _apply_code(function, source, sink, sink_dtype = None, sink_shape_per_pixel = None, parameter = None, sigma = None): """Helper to apply the core functions""" if source.ndim != 3: raise ValueError('The tubness measure is implemented for 3d data, found %dd!' % source.ndim); if source is sink: raise ValueError("Cannot perform operation in place!") if sink_shape_per_pixel is None: shape_per_pixel = (1,); else: shape_per_pixel = sink_shape_per_pixel; if sink is None: if sink_dtype is None: sink_dtype = float sink = np.zeros(source.shape + shape_per_pixel, dtype = sink_dtype, order = 'F') else: if shape_per_pixel != (1,): if sink.shape != source.shape + shape_per_pixel: raise ValueError('The sink of shape %r does not have expected shape %r!' % (sink.shape, source.shape + shape_per_pixel)); if sink.shape != source.shape + shape_per_pixel: sink = sink.reshape(source.shape + shape_per_pixel) if sink.dtype == bool: s = sink.view('uint8') else: s = sink; sink_stride = io.element_strides(sink)[-1]; if parameter is None: parameter = np.zeros(0); parameter = np.asarray([parameter], dtype = float).flatten(); if sigma is not None: data = ndi.gaussian_filter(np.asarray(source, dtype=float), sigma=sigma); else: data = np.asarray(source, dtype=float); function(source=data, sink=s, sink_stride=sink_stride, parameter=parameter) if sink_shape_per_pixel is None: sink = sink.reshape(sink.shape[:-1]); return sink
def _initialize_sink(sink = None, source = None, return_1d = False, return_strides = False, shape = None, dtype = None, order = None, memory = None, location = None): """Helper to initialize sinks.""" if sink is None: if shape is None: if source is None: raise ValueError("Cannot determine sink without source."); else: shape = source.shape; if dtype is None: if source is None: raise ValueError("Cannot determine sink without source."); else: dtype = source.dtype; if order is None and source is not None: order = io.order(source); #print('shape=%r, dtype=%r, order=%r, memory=%r, location=%r' % (shape, dtype, order, memory, location)); sink = io.prepare_sink(sink, shape=shape, dtype=dtype, order=order, memory=memory, location=location); if sink.dtype == bool: sink = sink.view('uint8'); if return_strides: strides = io.element_strides(sink); if return_1d: sink1d = sink.reshape(-1, order = 'A'); if not return_strides and not return_1d: return sink; result = (sink,) if return_strides: result += (strides,); if return_1d: result += (sink1d,); return result;
def threshold(source, sink=None, threshold=None, hysteresis_threshold=None, seeds=None, background=None): """Hysteresis thresholding. Arguments --------- source : array Input source. sink : array or None If None, a new array is allocated. threshold : float The threshold for the intial seeds. hysteresis_threshold : float or None The hysteresis threshold to extend the initial seeds. If None, no hysteresis thresholding is performed. seeds : array or None The seeds from which to start the hysteresis thresholds background : array or None Exclude this area from the hysteresis thresholding. Returns ------- sink : array Thresholded output. """ if threshold is None and seeds is None: raise ValueError('The threshold and seeds cannot both be None!') if source is sink: raise NotImplementedError("Cannot perform operation in place.") if sink is None: sink = np.zeros(source.shape, dtype='int8', order=io.order(source)) source_flat = source.reshape(-1, order=io.order(source)) sink_flat = sink.reshape(-1, order=io.order(sink)) strides = np.array(io.element_strides(source)) if seeds is None: seeds = np.where(source_flat >= threshold)[0] else: seeds_flat = seeds.reshape(-1, order=io.order(seeds)) seeds = np.where(seeds_flat)[0] if hysteresis_threshold is not None: parameter_index = np.zeros(0, dtype=int) parameter_double = np.array([hysteresis_threshold], dtype=float) if background is not None: background_flat = background.reshape(-1, order=io.order(background)) background_flat = background_flat.view(dtype='uint8') code.threshold_to_background(source_flat, sink_flat, background_flat, strides, seeds, parameter_index, parameter_double) else: code.threshold(source_flat, sink_flat, strides, seeds, parameter_index, parameter_double) else: sink_flat[seeds] = 1 return sink
def fill(source, sink=None, seeds=None, processes=None, verbose=False): """Fill binary holes. Arguments --------- source : array Input source. sink : array or None If None, a new array is allocated. Returns ------- sink : array Binary image with filled holes. """ if source is sink: raise NotImplementedError("Cannot perform operation in place.") if verbose: print('Binary filling: initialized!') timer = tmr.Timer() #create temporary shared array order = io.order(source) #temp = sma.empty(source.shape, dtype='int8', order=order); temp = np.empty(source.shape, dtype='int8', order=order) source_flat = source.reshape(-1, order='A') temp_flat = temp.reshape(-1, order='A') if source_flat.dtype == bool: source_flat = source_flat.view(dtype='uint8') if processes is None: processes = mp.cpu_count() if not isinstance(processes, int): processes = 1 #prepare flood fill code.prepare_temp(source_flat, temp_flat, processes=processes) #flood fill in parallel using mp if seeds is None: seeds = border_indices(source) else: seeds = np.where(seeds.reshape(-1, order=order))[0] strides = np.array(io.element_strides(source)) code.label_temp(temp_flat, strides, seeds, processes=processes) if sink is None: #sink = sma.empty(source.shape, dtype=bool, order=order); sink = np.empty(source.shape, dtype=bool, order=order) sink_flat = sink.reshape(-1, order=order) if sink_flat.dtype == 'bool': sink_flat = sink_flat.view(dtype='uint8') code.fill(source_flat, temp_flat, sink_flat, processes=processes) if verbose: timer.print_elapsed_time('Binary filling') del temp, temp_flat gc.collect() return sink
def convolve_3d_indices_if_smaller_than(source, kernel, indices, max_value, sink = None, strides = None, check_border = True, processes = cpu_count()): """Convolves source with a specified kernel at specific points given by a flat array indx under conditon the value is smaller than a number Arguments --------- source : array 3d binary array to convolve. kernel : array Convolution kernel. indices : array Indices to convolve. max_value : float Checks if the convolution result is smaller than this value. sink : array Optional sink to write result to. strides : array The strides of the source in case its given as a 1d list. check_border : bool If True, check if each kernel element is inside the source array shape. processes : int or None Number of processes to use. Returns ------- convolved : array List of results of convolution at specified points """ d = source.reshape(-1, order = 'A'); if source.dtype == bool: d = d.view('uint8'); npts = indices.shape[0]; if sink is None: sink = np.zeros(npts, dtype = bool); if sink.shape[0] != npts: raise RuntimeError('The sinkput has not the expected size of %d but %d' % (npts, sink.shape[0])); if sink.dtype == bool: o = sink.view('uint8'); else: o = sink; if kernel.dtype == bool: k = np.array(kernel, dtype = 'uint8'); else: k = kernel; if processes is None: processes = cpu_count(); if strides is None: strides = np.array(io.element_strides(source), dtype=int); #print d.dtype, strides.dtype, kernel.dtype, o.dtype if check_border: print(d.dtype, strides.dtype, k.dtype, indices.dtype, np.array(max_value).dtype, o.dtype) code.convolve_3d_indices_if_smaller_than(d, strides, k, indices, max_value, o, processes); else: code.convolve_3d_indices_if_smaller_than_no_check(d, strides, k, indices, max_value, o, processes); return sink;
def convolve_3d_indices(source, kernel, indices, sink = None, sink_dtype = None, strides = None, check_border = True, processes = cpu_count()): """Convolves source with a specified kernel at specific points given by a flat array index. Arguments --------- source : array 3d binary array to convolve. kernel : array Convolution kernel. indices : array Indices to convolve. sink : array Optional sink to write result to. sink_dtype : dtype) Optional type of the sink. If None, the kernel type is used as default. strides : array The strides of the source in case its given as a 1d list. check_border : bool If True, check if each kernel element is inside the source array shape. processes : int or None Number of processes to use. Returns ------- convolved : array List of results of convolution. """ d = source.reshape(-1, order = 'A'); if source.dtype == bool: d = d.view('uint8'); npts = indices.shape[0]; if sink is None: if sink_dtype is None: sink_dtype = kernel.dtype; sink = np.zeros(npts, dtype = sink_dtype); if sink.shape[0] != npts: raise RuntimeError('The sinkput has not the expected size of %d but %d' % (npts, sink.shape[0])); if sink.dtype == bool: o = sink.view('uint8'); else: o = sink; if kernel.dtype == bool: k = np.array(kernel, 'uint8'); else: k = kernel; if processes is None: processes = cpu_count(); if strides is None: strides = np.array(io.element_strides(source)); #print d.dtype, strides.dtype, kernel.dtype, o.dtype if check_border: code.convolve_3d_indices(d, strides, k, indices, o, processes); else: code.convolve_3d_indices_no_check(d, strides, k, indices, o, processes); return sink;