def run_function(self, function, pixel_data, mask): '''Apply the function once to the image, returning the result''' count = function.repeat_count function_name = function.function.value scale = function.scale.value custom_repeats = function.custom_repeats.value is_binary = pixel_data.dtype.kind == 'b' if function.structuring_element == SE_ARBITRARY: strel = np.array(function.strel.get_matrix()) elif function.structuring_element == SE_DISK: strel = morph.strel_disk(scale / 2.0) elif function.structuring_element == SE_DIAMOND: strel = morph.strel_diamond(scale / 2.0) elif function.structuring_element == SE_LINE: strel = morph.strel_line(scale, function.angle.value) elif function.structuring_element == SE_OCTAGON: strel = morph.strel_octagon(scale / 2.0) elif function.structuring_element == SE_PAIR: strel = morph.strel_pair(function.x_offset.value, function.y_offset.value) elif function.structuring_element == SE_PERIODIC_LINE: xoff = function.x_offset.value yoff = function.y_offset.value n = max(scale / 2.0 / np.sqrt(float(xoff * xoff + yoff * yoff)), 1) strel = morph.strel_periodicline(xoff, yoff, n) elif function.structuring_element == SE_RECTANGLE: strel = morph.strel_rectangle(function.width.value, function.height.value) else: strel = morph.strel_square(scale) if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary): # Apply a very crude threshold to the image for binary algorithms logger.warning("Warning: converting image to binary for %s\n" % function_name) pixel_data = pixel_data != 0 if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK, F_OPENLINES) or (is_binary and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))): # All of these have an iterations argument or it makes no # sense to iterate if function_name == F_BRANCHPOINTS: return morph.branchpoints(pixel_data, mask) elif function_name == F_BRIDGE: return morph.bridge(pixel_data, mask, count) elif function_name == F_CLEAN: return morph.clean(pixel_data, mask, count) elif function_name == F_CLOSE: if mask is None: return scind.binary_closing(pixel_data, strel, iterations=count) else: return (scind.binary_closing( pixel_data & mask, strel, iterations=count) | (pixel_data & ~mask)) elif function_name == F_CONVEX_HULL: if mask is None: return morph.convex_hull_image(pixel_data) else: return morph.convex_hull_image(pixel_data & mask) elif function_name == F_DIAG: return morph.diag(pixel_data, mask, count) elif function_name == F_DILATE: return scind.binary_dilation(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_DISTANCE: image = scind.distance_transform_edt(pixel_data) if function.rescale_values.value: image = image / np.max(image) return image elif function_name == F_ENDPOINTS: return morph.endpoints(pixel_data, mask) elif function_name == F_ERODE: return scind.binary_erosion(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_FILL: return morph.fill(pixel_data, mask, count) elif function_name == F_FILL_SMALL: def small_fn(area, foreground): return (not foreground) and (area <= custom_repeats) return morph.fill_labeled_holes(pixel_data, mask, small_fn) elif function_name == F_HBREAK: return morph.hbreak(pixel_data, mask, count) elif function_name == F_INVERT: if is_binary: if mask is None: return ~pixel_data result = pixel_data.copy() result[mask] = ~result[mask] return result elif mask is None: return 1 - pixel_data else: result = pixel_data.copy() result[mask] = 1 - result[mask] return result elif function_name == F_LIFE: return morph.life(pixel_data, count) elif function_name == F_MAJORITY: return morph.majority(pixel_data, mask, count) elif function_name == F_OPEN: if mask is None: return scind.binary_opening(pixel_data, strel, iterations=count) else: return (scind.binary_opening( pixel_data & mask, strel, iterations=count) | (pixel_data & ~mask)) elif function_name == F_OPENLINES: return morph.openlines(pixel_data, linelength=custom_repeats, mask=mask) elif function_name == F_REMOVE: return morph.remove(pixel_data, mask, count) elif function_name == F_SHRINK: return morph.binary_shrink(pixel_data, count) elif function_name == F_SKEL: return morph.skeletonize(pixel_data, mask) elif function_name == F_SKELPE: return morph.skeletonize( pixel_data, mask, scind.distance_transform_edt(pixel_data) * poisson_equation(pixel_data)) elif function_name == F_SPUR: return morph.spur(pixel_data, mask, count) elif function_name == F_THICKEN: return morph.thicken(pixel_data, mask, count) elif function_name == F_THIN: return morph.thin(pixel_data, mask, count) elif function_name == F_VBREAK: return morph.vbreak(pixel_data, mask) else: raise NotImplementedError( "Unimplemented morphological function: %s" % function_name) else: for i in range(count): if function_name == F_BOTHAT: new_pixel_data = morph.black_tophat(pixel_data, mask=mask, footprint=strel) elif function_name == F_CLOSE: new_pixel_data = morph.closing(pixel_data, mask=mask, footprint=strel) elif function_name == F_DILATE: new_pixel_data = morph.grey_dilation(pixel_data, mask=mask, footprint=strel) elif function_name == F_ERODE: new_pixel_data = morph.grey_erosion(pixel_data, mask=mask, footprint=strel) elif function_name == F_OPEN: new_pixel_data = morph.opening(pixel_data, mask=mask, footprint=strel) elif function_name == F_TOPHAT: new_pixel_data = morph.white_tophat(pixel_data, mask=mask, footprint=strel) else: raise NotImplementedError( "Unimplemented morphological function: %s" % function_name) if np.all(new_pixel_data == pixel_data): break pixel_data = new_pixel_data return pixel_data
def run(self, workspace): image = workspace.image_set.get_image(self.image_name.value, must_be_grayscale=True) # # Match against Matlab's strel('disk') operation. # radius = (float(self.object_size.value) - 1.0) / 2.0 mask = image.mask if image.has_mask else None pixel_data = image.pixel_data if self.method == ENHANCE: if self.enhance_method == E_SPECKLES: if self.speckle_accuracy == S_SLOW: result = white_tophat(pixel_data, radius, mask) else: # # white_tophat = img - opening # = img - dilate(erode) # = img - median_filter(median_filter(0%) 100%) result = pixel_data - median_filter(median_filter( pixel_data, mask, radius, percent=0), mask, radius, percent=100) if mask is not None: result[~mask] = pixel_data[~mask] elif self.enhance_method == E_NEURITES: if self.neurite_choice == N_GRADIENT: # # white_tophat = img - opening # black_tophat = closing - img # desired effect = img + white_tophat - black_tophat # = img + img - opening - closing + img # = 3*img - opening - closing result = (3 * pixel_data - opening(pixel_data, radius, mask) - closing(pixel_data, radius, mask)) result[result > 1] = 1 result[result < 0] = 0 else: sigma = self.smoothing.value smoothed = gaussian_filter(pixel_data, sigma) L = hessian(smoothed, return_hessian=False, return_eigenvectors=False) # # The positive values are darker pixels with lighter # neighbors. The original ImageJ code scales the result # by sigma squared - I have a feeling this might be # a first-order correction for e**(-2*sigma), possibly # because the hessian is taken from one pixel away # and the gradient is less as sigma gets larger. # result = -L[:, :, 0] * (L[:, :, 0] < 0) * sigma * sigma if image.has_mask: result[~mask] = pixel_data[~mask] elif self.enhance_method == E_DARK_HOLES: min_radius = max(1, int(self.hole_size.min / 2)) max_radius = int((self.hole_size.max + 1) / 2) result = enhance_dark_holes(pixel_data, min_radius, max_radius, mask) elif self.enhance_method == E_CIRCLES: result = circular_hough(pixel_data, radius + .5, mask=mask) elif self.enhance_method == E_TEXTURE: result = variance_transform(pixel_data, self.smoothing.value, mask=mask) elif self.enhance_method == E_DIC: result = line_integration(pixel_data, self.angle.value, self.decay.value, self.smoothing.value) else: raise NotImplementedError("Unimplemented enhance method: %s" % self.enhance_method.value) elif self.method == SUPPRESS: if image.has_mask: result = opening(image.pixel_data, radius, image.mask) else: result = opening(image.pixel_data, radius) else: raise ValueError("Unknown filtering method: %s" % self.method) result_image = cpi.Image(result, parent_image=image) workspace.image_set.add(self.filtered_image_name.value, result_image) if self.show_window: workspace.display_data.image = image.pixel_data workspace.display_data.result = result
def run(self, workspace): image = workspace.image_set.get_image(self.image_name.value, must_be_grayscale = True) # # Match against Matlab's strel('disk') operation. # radius = (float(self.object_size.value)-1.0) / 2.0 mask = image.mask if image.has_mask else None pixel_data = image.pixel_data if self.method == ENHANCE: if self.enhance_method == E_SPECKLES: if self.speckle_accuracy == S_SLOW: result = white_tophat(pixel_data, radius, mask) else: # # white_tophat = img - opening # = img - dilate(erode) # = img - median_filter(median_filter(0%) 100%) result = pixel_data - median_filter( median_filter(pixel_data, mask, radius, percent = 0), mask, radius, percent = 100) if mask is not None: result[~mask] = pixel_data[~mask] elif self.enhance_method == E_NEURITES: if self.neurite_choice == N_GRADIENT: # # white_tophat = img - opening # black_tophat = closing - img # desired effect = img + white_tophat - black_tophat # = img + img - opening - closing + img # = 3*img - opening - closing result = (3 * pixel_data - opening(pixel_data, radius, mask) - closing(pixel_data, radius, mask)) result[result > 1] = 1 result[result < 0] = 0 else: sigma = self.smoothing.value smoothed = gaussian_filter(pixel_data, sigma) L = hessian(smoothed, return_hessian = False, return_eigenvectors = False) # # The positive values are darker pixels with lighter # neighbors. The original ImageJ code scales the result # by sigma squared - I have a feeling this might be # a first-order correction for e**(-2*sigma), possibly # because the hessian is taken from one pixel away # and the gradient is less as sigma gets larger. # result = -L[:, :, 0] * (L[:, :, 0] < 0) * sigma * sigma if image.has_mask: result[~mask] = pixel_data[~mask] elif self.enhance_method == E_DARK_HOLES: min_radius = max(1,int(self.hole_size.min / 2)) max_radius = int((self.hole_size.max+1)/2) result = enhance_dark_holes(pixel_data, min_radius, max_radius, mask) elif self.enhance_method == E_CIRCLES: result = circular_hough(pixel_data, radius + .5, mask=mask) elif self.enhance_method == E_TEXTURE: result = variance_transform(pixel_data, self.smoothing.value, mask = mask) elif self.enhance_method == E_DIC: result = line_integration(pixel_data, self.angle.value, self.decay.value, self.smoothing.value) else: raise NotImplementedError("Unimplemented enhance method: %s"% self.enhance_method.value) elif self.method == SUPPRESS: if image.has_mask: result = opening(image.pixel_data, radius, image.mask) else: result = opening(image.pixel_data, radius) else: raise ValueError("Unknown filtering method: %s"%self.method) result_image = cpi.Image(result, parent_image=image) workspace.image_set.add(self.filtered_image_name.value, result_image) if self.show_window: workspace.display_data.image = image.pixel_data workspace.display_data.result = result
def run_function(self, function, pixel_data, mask): '''Apply the function once to the image, returning the result''' count = function.repeat_count function_name = function.function.value scale = function.scale.value custom_repeats = function.custom_repeats.value is_binary = pixel_data.dtype.kind == 'b' if function.structuring_element == SE_ARBITRARY: strel = np.array(function.strel.get_matrix()) elif function.structuring_element == SE_DISK: strel = morph.strel_disk(scale / 2.0) elif function.structuring_element == SE_DIAMOND: strel = morph.strel_diamond(scale / 2.0) elif function.structuring_element == SE_LINE: strel = morph.strel_line(scale, function.angle.value) elif function.structuring_element == SE_OCTAGON: strel = morph.strel_octagon(scale / 2.0) elif function.structuring_element == SE_PAIR: strel = morph.strel_pair(function.x_offset.value, function.y_offset.value) elif function.structuring_element == SE_PERIODIC_LINE: xoff = function.x_offset.value yoff = function.y_offset.value n = max(scale / 2.0 / np.sqrt(float(xoff * xoff + yoff * yoff)), 1) strel = morph.strel_periodicline( xoff, yoff, n) elif function.structuring_element == SE_RECTANGLE: strel = morph.strel_rectangle( function.width.value, function.height.value) else: strel = morph.strel_square(scale) if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary): # Apply a very crude threshold to the image for binary algorithms logger.warning("Warning: converting image to binary for %s\n" % function_name) pixel_data = pixel_data != 0 if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK, F_OPENLINES) or (is_binary and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))): # All of these have an iterations argument or it makes no # sense to iterate if function_name == F_BRANCHPOINTS: return morph.branchpoints(pixel_data, mask) elif function_name == F_BRIDGE: return morph.bridge(pixel_data, mask, count) elif function_name == F_CLEAN: return morph.clean(pixel_data, mask, count) elif function_name == F_CLOSE: if mask is None: return scind.binary_closing(pixel_data, strel, iterations=count) else: return (scind.binary_closing(pixel_data & mask, strel, iterations=count) | (pixel_data & ~ mask)) elif function_name == F_CONVEX_HULL: if mask is None: return morph.convex_hull_image(pixel_data) else: return morph.convex_hull_image(pixel_data & mask) elif function_name == F_DIAG: return morph.diag(pixel_data, mask, count) elif function_name == F_DILATE: return scind.binary_dilation(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_DISTANCE: image = scind.distance_transform_edt(pixel_data) if function.rescale_values.value: image = image / np.max(image) return image elif function_name == F_ENDPOINTS: return morph.endpoints(pixel_data, mask) elif function_name == F_ERODE: return scind.binary_erosion(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_FILL: return morph.fill(pixel_data, mask, count) elif function_name == F_FILL_SMALL: def small_fn(area, foreground): return (not foreground) and (area <= custom_repeats) return morph.fill_labeled_holes(pixel_data, mask, small_fn) elif function_name == F_HBREAK: return morph.hbreak(pixel_data, mask, count) elif function_name == F_INVERT: if is_binary: if mask is None: return ~ pixel_data result = pixel_data.copy() result[mask] = ~result[mask] return result elif mask is None: return 1 - pixel_data else: result = pixel_data.copy() result[mask] = 1 - result[mask] return result elif function_name == F_LIFE: return morph.life(pixel_data, count) elif function_name == F_MAJORITY: return morph.majority(pixel_data, mask, count) elif function_name == F_OPEN: if mask is None: return scind.binary_opening(pixel_data, strel, iterations=count) else: return (scind.binary_opening(pixel_data & mask, strel, iterations=count) | (pixel_data & ~ mask)) elif function_name == F_OPENLINES: return morph.openlines(pixel_data, linelength=custom_repeats, mask=mask) elif function_name == F_REMOVE: return morph.remove(pixel_data, mask, count) elif function_name == F_SHRINK: return morph.binary_shrink(pixel_data, count) elif function_name == F_SKEL: return morph.skeletonize(pixel_data, mask) elif function_name == F_SKELPE: return morph.skeletonize( pixel_data, mask, scind.distance_transform_edt(pixel_data) * poisson_equation(pixel_data)) elif function_name == F_SPUR: return morph.spur(pixel_data, mask, count) elif function_name == F_THICKEN: return morph.thicken(pixel_data, mask, count) elif function_name == F_THIN: return morph.thin(pixel_data, mask, count) elif function_name == F_VBREAK: return morph.vbreak(pixel_data, mask) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) else: for i in range(count): if function_name == F_BOTHAT: new_pixel_data = morph.black_tophat(pixel_data, mask=mask, footprint=strel) elif function_name == F_CLOSE: new_pixel_data = morph.closing(pixel_data, mask=mask, footprint=strel) elif function_name == F_DILATE: new_pixel_data = morph.grey_dilation(pixel_data, mask=mask, footprint=strel) elif function_name == F_ERODE: new_pixel_data = morph.grey_erosion(pixel_data, mask=mask, footprint=strel) elif function_name == F_OPEN: new_pixel_data = morph.opening(pixel_data, mask=mask, footprint=strel) elif function_name == F_TOPHAT: new_pixel_data = morph.white_tophat(pixel_data, mask=mask, footprint=strel) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) if np.all(new_pixel_data == pixel_data): break pixel_data = new_pixel_data return pixel_data