【问题标题】:Python/OpenCV — Intelligent Centroid Tracking in Bacterial Images?Python/OpenCV — 细菌图像中的智能质心跟踪?
【发布时间】:2020-11-16 01:05:08
【问题描述】:

我目前正在研究一种在显微镜图像中检测细菌质心的算法。

本题继续:OpenCV/Python — Matching Centroid Points of Bacteria in Two Images: Python/OpenCV — Matching Centroid Points of Bacteria in Two Images

我正在使用 Rahul Kedia 提出的程序的修改版本。 https://stackoverflow.com/a/63049277/13696853

目前,我正在处理的细分问题是:

  1. 低对比度
  2. 集群

下面的图片间隔一秒采样。然而,在后一张图片中,没有检测到其中一种细菌。

Bright-field Image #1

Bright-Field Image #2

明场轮廓图像 #1

明场轮廓图像 #2

Bright-Field Image #1 (Unsegmented)

Bright-Field Image #2 (Unsegmented)

我想知道,鉴于我可以成功确定图像中的细菌质心,我是否可以使用数据智能地在后续图像中寻找相同的细菌?

我无法在网上找到任何实质性的东西;我相信 SIFT/SURF 可能无效,因为细菌具有相同的外观。此外,我正在寻找图像中的特定点。你可以在下面查看我的程序。如果您想运行该程序,请按照指示插入特定路径。

import cv2
import numpy as np
import os

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


path = r"[INSERT PATH]"
img_files = [file for file in os.listdir(path)]


def segment_index(index: int):
    segment_file(img_files[index])


def segment_file(img_file: str):
    img_path = path + "\\" + img_file
    print(img_path)
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 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, 3)

    # 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)


# Segment Each Image
for i in range(len(img_files)):
    segment_index(i)

编辑 #1:应用 frmw42 的方法,此图像似乎丢失了。我尝试调整了一些参数,但图像似乎没有显示出来。

Bright-Field Image #3

Bright-Field Image #4

【问题讨论】:

  • 我建议你展示你的代码并在每一步之后查看图像,看看一个细菌在哪里丢失。您也许可以调整某些参数或命令以将其引入。
  • @fmw42 我已经添加了我的代码。
  • 您是否查看了每个步骤后创建的图像以找到一种细菌丢失的位置?特别是查看您的阈值结果并尝试更改参数。在阈值化之前,您是否尝试过中值滤波或其他降噪?您是否在阈值化之前尝试过锐化?其他内核形状或大小呢?
  • @fmw42 查看轮廓图,好像是算法中的轮廓碎片,我贴图吧。你建议改变哪些论点?我正在做一个研究项目,之前从未做过任何计算机视觉,我可以在正确的方向上使用一些指针。
  • 您是否查看了阈值处理的结果以查看该图像是否正常?您的圆形噪声过滤是否会损坏您的第一个轮廓?联合过滤和比率过滤呢?它们是否会损害您的结果?请查看每个结果或保存该结果的图像,以便您可以调试出现问题的位置。不要只看最终结果。查看每个步骤。

标签: python python-3.x opencv image-processing computer-vision


【解决方案1】:

这是我提取细菌的 Python/OpenCV 代码。我只是简单地设置阈值,然后获取轮廓并为特定区域范围内的轮廓绘制填充轮廓。我会让你做任何你想做的进一步处理。我只是查看了每个步骤,以确保在进行下一步之前我已经适当地调整了参数。

输入 1:

输入 2:

import cv2
import numpy as np

# read image
#img = cv2.imread("bacteria1.png")
img = cv2.imread("bacteria2.png")

# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255 - gray

# do adaptive threshold on inverted gray image
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21, 5)

result = np.zeros_like(img)
contours = cv2.findContours(thresh , cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for cntr in contours:
    area = cv2.contourArea(cntr)
    if area > 600 and area < 1100:
        cv2.drawContours(result, [cntr], 0, (255,255,255), -1)


# write results to disk
#cv2.imwrite("bacteria_filled_contours1.png", result)
cv2.imwrite("bacteria_filled_contours2.png", result)

# display it
cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)

结果 1:

结果 2:

根据需要进行调整。

【讨论】:

  • 谢谢!我了解您的算法,但我无法找到使 Bright-Field Image #3 出现的方法(这是目前最容易丢失的图像)。我尝试过使用参数,但它完全把它搞砸了。我以前从未做过任何图像处理,因此非常感谢您的专业知识。
  • 我应该尝试优化哪些参数?似乎针对特定图像进行优化会使其他图像变得更糟。
  • 这条线似乎适用于我的所有 3 张图片。 thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21, 2)
  • 我一直在玩这个。当细菌处于分裂过程中时,问题似乎就出现了。你能看看 Bright-Field Image #4 吗?
【解决方案2】:

似乎自适应阈值无法处理您所有的各种图像。我怀疑没有什么简单的事情会发生。您可能需要在训练中使用 AI。不过,这适用于您的图像:Python/OpenCV 中的 1、2 和 4。我不保证它适用于您的任何其他图像。

首先,我找到了一个看似可行的简单阈值,但会引入其他区域。因此,由于您所有的细菌都具有相似的形状和方向范围,因此我拟合并椭圆化您的细菌并获得主轴的方向并使用面积和角度过滤轮廓。


import cv2
import numpy as np

# read image
#img = cv2.imread("bacteria1.png")
#img = cv2.imread("bacteria2.png")
img = cv2.imread("bacteria4.png")

# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255 - gray

# median filter
#gray = cv2.medianBlur(gray, 1)

# do simple threshold on inverted gray image
thresh = cv2.threshold(gray, 170, 255, cv2.THRESH_BINARY)[1]

result = np.zeros_like(img)
contours = cv2.findContours(thresh , cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for cntr in contours:
    area = cv2.contourArea(cntr)
    if area > 600 and area < 1100:
        ellipse = cv2.fitEllipse(cntr)
        (xc,yc),(d1,d2),angle = ellipse
        if angle > 90:
            angle = angle - 90
        else:
            angle = angle + 90
        print(angle,area)
        if angle >= 150 and angle <= 250:
            cv2.drawContours(result, [cntr], 0, (255,255,255), -1)
  
# write results to disk
#cv2.imwrite("bacteria_filled_contours1.png", result)
#cv2.imwrite("bacteria_filled_contours2.png", result)
cv2.imwrite("bacteria_filled_contours4.png", result)

# display it
cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)

图片 1 的结果:

图片 2 的结果:

图片 4 的结果:

您可以在阈值化之前探索降噪。我在使用一些 ImageMagick 工具方面取得了一些成功,并且有一个名为 Python Wand 的 Python 版本使用了 ImageMagick。

【讨论】:

    猜你喜欢
    • 2020-11-08
    • 2020-11-20
    • 1970-01-01
    • 2020-10-21
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    相关资源
    最近更新 更多