trim_floating_solid
#
trim_floating_solid
function is a filter which removes solids not attached to primary solid structure.
import numpy as np
import porespy as ps
import scipy.ndimage as spim
import scipy
import matplotlib.pyplot as plt
import skimage
ps.visualization.set_mpl_style()
im
#
How floating solids are removed and the result can depend on whether the image is 2D or 3D so here we investigate both cases. The 2D and 3D images are visualized but only a slice of the 3D image is shown.
im2d = ps.generators.blobs(shape=[500, 500])
im3d = ps.generators.blobs(shape=[500, 500, 500])
fig, ax = plt.subplots(1, 2, figsize=[12, 12]);
ax[0].imshow(im2d);
ax[0].axis(False);
ax[0].set_title('2D', fontdict={'fontsize': 18});
ax[1].imshow(im3d.take(indices=250, axis=2));
ax[1].axis(False);
ax[1].set_title('3D', fontdict={'fontsize': 18});
Error in callback <function flush_figures at 0x7f1f23c974c0> (for post_execute):
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib_inline/backend_inline.py:126, in flush_figures()
123 if InlineBackend.instance().close_figures:
124 # ignore the tracking, just draw and close all figures
125 try:
--> 126 return show(True)
127 except Exception as e:
128 # safely show traceback if in IPython, else raise
129 ip = get_ipython()
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib_inline/backend_inline.py:90, in show(close, block)
88 try:
89 for figure_manager in Gcf.get_all_fig_managers():
---> 90 display(
91 figure_manager.canvas.figure,
92 metadata=_fetch_figure_metadata(figure_manager.canvas.figure)
93 )
94 finally:
95 show._to_draw = []
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/display_functions.py:298, in display(include, exclude, metadata, transient, display_id, raw, clear, *objs, **kwargs)
296 publish_display_data(data=obj, metadata=metadata, **kwargs)
297 else:
--> 298 format_dict, md_dict = format(obj, include=include, exclude=exclude)
299 if not format_dict:
300 # nothing to display (e.g. _ipython_display_ took over)
301 continue
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/formatters.py:177, in DisplayFormatter.format(self, obj, include, exclude)
175 md = None
176 try:
--> 177 data = formatter(obj)
178 except:
179 # FIXME: log the exception
180 raise
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/decorator.py:232, in decorate.<locals>.fun(*args, **kw)
230 if not kwsyntax:
231 args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/formatters.py:221, in catch_format_error(method, self, *args, **kwargs)
219 """show traceback on failed format call"""
220 try:
--> 221 r = method(self, *args, **kwargs)
222 except NotImplementedError:
223 # don't warn on NotImplementedErrors
224 return self._check_return(None, args[0])
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/formatters.py:338, in BaseFormatter.__call__(self, obj)
336 pass
337 else:
--> 338 return printer(obj)
339 # Finally look for special method names
340 method = get_real_method(obj, self.print_method)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/pylabtools.py:169, in retina_figure(fig, base64, **kwargs)
160 def retina_figure(fig, base64=False, **kwargs):
161 """format a figure as a pixel-doubled (retina) PNG
162
163 If `base64` is True, return base64-encoded str instead of raw bytes
(...)
167 base64 argument
168 """
--> 169 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
170 # Make sure that retina_figure acts just like print_figure and returns
171 # None when the figure is empty.
172 if pngdata is None:
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/IPython/core/pylabtools.py:152, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
149 from matplotlib.backend_bases import FigureCanvasBase
150 FigureCanvasBase(fig)
--> 152 fig.canvas.print_figure(bytes_io, **kw)
153 data = bytes_io.getvalue()
154 if fmt == 'svg':
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/backend_bases.py:2338, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2334 try:
2335 # _get_renderer may change the figure dpi (as vector formats
2336 # force the figure dpi to 72), so we need to set it again here.
2337 with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2338 result = print_method(
2339 filename,
2340 facecolor=facecolor,
2341 edgecolor=edgecolor,
2342 orientation=orientation,
2343 bbox_inches_restore=_bbox_inches_restore,
2344 **kwargs)
2345 finally:
2346 if bbox_inches and restore_bbox:
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/backend_bases.py:2204, in FigureCanvasBase._switch_canvas_and_return_print_method.<locals>.<lambda>(*args, **kwargs)
2200 optional_kws = { # Passed by print_figure for other renderers.
2201 "dpi", "facecolor", "edgecolor", "orientation",
2202 "bbox_inches_restore"}
2203 skip = optional_kws - {*inspect.signature(meth).parameters}
-> 2204 print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
2205 *args, **{k: v for k, v in kwargs.items() if k not in skip}))
2206 else: # Let third-parties do as they see fit.
2207 print_method = meth
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/_api/deprecation.py:410, in delete_parameter.<locals>.wrapper(*inner_args, **inner_kwargs)
400 deprecation_addendum = (
401 f"If any parameter follows {name!r}, they should be passed as "
402 f"keyword, not positionally.")
403 warn_deprecated(
404 since,
405 name=repr(name),
(...)
408 else deprecation_addendum,
409 **kwargs)
--> 410 return func(*inner_args, **inner_kwargs)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:517, in FigureCanvasAgg.print_png(self, filename_or_obj, metadata, pil_kwargs, *args)
468 @_api.delete_parameter("3.5", "args")
469 def print_png(self, filename_or_obj, *args,
470 metadata=None, pil_kwargs=None):
471 """
472 Write the figure to a PNG file.
473
(...)
515 *metadata*, including the default 'Software' key.
516 """
--> 517 self._print_pil(filename_or_obj, "png", pil_kwargs, metadata)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:463, in FigureCanvasAgg._print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata)
458 def _print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata=None):
459 """
460 Draw the canvas, then save it using `.image.imsave` (to which
461 *pil_kwargs* and *metadata* are forwarded).
462 """
--> 463 FigureCanvasAgg.draw(self)
464 mpl.image.imsave(
465 filename_or_obj, self.buffer_rgba(), format=fmt, origin="upper",
466 dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:405, in FigureCanvasAgg.draw(self)
401 # Acquire a lock on the shared font cache.
402 with RendererAgg.lock, \
403 (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
404 else nullcontext()):
--> 405 self.figure.draw(self.renderer)
406 # A GUI class may be need to update a window using this draw, so
407 # don't forget to call the superclass.
408 super().draw()
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/artist.py:74, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
72 @wraps(draw)
73 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74 result = draw(artist, renderer, *args, **kwargs)
75 if renderer._rasterizing:
76 renderer.stop_rasterizing()
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
48 if artist.get_agg_filter() is not None:
49 renderer.start_filter()
---> 51 return draw(artist, renderer)
52 finally:
53 if artist.get_agg_filter() is not None:
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/figure.py:3071, in Figure.draw(self, renderer)
3068 # ValueError can occur when resizing a window.
3070 self.patch.draw(renderer)
-> 3071 mimage._draw_list_compositing_images(
3072 renderer, self, artists, self.suppressComposite)
3074 for sfig in self.subfigs:
3075 sfig.draw(renderer)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
129 if not_composite or not has_images:
130 for a in artists:
--> 131 a.draw(renderer)
132 else:
133 # Composite any adjacent images together
134 image_group = []
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
48 if artist.get_agg_filter() is not None:
49 renderer.start_filter()
---> 51 return draw(artist, renderer)
52 finally:
53 if artist.get_agg_filter() is not None:
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/axes/_base.py:3107, in _AxesBase.draw(self, renderer)
3104 a.draw(renderer)
3105 renderer.stop_rasterizing()
-> 3107 mimage._draw_list_compositing_images(
3108 renderer, self, artists, self.figure.suppressComposite)
3110 renderer.close_group('axes')
3111 self.stale = False
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
129 if not_composite or not has_images:
130 for a in artists:
--> 131 a.draw(renderer)
132 else:
133 # Composite any adjacent images together
134 image_group = []
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
48 if artist.get_agg_filter() is not None:
49 renderer.start_filter()
---> 51 return draw(artist, renderer)
52 finally:
53 if artist.get_agg_filter() is not None:
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/image.py:641, in _ImageBase.draw(self, renderer, *args, **kwargs)
639 renderer.draw_image(gc, l, b, im, trans)
640 else:
--> 641 im, l, b, trans = self.make_image(
642 renderer, renderer.get_image_magnification())
643 if im is not None:
644 renderer.draw_image(gc, l, b, im)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/image.py:949, in AxesImage.make_image(self, renderer, magnification, unsampled)
946 transformed_bbox = TransformedBbox(bbox, trans)
947 clip = ((self.get_clip_box() or self.axes.bbox) if self.get_clip_on()
948 else self.figure.bbox)
--> 949 return self._make_image(self._A, bbox, transformed_bbox, clip,
950 magnification, unsampled=unsampled)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/matplotlib/image.py:515, in _ImageBase._make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification, unsampled, round_to_pixel_border)
510 if isinstance(self.norm, mcolors.NoNorm):
511 A_resampled = A_resampled.astype(A.dtype)
513 mask = (np.where(A.mask, np.float32(np.nan), np.float32(1))
514 if A.mask.shape == A.shape # nontrivial mask
--> 515 else np.ones_like(A, np.float32))
516 # we always have to interpolate the mask to account for
517 # non-affine transformations
518 out_alpha = _resample(self, mask, out_shape, t, resample=True)
File <__array_function__ internals>:180, in ones_like(*args, **kwargs)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/numpy/core/numeric.py:280, in ones_like(a, dtype, order, subok, shape)
218 @array_function_dispatch(_ones_like_dispatcher)
219 def ones_like(a, dtype=None, order='K', subok=True, shape=None):
220 """
221 Return an array of ones with the same shape and type as a given array.
222
(...)
278
279 """
--> 280 res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
281 multiarray.copyto(res, 1, casting='unsafe')
282 return res
File <__array_function__ internals>:180, in empty_like(*args, **kwargs)
File /usr/share/miniconda/envs/test/lib/python3.8/site-packages/numpy/ma/core.py:3043, in MaskedArray.__array_finalize__(self, obj)
3040 _mask = _mask.astype(_mask_dtype, order)
3041 else:
3042 # Take a view so shape changes, etc., do not propagate back.
-> 3043 _mask = _mask.view()
3044 else:
3045 _mask = nomask
KeyboardInterrupt:
2D conn
#
conn
options for 2D images are 4 and 8 for square and diagonal neighbours respectively.
x1 = ps.filters.trim_floating_solid(im=im2d, conn=4)
x2 = ps.filters.trim_floating_solid(im=im2d, conn=8)
fig, ax = plt.subplots(1, 2, figsize=[12, 12]);
ax[0].imshow(x1);
ax[0].axis(False);
ax[0].set_title('conn = 4', fontdict={'fontsize': 18});
ax[1].imshow(x2);
ax[1].axis(False);
ax[1].set_title('conn = 8', fontdict={'fontsize': 18});
conn
options for 3D images are 6 and 26 for similarly square and diagonal neighbours. Apparent floating solids in the visualized 2D slice may actually be attached to solid in adjacent 2D slice and therefore may not actually be considered to be a floating solid.
x3 = ps.filters.trim_floating_solid(im=im3d, conn=6)
x4 = ps.filters.trim_floating_solid(im=im3d, conn=26)
fig, ax = plt.subplots(1, 2, figsize=[12, 12]);
ax[0].imshow(x3.take(indices=250, axis=2));
ax[0].axis(False);
ax[0].set_title('conn = 6', fontdict={'fontsize': 18});
ax[1].imshow(x4.take(indices=250, axis=2));
ax[1].axis(False);
ax[1].set_title('conn = 26', fontdict={'fontsize': 18});