OpenCV的滚球背景减除算法

Rolling ball background subtraction algorithm for OpenCV

在 ImageJ 中是否有 "rolling ball" 背景减法算法的 OpenCV (android) 实现:处理->减去背景?

OpenCV 有一个 BackgroundSubtractorMOG class,但它用于视频流而不是单个独立图像。

这是此方法的作用示例: http://imgur.com/8SN2CFz

这里是过程的文档:http://imagejdocu.tudor.lu/doku.php?id=gui:process:subtract_background

我所知道的 OpenCV C 库中没有实现,Android JNI 包装器就是这样 - 主要库的包装器。

话虽如此,ImageJ 实现的源代码可在线获得 here,因此您应该能够将其直接合并到您的 Android 图像处理管道中。

有一些关于滚球与例如滚球的相对优点的讨论。使用磁盘结构元素(在 OpenCV 中可用here.

如果您绝对需要 Rolling Ball 和 OpenCV,那么很遗憾,它们不可用 'out of the box'。

编辑: 在使用此 post 中的方法之前,请阅读下面的评论并考虑@renat 和 @David Hoffman 的回答。

以防有人仍在 python 中寻找滚球背景校正。对我来说,以下效果非常好。

  1. 加载图像并分别处理每个通道。
  2. 创建一个加权球结构元素
  3. 使用白礼帽变换

下面是一些单色图像的代码:

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)

这给你的结果与 imagej 实现不完全相同,但结果非常相似。在我的例子中,有更好和更差的校正区域。而且整体颜色强度更高。

基于@Xenthor 的回答,这就是我想出的:

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

您可以在此处找到 opencv 中最近的滚动球实现

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

简而言之

安装pip install opencv-rolling-ball

示例

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)

ImageJ 实现的原始算法来自 1983 年的一篇论文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),根据结构元素的半径对图像进行下采样,然后上采样到原始分辨率,默认情况下,平滑操作使用在计算要减去的背景之前应用 3x3 均值过滤器。这可能解释了使用 Xenthor 提出的方法观察到的差异。

如果您在 Android 上工作,您有多种选择:1) 使用 ImageJ 库,因为它在 Java 中,但是您需要实现 OpenCV-ImageJ 图像桥; 2) 如果您使用 Android NDK 在 C++ 中工作,并且由于 OpenCV 没有为非平面结构元素实现灰度形态,您可以使用 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,但它仍在进行中。

我意识到它不是opencv,但是有一个implementation in scikit-image(版本≥0.18)。

from skimage import data, restoration

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

documentation

中提供了更详细的演练