Skip to content

Commit 0a98765

Browse files
author
Vaibhav
committed
Everything
0 parents  commit 0a98765

File tree

11 files changed

+225
-0
lines changed

11 files changed

+225
-0
lines changed

Paper.pdf

8.51 MB
Binary file not shown.

Readme.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# VideoStabilization
2+
3+
## Description
4+
5+
This is a python-openCV implementation of this [research paper](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37041.pdf) by Matthias Grundmann, Vivek Kwatra and Irfan Essa. The basic goal of this piece of code is to do some PreProcessing on a given wobbly video, and then output a relatively stable video which resembles professional videography(without bumps).
6+
7+
## Basic Algorithm and Working
8+
9+
### Transforms Extraction(achieved by preproc.py)
10+
11+
Firstly, the affine transform between all the consecutive 2 frames are computed, so that we achieve an estimate of the camera trajectory.
12+
13+
### Stabilizing(achieved by stabilize.py)
14+
15+
Now, the above camera trajectory will be wobbly and needs to be smoothened. This is achieved by converting the problem to an optimization problem where the derivative needs to be minimized and the constraint is that the values don't differ more than a threshold from the original ones.
16+
17+
### Video Output(achieved by generate.py)
18+
19+
Finally a cropped region of the video is outputted where the cropped region box follows a trajectory that smoothens the output.
20+
21+
## How To
22+
23+
* Run the webapp:
24+
```console
25+
bar@foo:~/VideoStabilization$ python3 preproc.py <video_file>
26+
bar@foo:~/VideoStabilization$ python3 stabilize.py
27+
bar@foo:~/VideoStabilization$ python3 generate.py <video_file>
28+
```
29+
30+
## Built With
31+
32+
* [Python3](https://www.python.org/download/releases/3.0/)
33+
* [OpenCV](https://docs.opencv.org/)
34+
35+
## Author
36+
37+
* Vaibhav Garg

generate.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import cv2
2+
import numpy as np
3+
import matplotlib.pyplot as plt
4+
import pickle
5+
import sys
6+
7+
# Loading the data
8+
with open('transforms.pkl', 'rb') as f:
9+
transforms = pickle.load(f)
10+
11+
with open('trajectory.pkl', 'rb') as f:
12+
trajectory = pickle.load(f)
13+
14+
with open('smoothTrajectory.pkl', 'rb') as f:
15+
smoothTrajectory = pickle.load(f)
16+
17+
difference = trajectory - smoothTrajectory
18+
19+
# Creating output video
20+
v = cv2.VideoCapture(sys.argv[1])
21+
W = int(v.get(cv2.CAP_PROP_FRAME_WIDTH))
22+
H = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT))
23+
fps = int(v.get(cv2.CAP_PROP_FPS))
24+
DispThresh = 50
25+
DispThresh += 10
26+
27+
curX = DispThresh
28+
curY = DispThresh
29+
30+
out = cv2.VideoWriter('video_out.avi', cv2.VideoWriter_fourcc(*'XVID'), fps, (W-2*DispThresh, H-2*DispThresh))
31+
count = 0
32+
m = np.zeros([2, 3])
33+
34+
while(v.isOpened()):
35+
ret, frame = v.read()
36+
if ret == True:
37+
if count > 0:
38+
curX = DispThresh+difference[0][count-1]
39+
curY = DispThresh+difference[1][count-1]
40+
41+
# Writing to output file
42+
boxPart = frame[np.int(curY):np.int(curY)+H-2*DispThresh, np.int(curX):np.int(curX)+W-2*DispThresh, :]
43+
out.write(boxPart)
44+
45+
# Displaying the difference
46+
cv2.rectangle(frame, (np.int(curX), np.int(curY)), (np.int(curX)+W-2*DispThresh, np.int(curY)+H-2*DispThresh), (0, 255, 0), 3)
47+
frame = cv2.resize(frame, (W-2*DispThresh, H-2*DispThresh))
48+
dispFrame = cv2.hconcat([frame, boxPart])
49+
if dispFrame.shape[1] > 1920:
50+
dispFrame = cv2.resize(dispFrame, (dispFrame.shape[1]//2, dispFrame.shape[0]//2));
51+
cv2.imshow('DispFrame', dispFrame)
52+
53+
count +=1
54+
if count == 1000:
55+
break
56+
57+
if cv2.waitKey(20) & 0xFF == ord('q'):
58+
break
59+
else:
60+
break
61+
62+
# When everything done, release the capture
63+
v.release()
64+
out.release()

preproc.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import cv2
2+
import numpy as np
3+
import pickle
4+
import sys
5+
6+
sift = cv2.xfeatures2d.SIFT_create()
7+
def getAffMat(I1, I2):
8+
I1 = cv2.cvtColor(I1, cv2.COLOR_BGR2GRAY)
9+
I2 = cv2.cvtColor(I2, cv2.COLOR_BGR2GRAY)
10+
11+
# Finding sift features
12+
kp1, desc1 = sift.detectAndCompute(I1, None)
13+
kp2, desc2 = sift.detectAndCompute(I2, None)
14+
15+
# Finding good matches using ratio testing
16+
bf = cv2.BFMatcher()
17+
matches = bf.knnMatch(desc1, desc2, k=2)
18+
19+
good = []
20+
for m,n in matches:
21+
if m.distance < 0.7*n.distance:
22+
good.append(m)
23+
24+
pts_src = []
25+
pts_dst = []
26+
for i in range(len(good)):
27+
pts_src.append([kp1[good[i].queryIdx].pt[0], kp1[good[i].queryIdx].pt[1]])
28+
pts_dst.append([kp2[good[i].trainIdx].pt[0], kp2[good[i].trainIdx].pt[1]])
29+
30+
pts_src = np.array(pts_src).astype(np.float32)
31+
pts_dst = np.array(pts_dst).astype(np.float32)
32+
33+
# Computing affine matrix using the best matches
34+
return cv2.estimateRigidTransform(pts_src, pts_dst, fullAffine=False)
35+
36+
v = cv2.VideoCapture(sys.argv[1])
37+
38+
# Generating the Xdata and Ydata
39+
transforms = [[], []]
40+
count = 0
41+
while v.isOpened():
42+
ret, frame = v.read()
43+
if ret == True:
44+
if count > 0:
45+
transMat = getAffMat(prev, frame)
46+
transforms[0].append(transMat[0][2])
47+
transforms[1].append(transMat[1][2])
48+
49+
count += 1
50+
prev = frame
51+
print(count)
52+
if count == 1000:
53+
break
54+
else:
55+
break
56+
57+
v.release()
58+
59+
# Storing the data
60+
with open('transforms.pkl', 'wb') as f:
61+
pickle.dump(transforms, f)

shakey.mp4

18.6 MB
Binary file not shown.

shakey2.mp4

2.34 MB
Binary file not shown.

smoothTrajectory.pkl

7.11 KB
Binary file not shown.

stabilize.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import cv2
2+
import numpy as np
3+
import matplotlib.pyplot as plt
4+
import pickle
5+
import cvxpy as cp
6+
7+
# Loading the data
8+
with open('transforms.pkl', 'rb') as f:
9+
transforms = pickle.load(f)
10+
11+
# Computing the trajectory
12+
trajectory = np.cumsum(transforms, axis=1)
13+
with open('trajectory.pkl', 'wb') as f:
14+
pickle.dump(trajectory, f)
15+
16+
17+
# Smoothening the trajectories
18+
# fx is the optimal x trajectory
19+
# fy is the optimal y trajectory
20+
fx = cp.Variable(len(trajectory[0]))
21+
fy = cp.Variable(len(trajectory[1]))
22+
23+
lbd1 = 1000
24+
lbd2 = 100
25+
lbd3 = 10000
26+
DispThresh = 50
27+
constraints = [cp.abs(fx - trajectory[0]) <= DispThresh,
28+
cp.abs(fy - trajectory[1]) <= DispThresh]
29+
30+
# Defining the minimization objective function
31+
obj = 0
32+
for i in range(len(trajectory[0])):
33+
obj += ( (trajectory[0][i]-fx[i])**2 + (trajectory[1][i]-fy[i])**2 )
34+
35+
# DP1
36+
for i in range(len(trajectory[0])-1):
37+
obj += lbd1*(cp.abs(fx[i+1]-fx[i]) + cp.abs(fy[i+1]-fy[i]))
38+
39+
# DP2
40+
for i in range(len(trajectory[0])-2):
41+
obj += lbd2*(cp.abs(fx[i+2]-2*fx[i+1]+fx[i]) + cp.abs(fy[i+2]-2*fy[i+1]+fy[i]))
42+
43+
# DP3
44+
for i in range(len(trajectory[0])-3):
45+
obj += lbd3*(cp.abs(fx[i+3]-3*fx[i+2] + 3*fx[i+1]-fx[i]) + cp.abs(fy[i+3]-3*fy[i+2]+3*fy[i+1]-fy[i]))
46+
47+
prob = cp.Problem(cp.Minimize(obj), constraints)
48+
prob.solve()
49+
50+
# Results
51+
plt.subplot(1, 2, 1)
52+
plt.plot(trajectory[0])
53+
plt.plot(fx.value)
54+
plt.subplot(1, 2, 2)
55+
plt.plot(trajectory[1])
56+
plt.plot(fy.value)
57+
plt.show()
58+
59+
smoothTrajectory = np.array([fx.value, fy.value])
60+
61+
# Storing smooth trajectory
62+
with open('smoothTrajectory.pkl', 'wb') as f:
63+
pickle.dump(smoothTrajectory, f)

trajectory.pkl

7.11 KB
Binary file not shown.

transforms.pkl

26.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)