-
Notifications
You must be signed in to change notification settings - Fork 4
/
frame_stabilizer.py
74 lines (68 loc) · 2.98 KB
/
frame_stabilizer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import cv2
import numpy as np
# This class is used to stabilize the frames of the video.
# It uses ORB features to match keypoints between frames and calculate an affine transform to
# warp the frame.
class FrameStabilizer:
def __init__(self):
self.stabilizationFrame = None
self.stabilizationFrameCount = 0
self.stabilizationBurnInCompleted = False
self.stabilizationKPs = None
self.stabilizationDesc = None
self.orb = None
self.matcher = None
def reset(self):
self.stabilizationFrame = None
self.stabilizationFrameCount = 0
self.stabilizationBurnInCompleted = False
self.stabilizationKPs = None
self.stabilizationDesc = None
def stabilize_frame(self, frame_rgb):
if self.stabilizationFrame is None:
self.stabilizationFrame = frame_rgb
self.stabilizationFrameCount = 0
elif not self.stabilizationBurnInCompleted:
self.stabilizationFrameCount += 1
# add the new frame to the stabilization frame
frame_rgb = cv2.addWeighted(frame_rgb, 0.5, self.stabilizationFrame, 0.5, 0)
if self.stabilizationFrameCount == 10:
self.stabilizationBurnInCompleted = True
# extract ORB features from the stabilization frame
self.orb = cv2.ORB_create()
self.stabilizationKPs, self.stabilizationDesc = (
self.orb.detectAndCompute(self.stabilizationFrame, None)
)
self.matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
if (
self.stabilizationBurnInCompleted
and self.stabilizationFrame is not None
and self.orb is not None
and self.matcher is not None
and self.stabilizationKPs is not None
and self.stabilizationDesc is not None
):
# stabilization burn-in period is over, start stabilization
# extract features from the current frame
kps, desc = self.orb.detectAndCompute(frame_rgb, None)
# match the features
matches = self.matcher.match(self.stabilizationDesc, desc)
# sort the matches by distance
matches = sorted(matches, key=lambda x: x.distance)
# calculate an affine transform from the matched keypoints
src_pts = np.float32(
[self.stabilizationKPs[m.queryIdx].pt for m in matches]
).reshape(-1, 1, 2)
dst_pts = np.float32([kps[m.trainIdx].pt for m in matches]).reshape(
-1, 1, 2
)
h, _ = cv2.estimateAffinePartial2D(src_pts, dst_pts)
# warp the frame
if h is not None:
frame_rgb = cv2.warpAffine(
frame_rgb,
h,
(frame_rgb.shape[1], frame_rgb.shape[0]),
flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR,
)
return frame_rgb