OpenCV的滚球背景减法算法
Posted
技术标签:
【中文标题】OpenCV的滚球背景减法算法【英文标题】:Rolling ball background subtraction algorithm for OpenCV 【发布时间】:2015-03-28 18:29:51 【问题描述】:在 ImageJ: Process->Subtract Background 中找到“滚球”背景减法算法的 OpenCV (android) 实现吗?
OpenCV 有一个 BackgroundSubtractorMOG 类,但它用于视频流而不是单个独立图像。
这是这个方法的一个例子: http://imgur.com/8SN2CFz
这是该过程的文档:http://imagejdocu.tudor.lu/doku.php?id=gui:process:subtract_background
【问题讨论】:
【参考方案1】:据我所知,OpenCV C 库中没有实现,Android JNI 包装器就是这样 - 围绕主库的包装器。
话虽如此,ImageJ 实现的源代码可在线获得 here,因此您应该能够将其直接合并到您的 Android 图像处理管道中。
有一些关于滚球与例如滚球的相对优点的讨论。使用磁盘结构元素(在 OpenCV 中可以使用)here。
如果您绝对需要 Rolling Ball 和 OpenCV,那么很遗憾,它不是“开箱即用”的。
【讨论】:
【参考方案2】:基于@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
【讨论】:
感谢您的贡献,但这有效吗?我不得不将spacing = np.ones_like(ndim)
更改为spacing = np.ones(ndim)
。但是现在1 - ((mesh / radius.reshape(-1, *((1,) * ndim)))**2).sum(0)
都是负值,因此np.sqrt
会引发错误。另外:这适用于浮动图像吗?
spacing = np.ones_like(ndim)
是正确的。你之前遇到了什么错误?
@DavidHoffman 感谢您发布您的实施。我尝试使用您的功能但收到错误zip argument #2 must support iteration
。我已经使用以下参数运行了该函数rolling_ball_filter(image, 50, spacing=None, top=False)
@packoman 我想我错了,我更新了答案,如果失败请告诉我。
@DavidHoffman 当你添加 presmoothing data = cv2.filter2D(data, -1, np.ones((3, 3), dtype = np.float64) / 9)
结果会更好。【参考方案3】:
编辑:在使用这篇文章中的方法之前,请阅读下面的 cmets 并考虑@renat 和 @David Hoffman 的答案。
如果有人仍在寻找 python 中的滚动球背景校正。对我来说,以下工作非常顺利。
-
加载图像并分别处理每个通道。
创建加权球结构元素
使用白色礼帽变换
这里是一些单色图像的代码:
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 实现不完全相同的结果,但结果非常相似。就我而言,有更好和更差的校正区域。而且整体色彩强度更高。
【讨论】:
将 Xenthors 代码与 opencv-rolling-ball 中的算法进行比较我使用来自 skimageskimage.data.microaneurysms()
的黑白图像得到完全不同的结果,半径为 25 的结果可以在这里看到:i.stack.imgur.com/MPVgh.png图像显示 1. 原始图像,2. Xenthor 的结果,3. &。 4. opencv-rolling-ball算法的背景扣除图像和背景本身
哦,哇,这些结果非常不同。您是否也与大卫霍夫曼的答案进行了比较?
我查看了来自 opencv-rolling-ball
的代码,在向 David Hoffman 代码添加一行代码后,我得到了类似的结果。在返回语句之前,我添加了以下行:data = (data + .5) % 255 if top else (data + 255.5) % 255
结果可以在这里看到:i.stack.imgur.com/pzsbo.png(首先是原始图像,然后是 Davids 算法的图像和背景,然后是 opencv-rolling-ball 的图像)。但是正如你所看到的,值范围是完全不同的。
也很有趣。非常感谢您的意见和评价。【参考方案4】:
您可以在此处找到 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)
【讨论】:
【参考方案5】:ImageJ 实现的原始算法来自 1983 年的一篇论文https://www.computer.org/csdl/magazine/co/1983/01/01654163/13rRUwwJWBB。我看了看,它实际上是一个带有球形灰度结构元素的灰度形态白色礼帽(见https://en.wikipedia.org/wiki/Top-hat_transform)。在 ImageJ 实现中(可在此处获得 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/) 代替执行灰度白色礼帽; 3) 仍在使用 NDK,这里有一个基于 OpenCV 的 C++ 算法端口:https://github.com/folterj/BioImageOperation/tree/master/BioImageOperation,但它仍在进行中。
【讨论】:
【参考方案6】:现在有一个implementation in scikit-image
from skimage import data, restoration
image = data.coins()
background = restoration.rolling_ball(image)
result = image - background
documentation 中提供了更详细的演练
【讨论】:
以上是关于OpenCV的滚球背景减法算法的主要内容,如果未能解决你的问题,请参考以下文章