【问题标题】:What OpenCV tracking API should I use to track running cells?我应该使用什么 OpenCV 跟踪 API 来跟踪正在运行的细胞?
【发布时间】:2019-04-15 16:11:58
【问题描述】:

我正在开展一个项目,以跟踪通过流体通道运行的多个细胞。我需要跟踪细胞从它们进入通道直到它们离开通道。

我正在使用 LabVIEW 来完成这个跟踪任务,其中我在帧之间使用 X 协调器。这种方法只有在流量很慢的时候才有用。当有大量细胞进入时,它会变得不太准确。

您能否就我的任务使用哪些 OpenCV 跟踪方法给我一些建议?我正在使用 Python。

【问题讨论】:

  • 细胞看起来有多相似?您是否需要一种在它们出现时识别它们的方法,或者只是在它们移动时对其进行跟踪?
  • 我使用灰度图像。在某些情况下,它们看起来彼此相似。我需要检测和跟踪。感谢您的回复。
  • 当你想检测时,背景变化大吗,还是只是流过的细胞?
  • 背景没有变化,只有细胞流过通道。

标签: python opencv tracking


【解决方案1】:

首先,在流动任何细胞之前,我会通过累积帧来获取平均背景图像。这将允许您稍后使用背景减法来更轻松地找到您关心的单元格。我使用一个看起来像这样的函数来执行此操作,其中images 是您要平均的帧系列。

# returns a frame that is the average of all the provided frames
def average(images):
    average = np.zeros(images[0].shape, np.float32)
    for image in images:
        cv2.accumulate(image, average)
    average = average / len(images)
    average = np.uint8(average)
    return average

一旦你有一个好的背景(使用适当数量的帧,如果你在减法后看到噪音,增加用于平均的帧数),你可以使用一系列背景减法、腐蚀和膨胀(如果需要)和轮廓查找来定位细胞。

这个函数有所有这些部分,你可以用它作为例子。它将减去背景,对其进行阈值化,然后侵蚀和扩张以消除任何剩余的噪声,并寻找剩下的轮廓,理想情况下是您关心的细胞。您可能必须更改腐蚀和膨胀大小(它们以像素为单位)才能获得良好的图片。

# detect a moving ball with countours and background subtraction
def detectBall(frame, background, roi):
    # background subtraction, thresholding, erosion and dialation
    motion = cv2.absdiff(background, frame[roi[0][1]:roi[1][1], roi[0][0]:roi[1][0]])
    _, thresh1 = cv2.threshold(motion, 10, 255, cv2.THRESH_BINARY)
    gray = cv2.cvtColor(thresh1, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    thresh = cv2.threshold(blurred, 30, 255, cv2.THRESH_BINARY)[1]
    erosion_size = 10
    dilate_size = 8
    thresh = cv2.erode(thresh, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (erosion_size, erosion_size)))
    thresh = cv2.dilate(thresh, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (dilate_size, dilate_size)))

    # find countours
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    center = None
    for c in cnts:
        # compute the center of the contour
        M = cv2.moments(c)
        cX = int((M["m10"] / (M["m00"] + 1e-7)))
        cY = int((M["m01"] / (M["m00"] + 1e-7)))

        # do some color filtering on each contour to eliminate extra contours
        hsv = cv2.cvtColor(motion, cv2.COLOR_BGR2HSV)
        (_, _, h) = hsv[cY, cX]
        if 80 < h < 140:
            # calculate the center and draw the detected ball
            c = c.astype("float")
            c = c.astype("int")
            area = int(cv2.contourArea(c))
            diam = int(math.sqrt(area / math.pi))
            center = (cX + roi[0][0], cY + roi[0][1])
            cv2.circle(frame, center, 1, RED, 5)
            cv2.circle(frame, center, diam, GREEN, 2)
            cv2.putText(frame, str(center), (center[0] + 10, center[1] + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, BLUE, 2) # draw center text
    return center, frame

这将为您提供与背景不同的任何对象的位置,并且在我的系统上运行得非常快。

这对您来说可能已经足够了,但是如果在确定您想要尝试跟踪特定单元格之后,您可以使用模板匹配并围绕已知单元格位置的中心进行切片,以查看它们移动到的位置。如果细胞看起来都非常相似,这可能会也可能不会很好,但您可以尝试一下。

为此,我使用如下函数:

def findMatchingPoints(old_frame, new_frame, pts, templateSize=5, searchWindowSize=20):
    output_points = []
    strengths = []
    for pt in pts:
        # make template and window images
        l_template = int(templateSize / 2)      # calculate half template size
        l_window = int(searchWindowSize /2)     # calculate half window size
        x = int(pt[0][0])                       # get x coordinate
        y = int(pt[0][1])                       # get y coordinate
        template = old_frame[y-l_template:y+l_template, x-l_template:x+l_template]      # templeate comes from old
        window = new_frame[y-l_window:y+l_window, x-l_window:x+l_window]                # look in the new
        # skip poorly formed windows or templates
        if 0 in window.shape or 0 in template.shape:
            strengths.append(float(10))
            output_points.append([[np.float32(0), np.float32(0)]])
            continue
        # Apply template matching and save results
        res = cv2.matchTemplate(window, template, cv2.TM_SQDIFF_NORMED)
        strength, _, top_left, _ = cv2.minMaxLoc(res)                    # returns (min_val, max_val, min_loc, max_loc), for SSD we want min location, is top left of template
        center = [[np.float32(top_left[0]+x-l_window), np.float32(top_left[1]+y-l_window)]]
        strengths.append(strength)
        output_points.append(center)

    # create a boolean mask to keep good points
    output_points = np.asarray(output_points)
    _, mask = cv2.findFundamentalMat(pts, output_points, cv2.FM_RANSAC)

    return output_points, strengths, mask

# see which matches are good
def filter_matches(p0, p1, st, good):
    good_old, good_new = [], []
    for old, new, s, g in zip(p0, p1, st, good):
        s = int(s*100)
        if s < 1 and g == 1:
            good_new.append(new)
            good_old.append(old)
    return good_old, good_new

希望这会有所帮助。这里的关键功能/想法是背景平均和减法、轮廓查找和模板匹配。

【讨论】:

  • 谢谢马修,我会听从你的指示的。我唯一想知道的是模板匹配,因为有些情况下所有单元格看起来都相似。
  • 如果它们看起来非常相似,那么您可能不需要模板匹配。在这种情况下,最好快速采样,这样您就可以知道哪些细胞移动到了哪里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-06
  • 2011-06-21
相关资源
最近更新 更多