Open-source image processing tools are rapidly spreading across programming languages and scientific domains. The widely-used ImageJ platform, along with its distributions such as Fiji, offer an excellent Java-based suite. In the last few years, image processing and computer vision in Python has rapidly matured1 with the emergence of libraries like scikit-image, Mahotas, and SimpleCV. scikit-image and Mahotas utilize 2d NumPy arrays for their image data structures, thus providing a familiar API to users of NumPy and the other core SciPy libraries, especially Matplotlib, IPython.
The motivation for pyparty arose from difficulties encountered when quantifying nanoparticle distributions on silica substrates. We had found that after acquiring an image, preprocessing, segmentation and labelling techniques were easily applied; however, separating and measuring the various particle species in post-processing was difficult. This is primarily because the objects of interest were stored as boolean or integer-labeled arrays, which become unwieldy under heavy manipulation.
pyparty emerged as a means to abstract the concept of particles (i.e. image blobs) into custom data structures for intuitive manipulation and characterization whilst preserving the image API. In addition to integrating new particle constructs with the existing array and image processing functions, pyparty extends scikit-image’s rasterization toolbox with new particle types, patterning, and an interface to Matplotlib patch objects for vectorized particle renderings. Thus, pyparty leverages the conventional imaging pipeline at both ends; it provides a tool set for artificial image composition, and a framework for particle post-processing.
Implementation and architecture
Rasterization, as well as particle labels and descriptors (e.g. area, eccentricity etc...), are all deferred to algorithms implemented in scikit-image. pyparty adds a convenience layer to extend some of this functionality. For example, pyparty’s implements a Grid object for patterning, and provides a simple framework for drawing multiparticles, for example circle dimers and trimers.
The Canvas is pyparty’s primary datastructure. The Canvas is comprised of a background image, a Grid and ParticleManager, which is a container for particle storage and manipulation. The Canvas fully decouples particle information such as position, color, and pixel from the background image. Since particle information is stored in python containers, slicing, arithmetic, boolean indexing and other common operations are very easy to perform. For example, an operation like dilate all circular particles in an image that are over a certain size and mean brightness is a simple task in pyparty. In addition to the Canvas, the MultiCanvas is provided for images with multiple particle types.
Finally, pyparty simplifies some common image processing tasks involving thresholding, artificial noise generation, color assignment, image type conversions, plotting and blob filtering, albeit not to the extent of IJBlob.
Examples of Use
We will first highlight pyparty’s drawing capabilities to create pseudo electron microscope images. A more involved version is used to compare segmentation algorithms in a corollary study. In the example code snippets, Canvas and MultiCanvas methods are bolded for clarity. To begin, let us define the parameters corresponding to ranges for particle size and intensity, the noise fill-fraction and the image resolution. For brevity, we load a previously generated pyparty background image (see documentation) of a trigonometrically-varying contrast gradient.
from pyparty import Canvas, MultiCanvas BG = 'https://raw.github.com/hugadams...' REZ = (1024, 1024) #Image resolution RAD = (12, 18) #Radius range (px) COLOR = (200, 255) #Color range NOISE = 0.10 #Percent noise
The Canvas crops the image to our desired resolution upon initialization. Next, randomly-sized ellipses are added, and the Canvas grid is set to 20 pixels per division; this controls the inter-particle spacing.
from random import randint as R_int cnvs = Canvas(rez=REZ, background=BG) cnvs.grid.div = 20 #20x20 grid for (cx, cy) in cnvs.gpairs('centers'): cnvs.add('ellipse', center = (cx,cy), xradius = R_int(*RAD), yradius = R_int(*RAD), phi = R_int(0, 360), color = R_int(*COLOR) ) cnvs.show(annotate=True)
This results in 380 particles covering 23% of the image surface as shown in Figure 1. Next, we apply a Gaussian filter through scikit-image (σ = 3px) to smooth the particle boundaries. This negligibly affects the background, which varies over a much larger scale. In pyparty, spectral noise can be assigned according to any NumPy distribution function (Gaussian, gamma etc..), but for clearer visualization, we'll use black pixels, also known as "pepper" noise (see Figure 2).
from skimage.filter import gaussian_filter from pyparty.noise import pepper from pyparty.plots import zoom smooth = gaussian_filter(cnvs.image, 3) noisy = pepper(smooth, NOISE) zoom(noisy, (0, 0, 250, 250))
As a final exercise, we employ the MultiCanvas to characterize nanoparticle species in an electron microscope image. The image, which is packaged with pyparty, has been segmented using Ilastik's object classification workflow into groups labelled as singles, dimers, trimers and clusters as shown in Figure 3. pyparty has an API for shape filtering, but it is not explored here. The MultiCanvas is built directly from the image via the from_labels() constructor. Persistent names and colors are assigned to the labels for easier identification between plots. Figures 4 and Figure 5 shows pyparty's decomposition of the image into these colored particle groups.
from pyparty.plots import showim, splot from pyparty.data import nanolabels NANOLABELS = nanolabels() NAMES = ('singles', 'dimers', 'trimers', 'clusters') showim(NANOLABELS, 'spectral', title='Labeled Nanoparticles') mc = MultiCanvas.from_labeled(nanolabels(), *NAMES) mc.set_colors('r', 'g', 'y', 'magenta') mc.show(names = True, ncols=2, figsize=(7,5)) ax1, ax2 = splot(1,2) mc.hist(ax1, attr='eccentricity', bins=30) mc.pie(ax2, attr='area', explode=(0,0,0, 0.1))
Development and testing is performed on Mac, Linux and Windows 7 systems. Tutorials and case studies are provided as IPython notebooks, which are also integrated into a quasi regression test suite.
pyparty runs on operating systems that support Python and the numpy stack. It has been tested on Ubuntu 10.04, 12.04, 13.10; Mac OS X 10.7, 10.8, and Windows 7.
Python 2.7; some features of pyparty may be inaccessible on older versions. pyparty has not been tested for Python 3 compatibility.
Additional system requirements
At least 4GB RAM is suggested for working with multiple high-resolution images.
scikit-image, Matplotlib, Traits. IPython2 is required to run notebooks; static versions are provided as well. We recommend Enthought Canopy for a powerful scientific computing environment with intrinsic notebook support.
List of contributors
Adam Hughes, Zhaowen Liu
BSD 3-Clause License
08/05/2014 (v 0.3.1)
Fall 2013; some legacy code hosted since 2011.
(3) Reuse potential
Although developed in the context of nanotechnology, pyparty’s post-processing and drawing capabilities are general to 2D imaging workflows, especially those found in microscopy3. pyparty is currently used for in-house research pursuits and continues to develop. New applications like Ilastik integrate semi-supervised learning and object classification tools with unprecedented accessibility. This trend suggests that the need for general particle analysis tools will increase in coming years. As image analysis in Python continually expands to meet the needs of researchers, we believe pyparty will emerge as a small but important component in particle analysis workflows.