def test_mismatched_dims(): f = np.arange(128 * 128, dtype=float).reshape((128, 128)) filter = np.arange(17, dtype=float) - 8 filter **= 2 filter /= -16 np.exp(filter, filter) mahotas.convolve(f, filter)
def test_mismatched_dims(): f = np.arange(128*128, dtype=float).reshape((128,128)) filter = np.arange(17,dtype=float)-8 filter **= 2 filter /= -16 np.exp(filter,filter) mahotas.convolve(f,filter)
def test_22(): A = np.arange(1024).reshape((32,32)) B = np.array([ [0,1], [2,3], ]) C = np.array([ [0,1,0], [2,3,0], [0,0,0], ]) AB = mahotas.convolve(A,B) AC = mahotas.convolve(A,C) assert AB.shape == AC.shape assert np.all(AB == AC)
def test_22(): A = np.arange(1024).reshape((32, 32)) B = np.array([ [0, 1], [2, 3], ]) C = np.array([ [0, 1, 0], [2, 3, 0], [0, 0, 0], ]) AB = mahotas.convolve(A, B) AC = mahotas.convolve(A, C) assert AB.shape == AC.shape assert np.all(AB == AC)
def test_compare_w_ndimage(): from scipy import ndimage A = np.arange(34*340, dtype='float64').reshape((34,340))%3 B = np.ones((3,3), A.dtype) for mode in mahotas._filters.modes: if mode == 'ignore': continue assert np.all(mahotas.convolve(A, B, mode=mode) == ndimage.convolve(A, B, mode=mode))
def test_convolve1d(): ws = [ np.array([-.1, .5,.7,.7,.5]), np.array([.1,.7,.5]), ] for i in range(8): for w in ws: f = np.random.random((128,96)) ww = np.atleast_2d(w) fw = mh.convolve(f, ww) fww = mh.convolve(f, ww.T) f0w = mh.convolve1d(f, w, 0) f1w = mh.convolve1d(f, w, 1) assert np.all(fw == f1w) assert np.all(fww == f0w)
def test_compare_w_ndimage(): from scipy import ndimage A = np.arange(34 * 340).reshape((34, 340)) % 3 B = np.ones((3, 3), A.dtype) for mode in mahotas._filters.modes: assert np.all( mahotas.convolve(A, B, mode=mode) == ndimage.convolve( A, B, mode=mode))
def test_convolve1d(): ws = [ np.array([-.1, .5, .7, .7, .5]), np.array([.1, .7, .5]), ] for i in range(8): for w in ws: f = np.random.random((128, 96)) ww = np.atleast_2d(w) fw = mh.convolve(f, ww) fww = mh.convolve(f, ww.T) f0w = mh.convolve1d(f, w, 0) f1w = mh.convolve1d(f, w, 1) assert np.all(fw == f1w) assert np.all(fww == f0w)
def perimeter(bwimage, n=4, mode="constant"): """ p = perimeter(bwimage, n=4, mode="constant") Calculate total perimeter of all objects in binary image. Parameters ---------- bwimage : array binary image n : int, optional passed to ``bwperim`` as is mode : str, optional passed to ``bwperim`` as is Returns ------- p : float total perimeter of all objects in binary image See Also -------- bwperim : function Finds the perimeter region References ---------- .. [1] K. Benkrid, D. Crookes. Design and FPGA Implementation of a Perimeter Estimator. The Queen's University of Belfast. http://www.cs.qub.ac.uk/~d.crookes/webpubs/papers/perimeter.doc """ global _perimeter_values perim = bwperim(bwimage, n, mode) perim = perim.astype(np.uint8) histogram = mh.fullhistogram( mh.convolve(perim, _perimeter_magic)) if _perimeter_values is None: _perimeter_values = np.zeros(34, float) _perimeter_values[[5, 7, 15, 17, 25, 27]] = 1 _perimeter_values[[21, 33]] = np.sqrt(2) _perimeter_values[[13, 23]] = (1 + np.sqrt(2)) / 2 size = min(34, len(histogram)) return np.dot(histogram[:size], _perimeter_values[:size])
from pylab import imshow import mahotas import numpy as np wally = mahotas.imread('data/DepartmentStore.jpg') wfloat = wally.astype(float) r, g, b = wfloat.transpose((2, 0, 1)) w = wfloat.mean(2) pattern = np.ones((24, 16), float) for i in range(2): pattern[i::4] = -1 v = mahotas.convolve(r - w, pattern) mask = (v == v.max()) mask = mahotas.dilate(mask, np.ones((48, 24))) wally -= .8 * wally * ~mask[:, :, None] imshow(wally)
gap_completion_distances * gap_completion_factor combined_outflow = outflow_smooth * smoothing_factor + \ outflow_gap * gap_completion_factor combined_inflow = inflow_smooth * smoothing_factor + \ inflow_gap * gap_completion_factor # Find ignorable nodes - those where full 3x3 neighborhood has # source_sink_cap > 0 and sum of source_sink_cap in # neighborhood (except center) is greater than neighborhood # outflow. Similar for inflow. hollow = np.ones((3, 3)) hollow[1, 1] = 0 ignorable = np.logical_and( mahotas.erode(source_sink_cap > 0, np.ones((3, 3))), (mahotas.convolve(source_sink_cap, hollow, mode='ignore') > combined_outflow)) np.logical_or( ignorable, np.logical_and( mahotas.erode(source_sink_cap < 0, np.ones( (3, 3))), (mahotas.convolve( source_sink_cap, hollow, mode='ignore') < -combined_inflow)), # careful with signs out=ignorable) print "Can ignore {0} of {1}".format( np.sum(ignorable), ignorable.size) ## Load the adjacency matrix
combined_distances = smooth_distances * smoothing_factor + \ gap_completion_distances * gap_completion_factor combined_outflow = outflow_smooth * smoothing_factor + \ outflow_gap * gap_completion_factor combined_inflow = inflow_smooth * smoothing_factor + \ inflow_gap * gap_completion_factor # Find ignorable nodes - those where full 3x3 neighborhood has # source_sink_cap > 0 and sum of source_sink_cap in # neighborhood (except center) is greater than neighborhood # outflow. Similar for inflow. hollow = np.ones((3, 3)) hollow[1, 1] = 0 ignorable = np.logical_and(mahotas.erode(source_sink_cap > 0, np.ones((3,3))), (mahotas.convolve(source_sink_cap, hollow, mode='ignore') > combined_outflow)) np.logical_or(ignorable, np.logical_and(mahotas.erode(source_sink_cap < 0, np.ones((3,3))), (mahotas.convolve(source_sink_cap, hollow, mode='ignore') < - combined_inflow)), # careful with signs out=ignorable) print "Can ignore {0} of {1}".format(np.sum(ignorable), ignorable.size) ## Load the adjacency matrix for di, direction in enumerate(directions[:4]): dest_coords = shift_coords(coords, direction) source_coords, dest_coords = validate_and_broadcast(coords, dest_coords) mask = np.logical_or(ignorable[source_coords],
from pylab import imshow import numpy as np import mahotas wally = mahotas.imread('waldo.jpg') imshow(wally) wfloat = wally.astype(float) r,g,b = wfloat.transpose((2,0,1)) # split into rgb channels, better to use floats w = wfloat.mean(2) # w is the white channel pattern = np.ones((24,16),float) for i in xrange(2): pattern[i::4] = -1 # build a pattern of +1,+1,-1,-1 on vertical axis -> Wally's shirt. v = mahotas.convolve(r-w, pattern) # convolve red-white, will give strong response where shirt mask = (v == v.max()) mask = mahotas.dilate(mask,np.ones((48,24))) # look for max and dilate it to make it visible wally -= .8*wally * ~mask[:,:,None] imshow(wally)# imshow(wally)
def _patch_ms(patch, args): histogram = np.histogram(patch,bins=256,range=(0,256))[0] # np.set_printoptions(precision=2,threshold=256) # print(histogram) thresholds = threshold.multi_kapur(histogram, 2) tee.log('Maximum entropy discretization (Kapur et al. 1985, 3 bins):', thresholds) minint = thresholds[0] if minint != thresholds[0]: tee.log('Warning: minint was lowered') if minint <= 1: tee.log('minint threshold too low (%d) - I believe there are no cells in this substack' % minint) return None if thresholds[1] < args.min_second_threshold: tee.log('thresholds[1] threshold too low (%d) - I believe there are no cells in this substack' % thresholds[1]) return None (Lx,Ly,Lz) = np.where(patch > minint) intensities = patch[(Lx,Ly,Lz)] L = np.array(list(zip(Lx,Ly,Lz)), dtype=np.uint16) tee.log('Found', len(L), 'voxels above the threshold', minint) if len(L) < 10: tee.log('Too few points (%d) - I believe there are no cells in this substack' % len(L)) return None bandwidth = args.mean_shift_bandwidth radius = args.hi_local_max_radius reg_maxima = mh.regmax(patch.astype(np.int)) if args.seeds_filtering_mode == 'hard': xx, yy, zz = np.mgrid[:2*radius+1, :2*radius+1, :2*radius+1] sphere = (xx - radius) ** 2 + (yy - radius) ** 2 + (zz - radius) ** 2 se = sphere <= radius*radius f = se.astype(np.float64) elif args.seeds_filtering_mode == 'soft': f=np.zeros((int(radius+1)*2+1,int(radius+1)*2+1,int(radius+1)*2+1), dtype=np.float64);f[int(radius+1),int(radius+1),int(radius+1)]=1 f=gaussian_filter(f, radius/2.,mode='constant', cval=0.0);f=f/np.amax(f) else: raise ValueError("Invalid seeds filtering mode", args.seeds_filtering_mode) min_mass = np.sum(f)*minint local_mass = mh.convolve(patch, weights=f, mode='constant', cval=0.0) above = local_mass > min_mass himaxima = np.logical_and(reg_maxima,above) if np.sum(himaxima) > args.max_expected_cells: tee.log('Too many candidates,', np.sum(himaxima), 'I believe this substack is messy and give up') return None C = [volume.Center(x,y,z) for (x,y,z) in zip(*np.where(himaxima))] if len(C) == 0: tee.log('No maxima above. #himaxima=', np.sum(himaxima), '#above=', np.sum(above), '. Giving up') return None seeds = np.array([[c.x, c.y, c.z] for c in C]) # Save seeds with some info for debugging purposes for c in C: c.name = 'seed' c.mass = patch[c.x,c.y,c.z] c.volume = thresholds[0] cluster_centers, labels, volumes, masses, trajectories = mean_shift(L, intensities=intensities, bandwidth=bandwidth, seeds=seeds) if cluster_centers is None: return None masses = np.zeros(len(cluster_centers)) for i,c in enumerate(cluster_centers): masses[i] = local_mass[int(c[0]+0.5),int(c[1]+0.5),int(c[2]+0.5)] PatchMSRet = namedtuple('PatchMSRet', ['cluster_centers','labels','masses','L','seeds']) r = PatchMSRet(cluster_centers=cluster_centers, labels=labels, masses=masses, L=L, seeds=C) return r
def sujoy(img, kernel_nhood=0, just_filter=False): ''' edges = sujoy(img, kernel_nhood=0, just_filter=False) Compute edges using Sujoy's algorithm `edges` is a binary image of edges computed according to Sujoy's algorithm. PAPER LINK: https://www.ijert.org/research/a-better-first-derivative-approach-for-edge-detection-IJERTV2IS110616.pdf Parameters ---------- img : Any 2D-ndarray kernel_nhood : 0(default) or 1 if 0, kernel is based on 4-neighborhood else , kernel is based on 8-neighborhood just_filter : boolean, optional If true, then return the result of filtering the image with the Sujoy's filters, but do not threshold (default is False). Returns ------- edges : ndarray Binary image of edges, unless `just_filter`, in which case it will be an array of floating point values. ''' img = np.array(img, dtype=np.float) if img.ndim != 2: raise ValueError( 'mahotas.sujoy: Only available for 2-dimensional images') img -= img.min() ptp = img.ptp() if ptp == 0: return img img /= ptp if kernel_nhood: krnl_h = np.array([[0, -1, -1, -1, 0], [0, -1, -1, -1, 0], [0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0] ]) / 12. krnl_v = np.array([[0, 0, 0, 0, 0], [-1, -1, 0, 1, 1], [ -1, -1, 0, 1, 1 ], [-1, -1, 0, 1, 1], [0, 0, 0, 0, 0]]) / 12. else: krnl_h = np.array([[0, 0, -1, 0, 0], [0, -1, -1, -1, 0], [ 0, 0, 0, 0, 0 ], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]]) / 8. krnl_v = np.array([[0, 0, 0, 0, 0], [0, -1, 0, 1, 0], [ -1, -1, 0, 1, 1 ], [0, -1, 0, 1, 0], [0, 0, 0, 0, 0]]) / 8. grad_h = mh.convolve(img, krnl_h, mode='nearest') grad_v = mh.convolve(img, krnl_v, mode='nearest') grad_h **= 2 grad_v **= 2 grad = grad_h grad += grad_v if just_filter: return grad t = np.sqrt(grad.mean()) return mh.regmax(grad) * (np.sqrt(grad) > t)
import mahotas import mahotas.demos from pylab import gray, imshow, show import numpy as np # loading image(input image file name inplace of 'lena') img = mahotas.demos.load('lena') img = img.max(2) T_otsu = mahotas.otsu(img) img = img > T_otsu print("Image threshold using Otsu Mehtod") imshow(img) show() weight = np.ones((5, 5), float) new_img = mahotas.convolve(img, weight) print("Convolved Image") imshow(new_img) show()
wally = mahotas.demos.load('Wally') imshow(wally) show() wfloat = wally.astype(float) r, g, b = wfloat.transpose((2,0,1)) #transpose 시킴. 인덱스 번호는 0, 1, 2 순서로 갑니다. 그런데 지금 '2'가 면으로 왔습니다. ??????????????? w = wfloat.mean(2)# 2개씩 평균을 취해감. pattern = np.ones((24, 16), float)#직삭각형을 만든 것 입니다. 직사각형 패턴 #셔츠의 패턴 for i in np.arange(2): # i에는 0, 1 pattern[i::4] = -1 # 4칸씩 건너뛰면서 -1 v = mahotas.convolve(r-w, pattern) #컨볼루션 연산을 패턴을 가지고 합니다. 위에 셔츠의 패턴이 '필터'가 됩니다. #여기에는 stride 개념이 있습니다. 몇칸씩 건너뛸것이냐. 지금 지정이 없으니까 '1픽셀씩 건너뛰면서 = -1 ' mask = (v == v.max()) # v값과, v최고값인 것 mask = mahotas.dilate(mask, np.ones((48, 24))) #dilate 확장=키우라. 원래 24,16인데 48,24로 키웠음. np.subtract(wally, .8*wally * ~mask[:, :, None], out=wally, casting='unsafe') imshow(wally) show() ## from PIL import Image img = Image.open('C:/Users/Hyungi/Desktop/workplace/img/image.png') dim = (100, 100, 400, 400) crop_img = img.crop(dim) crop_img.show()
def _patch_ms(patch, args): histogram = np.histogram(patch,bins=256,range=(0,256))[0] # np.set_printoptions(precision=2,threshold=256) # print(histogram) thresholds = threshold.multi_kapur(histogram, 2) tee.log('Maximum entropy discretization (Kapur et al. 1985, 3 bins):', thresholds) minint = thresholds[0] if minint != thresholds[0]: tee.log('Warning: minint was lowered') if minint <= 1: tee.log('minint threshold too low (%d) - I believe there are no cells in this substack' % minint) return None if thresholds[1] < args.min_second_threshold: tee.log('thresholds[1] threshold too low (%d) - I believe there are no cells in this substack' % thresholds[1]) return None (Lx,Ly,Lz) = np.where(patch > minint) intensities = patch[(Lx,Ly,Lz)] L = np.array(zip(Lx,Ly,Lz), dtype=np.uint16) tee.log('Found', len(L), 'voxels above the threshold', minint) if len(L) < 10: tee.log('Too few points (%d) - I believe there are no cells in this substack' % len(L)) return None bandwidth = args.mean_shift_bandwidth radius = args.hi_local_max_radius reg_maxima = mh.regmax(patch.astype(np.int)) if args.seeds_filtering_mode == 'hard': xx, yy, zz = np.mgrid[:2*radius+1, :2*radius+1, :2*radius+1] sphere = (xx - radius) ** 2 + (yy - radius) ** 2 + (zz - radius) ** 2 se = sphere <= radius*radius f = se.astype(np.float64) elif args.seeds_filtering_mode == 'soft': f=np.zeros((int(radius+1)*2+1,int(radius+1)*2+1,int(radius+1)*2+1), dtype=np.float64);f[int(radius+1),int(radius+1),int(radius+1)]=1 f=gaussian_filter(f, radius/2.,mode='constant', cval=0.0);f=f/np.amax(f) else: raise ValueError("Invalid seeds filtering mode", args.seeds_filtering_mode) min_mass = np.sum(f)*minint local_mass = mh.convolve(patch, weights=f, mode='constant', cval=0.0) above = local_mass > min_mass himaxima = np.logical_and(reg_maxima,above) if np.sum(himaxima) > args.max_expected_cells: tee.log('Too many candidates,', np.sum(himaxima), 'I believe this substack is messy and give up') return None C = [volume.Center(x,y,z) for (x,y,z) in zip(*np.where(himaxima))] if len(C) == 0: tee.log('No maxima above. #himaxima=', np.sum(himaxima), '#above=', np.sum(above), '. Giving up') return None seeds = np.array([[c.x, c.y, c.z] for c in C]) # Save seeds with some info for debugging purposes for c in C: c.name = 'seed' c.mass = patch[c.x,c.y,c.z] c.volume = thresholds[0] cluster_centers, labels, volumes, masses, trajectories = mean_shift(L, intensities=intensities, bandwidth=bandwidth, seeds=seeds) if cluster_centers is None: return None masses = np.zeros(len(cluster_centers)) for i,c in enumerate(cluster_centers): masses[i] = local_mass[int(c[0]+0.5),int(c[1]+0.5),int(c[2]+0.5)] PatchMSRet = namedtuple('PatchMSRet', ['cluster_centers','labels','masses','L','seeds']) r = PatchMSRet(cluster_centers=cluster_centers, labels=labels, masses=masses, L=L, seeds=C) return r
def main(image, mask, threshold=25, mean_size=6, min_size=10, filter_type='log_2d', minimum_bead_intensity=150, z_step=0.333, pixel_size=0.1625, alpha=0, plot=False): '''Converts an image stack with labelled cell surface to a cell `volume` image Parameters ---------- image: numpy.ndarray[Union[numpy.uint8, numpy.uint16]] grayscale image in which beads should be detected (3D) mask: numpy.ndarray[Union[numpy.int32, numpy.bool]] binary or labeled image of cell segmentation (2D) threshold: int, optional intensity of bead in filtered image (default: ``25``) mean_size: int, optional mean size of bead (default: ``6``) min_size: int, optional minimal number of connected voxels per bead (default: ``10``) filter_type: str, optional filter used to emphasise the beads in 3D (options: ``log_2d`` (default) or ``log_3d``) minimum_bead_intensity: int, optional minimum intensity in the original image of an identified bead centre. Use to filter low intensity beads. z_step: float, optional distance between consecutive z-planes (um) (default: ``0.333``) pixel_size: float, optional size of pixel (um) (default: ``0.1625``) alpha: float, optional value of parameter for 3D alpha shape calculation (default: ``0``, no vertex filtering performed) plot: bool, optional whether a plot should be generated (default: ``False``) Returns ------- jtmodules.generate_volume_image.Output ''' # Check that there are cells identified in image if (np.max(mask) > 0): volume_image_calculated = True n_slices = image.shape[-1] logger.debug('input image has z-dimension %d', n_slices) # Remove high intensity pixels detect_image = image.copy() p = np.percentile(detect_image, 99.9) detect_image[detect_image > p] = p # Perform LoG filtering in 3D to emphasise beads if filter_type == 'log_2d': logger.info('using stacked 2D LoG filter to detect beads') f = -1 * log_2d(size=mean_size, sigma=float(mean_size - 1) / 3) filt = np.stack([f for _ in range(mean_size)], axis=2) elif filter_type == 'log_3d': logger.info('using 3D LoG filter to detect beads') filt = -1 * log_3d(mean_size, (float(mean_size - 1) / 3, float(mean_size - 1) / 3, 4 * float(mean_size - 1) / 3)) else: logger.info('using unfiltered image to detect beads') if filter_type == 'log_2d' or filter_type == 'log_3d': logger.debug('convolve image with filter kernel') detect_image = mh.convolve(detect_image.astype(float), filt) detect_image[detect_image < 0] = 0 logger.debug('threshold beads') labeled_beads, n_labels = mh.label(detect_image > threshold) logger.info('detected %d beads', n_labels) logger.debug('remove small beads') sizes = mh.labeled.labeled_size(labeled_beads) too_small = np.where(sizes < min_size) labeled_beads = mh.labeled.remove_regions(labeled_beads, too_small) mh.labeled.relabel(labeled_beads, inplace=True) logger.info( '%d beads remain after removing small beads', np.max(labeled_beads) ) logger.debug('localise beads in 3D') localised_beads = localise_bead_maxima_3D( image, labeled_beads, minimum_bead_intensity ) logger.debug('mask beads inside cells') '''NOTE: localised_beads.coordinate image is used only for beads outside cells and can therefore be modified here. For beads inside cells, localised_beads.coordinates are used instead. ''' # expand mask to ensure slide-beads are well away from cells slide = localised_beads.coordinate_image expand_mask = mh.dilate( A=mask > 0, Bc=np.ones([25,25], bool) ) slide[expand_mask] = 0 logger.debug('determine coordinates of slide surface') try: bottom_surface = slide_surface_params(slide) except InvalidSlideError: logger.error('slide surface calculation is invalid' + ' returning empty volume image') volume_image = np.zeros(shape=image[:,:,0].shape, dtype=image.dtype) figure = str() return Output(volume_image, figure) logger.debug('subtract slide surface to get absolute bead coordinates') bead_coords_abs = [] for i in range(len(localised_beads.coordinates)): bead_height = ( localised_beads.coordinates[i][2] - plane(localised_beads.coordinates[i][0], localised_beads.coordinates[i][1], bottom_surface.x) ) if bead_height > 0: bead_coords_abs.append( (localised_beads.coordinates[i][0], localised_beads.coordinates[i][1], bead_height) ) logger.debug('convert absolute bead coordinates to image') coord_image_abs = coordinate_list_to_array( bead_coords_abs, shape=image[:,:,0].shape, dtype=np.float32 ) filtered_coords_global = filter_vertices_per_cell_alpha_shape( coord_image_abs=coord_image_abs, mask=mask, alpha=alpha, z_step=z_step, pixel_size=pixel_size ) logger.info('interpolate cell surface') volume_image = interpolate_surface( coords=np.asarray(filtered_coords_global, dtype=np.uint16), output_shape=np.shape(image[:,:,0]), method='linear' ) volume_image = volume_image.astype(image.dtype) logger.debug('set regions outside mask to zero') volume_image[mask == 0] = 0 else: logger.warn( 'no objects in input mask, skipping cell volume calculation.' ) volume_image_calculated = False volume_image = np.zeros(shape=image[:,:,0].shape, dtype=image.dtype) if (plot and volume_image_calculated): logger.debug('convert bottom surface plane to image for plotting') dt = np.dtype(float) bottom_surface_image = np.zeros(slide.shape, dtype=dt) for ix in range(slide.shape[0]): for iy in range(slide.shape[1]): bottom_surface_image[ix, iy] = plane( ix, iy, bottom_surface.x) logger.info('create plot') from jtlib import plotting plots = [ plotting.create_intensity_image_plot( np.max(image, axis=-1), 'ul', clip=True ), plotting.create_float_image_plot( bottom_surface_image, 'll', clip=True ), plotting.create_intensity_image_plot( volume_image, 'ur', clip=True ) ] figure = plotting.create_figure( plots, title='Convert stack to volume image' ) else: figure = str() return Output(volume_image, figure)
from pylab import imshow import mahotas as mh import numpy as np wally = mh.demos.load('DepartmentStore') wfloat = wally.astype(float) r, g, b = wfloat.transpose((2, 0, 1)) w = wfloat.mean(2) pattern = np.ones((24, 16), float) for i in range(2): pattern[i::4] = -1 v = mh.convolve(r - w, pattern) mask = (v == v.max()) mask = mh.dilate(mask, np.ones((48, 24))) wally -= np.array(.8 * wally * ~mask[:, :, None], dtype=wally.dtype) imshow(wally)
def main(image, mask, threshold=1, min_area=3, mean_area=5, max_area=1000, clip_percentile=99.999, plot=False): '''Detects blobs in `image` using an implementation of `SExtractor <http://www.astromatic.net/software/sextractor>`_ [1]. The `image` is first convolved with a Laplacian of Gaussian filter of size `mean_area` to enhance blob-like structures. The enhanced image is then thresholded at `threshold` level and connected pixel components are subsequently deplended. Parameters ---------- image: numpy.ndarray[Union[numpy.uint8, numpy.uint16]] grayscale image in which blobs should be detected mask: numpy.ndarray[Union[numpy.int32, numpy.bool]] binary or labeled image that specifies pixel regions of interest in which blobs should be detected threshold: int, optional threshold level for pixel values in the convolved image (default: ``1``) min_area: int, optional minimal size a blob is allowed to have (default: ``3``) mean_area: int, optional estimated average size of a blob (default: ``5``) max_area: int, optional maximal size a blob is allowed to have to be subject to deblending; no attempt will be made to deblend blobs larger than `max_area` (default: ``100``) clip_percentile: float, optional clip intensity values in `image` above the given percentile; this may help in attenuating artifacts plot: bool, optional whether a plot should be generated (default: ``False``) Returns ------- jtmodules.detect_blobs.Output[Union[numpy.ndarray, str]] References ---------- .. [1] Bertin, E. & Arnouts, S. 1996: SExtractor: Software for source extraction, Astronomy & Astrophysics Supplement 317, 393 ''' logger.info('detect blobs above threshold {0}'.format(threshold)) detect_image = image.copy() p = np.percentile(image, clip_percentile) detect_image[image > p] = p # Enhance the image for blob detection by convoling it with a LOG filter f = -1 * log_2d(size=mean_area, sigma=float(mean_area - 1) / 3) detect_image = mh.convolve(detect_image.astype(float), f) detect_image[detect_image < 0] = 0 # Mask regions of too big blobs pre_blobs = mh.label(detect_image > threshold)[0] bad_blobs, n_bad = mh.labeled.filter_labeled(pre_blobs, min_size=max_area) logger.info( 'remove {0} blobs because they are bigger than {1} pixels'.format( n_bad, max_area)) detect_mask = np.invert(mask > 0) detect_mask[bad_blobs > 0] = True detect_image[bad_blobs > 0] = 0 logger.info('deblend blobs') blobs, centroids = detect_blobs(image=detect_image, mask=detect_mask, threshold=threshold, min_area=min_area) n = len(np.unique(blobs[blobs > 0])) logger.info('{0} blobs detected'.format(n)) if plot: logger.info('create plot') from jtlib import plotting colorscale = plotting.create_colorscale('Spectral', n=n, permute=True, add_background=True) plots = [ plotting.create_float_image_plot(detect_image, 'ul', clip=True), plotting.create_mask_image_plot(blobs, 'ur', colorscale=colorscale) ] figure = plotting.create_figure( plots, title=('detected #{0} blobs above threshold {1}' ' in LOG filtered image'.format(n, threshold))) else: figure = str() return Output(centroids, blobs, figure)
import mahotas as mh import numpy as np import cv2 # Imported mahotas & numpy with standard abbreviations im = mh.imread('./images/pool-table-above-17031042.jpg', as_grey=1) # Load the image & convert to gray # im2 = im[::2,::2] im2 = mh.gaussian_filter(im, 1.2) cv2.imshow('gaussian', im) # save first step # mh.imsave('1.png', im2.astype(np.uint8)) # Mean filtering is implemented "by hand" with a convolution. mean_filtered = mh.convolve(im2.astype(float), np.ones((9, 9))/81.) # iminv = 255 - mean_filtered # mh.imsave('binarized1.png', iminv.astype(np.uint8)) # You might need to adjust the number 4 here, but it worked well for this image. imc = im2 > mean_filtered - 4 # cv2.imshow('open circles', (imc*255).astype(np.uint8)); # cv2.waitKey(0) # mh.imsave('1.png', im2.astype(np.uint8)) # print(im2) # imc = mh.convolve(imc.astype(float), np.ones((9, 9))/81.) # imc = im2 > mean_filtered - 6
from pylab import imshow import mahotas wally = mahotas.imread('data/DepartmentStore.jpg') wfloat = wally.astype(float) r,g,b = wfloat.transpose((2,0,1)) w = wfloat.mean(2) pattern = np.ones((24,16), float) for i in xrange(2): pattern[i::4] = -1 v = mahotas.convolve(r-w, pattern) mask = (v == v.max()) mask = mahotas.dilate(mask, np.ones((48,24))) wally -= .8*wally * ~mask[:,:,None] imshow(wally)
def main(image, mask, threshold=1, min_area=3, mean_area=5, max_area=1000, clip_percentile=99.999, plot=False): '''Detects blobs in `image` using an implementation of `SExtractor <http://www.astromatic.net/software/sextractor>`_ [1]. The `image` is first convolved with a Laplacian of Gaussian filter of size `mean_area` to enhance blob-like structures. The enhanced image is then thresholded at `threshold` level and connected pixel components are subsequently deplended. Parameters ---------- image: numpy.ndarray[Union[numpy.uint8, numpy.uint16]] grayscale image in which blobs should be detected mask: numpy.ndarray[Union[numpy.int32, numpy.bool]] binary or labeled image that specifies pixel regions of interest in which blobs should be detected threshold: int, optional threshold level for pixel values in the convolved image (default: ``1``) min_area: int, optional minimal size a blob is allowed to have (default: ``3``) mean_area: int, optional estimated average size of a blob (default: ``5``) max_area: int, optional maximal size a blob is allowed to have to be subject to deblending; no attempt will be made to deblend blobs larger than `max_area` (default: ``100``) clip_percentile: float, optional clip intensity values in `image` above the given percentile; this may help in attenuating artifacts plot: bool, optional whether a plot should be generated (default: ``False``) Returns ------- jtmodules.detect_blobs.Output[Union[numpy.ndarray, str]] References ---------- .. [1] Bertin, E. & Arnouts, S. 1996: SExtractor: Software for source extraction, Astronomy & Astrophysics Supplement 317, 393 ''' logger.info('detect blobs above threshold {0}'.format(threshold)) detect_image = image.copy() p = np.percentile(image, clip_percentile) detect_image[image > p] = p # Enhance the image for blob detection by convoling it with a LOG filter f = -1 * log_2d(size=mean_area, sigma=float(mean_area - 1)/3) detect_image = mh.convolve(detect_image.astype(float), f) detect_image[detect_image < 0] = 0 # Mask regions of too big blobs pre_blobs = mh.label(detect_image > threshold)[0] bad_blobs, n_bad = mh.labeled.filter_labeled(pre_blobs, min_size=max_area) logger.info( 'remove {0} blobs because they are bigger than {1} pixels'.format( n_bad, max_area ) ) detect_mask = np.invert(mask > 0) detect_mask[bad_blobs > 0] = True detect_image[bad_blobs > 0] = 0 logger.info('deblend blobs') blobs, centroids = detect_blobs( image=detect_image, mask=detect_mask, threshold=threshold, min_area=min_area ) n = len(np.unique(blobs[blobs>0])) logger.info('{0} blobs detected'.format(n)) if plot: logger.info('create plot') from jtlib import plotting colorscale = plotting.create_colorscale( 'Spectral', n=n, permute=True, add_background=True ) plots = [ plotting.create_float_image_plot( detect_image, 'ul', clip=True ), plotting.create_mask_image_plot( blobs, 'ur', colorscale=colorscale ) ] figure = plotting.create_figure( plots, title=( 'detected #{0} blobs above threshold {1}' ' in LOG filtered image'.format(n, threshold) ) ) else: figure = str() return Output(centroids, blobs, figure)