Какой API-интерфейс отслеживания OpenCV следует использовать для отслеживания запущенных ячеек? - PullRequest
0 голосов
/ 15 апреля 2019

Я работаю над проектом по отслеживанию нескольких ячеек, проходящих через канал жидкости. Мне нужно отследить клетки с момента их появления в канале и до выхода из канала.

Я работал с LabVIEW для этой задачи отслеживания, в которой я использую координатор X между кадрами. Этот метод хорош только тогда, когда поток медленный. Он становится менее точным, когда поступает большое количество клеток.

Не могли бы вы дать мне несколько советов о том, какие методы отслеживания OpenCV для моей задачи? Я использую Python.

1 Ответ

1 голос
/ 15 апреля 2019

Во-первых, перед обтеканием любых ячеек я бы получил усредненное фоновое изображение, накапливая кадры. Это позволит вам позже использовать вычитание фона, чтобы легче находить нужные вам клетки. Я использую функцию, которая выглядит следующим образом, где 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

Надеюсь, это поможет. Ключевыми функциями / идеями здесь являются усреднение и вычитание фона, поиск контуров и сопоставление с шаблоном.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...