ZMolnar
ZMolnar

Reputation: 57

Rolling ball background subtraction algorithm for OpenCV

Is there an OpenCV (android) implementation of "rolling ball" background subtraction algorithm found in ImageJ: Process->Subtract Background?

OpenCV has a BackgroundSubtractorMOG class, but it is used for video streams not single, independent images.

This is an example of what this method does: https://i.sstatic.net/mXSLb.jpg

Here is a documentation of the process: http://imagejdocu.tudor.lu/doku.php?id=gui:process:subtract_background

Upvotes: 3

Views: 8207

Answers (6)

lanery
lanery

Reputation: 5364

I realize it's not opencv, but there is an implementation in scikit-image (version ≥ 0.18).

from skimage import data, restoration

image = data.coins()
background = restoration.rolling_ball(image, radius=100)
result = image - background

A more detailed walkthrough is provided in the documentation

Upvotes: 0

Pierre Wargnier
Pierre Wargnier

Reputation: 447

The original algorithm that ImageJ implements comes from a 1983 paper https://www.computer.org/csdl/magazine/co/1983/01/01654163/13rRUwwJWBB. I took a look at it and it is actually a grayscale morphological white top-hat with a ball-shaped grayscale structuring element (see https://en.wikipedia.org/wiki/Top-hat_transform). In the ImageJ implementation (available here https://imagej.nih.gov/ij/developer/source/ij/plugin/filter/BackgroundSubtracter.java.html), the image is downsampled depending on the structuring elements' radius, then upsampled to the original resolution and, by default, a smoothing operation using a 3x3 mean filter is applied before computing the background to subtract. This likely explains the differences observed with the method proposed by Xenthor.

If you are working on Android, you have several options: 1) using the ImageJ library, since it is in Java, you will however need to implement an OpenCV-ImageJ image bridge; 2) if you work in C++ using the Android NDK and since OpenCV does not implement grayscale morphology for non-flat structuring elements, you can use ITK (https://itk.org/) instead to perform the graycale white top-hat; 3) still using the NDK, there is an OpenCV-based C++ port of the algorithm available here: https://github.com/folterj/BioImageOperation/tree/master/BioImageOperation, however it is still a work in progress.

Upvotes: 0

Xenthor
Xenthor

Reputation: 443

Edit: Before using the method in this post read the comments below and also consider the answers of @renat and @David Hoffman.

In case someone is still looking for rolling ball background correction in python. For me, the following worked out very well.

  1. Load the image and process each channel separately.
  2. Create a weighted ball structuring element
  3. Use white tophat transform

Here is some code for a monochrome image:

import scipy.ndimage as scim
from scipy.misc import imsave
from skimage.morphology import ball

# Read image
im = scim.imread("path")[:, :, 0].astype(int)

# Create 3D ball with radius of 50 and a diameter of 2*50+1
s = ball(50)

# Take only the upper half of the ball
h = s.shape[1] // 2 + 1  # 50 + 1

# Flatten the 3D ball to a weighted 2D disc
s = s[:h, :, :].sum(axis=0)

# Rescale weights into 0-255
s = (255 * (s - s.min())) / (s.max() - s.min())

# Use im-opening(im,ball) (i.e. white tophat transform) (see original publication)
im_corr = scim.white_tophat(im, structure=s)

# Save corrected image
imsave('outfile', im_corr)

This gives you not the exact same result as the imagej implementation but the results are quite similar. In my case there were both, better and worse corrected regions. Moreover the overall color intensity was higher.

Upvotes: 1

David Hoffman
David Hoffman

Reputation: 2343

Building on @Xenthor's answer this is what I came up with:

import numpy as np
import scipy.ndimage as ndi
from scipy.ndimage._ni_support import _normalize_sequence

def rolling_ball_filter(data, ball_radius, spacing=None, top=False, **kwargs):
    """Rolling ball filter implemented with morphology operations

    This implenetation is very similar to that in ImageJ and uses a top hat transform
    with a ball shaped structuring element
    https://en.wikipedia.org/wiki/Top-hat_transform

    Parameters
    ----------
    data : ndarray
        image data (assumed to be on a regular grid)
    ball_radius : float
        the radius of the ball to roll
    spacing : int or sequence
        the spacing of the image data
    top : bool
        whether to roll the ball on the top or bottom of the data
    kwargs : key word arguments
        these are passed to the ndimage morphological operations

    Returns
    -------
    data_nb : ndarray
        data with background subtracted
    bg : ndarray
        background that was subtracted from the data
    """
    ndim = data.ndim
    if spacing is None:
        spacing = 1

    spacing = _normalize_sequence(spacing, ndim)

    radius = np.asarray(_normalize_sequence(ball_radius, ndim))
    mesh = np.array(np.meshgrid(*[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)], indexing="ij"))
    structure = 2 * np.sqrt(1 - ((mesh / radius.reshape(-1, *((1,) * ndim)))**2).sum(0))
    structure[~np.isfinite(structure)] = 0
    if not top:
        # ndi.white_tophat(data, structure=structure, output=background)
        background = ndi.grey_erosion(data, structure=structure, **kwargs)
        background = ndi.grey_dilation(background, structure=structure, **kwargs)
    else:
        # ndi.black_tophat(data, structure=structure, output=background)
        background = ndi.grey_dilation(data, structure=structure, **kwargs)
        background = ndi.grey_erosion(background, structure=structure, **kwargs)

    return data - background, background

Upvotes: 1

renat
renat

Reputation: 33

There is a recent rolling-ball implementation in opencv that you can find here

https://pypi.org/project/opencv-rolling-ball/

In short

Install pip install opencv-rolling-ball

Example

import cv2
from cv2_rolling_ball import subtract_background_rolling_ball

img = cv2.imread(f'path/to/img.tif', 0)
img, background = subtract_background_rolling_ball(img, 30, light_background=True, use_paraboloid=False, do_presmooth=True)

Upvotes: 2

Dave Durbin
Dave Durbin

Reputation: 3632

There's no implementation in the OpenCV C libraries that I know of and the Android JNI wrappers are just that - wrappers around the main libraries.

Having said that the source code for the ImageJ implementation is available online here and so you should be able to incorporate this directly into your Android image processing pipeline.

There is some discussion about the relative merits of rolling ball vs. e.g. using a disk structuring element (which is available in OpenCV) here.

If you absolutely require Rolling Ball and OpenCV then unfortunately it's not available 'out of the box'.

Upvotes: 6

Related Questions