Class: Postprocess

class pykitPIV.postprocess.Postprocess(image_tensor, random_seed=None)

Postprocesses images.

Note

Running multiple postprocessing functions on a single object of the Postprocess class will create a superposition of effects that those functions have. This way, the user can chain multiple transformations, such as:

adding shot noise \(\rightarrow\) adding Gaussian noise \(\rightarrow\) log-transformation.

The final outcome will always be stored in the class attribute processed_image_tensor.

Example:

from pykitPIV import Image, Postprocess

# Upload saved images:
image = Image()
images_tensor_dic = image.upload_from_h5(filename='pykitPIV-dataset.h5')
images_tensor = images_tensor_dic['I']

# Initialize a postprocessing object:
postprocess = Postprocess(image_tensor, random_seed=100)

# Check if paired or single image frames have been uploaded:
postprocess.image_pair
Parameters:
  • image_tensornumpy.ndarray specifying image or images to postprocess. It can be an array of size \((N, H, W)\) or \((N, 2, H, W)\).

  • random_seed – (optional) int specifying the random seed for random number generation in numpy. If specified, all operations are reproducible.

Attributes:

  • random_seed - (read-only) as per user input.

  • image_tensor - (read-only) as per user input.

  • image_pair - (read-only) bool specifying whether paired or single images have been uploaded.

  • processed_image_tensor - (read-only) numpy.ndarray storing the postprocessed image tensor. Note that when multiple postprocessing operations are called on the same object of class Postprocess, this quantity will store a superposition of them, corresponding to the sequence of execution.

pykitPIV.postprocess.Postprocess.add_defocus(self, grid_size=256, pupil_radius=72, defocus_waves=1.0, spherical_waves=0.0)

Adds defocus and/or spherical aberration to the image tensor or any image-like array of size \((N, H, W)\) using the point-spread function (PSF) model (see Joseph W. Goodman - Introduction to Fourier Optics for theory).

Note

In modeling the defocus, we assume that we start with an image of perfect (isotropic) point-source object(s) and convolve this image with the PSF (Tang et al. (2013)). Hence, the input image tensor should come from a perfect focus scenario for accurate simulation of the defocus.

We first compute the complex pupil function, assuming a circular pupil, for the point-source and then perform two-dimensional fast Fourier transform (FFT) of it to get the defocused and/or aberrated image.

Given the normalized radial distance from the point source:

\[\rho = \frac{r}{R}\]

where \(R\) is the pupil radius, we build the aberration function (see Eq. (3.5) in Guang-ming Dai - Wavefront Optics for Vision Correction) as:

\[\phi(\rho) = 2 \pi ( d \cdot \rho^2 + s \cdot \rho^4 )\]

where \(d\) is the defocus wave and \(s\) is the spherical wave.

We then compute the complex pupil function (see section 6.4 in Joseph W. Goodman - Introduction to Fourier Optics) as:

\[P(\rho) = A(\rho) \cdot \exp (i \cdot \phi(\rho) )\]

and compute the FFT of the pupil function:

\[E(x, y) = \mathcal{F} (P(\rho))\]

The point-spread function kernel is then:

\[\text{PSF} = |E(x, y)|^2\]

The perfect point-source image is convolved with this kernel in the FFT space to get the final image.

Here’s an example synthetic defocused PIV image with Gaussian noise added:

../_images/postprocess_add_defocus.png

Example:

from pykitPIV import Image, Postprocess

# Upload saved images:
image = Image()
images_tensor_dic = image.upload_from_h5(filename='pykitPIV-dataset.h5')
images_tensor = images_tensor_dic['I']

# Initialize a postprocessing object:
postprocess = Postprocess(image_tensor, random_seed=100)

# Add defocus to the uploaded images:
postprocess.add_defocus(grid_size=256,
                        pupil_radius=72,
                        defocus_waves=1.0,
                        spherical_waves=0.0)
Parameters:
  • grid_size – (optional) int or float specifying the grid size in pixels \([\text{px}]\) for the FFT.

  • pupil_radius – (optional) int or float specifying the pupil radius, \(R\), in pixels \([\text{px}]\).

  • defocus_waves – (optional) int or float specifying the defocus wave, \(d\). This adds a term proportional to \(\rho^2\) across the pupil and hence simulates the Zernike defocus.

  • spherical_waves – (optional) int or float specifying the spherical wave, \(s\). This adds a term proportional to \(\rho^4\) across the pupil and hence simulates the spherical aberration.

Returns:

  • point_spread_function_kernel - numpy.ndarray specifying the point-spread function kernel that was computed, \(\text{PSF}\).

pykitPIV.postprocess.Postprocess.add_gaussian_noise(self, loc=0.0, scale=(500, 1000), clip=None)

Adds Gaussian noise to the image tensor or any image-like array of size \((N, H, W)\) or \((N, 2, H, W)\).

Example:

from pykitPIV import Image, Postprocess

# Upload saved images:
image = Image()
images_tensor_dic = image.upload_from_h5(filename='pykitPIV-dataset.h5')
images_tensor = images_tensor_dic['I']

# Initialize a postprocessing object:
postprocess = Postprocess(image_tensor, random_seed=100)

# Add noise to the uploaded images:
postprocess.add_gaussian_noise(loc=0.0,
                               scale=(500,1000),
                               clip=2**16-1)
Parameters:
  • loc – (optional) int or float specifying the center of the Gaussian distribution.

  • scale – (optional) tuple of two numerical elements specifying the minimum (first element) and maximum (second element) standard deviation for the noise on an image to randomly sample from. It can also be set to int or float to generate a fixed standard deviation value across all \(N\) image pairs. The unit of the standard deviation is image intensity.

  • clip – (optional) int or float specifying whether maximum values on images should be clipped to a specified value. If set to None, values are not clipped.

pykitPIV.postprocess.Postprocess.add_shot_noise(self, strength=1, clip=None)

Adds shot noise to the image tensor or any image-like array of size \((N, H, W)\) or \((N, 2, H, W)\). The shot noise models a Poisson distribution of photons reaching the camera lens.

If \(\lambda\) is interpreted as the expected image intensity at a particular pixel, then the shot noise corrects that intensity by drawing the new intensity, \(k\), from the Poisson distribution:

\[f(k, \lambda) = \frac{\lambda^k e^{- \lambda}}{k!}\]

where \(f\) denotes the probability of observing intensity \(k\), given the expected intensity \(\lambda\).

Example:

from pykitPIV import Image, Postprocess

# Upload saved images:
image = Image()
images_tensor_dic = image.upload_from_h5(filename='pykitPIV-dataset.h5')
images_tensor = images_tensor_dic['I']

# Initialize a postprocessing object:
postprocess = Postprocess(image_tensor, random_seed=100)

# Add shot noise to the uploaded images:
postprocess.add_shot_noise(strength=1,
                           clip=2**16-1)
Parameters:
  • strength – (optional) int or float specifying the strength of the shot noise.

  • clip – (optional) int or float specifying whether maximum values on images should be clipped to a specified value. If set to None, values are not clipped.

pykitPIV.postprocess.Postprocess.log_transform_images(self, addition=1)

Log-transforms image tensor or any image-like array of size \((N, H, W)\) or \((N, 2, H, W)\).

\[\mathbf{T}_{\text{log}} = \log_{10} (\mathbf{T} + a)\]

Example:

from pykitPIV import Image, Postprocess

# Upload saved images:
image = Image()
images_tensor_dic = image.upload_from_h5(filename='pykitPIV-dataset.h5')
images_tensor = images_tensor_dic['I']

# Initialize a postprocessing object:
postprocess = Postprocess(image_tensor, random_seed=100)

# Create a log-transformation of image intensities:
postprocess.log_transform_images(addition=10000)
Parameters:

addition – (optional) int or float specifying the added constant, \(a\), whose purpose is to eliminate zero elements in the images tensor.

pykitPIV.postprocess.Postprocess.plot(self, original, idx, instance=1, xlabel=None, ylabel=None, xticks=True, yticks=True, title=None, vmin=None, vmax=None, cmap='Greys_r', cbar=False, figsize=(5, 5), dpi=300, filename=None)

Plots a single, post-processed static image. For PIV images, the user can select between \(I_1\) or \(I_2\).

Parameters:
  • originalbool specifying whether the original or the post-processed image tensor should be plotted.

  • idxint specifying the index of the image to plot out of n_images number of images.

  • instance – (optional) int specifying whether \(I_1\) (instance=1) or \(I_2\) (instance=2) should be plotted.

  • xlabel – (optional) str specifying \(x\)-label.

  • ylabel – (optional) str specifying \(y\)-label.

  • xticks – (optional) bool specifying if ticks along the \(x\)-axis should be plotted.

  • yticks – (optional) bool specifying if ticks along the \(y\)-axis should be plotted.

  • title – (optional) str specifying figure title.

  • cmap – (optional) str or an object of matplotlib.colors.ListedColormap specifying the color map to use.

  • cbar – (optional) bool specifying whether colorbar should be plotted.

  • figsize – (optional) tuple of two numerical elements specifying the figure size as per matplotlib.pyplot.

  • dpi – (optional) int specifying the dpi for the image.

  • filename – (optional) str specifying the path and filename to save an image. If set to None, the image will not be saved.

Returns:

  • plt - matplotlib.pyplot image handle.