在 python 中使用图像处理计数粒子

Posted

技术标签:

【中文标题】在 python 中使用图像处理计数粒子【英文标题】:Counting particles using image processing in python 【发布时间】:2013-04-13 04:51:58 【问题描述】:

有没有什么好的算法可以检测背景强度变化的粒子? 例如,如果我有以下图像:

有没有办法计算小白色颗粒,即使左下方出现明显不同的背景?

为了更清楚一点,我想标记图像并使用发现这些粒子显着的算法对粒子进行计数:

我用PILcvscipynumpy等模块尝试了很多东西。 我从this very similar SO question 那里得到了一些提示,乍一看你可以像这样取一个简单的阈值:

im = mahotas.imread('particles.jpg')
T = mahotas.thresholding.otsu(im)

labeled, nr_objects = ndimage.label(im>T)
print nr_objects
pylab.imshow(labeled)

但是由于背景的变化,您会得到以下信息:

我也尝试过其他的想法,比如a technique I found for measuring paws,我就是这样实现的:

import numpy as np
import scipy
import pylab
import pymorph
import mahotas
from scipy import ndimage
import cv


def detect_peaks(image):
    """
    Takes an image and detect the peaks usingthe local maximum filter.
    Returns a boolean mask of the peaks (i.e. 1 when
    the pixel's value is the neighborhood maximum, 0 otherwise)
    """

    # define an 8-connected neighborhood
    neighborhood = ndimage.morphology.generate_binary_structure(2,2)

    #apply the local maximum filter; all pixel of maximal value 
    #in their neighborhood are set to 1
    local_max = ndimage.filters.maximum_filter(image, footprint=neighborhood)==image
    #local_max is a mask that contains the peaks we are 
    #looking for, but also the background.
    #In order to isolate the peaks we must remove the background from the mask.

    #we create the mask of the background
    background = (image==0)

    #a little technicality: we must erode the background in order to 
    #successfully subtract it form local_max, otherwise a line will 
    #appear along the background border (artifact of the local maximum filter)
    eroded_background = ndimage.morphology.binary_erosion(background, structure=neighborhood, border_value=1)

    #we obtain the final mask, containing only peaks, 
    #by removing the background from the local_max mask
    detected_peaks = local_max - eroded_background

    return detected_peaks

im = mahotas.imread('particles.jpg')
imf = ndimage.gaussian_filter(im, 3)
#rmax = pymorph.regmax(imf)
detected_peaks = detect_peaks(imf)
pylab.imshow(pymorph.overlay(im, detected_peaks))
pylab.show()

但这也没有运气,显示了这个结果:

使用区域最大函数,我得到的图像几乎似乎给出了正确的粒子识别,但是根据我的高斯滤波,错误位置的粒子太多或太少(图像的高斯滤波器为 2, 3, & 4):

此外,它还需要处理与此类似的图像:

这是上面相同类型的图像,只是粒子密度更高。

编辑:已解决的解决方案:我能够使用以下代码为这个问题找到一个不错的工作解决方案:

import cv2
import pylab
from scipy import ndimage

im = cv2.imread('particles.jpg')
pylab.figure(0)
pylab.imshow(im)

gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0)
maxValue = 255
adaptiveMethod = cv2.ADAPTIVE_THRESH_GAUSSIAN_C#cv2.ADAPTIVE_THRESH_MEAN_C #cv2.ADAPTIVE_THRESH_GAUSSIAN_C
thresholdType = cv2.THRESH_BINARY#cv2.THRESH_BINARY #cv2.THRESH_BINARY_INV
blockSize = 5 #odd number like 3,5,7,9,11
C = -3 # constant to be subtracted
im_thresholded = cv2.adaptiveThreshold(gray, maxValue, adaptiveMethod, thresholdType, blockSize, C) 
labelarray, particle_count = ndimage.measurements.label(im_thresholded)
print particle_count
pylab.figure(1)
pylab.imshow(im_thresholded)
pylab.show()

这将显示如下图像:

(这是给定的图像)

(这是计数的粒子)

并计算粒子数为 60。

【问题讨论】:

正是我想要的!谢谢!要添加到解决方案中,还有一个adaptive threshold function in scikit-image library 【参考方案1】:

我通过使用一种称为自适应对比度的技术调整差异阈值解决了“背景中的可变亮度”问题。它的工作原理是对灰度图像与自身的模糊版本进行线性组合(在这种情况下是差异),然后对其应用阈值。

    使用合适的统计运算符对图像进行卷积。 从卷积图像中减去原始图像,必要时校正强度标度/伽玛。 用一个常数对差异图像进行阈值化。

(original paper)

我在浮点域中使用scipy.ndimage 非常成功地做到了这一点(比整数图像处理效果更好),如下所示:

original_grayscale = numpy.asarray(some_PIL_image.convert('L'), dtype=float)
blurred_grayscale = scipy.ndimage.filters.gaussian_filter(original_grayscale, blur_parameter)
difference_image = original_grayscale - (multiplier * blurred_grayscale);
image_to_be_labeled = ((difference_image > threshold) * 255).astype('uint8')  # not sure if it is necessary

labelarray, particle_count = scipy.ndimage.measurements.label(image_to_be_labeled)

希望这会有所帮助!

【讨论】:

感谢@heltonbiker 的帮助!这个想法将我指向this interesting idea of solving sudoku puzzles from images,我能够实现类似的东西,效果很好。如果其他人遇到此问题或类似问题,我将在我的问题中发布代码。【参考方案2】:

我真的不能给出一个明确的答案,但这里有几点建议:

    函数mahotas.morph.regmax 可能比最大过滤器更好,因为它消除了伪最大值。也许将其与全局阈值、局部阈值(例如窗口上的平均值)或两者结合起来。

    如果您有多个图像和相同的不均匀背景,那么也许您可以计算平均背景并对其进行归一化,或者使用空图像作为背景估计值。如果你有显微镜,就会出现这种情况,就像我见过的每个显微镜一样,照明不均匀。

类似:

average = average_of_many(images)
# smooth it
average = mahotas.gaussian_filter(average,24)

现在您可以对图像进行预处理,例如:

preproc = image/average

或类似的东西。

【讨论】:

谢谢@luispedro,我已经尝试了区域最大值,它看起来很有希望,但是出现了很多我不应该考虑的额外粒子。此外,平均是个好主意,这实际上是在去除背景之后。 尝试将其与全局阈值结合起来。

以上是关于在 python 中使用图像处理计数粒子的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 粒子发射器编辑器不使用〜ipad 图像?

带有 Python 的粒子过滤器 GUI

图像分割基于matlab粒子群算法优化模拟退火算法图像分割含Matlab源码 2020期

PYTHON:处理 KeyError

图像分割基于matlab粒子群算法优化模拟退火算法图像分割含Matlab源码 2020期

记录使用python实现PSO求解最大值问题时,最需要注意的事