一、原理


假设我们有一堆点(比如直方图反向投影得到的点),和一个小的圆形窗口,我们要完成的任务就是将这个窗口移动到最大灰度密度处(也就是点最多的地方)。如下图所示:





opencv 计算rect重叠面积_opencv 计算rect重叠面积


初始窗口是蓝色的C1,它的圆心为蓝色方框的C1_o,而窗口中所有点质心却是C1_r,很明显圆心和点的质心没有重合。所以移动圆心C1_o到质心C1_r,这样我们就得到了一个新的窗口。这时又可以找到新的窗口内所以点的质心,大多数情况下还是不重合,所以重复上面的操作直到:将新窗口的圆心和它所包含的点的质心重合,这样我们的窗口会落在像素值(和)最大的地方。如上图C2是窗口的最后位置,它包含的像素点最多。


具体数学原理,参考 MeanShift原理, MeanShift数学公式


二、实现步骤



1、读入参考图像,设置感兴趣区域(ROI)


2、获取感兴趣区域的色调直方图



3、读取新图像计算色调直方图的反向投影


4、使用meanshift算法在反向投影中定位


三、函数及代码


meanShift(probImage, window, criteria)


probImage: 概率分布图像,也就是ROI色调直方图的反向投影


window: 初始搜索窗口,就是定义ROI的rect


迭代次数达到设置的最大值;窗口中心的漂移值小于某个设定的限值。

由于OpenCV官方文档中的实例运行出错,所以需要自己设置参数,视频和代码已上传:下载地址


import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

cap = cv.VideoCapture('img/slow.flv')
#读取第一帧图像
ret, frame = cap.read()
cv.imwrite('img/shift/car.jpg', frame)
#设置初始窗口参数
r, h, c, w = 190, 30, 300, 90
track_window = (c, r, w, h)

#获取ROI
roi = frame[r:r+h, c:c+w]
cv.rectangle(frame, (c, r), (c+w, r+h), (0, 255, 0), 2)
cv.imshow('img', frame)
cv.imshow('roi', roi)
#转成HSV格式
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 将低亮度的值忽略掉
lower_hsv = np.array([0, 0, 0])
upper_hsv = np.array([180, 255, 46]) #黑色
mask = cv.inRange(hsv_roi, lowerb=lower_hsv, upperb=upper_hsv)
#颜色直方图
roi_hist = cv.calcHist([hsv_roi], [0], mask, [180], [0, 180])

#归一化
cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX)
#确定窗口搜索停止的准则,迭代次数达到设置的最大值,或者窗口中心的漂移值小于设定值
term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1)

while True:
    ret, frame = cap.read()

    if ret is True:
        hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
        #直方图反向投影
        dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
        #返回迭代次数和更新后的边框
        ret, track_window = cv.meanShift(dst, track_window, term_crit)
        print(ret)
        #在图像中画出
        x, y, w, h = track_window
        img2 = cv.rectangle(frame, (x, y), (x+w, y+h), 255, 2)
        cv.imshow('img2', img2)

        k = cv.waitKey(60)& 0xff
        if k == 27:
            break
        # else:
        #     cv.imwrite('img/shift/'+chr(k)+'.jpg', img2)
    else:
        break

cv.waitKey(0)
cv.destroyAllWindows()
cap.release()


opencv 计算rect重叠面积_反向投影_02