Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/python/mosse.py
16337 views
1
#!/usr/bin/env python
2
3
'''
4
MOSSE tracking sample
5
6
This sample implements correlation-based tracking approach, described in [1].
7
8
Usage:
9
mosse.py [--pause] [<video source>]
10
11
--pause - Start with playback paused at the first video frame.
12
Useful for tracking target selection.
13
14
Draw rectangles around objects with a mouse to track them.
15
16
Keys:
17
SPACE - pause video
18
c - clear targets
19
20
[1] David S. Bolme et al. "Visual Object Tracking using Adaptive Correlation Filters"
21
http://www.cs.colostate.edu/~draper/papers/bolme_cvpr10.pdf
22
'''
23
24
# Python 2/3 compatibility
25
from __future__ import print_function
26
import sys
27
PY3 = sys.version_info[0] == 3
28
29
if PY3:
30
xrange = range
31
32
import numpy as np
33
import cv2 as cv
34
from common import draw_str, RectSelector
35
import video
36
37
def rnd_warp(a):
38
h, w = a.shape[:2]
39
T = np.zeros((2, 3))
40
coef = 0.2
41
ang = (np.random.rand()-0.5)*coef
42
c, s = np.cos(ang), np.sin(ang)
43
T[:2, :2] = [[c,-s], [s, c]]
44
T[:2, :2] += (np.random.rand(2, 2) - 0.5)*coef
45
c = (w/2, h/2)
46
T[:,2] = c - np.dot(T[:2, :2], c)
47
return cv.warpAffine(a, T, (w, h), borderMode = cv.BORDER_REFLECT)
48
49
def divSpec(A, B):
50
Ar, Ai = A[...,0], A[...,1]
51
Br, Bi = B[...,0], B[...,1]
52
C = (Ar+1j*Ai)/(Br+1j*Bi)
53
C = np.dstack([np.real(C), np.imag(C)]).copy()
54
return C
55
56
eps = 1e-5
57
58
class MOSSE:
59
def __init__(self, frame, rect):
60
x1, y1, x2, y2 = rect
61
w, h = map(cv.getOptimalDFTSize, [x2-x1, y2-y1])
62
x1, y1 = (x1+x2-w)//2, (y1+y2-h)//2
63
self.pos = x, y = x1+0.5*(w-1), y1+0.5*(h-1)
64
self.size = w, h
65
img = cv.getRectSubPix(frame, (w, h), (x, y))
66
67
self.win = cv.createHanningWindow((w, h), cv.CV_32F)
68
g = np.zeros((h, w), np.float32)
69
g[h//2, w//2] = 1
70
g = cv.GaussianBlur(g, (-1, -1), 2.0)
71
g /= g.max()
72
73
self.G = cv.dft(g, flags=cv.DFT_COMPLEX_OUTPUT)
74
self.H1 = np.zeros_like(self.G)
75
self.H2 = np.zeros_like(self.G)
76
for _i in xrange(128):
77
a = self.preprocess(rnd_warp(img))
78
A = cv.dft(a, flags=cv.DFT_COMPLEX_OUTPUT)
79
self.H1 += cv.mulSpectrums(self.G, A, 0, conjB=True)
80
self.H2 += cv.mulSpectrums( A, A, 0, conjB=True)
81
self.update_kernel()
82
self.update(frame)
83
84
def update(self, frame, rate = 0.125):
85
(x, y), (w, h) = self.pos, self.size
86
self.last_img = img = cv.getRectSubPix(frame, (w, h), (x, y))
87
img = self.preprocess(img)
88
self.last_resp, (dx, dy), self.psr = self.correlate(img)
89
self.good = self.psr > 8.0
90
if not self.good:
91
return
92
93
self.pos = x+dx, y+dy
94
self.last_img = img = cv.getRectSubPix(frame, (w, h), self.pos)
95
img = self.preprocess(img)
96
97
A = cv.dft(img, flags=cv.DFT_COMPLEX_OUTPUT)
98
H1 = cv.mulSpectrums(self.G, A, 0, conjB=True)
99
H2 = cv.mulSpectrums( A, A, 0, conjB=True)
100
self.H1 = self.H1 * (1.0-rate) + H1 * rate
101
self.H2 = self.H2 * (1.0-rate) + H2 * rate
102
self.update_kernel()
103
104
@property
105
def state_vis(self):
106
f = cv.idft(self.H, flags=cv.DFT_SCALE | cv.DFT_REAL_OUTPUT )
107
h, w = f.shape
108
f = np.roll(f, -h//2, 0)
109
f = np.roll(f, -w//2, 1)
110
kernel = np.uint8( (f-f.min()) / f.ptp()*255 )
111
resp = self.last_resp
112
resp = np.uint8(np.clip(resp/resp.max(), 0, 1)*255)
113
vis = np.hstack([self.last_img, kernel, resp])
114
return vis
115
116
def draw_state(self, vis):
117
(x, y), (w, h) = self.pos, self.size
118
x1, y1, x2, y2 = int(x-0.5*w), int(y-0.5*h), int(x+0.5*w), int(y+0.5*h)
119
cv.rectangle(vis, (x1, y1), (x2, y2), (0, 0, 255))
120
if self.good:
121
cv.circle(vis, (int(x), int(y)), 2, (0, 0, 255), -1)
122
else:
123
cv.line(vis, (x1, y1), (x2, y2), (0, 0, 255))
124
cv.line(vis, (x2, y1), (x1, y2), (0, 0, 255))
125
draw_str(vis, (x1, y2+16), 'PSR: %.2f' % self.psr)
126
127
def preprocess(self, img):
128
img = np.log(np.float32(img)+1.0)
129
img = (img-img.mean()) / (img.std()+eps)
130
return img*self.win
131
132
def correlate(self, img):
133
C = cv.mulSpectrums(cv.dft(img, flags=cv.DFT_COMPLEX_OUTPUT), self.H, 0, conjB=True)
134
resp = cv.idft(C, flags=cv.DFT_SCALE | cv.DFT_REAL_OUTPUT)
135
h, w = resp.shape
136
_, mval, _, (mx, my) = cv.minMaxLoc(resp)
137
side_resp = resp.copy()
138
cv.rectangle(side_resp, (mx-5, my-5), (mx+5, my+5), 0, -1)
139
smean, sstd = side_resp.mean(), side_resp.std()
140
psr = (mval-smean) / (sstd+eps)
141
return resp, (mx-w//2, my-h//2), psr
142
143
def update_kernel(self):
144
self.H = divSpec(self.H1, self.H2)
145
self.H[...,1] *= -1
146
147
class App:
148
def __init__(self, video_src, paused = False):
149
self.cap = video.create_capture(video_src)
150
_, self.frame = self.cap.read()
151
cv.imshow('frame', self.frame)
152
self.rect_sel = RectSelector('frame', self.onrect)
153
self.trackers = []
154
self.paused = paused
155
156
def onrect(self, rect):
157
frame_gray = cv.cvtColor(self.frame, cv.COLOR_BGR2GRAY)
158
tracker = MOSSE(frame_gray, rect)
159
self.trackers.append(tracker)
160
161
def run(self):
162
while True:
163
if not self.paused:
164
ret, self.frame = self.cap.read()
165
if not ret:
166
break
167
frame_gray = cv.cvtColor(self.frame, cv.COLOR_BGR2GRAY)
168
for tracker in self.trackers:
169
tracker.update(frame_gray)
170
171
vis = self.frame.copy()
172
for tracker in self.trackers:
173
tracker.draw_state(vis)
174
if len(self.trackers) > 0:
175
cv.imshow('tracker state', self.trackers[-1].state_vis)
176
self.rect_sel.draw(vis)
177
178
cv.imshow('frame', vis)
179
ch = cv.waitKey(10)
180
if ch == 27:
181
break
182
if ch == ord(' '):
183
self.paused = not self.paused
184
if ch == ord('c'):
185
self.trackers = []
186
187
188
if __name__ == '__main__':
189
print (__doc__)
190
import sys, getopt
191
opts, args = getopt.getopt(sys.argv[1:], '', ['pause'])
192
opts = dict(opts)
193
try:
194
video_src = args[0]
195
except:
196
video_src = '0'
197
198
App(video_src, paused = '--pause' in opts).run()
199
200