在上篇博文《OpenCV3-Python简单移动目标跟踪》中介绍了简单移动目标跟踪方法,也提到在实际应用中,由于场景复杂其跟踪效果并不好;因此,本篇介绍常用的几种更有效的跟踪方法——背景分割器。

1. 背景分割器

OpenCV3中常用的背景分割器有三种:Mixture of Gaussians(MOG2)、K-Nearest(KNN)、Geometric Multigid(GMG) 。

OpenCV中提供了一个称为BackgroundSubtractor的类,在分割前景和背景时有两方面的优势:

<1> 专门用来进行视频分析,即:BackgroundSubtractor类会对每帧的环境进行 "学习" ;

<2> 可以计算阴影,通过检测阴影,可排除检测图像的阴影区域,从而更关注实际特征;

2. 应用举例

(1)MOG2

import cv2
import numpy as np

'''
retval = cv.createBackgroundSubtractorMOG2( [, history[, varThreshold[, detectShadows]]] )
Parameters
    history	Length of the history.
    varThreshold	Threshold on the squared Mahalanobis distance between the pixel and the model to decide whether a pixel is well described by the background model. This parameter does not affect the background update.
    detectShadows	If true, the algorithm will detect shadows and mark them. It decreases the speed a bit, so if you do not need this feature, set the parameter to false.
url: https://docs.opencv.org/3.4.2/de/de1/group__video__motion.html#ga2beb2dee7a073809ccec60f145b6b29c
'''
#MOG背景分割器
mog = cv2.createBackgroundSubtractorMOG2(detectShadows = True)

camera = cv2.VideoCapture("traffic.flv")

ret, frame = camera.read()
while ret:
  fgmask = mog.apply(frame)
  th = cv2.threshold(np.copy(fgmask), 244, 255, cv2.THRESH_BINARY)[1]
  
  th = cv2.erode(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)), iterations = 2)
  dilated = cv2.dilate(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (8,3)), iterations = 2)
  
  image, contours, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  for c in contours:
    if cv2.contourArea(c) > 1000:
      (x,y,w,h) = cv2.boundingRect(c)
      cv2.rectangle(frame, (x,y), (x+w, y+h), (255, 255, 0), 2)

  cv2.imshow("mog", fgmask)
  cv2.imshow("thresh", th)
  cv2.imshow("diff", frame & cv2.cvtColor(fgmask, cv2.COLOR_GRAY2BGR))
  cv2.imshow("detection", frame)
  ret, frame = camera.read()  #读取视频帧数据
  if cv2.waitKey(100) & 0xff == ord("q"):
      break

camera.release()
cv2.destroyAllWindows()

(2)KNN

import cv2
import numpy as np

'''
retval	=  cv.createBackgroundSubtractorKNN([, history[, dist2Threshold[, detectShadows]]])
Parameters
    history	Length of the history.
    dist2Threshold	Threshold on the squared distance between the pixel and the sample to decide whether a pixel is close to that sample. This parameter does not affect the background update.
    detectShadows	If true, the algorithm will detect shadows and mark them. It decreases the speed a bit, so if you do not need this feature, set the parameter to false.
'''
#KNN背景分割器
knn = cv2.createBackgroundSubtractorKNN(detectShadows = True)

camera = cv2.VideoCapture("traffic.flv")

def drawCnt(fn, cnt):
  if cv2.contourArea(cnt) > 1600:
    (x, y, w, h) = cv2.boundingRect(cnt)
    cv2.rectangle(fn, (x, y), (x + w, y + h), (255, 255, 0), 2)

while True:
  ret, frame = camera.read()
  if not ret: 
      break
  fgmask = knn.apply(frame)
  th = cv2.threshold(np.copy(fgmask), 244, 255, cv2.THRESH_BINARY)[1]
  
  es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
  dilated = cv2.dilate(th, es, iterations = 2)
  
  image, contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  for c in contours:
    drawCnt(frame, c)
  cv2.imshow("knn", fgmask)
  cv2.imshow("thresh", th)
  cv2.imshow("detection", frame)
  
  if cv2.waitKey(100) & 0xff == ord("q"):
      break

camera.release()
cv2.destroyAllWindows()

(2)GMG

import numpy as np
import cv2

'''
retval = cv.bgsegm.createBackgroundSubtractorGMG( [, initializationFrames[, decisionThreshold]]	)
Parameters
    initializationFrames	number of frames used to initialize the background models.
    decisionThreshold	Threshold value, above which it is marked foreground, else background.
'''
#GMG背景分割器
fgbg = cv2.bgsegm.createBackgroundSubtractorGMG()

camera = cv2.VideoCapture('traffic.flv')

ret, frame = camera.read()

while ret:
  fgmask = fgbg.apply(frame)
  th = cv2.threshold(np.copy(fgmask), 244, 255, cv2.THRESH_BINARY)[1]
  th = cv2.erode(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)), iterations = 2)
  dilated = cv2.dilate(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (8,3)), iterations = 2)
  image, contours, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  for c in contours:
    if cv2.contourArea(c) > 1000:
      (x,y,w,h) = cv2.boundingRect(c)
      cv2.rectangle(frame, (x,y), (x+w, y+h), (255, 255, 0), 2)

  cv2.imshow("GMG", fgmask)
  cv2.imshow("thresh", th)
  cv2.imshow("diff", frame & cv2.cvtColor(fgmask, cv2.COLOR_GRAY2BGR))
  cv2.imshow("detection", frame)
  ret, frame = camera.read()  #读取视频帧数据
  if cv2.waitKey(100) & 0xff == ord("q"):
      break

camera.release()
cv2.destroyAllWindows()

根据以上效果对比知:

MOG2算法阴影检测并不完美,但它有助于将目标轮廓按原始形状进行还原;KNN的精确性和阴影检测能力较好,即使是相邻对象也没有在一起检测,运动检测的结果相当精确。

下一篇:《OpenCV3-Python基于Kalman和CAMShift算法应用》

注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。 标签:opencv3,python,图像处理