目標跟蹤
本文主要介紹cv2中的視頻分析Camshift和Meanshift。
目標:
學習Meanshift算法和Camshift算法來尋找和追蹤視頻中的目標物體
Meanshift算法:
meanshift算法的原理很簡單。假設你有一堆點集,例如直方圖反向投影得到的點集。
你還有一個小的窗口,這個窗口可能是圓形的,現在你可能要移動這個窗口到點集密度最大的區域當中。
如下圖:
最開始的窗口是藍色圓環的區域,命名爲C1。藍色圓環的重音用一個藍色的矩形標註,命名爲C1_o。
然而,你發現在這個窗口當中所有點的點集構成的質心在藍色圓形點處。而且,圓環的型心和質心並不重合。所以,移動藍色的窗口,使得型心與之前得到的質心重合。在新移動後的圓環的區域當中再次尋找圓環當中所包圍點集的質心,然後再次移動,通常情況下,型心和質心是不重合的。不斷執行上面的移動過程,直到型心和質心大致重合結束。
這樣,最後圓形的窗口會落到像素分佈最大的地方,也就是圖中的綠色圈,命名爲C2。
meanshift算法不僅僅限制在二維的圖像處理問題當中,同樣也可以使用於高維的數據處理。可以通過選取不同的核函數,來改變區域當中偏移向量的權重,最後meanshift算法的過程一定會收斂到某一個位置。(可證明)
meanshift算法除了應用在視頻追蹤當中,在聚類,平滑等等各種涉及到數據以及非監督學習的場合當中均有重要應用,是一個應用廣泛的算法。
假如在二維環境當中,meanshift算法處理的數據是一羣離散的二維點集,但是圖像是一個矩陣信息,如何在一個視頻當中使用meanshift算法來追蹤一個運動的物體呢?
大致流程如下:
1.首先在圖像上使用矩形框或者圓形框選定一個目標區域
2.計算選定好區域的直方圖分佈。
3.對下一幀圖像b同樣計算直方圖分佈。
4.計算圖像b當中與選定區域直方圖分佈最爲相似的區域,使用meanshift算法將選定區域沿着最爲相似的部分進行移動。(樣例當中使用的是直方圖反向投影)
5.重複3到4的過程。
OpenCV中的meanshift算法:
在opencv中使用meanshift算法,首先要設定目標,找到它的直方圖,然後可以對這個直方圖在每一幀當中進行反向投影。我們需要提供一個初試的窗口位置,計算HSV模型當中H(色調)的直方圖。爲了避免低亮度造成的影響,使用 cv2.inRange()將低亮度值忽略。
import cv2
import numpy as np
# 設置初始化的窗口位置
r,h,c,w = 0,100,0,100 # 設置初試窗口位置和大小
track_window = (c,r,w,h)
cap = cv2.VideoCapture(0)
ret, frame= cap.read()
# 設置追蹤的區域
roi = frame[r:r+h, c:c+w]
# roi區域的hsv圖像
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 取值hsv值在(0,60,32)到(180,255,255)之間的部分
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
# 計算直方圖,參數爲 圖片(可多),通道數,蒙板區域,直方圖長度,範圍
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
# 歸一化
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# 設置終止條件,迭代10次或者至少移動1次
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret, frame = cap.read()
if ret == True:
# 計算每一幀的hsv圖像
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 計算反向投影
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 調用meanShift算法在dst中尋找目標窗口,找到後返回目標窗口
ret, track_window = cv2.meanShift(dst, track_window, term_crit)
# Draw it on image
x,y,w,h = track_window
img2 = cv2.rectangle(frame, (x,y), (x+w,y+h), 255,2)
cv2.imshow('img2',img2)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
CamShift算法:
在視頻或者是攝像頭當中,如果被追蹤的物體迎面過來,由於透視效果,物體會放大。之前設置好的窗口區域大小會不合適。
OpenCV實驗室實現了一個CAMshift算法,首先使用meanshift算法找到目標,然後調整窗口大小,而且還會計算目標對象的的最佳外接圓的角度,並調整窗口。並使用調整後的窗口對物體繼續追蹤。
使用方法與meanShift算法一樣,不過返回的是一個帶有旋轉角度的矩形。
Camshift,連續的自適應MeanShift算法,是對MeanShift算法的改進算法,可以在跟蹤的過程中隨着目標大小的變化實時調整搜索窗口大小,對於視頻序列中的每一幀還是採用MeanShift來尋找最優迭代結果,至於如何實現自動調整窗口大小的,可以查到的論述較少,我的理解是通過對MeanShift算法中零階矩的判斷實現的。
代碼:
1.python版本:可以自行通過鼠標設置區域進行追蹤
import cv2
import numpy as np
xs, ys, ws, hs = 0, 0, 0, 0 # selection.x selection.y
xo, yo = 0, 0 # origin.x origin.y
selectObject = False
trackObject = 0
def onMouse(event, x, y, flags, prams):
global xs, ys, ws, hs, selectObject, xo, yo, trackObject
if selectObject == True:
xs = min(x, xo)
ys = min(y, yo)
ws = abs(x - xo)
hs = abs(y - yo)
if event == cv2.EVENT_LBUTTONDOWN:
xo, yo = x, y
xs, ys, ws, hs = x, y, 0, 0
selectObject = True
elif event == cv2.EVENT_LBUTTONUP:
selectObject = False
trackObject = -1
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cv2.namedWindow('imshow')
cv2.setMouseCallback('imshow', onMouse)
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
while (True):
ret, frame = cap.read()
if trackObject != 0:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, np.array((0., 30., 10.)), np.array((180., 256., 255.)))
if trackObject == -1:
track_window = (xs, ys, ws, hs)
maskroi = mask[ys:ys + hs, xs:xs + ws]
hsv_roi = hsv[ys:ys + hs, xs:xs + ws]
roi_hist = cv2.calcHist([hsv_roi], [0], maskroi, [180], [0, 180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
trackObject = 1
dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
dst &= mask
ret, track_window = cv2.CamShift(dst, track_window, term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame, [pts], True, 255, 2)
if selectObject == True and ws > 0 and hs > 0:
cv2.imshow('imshow1', frame[ys:ys + hs, xs:xs + ws])
cv2.bitwise_not(frame[ys:ys + hs, xs:xs + ws], frame[ys:ys + hs, xs:xs + ws])
cv2.imshow('imshow', frame)
if cv2.waitKey(10) == 27:
break
cv2.destroyAllWindows()
對應的C++版本:
//---------------------------------【頭文件、命名空間包含部分】----------------------------
// 描述:包含程序所使用的頭文件和命名空間
//-------------------------------------------------------------------------------------------------
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
#include <windows.h>
using namespace cv;
using namespace std;
//-----------------------------------【全局變量聲明】-----------------------------------------
// 描述:聲明全局變量
//-------------------------------------------------------------------------------------------------
Mat image;
bool backprojMode = false;
bool selectObject = false;
int trackObject = 0;
bool showHist = true;
Point origin;
Rect selection;
int vmin = 10, vmax = 256, smin = 30;
//--------------------------------【onMouse( )回調函數】------------------------------------
// 描述:鼠標操作回調
//-------------------------------------------------------------------------------------------------
static void onMouse( int event, int x, int y, int, void* )
{
if( selectObject )
{
selection.x = MIN(x, origin.x);
selection.y = MIN(y, origin.y);
selection.width = std::abs(x - origin.x);
selection.height = std::abs(y - origin.y);
selection &= Rect(0, 0, image.cols, image.rows);
}
switch( event )
{
//此句代碼的OpenCV2版爲:
//case CV_EVENT_LBUTTONDOWN:
//此句代碼的OpenCV3版爲:
case EVENT_LBUTTONDOWN:
origin = Point(x,y);
selection = Rect(x,y,0,0);
selectObject = true;
break;
//此句代碼的OpenCV2版爲:
//case CV_EVENT_LBUTTONUP:
//此句代碼的OpenCV3版爲:
case EVENT_LBUTTONUP:
selectObject = false;
if( selection.width > 0 && selection.height > 0 )
trackObject = -1;
break;
}
}
//--------------------------------【help( )函數】----------------------------------------------
// 描述:輸出幫助信息
//-------------------------------------------------------------------------------------------------
static void ShowHelpText()
{
cout <<"\n\n\t\t\t非常感謝購買《OpenCV3編程入門》一書!\n"
<<"\n\n\t\t\t此爲本書OpenCV3版的第8個配套示例程序\n"
<< "\n\n\t\t\t 當前使用的OpenCV版本爲:" << CV_VERSION
<<"\n\n ----------------------------------------------------------------------------" ;
cout << "\n\n\t此Demo顯示了基於均值漂移的追蹤(tracking)技術\n"
"\t請用鼠標框選一個有顏色的物體,對它進行追蹤操作\n";
cout << "\n\n\t操作說明: \n"
"\t\t用鼠標框選對象來初始化跟蹤\n"
"\t\tESC - 退出程序\n"
"\t\tc - 停止追蹤\n"
"\t\tb - 開/關-投影視圖\n"
"\t\th - 顯示/隱藏-對象直方圖\n"
"\t\tp - 暫停視頻\n";
}
const char* keys =
{
"{1| | 0 | camera number}"
};
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-------------------------------------------------------------------------------------------------
int main( int argc, const char** argv )
{
ShowHelpText();
VideoCapture cap;
Rect trackWindow;
int hsize = 16;
float hranges[] = {0,180};
const float* phranges = hranges;
cap.open(0);
//cap.open("H:\\opencv\\ai.avi");
if( !cap.isOpened() )
{
cout << "不能初始化攝像頭\n";
}
namedWindow( "Histogram", 0 );//顏色直方圖窗口
namedWindow( "CamShift Demo", 0 );//跟蹤圖像窗口
setMouseCallback( "CamShift Demo", onMouse, 0 );//關聯鼠標事件
createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );//顏色空間參數設置
createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );
createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );
Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
bool paused = false;//暫停
LARGE_INTEGER _start, _stop;
double start, stop;
for(;;)
{
QueryPerformanceCounter(&_start);
start = (double)_start.QuadPart; //獲得計數器計數初值
if( !paused )
{
cap >> frame;
if( frame.empty() )
break;
}
QueryPerformanceCounter(&_stop); //獲取計數器當前值
stop = (double)_stop.QuadPart;
cout << (stop - start) * 10 / 25332 << endl;;
frame.copyTo(image);
if( !paused )//如果麼有暫停。。。要是我就不會那麼多事設置一個暫停在這
{
cvtColor(image, hsv, COLOR_BGR2HSV);//將圖像轉換爲hsv顏色空間
if( trackObject )//只有等於0的時候不跟蹤?
{
int _vmin = vmin, _vmax = vmax;//顏色空間參數
inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
Scalar(180, 256, MAX(_vmin, _vmax)), mask);
int ch[] = {0, 0};
hue.create(hsv.size(), hsv.depth());//反向直方圖
mixChannels(&hsv, 1, &hue, 1, ch, 1);
if( trackObject < 0 )//已經用鼠標選取完區域後就可以跟蹤了。。
{
Mat roi(hue, selection), maskroi(mask, selection);
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
//此句代碼的OpenCV3版爲:
normalize(hist, hist, 0, 255, NORM_MINMAX);
//此句代碼的OpenCV2版爲:
//normalize(hist, hist, 0, 255, CV_MINMAX);
trackWindow = selection;
trackObject = 1;
histimg = Scalar::all(0);
int binW = histimg.cols / hsize;
Mat buf(1, hsize, CV_8UC3);
for( int i = 0; i < hsize; i++ )
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);
//此句代碼的OpenCV3版爲:
cvtColor(buf, buf, COLOR_HSV2BGR);
//此句代碼的OpenCV2版爲:
//cvtColor(buf, buf, CV_HSV2BGR);
for( int i = 0; i < hsize; i++ )
{
int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);
rectangle( histimg, Point(i*binW,histimg.rows),
Point((i+1)*binW,histimg.rows - val),
Scalar(buf.at<Vec3b>(i)), -1, 8 );
}
}
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
cv::imshow("backproj", backproj);
backproj &= mask;
RotatedRect trackBox = CamShift(backproj, trackWindow,
//此句代碼的OpenCV3版爲:
TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 ));
//此句代碼的OpenCV2版爲:
//TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));
if( trackWindow.area() <= 1 )
{
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
trackWindow.x + r, trackWindow.y + r) &
Rect(0, 0, cols, rows);
}
if( backprojMode )
cvtColor( backproj, image, COLOR_GRAY2BGR );
//此句代碼的OpenCV3版爲:
ellipse( image, trackBox, Scalar(0,0,255), 3, LINE_AA );
//此句代碼的OpenCV2版爲:
//ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA );
}
}
else if( trackObject < 0 )//也就是說鼠標選定區域後,暫停鍵失效
paused = false;
if( selectObject && selection.width > 0 && selection.height > 0 )
{
Mat roi(image, selection);
bitwise_not(roi, roi);
}
cv::imshow( "CamShift Demo", image );
cv::imshow( "Histogram", histimg );
char c = (char)waitKey(90);
if( c == 27 )
break;
switch(c)
{
case 'b':
backprojMode = !backprojMode;
break;
case 'c':
trackObject = 0;
histimg = Scalar::all(0);
break;
case 'h':
showHist = !showHist;
if( !showHist )
destroyWindow( "Histogram" );
else
namedWindow( "Histogram", 1 );
break;
case 'p':
paused = !paused;
break;
case 'k':
{
imwrite("pic.jpg", image);
break;
}
default:
;
}
}
return 0;
}
效果:
或者直接預定義:
'''
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
# take first frame of the video
ret,frame = cap.read()
# setup initial location of window
r,h,c,w = 300,200,400,300 # simply hardcoded the values
track_window = (c,r,w,h)
roi = frame[r:r+h, c:c+w]
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((100., 30.,32.)), np.array((180.,120.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret ,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
ret, track_window = cv2.CamShift(dst, track_window, term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame,[pts],True, 255,2)
cv2.imshow('img2',img2)
k = cv2.waitKey(60) & 0xff
if k == 27:
break
else:
break
cv2.destroyAllWindows()
cap.release()
'''
import cv2
import numpy as np
# 設置初始化的窗口位置
r,h,c,w = 0,100,0,100 # 設置初試窗口位置和大小
track_window = (c,r,w,h)
cap = cv2.VideoCapture(0)
ret, frame= cap.read()
# 設置追蹤的區域
roi = frame[r:r+h, c:c+w]
# roi區域的hsv圖像
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 取值hsv值在(0,60,32)到(180,255,255)之間的部分
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
# 計算直方圖,參數爲 圖片(可多),通道數,蒙板區域,直方圖長度,範圍
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
# 歸一化
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# 設置終止條件,迭代10次或者至少移動1次
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret, frame = cap.read()
if ret == True:
# 計算每一幀的hsv圖像
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 計算反向投影
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 調用meanShift算法在dst中尋找目標窗口,找到後返回目標窗口
ret, track_window = cv2.CamShift(dst, track_window, term_crit)
# Draw it on image
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame,[pts],True, 255,2)
cv2.imshow('img2',img2)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
背景分割:
視頻的背景分割
本文用到的視頻traffic.flv,來源於原作者Github,地址爲:
https://github.com/techfort/pycv/tree/master/chapter8/surveillance_demo
OpenCV中有幾種背景分割器(Background Subtractor),這裏使用最常用的兩種:
K-Nearest (KNN)
Mixture of Gaussian (MOG2)
KNN背景分割器:
# -*- coding:utf-8 -*-
import cv2
# Step1. 構造VideoCapture對象
cap = cv2.VideoCapture('traffic.flv')
# Step2. 創建一個背景分割器
# createBackgroundSubtractorKNN()函數裏,可以指定detectShadows的值
# detectShadows=True,表示檢測陰影,反之不檢測陰影
knn = cv2.createBackgroundSubtractorKNN(detectShadows=True)
while True :
ret, frame = cap.read() # 讀取視頻
fgmask = knn.apply(frame) # 背景分割
cv2.imshow('frame', fgmask) # 顯示分割結果
if cv2.waitKey(100) & 0xff == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
運行效果如下:
MOG2背景分割器的小例子
# -*- coding:utf-8 -*-
import cv2
# Step1. 構造VideoCapture對象
cap = cv2.VideoCapture('traffic.flv')
# Step2. 創建一個背景分割器
# createBackgroundSubtractorMOG2()函數裏,可以指定detectShadows的值
# detectShadows=True,表示檢測陰影,反之不檢測陰影
mog = cv2.createBackgroundSubtractorMOG2()
while True :
ret, frame = cap.read() # 讀取視頻
fgmask = mog.apply(frame) # 背景分割
cv2.imshow('frame', fgmask) # 顯示分割結果
if cv2.waitKey(100) & 0xff == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
運動檢測跟蹤的小例子
# -*- coding:utf-8 -*-
import cv2
# Step1. 初始化VideoCapture對象
cap = cv2.VideoCapture('traffic.flv')
# Step2. 使用KNN背景分割器
knn= cv2.createBackgroundSubtractorKNN(detectShadows=True)
while True :
ret, frame = cap.read()
fgmask = knn.apply(frame) # 分割背景
# 閾值化,將非純白色(244~255)的所有像素設爲0
th = cv2.threshold(fgmask.copy(), 244, 255, cv2.THRESH_BINARY)[1]
# 爲了使效果更好,進行一次膨脹
dilated = cv2.dilate(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)), iterations=2)
# 檢測輪廓
image, contours, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 繪製輪廓
for c in contours:
if cv2.contourArea(c) > 1600:
(x,y,w,h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2)
cv2.imshow('detection', frame)
if cv2.waitKey(100) & 0xff == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Surveillance Demo: Tracking Pedestrians in Camera Feed
#! /usr/bin/python
# 目標跟蹤
"""Surveillance Demo: Tracking Pedestrians in Camera Feed
The application opens a video (could be a camera or a video file)
and tracks pedestrians in the video.
"""
__author__ = "joe minichino"
__copyright__ = "property of mankind."
__license__ = "MIT"
__version__ = "0.0.1"
__maintainer__ = "Joe Minichino"
__email__ = "[email protected]"
__status__ = "Development"
import cv2
import numpy as np
import os.path as path
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--algorithm",
help="m (or nothing) for meanShift and c for camshift")
args = vars(parser.parse_args())
def center(points):
"""calculates centroid of a given matrix"""
x = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4
y = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4
return np.array([np.float32(x), np.float32(y)], np.float32)
font = cv2.FONT_HERSHEY_SIMPLEX
class Pedestrian():
"""Pedestrian class
each pedestrian is composed of a ROI, an ID and a Kalman filter
so we create a Pedestrian class to hold the object state
"""
def __init__(self, id, frame, track_window):
"""init the pedestrian object with track window coordinates"""
# set up the roi
self.id = int(id)
x, y, w, h = track_window
self.track_window = track_window
self.roi = cv2.cvtColor(frame[y:y + h, x:x + w], cv2.COLOR_BGR2HSV)
roi_hist = cv2.calcHist([self.roi], [0], None, [16], [0, 180])
self.roi_hist = cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# set up the kalman
self.kalman = cv2.KalmanFilter(4, 2)
self.kalman.measurementMatrix = np.array([[1, 0, 0, 0], [0, 1, 0, 0]], np.float32)
self.kalman.transitionMatrix = np.array([[1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]], np.float32)
self.kalman.processNoiseCov = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
np.float32) * 0.03
self.measurement = np.array((2, 1), np.float32)
self.prediction = np.zeros((2, 1), np.float32)
self.term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
self.center = None
self.update(frame)
def __del__(self):
print("Pedestrian %d destroyed" % self.id)
def update(self, frame):
# print "updating %d " % self.id
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
back_project = cv2.calcBackProject([hsv], [0], self.roi_hist, [0, 180], 1)
if args.get("algorithm") == "c":
ret, self.track_window = cv2.CamShift(back_project, self.track_window, self.term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
self.center = center(pts)
cv2.polylines(frame, [pts], True, 255, 1)
if not args.get("algorithm") or args.get("algorithm") == "m":
ret, self.track_window = cv2.meanShift(back_project, self.track_window, self.term_crit)
x, y, w, h = self.track_window
self.center = center([[x, y], [x + w, y], [x, y + h], [x + w, y + h]])
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
self.kalman.correct(self.center)
prediction = self.kalman.predict()
cv2.circle(frame, (int(prediction[0]), int(prediction[1])), 4, (255, 0, 0), -1)
# fake shadow
cv2.putText(frame, "ID: %d -> %s" % (self.id, self.center), (11, (self.id + 1) * 25 + 1),
font, 0.6,
(0, 0, 0),
1,
cv2.LINE_AA)
# actual info
cv2.putText(frame, "ID: %d -> %s" % (self.id, self.center), (10, (self.id + 1) * 25),
font, 0.6,
(0, 255, 0),
1,
cv2.LINE_AA)
def main():
#camera = cv2.VideoCapture(path.join(path.dirname(__file__), "traffic.flv"))
camera = cv2.VideoCapture(path.join(path.dirname(__file__), "768x576.avi"))
# camera = cv2.VideoCapture(path.join(path.dirname(__file__), "..", "movie.mpg"))
# camera = cv2.VideoCapture(0)
history = 20
# KNN background subtractor
bs = cv2.createBackgroundSubtractorKNN()
# MOG subtractor
# bs = cv2.bgsegm.createBackgroundSubtractorMOG(history = history)
# bs.setHistory(history)
# GMG
# bs = cv2.bgsegm.createBackgroundSubtractorGMG(initializationFrames = history)
cv2.namedWindow("surveillance")
pedestrians = {}
firstFrame = True
frames = 0
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while True:
print(" -------------------- FRAME %d --------------------" % frames)
grabbed, frame = camera.read()
if (grabbed is False):
print("failed to grab frame.")
break
fgmask = bs.apply(frame)
# this is just to let the background subtractor build a bit of history
if frames < history:
frames += 1
continue
th = cv2.threshold(fgmask.copy(), 127, 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)
counter = 0
for c in contours:
if cv2.contourArea(c) > 500:
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 1)
# only create pedestrians in the first frame, then just follow the ones you have
if firstFrame is True:
pedestrians[counter] = Pedestrian(counter, frame, (x, y, w, h))
counter += 1
for i, p in pedestrians.items():
p.update(frame)
firstFrame = False
frames += 1
cv2.imshow("surveillance", frame)
out.write(frame)
if cv2.waitKey(110) & 0xff == 27:
break
out.release()
camera.release()
if __name__ == "__main__":
main()
參考文獻:
1.https://blog.csdn.net/github_39611196/article/details/81164962
2.https://blog.csdn.net/zhangruijerry/article/details/79088945?%3E
3.https://blog.csdn.net/u010429424/article/details/73863902
4.https://blog.csdn.net/tengfei461807914/article/details/80412482