mirror of
https://github.com/opencv/opencv.git
synced 2025-06-12 20:42:53 +08:00
Merge pull request #14314 from mehlukas:3.4-opticalflow
Extend optical flow tutorial (#14314) * extend python optical flow tutorial with cpp example code and add it to general tutorial directory * remove unused parameters, fix comparison between signed and unsigned int * fix hsv range problem * switch to samples::findFile for sample file location * switch to command line parameter for path * remove old tutorial as in 14393 * minor fixes
This commit is contained in:
parent
47c45e5bd3
commit
6bff0e24f8
@ -1,225 +1,4 @@
|
|||||||
Optical Flow {#tutorial_py_lucas_kanade}
|
Optical Flow {#tutorial_py_lucas_kanade}
|
||||||
============
|
============
|
||||||
|
|
||||||
Goal
|
Tutorial content has been moved: @ref tutorial_optical_flow
|
||||||
----
|
|
||||||
|
|
||||||
In this chapter,
|
|
||||||
- We will understand the concepts of optical flow and its estimation using Lucas-Kanade
|
|
||||||
method.
|
|
||||||
- We will use functions like **cv.calcOpticalFlowPyrLK()** to track feature points in a
|
|
||||||
video.
|
|
||||||
|
|
||||||
Optical Flow
|
|
||||||
------------
|
|
||||||
|
|
||||||
Optical flow is the pattern of apparent motion of image objects between two consecutive frames
|
|
||||||
caused by the movemement of object or camera. It is 2D vector field where each vector is a
|
|
||||||
displacement vector showing the movement of points from first frame to second. Consider the image
|
|
||||||
below (Image Courtesy: [Wikipedia article on Optical
|
|
||||||
Flow](http://en.wikipedia.org/wiki/Optical_flow)).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
It shows a ball moving in 5 consecutive frames. The arrow shows its displacement vector. Optical
|
|
||||||
flow has many applications in areas like :
|
|
||||||
|
|
||||||
- Structure from Motion
|
|
||||||
- Video Compression
|
|
||||||
- Video Stabilization ...
|
|
||||||
|
|
||||||
Optical flow works on several assumptions:
|
|
||||||
|
|
||||||
-# The pixel intensities of an object do not change between consecutive frames.
|
|
||||||
2. Neighbouring pixels have similar motion.
|
|
||||||
|
|
||||||
Consider a pixel \f$I(x,y,t)\f$ in first frame (Check a new dimension, time, is added here. Earlier we
|
|
||||||
were working with images only, so no need of time). It moves by distance \f$(dx,dy)\f$ in next frame
|
|
||||||
taken after \f$dt\f$ time. So since those pixels are the same and intensity does not change, we can say,
|
|
||||||
|
|
||||||
\f[I(x,y,t) = I(x+dx, y+dy, t+dt)\f]
|
|
||||||
|
|
||||||
Then take taylor series approximation of right-hand side, remove common terms and divide by \f$dt\f$ to
|
|
||||||
get the following equation:
|
|
||||||
|
|
||||||
\f[f_x u + f_y v + f_t = 0 \;\f]
|
|
||||||
|
|
||||||
where:
|
|
||||||
|
|
||||||
\f[f_x = \frac{\partial f}{\partial x} \; ; \; f_y = \frac{\partial f}{\partial y}\f]\f[u = \frac{dx}{dt} \; ; \; v = \frac{dy}{dt}\f]
|
|
||||||
|
|
||||||
Above equation is called Optical Flow equation. In it, we can find \f$f_x\f$ and \f$f_y\f$, they are image
|
|
||||||
gradients. Similarly \f$f_t\f$ is the gradient along time. But \f$(u,v)\f$ is unknown. We cannot solve this
|
|
||||||
one equation with two unknown variables. So several methods are provided to solve this problem and
|
|
||||||
one of them is Lucas-Kanade.
|
|
||||||
|
|
||||||
### Lucas-Kanade method
|
|
||||||
|
|
||||||
We have seen an assumption before, that all the neighbouring pixels will have similar motion.
|
|
||||||
Lucas-Kanade method takes a 3x3 patch around the point. So all the 9 points have the same motion. We
|
|
||||||
can find \f$(f_x, f_y, f_t)\f$ for these 9 points. So now our problem becomes solving 9 equations with
|
|
||||||
two unknown variables which is over-determined. A better solution is obtained with least square fit
|
|
||||||
method. Below is the final solution which is two equation-two unknown problem and solve to get the
|
|
||||||
solution.
|
|
||||||
|
|
||||||
\f[\begin{bmatrix} u \\ v \end{bmatrix} =
|
|
||||||
\begin{bmatrix}
|
|
||||||
\sum_{i}{f_{x_i}}^2 & \sum_{i}{f_{x_i} f_{y_i} } \\
|
|
||||||
\sum_{i}{f_{x_i} f_{y_i}} & \sum_{i}{f_{y_i}}^2
|
|
||||||
\end{bmatrix}^{-1}
|
|
||||||
\begin{bmatrix}
|
|
||||||
- \sum_{i}{f_{x_i} f_{t_i}} \\
|
|
||||||
- \sum_{i}{f_{y_i} f_{t_i}}
|
|
||||||
\end{bmatrix}\f]
|
|
||||||
|
|
||||||
( Check similarity of inverse matrix with Harris corner detector. It denotes that corners are better
|
|
||||||
points to be tracked.)
|
|
||||||
|
|
||||||
So from the user point of view, the idea is simple, we give some points to track, we receive the optical
|
|
||||||
flow vectors of those points. But again there are some problems. Until now, we were dealing with
|
|
||||||
small motions, so it fails when there is a large motion. To deal with this we use pyramids. When we go up in
|
|
||||||
the pyramid, small motions are removed and large motions become small motions. So by applying
|
|
||||||
Lucas-Kanade there, we get optical flow along with the scale.
|
|
||||||
|
|
||||||
Lucas-Kanade Optical Flow in OpenCV
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
OpenCV provides all these in a single function, **cv.calcOpticalFlowPyrLK()**. Here, we create a
|
|
||||||
simple application which tracks some points in a video. To decide the points, we use
|
|
||||||
**cv.goodFeaturesToTrack()**. We take the first frame, detect some Shi-Tomasi corner points in it,
|
|
||||||
then we iteratively track those points using Lucas-Kanade optical flow. For the function
|
|
||||||
**cv.calcOpticalFlowPyrLK()** we pass the previous frame, previous points and next frame. It
|
|
||||||
returns next points along with some status numbers which has a value of 1 if next point is found,
|
|
||||||
else zero. We iteratively pass these next points as previous points in next step. See the code
|
|
||||||
below:
|
|
||||||
@code{.py}
|
|
||||||
import numpy as np
|
|
||||||
import cv2 as cv
|
|
||||||
|
|
||||||
cap = cv.VideoCapture('slow.flv')
|
|
||||||
|
|
||||||
# params for ShiTomasi corner detection
|
|
||||||
feature_params = dict( maxCorners = 100,
|
|
||||||
qualityLevel = 0.3,
|
|
||||||
minDistance = 7,
|
|
||||||
blockSize = 7 )
|
|
||||||
|
|
||||||
# Parameters for lucas kanade optical flow
|
|
||||||
lk_params = dict( winSize = (15,15),
|
|
||||||
maxLevel = 2,
|
|
||||||
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
|
|
||||||
|
|
||||||
# Create some random colors
|
|
||||||
color = np.random.randint(0,255,(100,3))
|
|
||||||
|
|
||||||
# Take first frame and find corners in it
|
|
||||||
ret, old_frame = cap.read()
|
|
||||||
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
|
|
||||||
p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
|
|
||||||
|
|
||||||
# Create a mask image for drawing purposes
|
|
||||||
mask = np.zeros_like(old_frame)
|
|
||||||
|
|
||||||
while(1):
|
|
||||||
ret,frame = cap.read()
|
|
||||||
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
|
||||||
|
|
||||||
# calculate optical flow
|
|
||||||
p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
|
|
||||||
|
|
||||||
# Select good points
|
|
||||||
good_new = p1[st==1]
|
|
||||||
good_old = p0[st==1]
|
|
||||||
|
|
||||||
# draw the tracks
|
|
||||||
for i,(new,old) in enumerate(zip(good_new,good_old)):
|
|
||||||
a,b = new.ravel()
|
|
||||||
c,d = old.ravel()
|
|
||||||
mask = cv.line(mask, (a,b),(c,d), color[i].tolist(), 2)
|
|
||||||
frame = cv.circle(frame,(a,b),5,color[i].tolist(),-1)
|
|
||||||
img = cv.add(frame,mask)
|
|
||||||
|
|
||||||
cv.imshow('frame',img)
|
|
||||||
k = cv.waitKey(30) & 0xff
|
|
||||||
if k == 27:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Now update the previous frame and previous points
|
|
||||||
old_gray = frame_gray.copy()
|
|
||||||
p0 = good_new.reshape(-1,1,2)
|
|
||||||
|
|
||||||
cv.destroyAllWindows()
|
|
||||||
cap.release()
|
|
||||||
@endcode
|
|
||||||
(This code doesn't check how correct are the next keypoints. So even if any feature point disappears
|
|
||||||
in image, there is a chance that optical flow finds the next point which may look close to it. So
|
|
||||||
actually for a robust tracking, corner points should be detected in particular intervals. OpenCV
|
|
||||||
samples comes up with such a sample which finds the feature points at every 5 frames. It also run a
|
|
||||||
backward-check of the optical flow points got to select only good ones. Check
|
|
||||||
samples/python/lk_track.py).
|
|
||||||
|
|
||||||
See the results we got:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Dense Optical Flow in OpenCV
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Lucas-Kanade method computes optical flow for a sparse feature set (in our example, corners detected
|
|
||||||
using Shi-Tomasi algorithm). OpenCV provides another algorithm to find the dense optical flow. It
|
|
||||||
computes the optical flow for all the points in the frame. It is based on Gunner Farneback's
|
|
||||||
algorithm which is explained in "Two-Frame Motion Estimation Based on Polynomial Expansion" by
|
|
||||||
Gunner Farneback in 2003.
|
|
||||||
|
|
||||||
Below sample shows how to find the dense optical flow using above algorithm. We get a 2-channel
|
|
||||||
array with optical flow vectors, \f$(u,v)\f$. We find their magnitude and direction. We color code the
|
|
||||||
result for better visualization. Direction corresponds to Hue value of the image. Magnitude
|
|
||||||
corresponds to Value plane. See the code below:
|
|
||||||
@code{.py}
|
|
||||||
import cv2 as cv
|
|
||||||
import numpy as np
|
|
||||||
cap = cv.VideoCapture("vtest.avi")
|
|
||||||
|
|
||||||
ret, frame1 = cap.read()
|
|
||||||
prvs = cv.cvtColor(frame1,cv.COLOR_BGR2GRAY)
|
|
||||||
hsv = np.zeros_like(frame1)
|
|
||||||
hsv[...,1] = 255
|
|
||||||
|
|
||||||
while(1):
|
|
||||||
ret, frame2 = cap.read()
|
|
||||||
next = cv.cvtColor(frame2,cv.COLOR_BGR2GRAY)
|
|
||||||
|
|
||||||
flow = cv.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
|
|
||||||
|
|
||||||
mag, ang = cv.cartToPolar(flow[...,0], flow[...,1])
|
|
||||||
hsv[...,0] = ang*180/np.pi/2
|
|
||||||
hsv[...,2] = cv.normalize(mag,None,0,255,cv.NORM_MINMAX)
|
|
||||||
bgr = cv.cvtColor(hsv,cv.COLOR_HSV2BGR)
|
|
||||||
|
|
||||||
cv.imshow('frame2',bgr)
|
|
||||||
k = cv.waitKey(30) & 0xff
|
|
||||||
if k == 27:
|
|
||||||
break
|
|
||||||
elif k == ord('s'):
|
|
||||||
cv.imwrite('opticalfb.png',frame2)
|
|
||||||
cv.imwrite('opticalhsv.png',bgr)
|
|
||||||
prvs = next
|
|
||||||
|
|
||||||
cap.release()
|
|
||||||
cv.destroyAllWindows()
|
|
||||||
@endcode
|
|
||||||
See the result below:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
OpenCV comes with a more advanced sample on dense optical flow, please see
|
|
||||||
samples/python/opt_flow.py.
|
|
||||||
|
|
||||||
Additional Resources
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Exercises
|
|
||||||
---------
|
|
||||||
|
|
||||||
-# Check the code in samples/python/lk_track.py. Try to understand the code.
|
|
||||||
2. Check the code in samples/python/opt_flow.py. Try to understand the code.
|
|
||||||
|
@ -7,7 +7,7 @@ Video Analysis {#tutorial_py_table_of_contents_video}
|
|||||||
an example of color-based tracking. It is simpler. This time, we see significantly better
|
an example of color-based tracking. It is simpler. This time, we see significantly better
|
||||||
algorithms like "Meanshift", and its upgraded version, "Camshift" to find and track them.
|
algorithms like "Meanshift", and its upgraded version, "Camshift" to find and track them.
|
||||||
|
|
||||||
- @subpage tutorial_py_lucas_kanade
|
- @ref tutorial_optical_flow
|
||||||
|
|
||||||
Now let's discuss an important concept, "Optical Flow", which is related to videos and has many applications.
|
Now let's discuss an important concept, "Optical Flow", which is related to videos and has many applications.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
156
doc/tutorials/video/optical_flow/optical_flow.markdown
Normal file
156
doc/tutorials/video/optical_flow/optical_flow.markdown
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
Optical Flow {#tutorial_optical_flow}
|
||||||
|
============
|
||||||
|
|
||||||
|
Goal
|
||||||
|
----
|
||||||
|
|
||||||
|
In this chapter,
|
||||||
|
- We will understand the concepts of optical flow and its estimation using Lucas-Kanade
|
||||||
|
method.
|
||||||
|
- We will use functions like **cv.calcOpticalFlowPyrLK()** to track feature points in a
|
||||||
|
video.
|
||||||
|
- We will create a dense optical flow field using the **cv.calcOpticalFlowFarneback()** method.
|
||||||
|
|
||||||
|
Optical Flow
|
||||||
|
------------
|
||||||
|
|
||||||
|
Optical flow is the pattern of apparent motion of image objects between two consecutive frames
|
||||||
|
caused by the movemement of object or camera. It is 2D vector field where each vector is a
|
||||||
|
displacement vector showing the movement of points from first frame to second. Consider the image
|
||||||
|
below (Image Courtesy: [Wikipedia article on Optical Flow](http://en.wikipedia.org/wiki/Optical_flow)).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It shows a ball moving in 5 consecutive frames. The arrow shows its displacement vector. Optical
|
||||||
|
flow has many applications in areas like :
|
||||||
|
|
||||||
|
- Structure from Motion
|
||||||
|
- Video Compression
|
||||||
|
- Video Stabilization ...
|
||||||
|
|
||||||
|
Optical flow works on several assumptions:
|
||||||
|
|
||||||
|
-# The pixel intensities of an object do not change between consecutive frames.
|
||||||
|
2. Neighbouring pixels have similar motion.
|
||||||
|
|
||||||
|
Consider a pixel \f$I(x,y,t)\f$ in first frame (Check a new dimension, time, is added here. Earlier we
|
||||||
|
were working with images only, so no need of time). It moves by distance \f$(dx,dy)\f$ in next frame
|
||||||
|
taken after \f$dt\f$ time. So since those pixels are the same and intensity does not change, we can say,
|
||||||
|
|
||||||
|
\f[I(x,y,t) = I(x+dx, y+dy, t+dt)\f]
|
||||||
|
|
||||||
|
Then take taylor series approximation of right-hand side, remove common terms and divide by \f$dt\f$ to
|
||||||
|
get the following equation:
|
||||||
|
|
||||||
|
\f[f_x u + f_y v + f_t = 0 \;\f]
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
\f[f_x = \frac{\partial f}{\partial x} \; ; \; f_y = \frac{\partial f}{\partial y}\f]\f[u = \frac{dx}{dt} \; ; \; v = \frac{dy}{dt}\f]
|
||||||
|
|
||||||
|
Above equation is called Optical Flow equation. In it, we can find \f$f_x\f$ and \f$f_y\f$, they are image
|
||||||
|
gradients. Similarly \f$f_t\f$ is the gradient along time. But \f$(u,v)\f$ is unknown. We cannot solve this
|
||||||
|
one equation with two unknown variables. So several methods are provided to solve this problem and
|
||||||
|
one of them is Lucas-Kanade.
|
||||||
|
|
||||||
|
### Lucas-Kanade method
|
||||||
|
|
||||||
|
We have seen an assumption before, that all the neighbouring pixels will have similar motion.
|
||||||
|
Lucas-Kanade method takes a 3x3 patch around the point. So all the 9 points have the same motion. We
|
||||||
|
can find \f$(f_x, f_y, f_t)\f$ for these 9 points. So now our problem becomes solving 9 equations with
|
||||||
|
two unknown variables which is over-determined. A better solution is obtained with least square fit
|
||||||
|
method. Below is the final solution which is two equation-two unknown problem and solve to get the
|
||||||
|
solution.
|
||||||
|
|
||||||
|
\f[\begin{bmatrix} u \\ v \end{bmatrix} =
|
||||||
|
\begin{bmatrix}
|
||||||
|
\sum_{i}{f_{x_i}}^2 & \sum_{i}{f_{x_i} f_{y_i} } \\
|
||||||
|
\sum_{i}{f_{x_i} f_{y_i}} & \sum_{i}{f_{y_i}}^2
|
||||||
|
\end{bmatrix}^{-1}
|
||||||
|
\begin{bmatrix}
|
||||||
|
- \sum_{i}{f_{x_i} f_{t_i}} \\
|
||||||
|
- \sum_{i}{f_{y_i} f_{t_i}}
|
||||||
|
\end{bmatrix}\f]
|
||||||
|
|
||||||
|
( Check similarity of inverse matrix with Harris corner detector. It denotes that corners are better
|
||||||
|
points to be tracked.)
|
||||||
|
|
||||||
|
So from the user point of view, the idea is simple, we give some points to track, we receive the optical
|
||||||
|
flow vectors of those points. But again there are some problems. Until now, we were dealing with
|
||||||
|
small motions, so it fails when there is a large motion. To deal with this we use pyramids. When we go up in
|
||||||
|
the pyramid, small motions are removed and large motions become small motions. So by applying
|
||||||
|
Lucas-Kanade there, we get optical flow along with the scale.
|
||||||
|
|
||||||
|
Lucas-Kanade Optical Flow in OpenCV
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
OpenCV provides all these in a single function, **cv.calcOpticalFlowPyrLK()**. Here, we create a
|
||||||
|
simple application which tracks some points in a video. To decide the points, we use
|
||||||
|
**cv.goodFeaturesToTrack()**. We take the first frame, detect some Shi-Tomasi corner points in it,
|
||||||
|
then we iteratively track those points using Lucas-Kanade optical flow. For the function
|
||||||
|
**cv.calcOpticalFlowPyrLK()** we pass the previous frame, previous points and next frame. It
|
||||||
|
returns next points along with some status numbers which has a value of 1 if next point is found,
|
||||||
|
else zero. We iteratively pass these next points as previous points in next step. See the code
|
||||||
|
below:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/video/optical_flow/optical_flow.cpp)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/cpp/tutorial_code/video/optical_flow/optical_flow.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/video/optical_flow/optical_flow.py)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/python/tutorial_code/video/optical_flow/optical_flow.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
(This code doesn't check how correct are the next keypoints. So even if any feature point disappears
|
||||||
|
in image, there is a chance that optical flow finds the next point which may look close to it. So
|
||||||
|
actually for a robust tracking, corner points should be detected in particular intervals. OpenCV
|
||||||
|
samples comes up with such a sample which finds the feature points at every 5 frames. It also run a
|
||||||
|
backward-check of the optical flow points got to select only good ones. Check
|
||||||
|
samples/python/lk_track.py).
|
||||||
|
|
||||||
|
See the results we got:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Dense Optical Flow in OpenCV
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Lucas-Kanade method computes optical flow for a sparse feature set (in our example, corners detected
|
||||||
|
using Shi-Tomasi algorithm). OpenCV provides another algorithm to find the dense optical flow. It
|
||||||
|
computes the optical flow for all the points in the frame. It is based on Gunner Farneback's
|
||||||
|
algorithm which is explained in "Two-Frame Motion Estimation Based on Polynomial Expansion" by
|
||||||
|
Gunner Farneback in 2003.
|
||||||
|
|
||||||
|
Below sample shows how to find the dense optical flow using above algorithm. We get a 2-channel
|
||||||
|
array with optical flow vectors, \f$(u,v)\f$. We find their magnitude and direction. We color code the
|
||||||
|
result for better visualization. Direction corresponds to Hue value of the image. Magnitude
|
||||||
|
corresponds to Value plane. See the code below:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/video/optical_flow/optical_flow_dense.cpp)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/cpp/tutorial_code/video/optical_flow/optical_flow_dense.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/video/optical_flow/optical_flow_dense.py)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/python/tutorial_code/video/optical_flow/optical_flow_dense.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
|
||||||
|
See the result below:
|
||||||
|
|
||||||
|

|
@ -20,3 +20,9 @@ tracking and foreground extractions.
|
|||||||
*Languages:* C++, Python
|
*Languages:* C++, Python
|
||||||
|
|
||||||
Learn how to use the Meanshift and Camshift algorithms to track objects in videos.
|
Learn how to use the Meanshift and Camshift algorithms to track objects in videos.
|
||||||
|
|
||||||
|
- @subpage tutorial_optical_flow
|
||||||
|
|
||||||
|
*Languages:* C++, Python
|
||||||
|
|
||||||
|
We will learn how to use optical flow methods to track sparse features or to create a dense representation.
|
||||||
|
101
samples/cpp/tutorial_code/video/optical_flow/optical_flow.cpp
Normal file
101
samples/cpp/tutorial_code/video/optical_flow/optical_flow.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
#include <opencv2/highgui.hpp>
|
||||||
|
#include <opencv2/imgproc.hpp>
|
||||||
|
#include <opencv2/videoio.hpp>
|
||||||
|
#include <opencv2/video.hpp>
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const string about =
|
||||||
|
"This sample demonstrates Lucas-Kanade Optical Flow calculation.\n"
|
||||||
|
"The example file can be downloaded from:\n"
|
||||||
|
" https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4";
|
||||||
|
const string keys =
|
||||||
|
"{ h help | | print this help message }"
|
||||||
|
"{ @image |<none>| path to image file }";
|
||||||
|
CommandLineParser parser(argc, argv, keys);
|
||||||
|
parser.about(about);
|
||||||
|
if (parser.has("help"))
|
||||||
|
{
|
||||||
|
parser.printMessage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
string filename = parser.get<string>("@image");
|
||||||
|
if (!parser.check())
|
||||||
|
{
|
||||||
|
parser.printErrors();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCapture capture(filename);
|
||||||
|
if (!capture.isOpened()){
|
||||||
|
//error in opening the video input
|
||||||
|
cerr << "Unable to open file!" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create some random colors
|
||||||
|
vector<Scalar> colors;
|
||||||
|
RNG rng;
|
||||||
|
for(int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
int r = rng.uniform(0, 256);
|
||||||
|
int g = rng.uniform(0, 256);
|
||||||
|
int b = rng.uniform(0, 256);
|
||||||
|
colors.push_back(Scalar(r,g,b));
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat old_frame, old_gray;
|
||||||
|
vector<Point2f> p0, p1;
|
||||||
|
|
||||||
|
// Take first frame and find corners in it
|
||||||
|
capture >> old_frame;
|
||||||
|
cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
|
||||||
|
goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);
|
||||||
|
|
||||||
|
// Create a mask image for drawing purposes
|
||||||
|
Mat mask = Mat::zeros(old_frame.size(), old_frame.type());
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
Mat frame, frame_gray;
|
||||||
|
|
||||||
|
capture >> frame;
|
||||||
|
if (frame.empty())
|
||||||
|
break;
|
||||||
|
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
|
||||||
|
|
||||||
|
// calculate optical flow
|
||||||
|
vector<uchar> status;
|
||||||
|
vector<float> err;
|
||||||
|
TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
|
||||||
|
calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);
|
||||||
|
|
||||||
|
vector<Point2f> good_new;
|
||||||
|
for(uint i = 0; i < p0.size(); i++)
|
||||||
|
{
|
||||||
|
// Select good points
|
||||||
|
if(status[i] == 1) {
|
||||||
|
good_new.push_back(p1[i]);
|
||||||
|
// draw the tracks
|
||||||
|
line(mask,p1[i], p0[i], colors[i], 2);
|
||||||
|
circle(frame, p1[i], 5, colors[i], -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mat img;
|
||||||
|
add(frame, mask, img);
|
||||||
|
|
||||||
|
imshow("Frame", img);
|
||||||
|
|
||||||
|
int keyboard = waitKey(30);
|
||||||
|
if (keyboard == 'q' || keyboard == 27)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Now update the previous frame and previous points
|
||||||
|
old_gray = frame_gray.clone();
|
||||||
|
p0 = good_new;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
#include <opencv2/highgui.hpp>
|
||||||
|
#include <opencv2/imgproc.hpp>
|
||||||
|
#include <opencv2/videoio.hpp>
|
||||||
|
#include <opencv2/video.hpp>
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
VideoCapture capture(samples::findFile("vtest.avi"));
|
||||||
|
if (!capture.isOpened()){
|
||||||
|
//error in opening the video input
|
||||||
|
cerr << "Unable to open file!" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat frame1, prvs;
|
||||||
|
capture >> frame1;
|
||||||
|
cvtColor(frame1, prvs, COLOR_BGR2GRAY);
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
Mat frame2, next;
|
||||||
|
capture >> frame2;
|
||||||
|
if (frame2.empty())
|
||||||
|
break;
|
||||||
|
cvtColor(frame2, next, COLOR_BGR2GRAY);
|
||||||
|
|
||||||
|
Mat flow(prvs.size(), CV_32FC2);
|
||||||
|
calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
|
||||||
|
|
||||||
|
// visualization
|
||||||
|
Mat flow_parts[2];
|
||||||
|
split(flow, flow_parts);
|
||||||
|
Mat magnitude, angle, magn_norm;
|
||||||
|
cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true);
|
||||||
|
normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX);
|
||||||
|
angle *= ((1.f / 360.f) * (180.f / 255.f));
|
||||||
|
|
||||||
|
//build hsv image
|
||||||
|
Mat _hsv[3], hsv, hsv8, bgr;
|
||||||
|
_hsv[0] = angle;
|
||||||
|
_hsv[1] = Mat::ones(angle.size(), CV_32F);
|
||||||
|
_hsv[2] = magn_norm;
|
||||||
|
merge(_hsv, 3, hsv);
|
||||||
|
hsv.convertTo(hsv8, CV_8U, 255.0);
|
||||||
|
cvtColor(hsv8, bgr, COLOR_HSV2BGR);
|
||||||
|
|
||||||
|
imshow("frame2", bgr);
|
||||||
|
|
||||||
|
int keyboard = waitKey(30);
|
||||||
|
if (keyboard == 'q' || keyboard == 27)
|
||||||
|
break;
|
||||||
|
|
||||||
|
prvs = next;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
import numpy as np
|
||||||
|
import cv2 as cv
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='This sample demonstrates Lucas-Kanade Optical Flow calculation. \
|
||||||
|
The example file can be downloaded from: \
|
||||||
|
https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4')
|
||||||
|
parser.add_argument('image', type=str, help='path to image file')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
cap = cv.VideoCapture(args.image)
|
||||||
|
|
||||||
|
# params for ShiTomasi corner detection
|
||||||
|
feature_params = dict( maxCorners = 100,
|
||||||
|
qualityLevel = 0.3,
|
||||||
|
minDistance = 7,
|
||||||
|
blockSize = 7 )
|
||||||
|
|
||||||
|
# Parameters for lucas kanade optical flow
|
||||||
|
lk_params = dict( winSize = (15,15),
|
||||||
|
maxLevel = 2,
|
||||||
|
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
|
||||||
|
|
||||||
|
# Create some random colors
|
||||||
|
color = np.random.randint(0,255,(100,3))
|
||||||
|
|
||||||
|
# Take first frame and find corners in it
|
||||||
|
ret, old_frame = cap.read()
|
||||||
|
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
|
||||||
|
p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
|
||||||
|
|
||||||
|
# Create a mask image for drawing purposes
|
||||||
|
mask = np.zeros_like(old_frame)
|
||||||
|
|
||||||
|
while(1):
|
||||||
|
ret,frame = cap.read()
|
||||||
|
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
||||||
|
|
||||||
|
# calculate optical flow
|
||||||
|
p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
|
||||||
|
|
||||||
|
# Select good points
|
||||||
|
good_new = p1[st==1]
|
||||||
|
good_old = p0[st==1]
|
||||||
|
|
||||||
|
# draw the tracks
|
||||||
|
for i,(new,old) in enumerate(zip(good_new, good_old)):
|
||||||
|
a,b = new.ravel()
|
||||||
|
c,d = old.ravel()
|
||||||
|
mask = cv.line(mask, (a,b),(c,d), color[i].tolist(), 2)
|
||||||
|
frame = cv.circle(frame,(a,b),5,color[i].tolist(),-1)
|
||||||
|
img = cv.add(frame,mask)
|
||||||
|
|
||||||
|
cv.imshow('frame',img)
|
||||||
|
k = cv.waitKey(30) & 0xff
|
||||||
|
if k == 27:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Now update the previous frame and previous points
|
||||||
|
old_gray = frame_gray.copy()
|
||||||
|
p0 = good_new.reshape(-1,1,2)
|
@ -0,0 +1,23 @@
|
|||||||
|
import numpy as np
|
||||||
|
import cv2 as cv
|
||||||
|
cap = cv.VideoCapture(cv.samples.findFile("vtest.avi"))
|
||||||
|
ret, frame1 = cap.read()
|
||||||
|
prvs = cv.cvtColor(frame1,cv.COLOR_BGR2GRAY)
|
||||||
|
hsv = np.zeros_like(frame1)
|
||||||
|
hsv[...,1] = 255
|
||||||
|
while(1):
|
||||||
|
ret, frame2 = cap.read()
|
||||||
|
next = cv.cvtColor(frame2,cv.COLOR_BGR2GRAY)
|
||||||
|
flow = cv.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
|
||||||
|
mag, ang = cv.cartToPolar(flow[...,0], flow[...,1])
|
||||||
|
hsv[...,0] = ang*180/np.pi/2
|
||||||
|
hsv[...,2] = cv.normalize(mag,None,0,255,cv.NORM_MINMAX)
|
||||||
|
bgr = cv.cvtColor(hsv,cv.COLOR_HSV2BGR)
|
||||||
|
cv.imshow('frame2',bgr)
|
||||||
|
k = cv.waitKey(30) & 0xff
|
||||||
|
if k == 27:
|
||||||
|
break
|
||||||
|
elif k == ord('s'):
|
||||||
|
cv.imwrite('opticalfb.png',frame2)
|
||||||
|
cv.imwrite('opticalhsv.png',bgr)
|
||||||
|
prvs = next
|
Loading…
Reference in New Issue
Block a user