1.背景消除

1.帧差法

对于视频,不同影像所在的位置不同,那么相邻两帧的图像进行差分运算,如果所得区域灰度差值的绝对值超过一定的阈值时,那么判定为运动的目标,否则为背景。

为什么要用阈值?因为背景中可能也会有些物品动,比如风吹过的树。

因为图像中容易有噪音点和空洞问题,该方法基本不会使用。

java opencv 图片去黑色背景色 opencv背景消除_计算机视觉

2.混合高斯模型

(1)若第一帧图像像素值为100,xigema值为5(假设,默认);

(2)取t帧数据图像(要足够大!!200up)进行模型的训练;

(3)接下来像素值为105,比较,若当前的105与前面的像素值进行差值比较,结果为5,若大于5,说明差距大,说明此分布不太合适,需要新建一个分布;若没有大于,所以比较合适,则把他们两个放一起,是同一个分布,更新xigema和像素值。

(4)。。。循环往复,建立多个分布,最好是3-5个。

java opencv 图片去黑色背景色 opencv背景消除_计算机视觉_02

测试,实战

(1)逐帧读取视频。

(2)mask掩码操作,前景置为白色,背景置为黑色。

(3)形态学变换,去掉一些噪音点,让得到的结果更好。

(4)检测每个人的轮廓(这里用的是opencv的方法而不是深度学习,深度学习的方法是现在的主流方法)。计算轮廓的周长,若周长比较大就代表是个人。绘制轮廓。

java opencv 图片去黑色背景色 opencv背景消除_计算机视觉_03

import numpy as np
import cv2

#经典的测试视频
cap = cv2.VideoCapture('test.avi')
#形态学操作需要使用
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
#创建混合高斯模型用于背景建模
fgbg = cv2.createBackgroundSubtractorMOG2()

while(True):
    ret, frame = cap.read()
    fgmask = fgbg.apply(frame)
    #形态学开运算去噪点
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    #寻找视频中的轮廓
    im, contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for c in contours:
        #计算各轮廓的周长
        perimeter = cv2.arcLength(c,True)
        if perimeter > 188:
            #找到一个直矩形(不会旋转)
            x,y,w,h = cv2.boundingRect(c)
            #画出这个矩形
            cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)    

    cv2.imshow('frame',frame)
    cv2.imshow('fgmask', fgmask)
    k = cv2.waitKey(150) & 0xff
    if k == 27:
        break

cap.release()
cv2.destroyAllWindows()

2.光流估计

像素运动的瞬时速度,也就是速度的矢量特征。,可以对图像进行动态分析,例如目标追踪。

有三个前提:亮度恒定、小运动(帧与帧之间)(不是夸张的剧烈运动)、空间一致(邻近像素点变化总的来说一致)。

java opencv 图片去黑色背景色 opencv背景消除_计算机视觉_04

1.Lucas-kanada算法

泰勒级数展开函数(第二个等号处):

对xy的偏导,之前在sobel算子中有进行求导,所以我们可以求得;对t的偏导由前一帧减去后一帧(导数就是变化率嘛)。

java opencv 图片去黑色背景色 opencv背景消除_python_05


移项,想要求得u,v两个未知数,至少要两个像素点。

java opencv 图片去黑色背景色 opencv背景消除_opencv_06


Ab区域,5*5,一共有25个点,若得到的uv没办法满足所有的xy,那就尽可能多地满足。

java opencv 图片去黑色背景色 opencv背景消除_计算机视觉_07


矩阵什么时候可逆?入1入2,在角点检测时,当两个值都大时,他就是一个角点,通常情况下,角点是可逆的。

java opencv 图片去黑色背景色 opencv背景消除_python_08

### cv2.calcOpticalFlowPyrLK():
参数:
- prevImage 前一帧图像

- nextImage 当前帧图像

- prevPts 待跟踪的特征点向量

- winSize 搜索窗口的大小

- maxLevel 最大的金字塔层数

返回:

- nextPts 输出跟踪特征点向量(这里的特征点从数学的意义上看就是角点)

- status 特征点是否找到角点,找到的状态为1,未找到的状态为0

实战:
(1)初始化参数
(2)预处理
(3)返回所有检测点:
(4)创建mask:
(5)循环处理

import numpy as np
import cv2

#输入视频
cap = cv2.VideoCapture('test.avi')

# 角点检测所需参数
#角点最大数量(效率),品质因子,最小距离:该角点最小距离内的其他角点都可以筛选
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7)

# lucas kanade参数
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2)

# 随机颜色条
color = np.random.randint(0,255,(100,3))

# 拿到第一帧图像
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 返回所有检测特征点,需要输入图像
# 距离相当于这区间有比这个角点强的,就不要这个弱的了
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)

# 创建一个mask
mask = np.zeros_like(old_frame)

while(True):
    ret,frame = cap.read()
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 光流估计:需要传入前一帧和当前图像以及前一帧检测到的角点。
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    # st=1表示,st==1就传入
    good_new = p1[st==1]
    good_old = p0[st==1]

    # 绘制轨迹
    for i,(new,old) in enumerate(zip(good_new,good_old)):
        a,b = new.ravel()
        c,d = old.ravel()
        #绘制新旧两点之间的线
        mask = cv2.line(mask, (int(a),int(b)),(int(c),int(d)), color[i].tolist(), 2)
        #绘制新点的线
        frame = cv2.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1)
    img = cv2.add(frame,mask)

    cv2.imshow('frame',img)
    k = cv2.waitKey(150) & 0xff
    if k == 27:
        break

    # 更新,将当前帧赋值给旧帧
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()

总结:目标追踪的方法:
深度学习的算法;光流估计。