【youcans 的 OpenCV 例程200篇】176.图像分割之均值漂移算法 Mean Shift

OpenCV 例程200篇 总目录-202205更新


【youcans 的 OpenCV 例程200篇】176.图像分割之均值漂移算法 (Mean Shift)


5.6 图像分割之均值漂移算法

均值漂移算法(Mean Shift)是一种基于模式识别的特征空间分析方法,提供了一种目标描述与定位的算法框架。

均值漂移算法的基本思想是,通过反复迭代搜索特征空间中样本最密集的区域,搜索点沿着样本点密度增加的方向“漂移”到局部密度极大值点。采用基于核密度估计的爬山算法,自适应调整步长进行迭代搜索,可以收敛到局部极值。

基于 Mean Shift 的目标跟踪技术采用核概率密度描述目标特征,对于图像分割通常采用直方图对目标建模,然后通过相似性度量搜索目标位置,实现目标的匹配与跟踪。

对于图像分割,通常将 RGB 图像映射到 LUV 颜色特征空间,结合位置信息可以构造 5维特征变量 (x,y,l,u,v)。均值漂移算法不仅可以应用于二维图像处理,也可以用于高维数据处理。可以通过选取不同的核函数,来改变区域当中偏移向量的权重。

均值漂移算法将目标特征与空间信息有效结合,避免使用复杂模型描述目标形状、外观和运动,因此对边缘遮挡、目标旋转、变形和背景运动不敏感,能够适应目标的形状、大小的连续变换,而且计算速度快、抗干扰能力强。

均值漂移算法的缺点是:
(1)缺乏必要的模板更新;
(2)跟踪过程中由于窗口宽度大小保持不变,当目标尺度变化时,跟踪就会失败;
(3)当目标速度较快时,跟踪效果不好;
(4)直方图特征在目标颜色特征描述方面略显匮乏,缺少空间信息;

对于这些缺点,在工程实际中可以进行改进:
(1)引入目标位置变化的预测机制,以减少跟踪的搜索时间,降低计算量;
(2)将传统算法中的核函数固定带宽改为动态变化的带宽;
(3)使用前一帧的目标跟踪结果作为目标跟踪模板。

OpenCV 也提供了函数 cv.meanShift 实现均值漂移算法,该函数只考虑颜色相似性,不考虑像素的位置坐标。

该函数采用目标对象的输入反投影和初始位置,通过迭代搜索算法,计算反投影图像窗口中的质心,将搜索窗口中心移向质心,直到达到迭代终止条件。

函数说明:

cv.meanShift(probImage, window, criteria[, ]) →	retval, window

cv.meanshift() 函数使用时要先设定目标,需要提供目标的初始窗口位置,计算 HSV 模型中 H (色调)的直方图。为了减少低亮度的影响,可以使用 cv.inRange() 将低亮度值忽略。

参数说明:

  • probImage:对象直方图的反向投影,参见 calcBackProject
  • window:初始的搜索窗口
  • criteria:迭代搜索的终止条件

注意事项:

  • 如果对反投影进行预滤波并去除噪声,可以获得更好的结果。
  • 可以通过使用 findContours 检索连接的组件,丢弃面积较小的轮廓(contourArea),并使用drawContours渲染其余轮廓来实现。
  • OpenCV 中的另一个函数 pyrMeanShiftFiltering(),是图像在色彩层面的平滑滤波,可以中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域。

例程 11.33: 图像分割之均值漂移算法

    # 11.33 图像分割之均值漂移算法
    def meanShiftTracker(src, trackWindow):
        # meanShift 算法: 在 dst 寻找目标窗口,找到后返回目标窗口位置
        hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)  # BGR-HSV 转换
        dst = cv2.calcBackProject([hsv], [0], roiHist, [0, 180], 1)  # 计算反向投影
        term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
        _, trackWin = cv2.meanShift(dst, trackWindow, term_crit)
        x, y, w, h = trackWin
        imgTrack = src.copy()
        imgTrack = cv2.rectangle(imgTrack, (x, y), (x + w, y + h), 255, 2)
        print(x, y, w, h)
        return imgTrack

    # if __name__ == '__main__':  # 图像分割之均值漂移算法
    img = cv2.imread("../images/FigCross1.png", flags=1)  # 基准参考图像
    imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL)  # BGR-HSV 转换

    # 设置初始化的窗口位置
    # print("Select a ROI and then press SPACE or ENTER button!\n")
    # roi = cv2.selectROI(img, showCrosshair=True, fromCenter=False)
    # x0, y0, w, h = roi  # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w) 的位置参数
    # rect = (x0, y0, w, h)  # 边界框矩形的坐标和尺寸  # rect = (990 311 94 72)
    (x0, y0, w, h) = (990, 310, 95, 72)  # 直接设置矩形窗口的位置参数,也可以鼠标框选 ROI
    trackWindow = (x0, y0, w, h)  # 矩形 ROI
    print(x0, y0, w, h)

    imgROI = np.zeros_like(img)  # 创建与 image 相同形状的黑色图像
    imgROI[y0:y0+h, x0:x0+w] = img[y0:y0+h, x0:x0+w].copy()
    frameROI = imgROI[y0:y0+h, x0:x0+w]  # 设置追踪的区域
    roiHSV = cv2.cvtColor(frameROI, cv2.COLOR_BGR2HSV)  # BGR-HSV 转换

    # 取 HSV 在 (0,60,32)~(180,255,255) 之间的部分
    mask = cv2.inRange(roiHSV, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
    roiHist = cv2.calcHist([roiHSV], [0], mask, [180], [0, 180])  # 计算直方图
    cv2.normalize(roiHist, roiHist, 0, 255, cv2.NORM_MINMAX)  # 归一化

    # # meanShift 算法: 在 dst 寻找目标窗口,找到后返回目标窗口位置
    img1 = cv2.imread("../images/FigCross2.png", flags=1)  # 读取彩色图像(BGR)
    imgTrack1 = meanShiftTracker(img1, trackWindow)
    img2 = cv2.imread("../images/FigCross4.png", flags=1)  # 读取彩色图像(BGR)
    imgTrack2 = meanShiftTracker(img2, trackWindow)

    plt.figure(figsize=(10, 6))
    plt.subplot(231), plt.axis('off'), plt.title("Initial image")
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(234), plt.axis('off'), plt.title("ROI image")
    plt.imshow(cv2.cvtColor(imgROI, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(232), plt.axis('off'), plt.title("image 1")
    plt.imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(235), plt.axis('off'), plt.title("image track 1")
    plt.imshow(cv2.cvtColor(imgTrack1, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(233), plt.axis('off'), plt.title("image 2")
    plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.subplot(236), plt.axis('off'), plt.title("image track 2")
    plt.imshow(cv2.cvtColor(imgTrack2, cv2.COLOR_BGR2RGB))  # 显示 img(RGB)
    plt.tight_layout()
    plt.show()

在这里插入图片描述

程序说明:

均值漂移算法进行图像分割的效果并不是很好,所以 img2 中的目标并未成功追踪,这是由于拍摄角度导致目标物体运动后的尺寸发生了变化。不过,如果应用于视频跟踪,每次使用上一帧的目标窗口作为追踪区域,而不是一直使用初始目标窗口,由于目标在相邻帧的尺寸变化很小,可以解决这个问题。


(本节完)


版权声明:

OpenCV 例程200篇 总目录-202205更新
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/124695824)

Copyright 2022 youcans, XUPT
Crated:2022-5-8


欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中

【youcans 的 OpenCV 例程200篇】147. 图像分割之孤立点检测
【youcans 的 OpenCV 例程200篇】148. 图像分割之线检测
【youcans 的 OpenCV 例程200篇】149. 图像分割之边缘模型
【youcans 的 OpenCV 例程200篇】150. 边缘检测梯度算子
【youcans 的 OpenCV 例程200篇】151. 边缘检测中的平滑处理
【youcans 的 OpenCV 例程200篇】152. 边缘检测之 LoG 算子
【youcans 的 OpenCV 例程200篇】153. 边缘检测之 DoG 算子
【youcans 的 OpenCV 例程200篇】154. 边缘检测之 Canny 算子
【youcans 的 OpenCV 例程200篇】155. 边缘连接的局部处理方法
【youcans 的 OpenCV 例程200篇】156. 边缘连接局部处理的简化算法
【youcans 的 OpenCV 例程200篇】157. 霍夫变换直线检测
【youcans 的 OpenCV 例程200篇】158. 阈值处理之固定阈值法
【youcans 的 OpenCV 例程200篇】159. 图像分割之全局阈值处理
【youcans 的 OpenCV 例程200篇】160. 图像处理之OTSU 方法
【youcans 的 OpenCV 例程200篇】161. OTSU 阈值处理算法的实现
【youcans 的 OpenCV 例程200篇】162. 全局阈值处理改进方法
【youcans 的 OpenCV 例程200篇】163. 基于边缘信息改进全局阈值处理
【youcans 的 OpenCV 例程200篇】164.使用 Laplace 边缘信息改进全局阈值处理
【youcans 的 OpenCV 例程200篇】165.多阈值 OTSU 处理方法
【youcans 的 OpenCV 例程200篇】166.自适应阈值处理
【youcans 的 OpenCV 例程200篇】167.基于移动平均的可变阈值处理
【youcans 的 OpenCV 例程200篇】168.图像分割之区域生长
【youcans 的 OpenCV 例程200篇】169.图像分割之区域分离
【youcans 的 OpenCV 例程200篇】170.图像分割之K均值聚类
【youcans 的 OpenCV 例程200篇】171.SLIC 超像素区域分割
【youcans 的 OpenCV 例程200篇】172.SLIC 超像素区域分割算法比较
【youcans 的 OpenCV 例程200篇】173.SEEDS 超像素区域分割
【youcans 的 OpenCV 例程200篇】174.LSC 超像素区域分割
【youcans 的 OpenCV 例程200篇】175.超像素区域分割方法比较
【youcans 的 OpenCV 例程200篇】176.图像分割之均值漂移算法

猜你喜欢

转载自blog.csdn.net/youcans/article/details/124695824