首页 > 解决方案 > Python/OpenCV — 匹配两个图像中细菌的质心点

问题描述

我正在研究一种使用计算机视觉匹配细菌质心的算法。

由于我是计算机视觉的本科生和初学者,因此我没有专门针对此问题的代码。只是为了提供一些背景知识,我在我的 GUI 中使用了以下功能。

'bact' 变量指的是 Bacteria 对象,它存储了每个细菌的 ID、位置等。

 def identify_fluor(img, frame: int):

    darkBlue = (139, 0, 0)

    for bact in fluor_at_frame(frame):
    
        pos = tuple([int(coord) for coord in bact.position[frame]])
        img = cv2.circle(img, pos, 5, darkBlue, -1)

    return img
 def identify_bright(img, frame: int):

    darkRed = (0, 0, 139)

    for bact in bright_at_frame(frame):

        pos = tuple([int(coord) for coord in bact.position[frame]])
        img = cv2.circle(img, pos, 5, darkRed, -1)

    return img

这些质心是使用当前图像处理文献中可用的最佳软件找到的。如您所见,右侧(明场)的处理图像明显欠发达,对细菌学研究人员来说是一个重大障碍和麻烦。

我们需要处理右侧的这些图像,因为它们具有明显更高的图像采样率(1 秒 [右] 与 11 秒 [左])。当采样过于频繁时,荧光图像(左)会累积化学损伤,从而失去荧光。

这些是图像完美对齐的一些情况:

Bacteria Match 样品 1

Bacteria Match 样本 2 :

Bacteria Match 样本 3 :

在这些情况下,右侧的图像在到达下一个对齐图像之前处于中间阶段。

细菌火柴样品 4

菌种样本 5

细菌匹配样本 6

明场图像

明场样品 1 明场样品 1

明场样品 2 明场样品 2

明场样品 3 明场样品 3

附加链接

明场样品 4

明场样品 5

明场样品 6

明场样品 7

明场样品 8

明场样品 9

注意:这不是家庭作业。我正在做一个研究项目,试图获取有关细菌时间动态的信息。我正在尝试在其中一个图像样本上实现工作解决方案。

编辑#1:为澄清起见,我试图使用左侧的细菌找到右侧细菌的质心。

编辑#2:我不希望通过应用线性变换来匹配图像。寻求一种计算机视觉算法。

编辑#3:为测试目的单独添加了额外的明场图像。

标签: pythonpython-3.xopencvimage-processingcomputer-vision

解决方案


我的方法直接适用于正确的图像。

代码在下面共享并用注释解释:

我在开始时创建了一个函数,它使用指定次数的圆形内核侵蚀和膨胀图像。

kernel = np.array([[0, 0, 1, 0, 0], 
                   [0, 1, 1, 1, 0], 
                   [1, 1, 1, 1, 1], 
                   [0, 1, 1, 1, 0], 
                   [0, 0, 1, 0, 0]], dtype=np.uint8)
def e_d(image, it):
    image = cv2.erode(image, kernel, iterations=it)
    image = cv2.dilate(image, kernel, iterations=it)
    return image

注意:右边的图像是在变量“img”中以灰度格式读取的。

# Applying adaptive mean thresholding
th = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV,11,2)
# Removing small noise
th = e_d(th.copy(), 1)

# Finding contours with RETR_EXTERNAL flag and removing undesired contours and 
# drawing them on a new image.
cnt, hie = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cntImg = th.copy()
for contour in cnt:
    x,y,w,h = cv2.boundingRect(contour)
    # Eliminating the contour if its width is more than half of image width
    # (bacteria will not be that big).
    if w > img.shape[1]/2:      
        continue
    cntImg = cv2.drawContours(cntImg, [cv2.convexHull(contour)], -1, 255, -1)

# Removing almost all the remaining noise. 
# (Some big circular noise will remain along with bacteria contours)
cntImg = e_d(cntImg, 5)


# Finding new filtered contours again
cnt2, hie2 = cv2.findContours(cntImg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Now eliminating circular type noise contours by comparing each contour's 
# extent of overlap with its enclosing circle.
finalContours = []      # This will contain the final bacteria contours
for contour in cnt2:
    # Finding minimum enclosing circle
    (x,y),radius = cv2.minEnclosingCircle(contour)
    center = (int(x),int(y))
    radius = int(radius)

    # creating a image with only this circle drawn on it(filled with white colour)
    circleImg = np.zeros(img.shape, dtype=np.uint8)
    circleImg = cv2.circle(circleImg, center, radius, 255, -1)

    # creating a image with only the contour drawn on it(filled with white colour)    
    contourImg = np.zeros(img.shape, dtype=np.uint8)
    contourImg = cv2.drawContours(contourImg, [contour], -1, 255, -1)

    # White pixels not common in both contour and circle will remain white 
    # else will become black.
    union_inter = cv2.bitwise_xor(circleImg, contourImg)
    
    # Finding ratio of the extent of overlap of contour to its enclosing circle. 
    # Smaller the ratio, more circular the contour.
    ratio = np.sum(union_inter == 255) / np.sum(circleImg == 255)
    
    # Storing only non circular contours(bacteria)
    if ratio > 0.55:
        finalContours.append(contour)

finalContours = np.asarray(finalContours)


# Finding center of bacteria and showing it.
bacteriaImg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

for bacteria in finalContours:
    M = cv2.moments(bacteria)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])

    bacteriaImg = cv2.circle(bacteriaImg, (cx, cy), 5, (0, 0, 255), -1)
    
cv2.imshow("bacteriaImg", bacteriaImg)
cv2.waitKey(0)

注意:我只拍摄右侧的图像,我的图像尺寸为 (221, 828)。如果您的输入图像小于或大于此值,请调整腐蚀和膨胀的迭代次数值以相应地去除噪声以获得良好的结果。

以下是输出图像:

1 2 3

此外,正如您在第三张图片中看到的那样,最左边的细菌,其中心并不完全位于中心。发生这种情况是因为,在代码中,我在一个地方使用了轮廓的凸包。您可以通过跟踪所有轮廓来解决此问题,然后在最后取初始轮廓的中心。

我确信这段代码也可以修改并变得更好,但这是我现在能想到的。任何建议都非常受欢迎。


推荐阅读