如何追踪图像轮廓并关联细菌细胞质心以计算瞬时速度?
细菌细胞瞬时速度计算的帧间匹配问题
我将细菌细胞运动的视频转换为帧图像,想要计算每个细菌的瞬时速度,需要确定每个细菌的位移,但无法让程序准确识别前后帧中的同一个细菌。目前已获取每帧中每个细菌的质心(COM)坐标,但不知道如何关联这些数据来确定特定细菌的质心变化量。附上两张示例帧图像供参考。
我曾尝试为每个轮廓分配唯一ID,并关联其长轴、短轴长度等特征来匹配初始与最终质心,但该方法假设所有细菌细胞特征唯一且轮廓识别精准,而我的代码无法满足这一前提。
附上用于识别细菌细胞轮廓的代码:
import cv2 as cv import numpy as np from numpy.typing import NDArray import math def gaussian_filter_multiscale_retinex(image: NDArray, sigmas: list[float], weights: list[float]) -> NDArray: img32 = image.astype('float32') / 255 img32_log = cv.log(img32 + 1) msr = np.zeros(image.shape, np.float32) for sigma, weight in zip(sigmas, weights): blur = cv.GaussianBlur(img32, ksize=(0, 0), sigmaX=sigma) blur_log = cv.log(blur + 1) ssr = cv.subtract(img32_log, blur_log) ssr = cv.multiply(ssr, weight) msr = cv.add(msr, ssr) msr = cv.divide(msr, sum(weights)) msr = cv.normalize(msr, None, 0, 255, cv.NORM_MINMAX, cv.CV_8U) return msr def calculate_ellipse_area(ellipse): (cx, cy), (a, b), angle = ellipse semi_major_axis = a / 2 semi_minor_axis = b / 2 area = math.pi * semi_major_axis * semi_minor_axis return area,angle def process_image(img, size_threshold): gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) rtnx = gaussian_filter_multiscale_retinex(gray, sigmas=[15, 55, 185], weights=[10, 5, 1]) thresholded = cv.adaptiveThreshold(rtnx, 255, adaptiveMethod=cv.ADAPTIVE_THRESH_GAUSSIAN_C, thresholdType=cv.THRESH_BINARY, blockSize=7, C=-7) nb_components, output, stats, _ = cv.connectedComponentsWithStats(thresholded, connectivity=8) sizes = stats[1:, -1] new_img = np.zeros_like(thresholded) for i in range(0, nb_components - 1): if sizes[i] >= size_threshold: new_img[output == i + 1] = 255 connected_components = cv.connectedComponentsWithStats(new_img) (numLabels, labels, stats, centroids) = connected_components result_image = np.ones_like(img) * 255 for i in range(1, numLabels): componentMask = (labels == i).astype('uint8') contours, _ = cv.findContours(componentMask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) if len(contours) > 0: cnt = contours[0] if len(cnt) >= 5: ellipse = cv.fitEllipse(cnt) area, angle = calculate_ellipse_area(ellipse) if area < 250: cv.ellipse(result_image, ellipse, (0, 0, 0), 1) # Draw black contours on white background return result_image img1path = "/Users/yahya2/Desktop/1.png" img = cv.imread(img1path) size_threshold = 16 result_image = process_image(img, size_threshold) cv.imshow('Contours', result_image) cv.waitKey(0) cv.destroyAllWindows()
解决方案
1. 基于匈牙利算法的质心匹配
针对帧间质心的关联问题,最直接的方法是用匈牙利算法解决分配问题:
- 构建代价矩阵:矩阵元素为前帧每个质心到后帧每个质心的欧氏距离,也可以结合特征加权(比如质心距离+面积差异+角度差异的加权和)
- 用匈牙利算法找到代价最小的匹配,确保每个前帧质心对应唯一后帧质心,反之亦然
- 设定距离阈值,超过阈值的匹配视为无效(对应细菌移出视野或新细菌进入的情况)
示例代码片段(依赖scipy库):
from scipy.optimize import linear_sum_assignment import numpy as np def match_centroids(prev_centroids, curr_centroids, max_dist=30): # 构建代价矩阵 cost_matrix = np.zeros((len(prev_centroids), len(curr_centroids))) for i, prev in enumerate(prev_centroids): for j, curr in enumerate(curr_centroids): cost_matrix[i,j] = np.linalg.norm(prev - curr) # 匈牙利算法求解最优匹配 row_ind, col_ind = linear_sum_assignment(cost_matrix) # 过滤超出距离阈值的无效匹配 valid_matches = [] for r, c in zip(row_ind, col_ind): if cost_matrix[r,c] <= max_dist: valid_matches.append((r, c)) return valid_matches
2. 卡尔曼滤波预测+匹配
对于连续帧的跟踪,结合卡尔曼滤波预测细菌下一帧位置,再和检测到的质心匹配:
- 对每个已跟踪的细菌,用卡尔曼滤波预测其在下一帧的位置
- 将预测位置与当前帧检测到的质心计算距离,优先匹配距离最近的目标
- 单独处理新出现的细菌(无对应预测轨迹)和消失的细菌(无对应检测结果)
3. 稀疏光流法直接跟踪
如果细菌运动速度不快、帧间隔小,可以用Lucas-Kanade稀疏光流法直接跟踪特征点:
- 在第一帧检测细菌的质心或轮廓特征点
- 用
cv.calcOpticalFlowPyrLK跟踪这些特征点到后续帧 - 过滤掉跟踪失败的点,保留有效轨迹用于计算位移
4. 优化当前轮廓检测稳定性
先提升轮廓识别的准确性,减少匹配难度:
- 调整自适应阈值的
blockSize和C参数,减少误检和漏检 - 修改
process_image函数,返回每帧细菌的完整特征列表(包含质心、面积、角度等),而不仅仅是绘图结果 - 加入形态学操作(比如开运算去除噪点、闭运算填补轮廓缺口),让细菌轮廓更清晰
整合思路
- 修改
process_image函数,输出每帧的细菌特征集合(质心、面积、角度等) - 对连续两帧,先用卡尔曼滤波预测前帧细菌的位置
- 用匈牙利算法结合预测位置与当前帧特征的加权距离完成匹配
- 维护全局跟踪ID,处理新出现和消失的细菌
内容的提问来源于stack exchange,提问作者yahya ashraf
相关产品推荐
相关产品推荐

