├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
└── source
└── opencv-kalman.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 |
3 | *.slo
4 | *.lo
5 | *.o
6 | *.a
7 | *.la
8 | *.lai
9 | *.so
10 | *.dll
11 | *.dylib
12 |
13 | # Qt-es
14 |
15 | /.qmake.cache
16 | /.qmake.stash
17 | *.pro.user
18 | *.pro.user.*
19 | *.qbs.user
20 | *.qbs.user.*
21 | *.moc
22 | moc_*.cpp
23 | qrc_*.cpp
24 | ui_*.h
25 | Makefile*
26 | *-build-*
27 |
28 | # QtCreator
29 |
30 | *.autosave
31 |
32 | #QtCtreator Qml
33 | *.qmlproject.user
34 | *.qmlproject.user.*
35 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | project(simple-opencv-kalman-tracker)
4 |
5 | set(CMAKE_BUILD_TYPE Debug)
6 |
7 | find_package( OpenCV REQUIRED )
8 |
9 |
10 |
11 | set(SRC_PATH source )
12 |
13 | set(${PROJECT_NAME}_SRC
14 | ${SRC_PATH}/opencv-kalman.cpp
15 | )
16 |
17 |
18 | #########################################################
19 | # Executable
20 | add_executable( ${PROJECT_NAME} ${${PROJECT_NAME}_SRC} )
21 | target_link_libraries( ${PROJECT_NAME} ${OpenCV_LIBS} )
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # simple-opencv-kalman-tracker
2 | A simple Ball Tracker made using OpenCV to demonstrate the use of the Kalman Filter in Computer Vision
3 |
4 | You can find the full tutorial on [Robot-home website](http://www.robot-home.it/blog/en/software/ball-tracker-con-filtro-di-kalman/)
5 |
6 | Watch the demo working on YouTube: https://www.youtube.com/watch?v=sG-h5ONsj9s
7 |
--------------------------------------------------------------------------------
/source/opencv-kalman.cpp:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * OpenCV Tutorial: Ball Tracking using *
3 | * Kalman Filter *
4 | ******************************************/
5 |
6 | // Module "core"
7 | #include
8 |
9 | // Module "highgui"
10 | #include
11 |
12 | // Module "imgproc"
13 | #include
14 |
15 | // Module "video"
16 | #include
17 |
18 | // Output
19 | #include
20 |
21 | // Vector
22 | #include
23 |
24 | using namespace std;
25 |
26 | // >>>>> Color to be tracked
27 | #define MIN_H_BLUE 200
28 | #define MAX_H_BLUE 300
29 | // <<<<< Color to be tracked
30 |
31 |
32 | int main()
33 | {
34 | // Camera frame
35 | cv::Mat frame;
36 |
37 | // >>>> Kalman Filter
38 | int stateSize = 6;
39 | int measSize = 4;
40 | int contrSize = 0;
41 |
42 | unsigned int type = CV_32F;
43 | cv::KalmanFilter kf(stateSize, measSize, contrSize, type);
44 |
45 | cv::Mat state(stateSize, 1, type); // [x,y,v_x,v_y,w,h]
46 | cv::Mat meas(measSize, 1, type); // [z_x,z_y,z_w,z_h]
47 | //cv::Mat procNoise(stateSize, 1, type)
48 | // [E_x,E_y,E_v_x,E_v_y,E_w,E_h]
49 |
50 | // Transition State Matrix A
51 | // Note: set dT at each processing step!
52 | // [ 1 0 dT 0 0 0 ]
53 | // [ 0 1 0 dT 0 0 ]
54 | // [ 0 0 1 0 0 0 ]
55 | // [ 0 0 0 1 0 0 ]
56 | // [ 0 0 0 0 1 0 ]
57 | // [ 0 0 0 0 0 1 ]
58 | cv::setIdentity(kf.transitionMatrix);
59 |
60 | // Measure Matrix H
61 | // [ 1 0 0 0 0 0 ]
62 | // [ 0 1 0 0 0 0 ]
63 | // [ 0 0 0 0 1 0 ]
64 | // [ 0 0 0 0 0 1 ]
65 | kf.measurementMatrix = cv::Mat::zeros(measSize, stateSize, type);
66 | kf.measurementMatrix.at(0) = 1.0f;
67 | kf.measurementMatrix.at(7) = 1.0f;
68 | kf.measurementMatrix.at(16) = 1.0f;
69 | kf.measurementMatrix.at(23) = 1.0f;
70 |
71 | // Process Noise Covariance Matrix Q
72 | // [ Ex 0 0 0 0 0 ]
73 | // [ 0 Ey 0 0 0 0 ]
74 | // [ 0 0 Ev_x 0 0 0 ]
75 | // [ 0 0 0 Ev_y 0 0 ]
76 | // [ 0 0 0 0 Ew 0 ]
77 | // [ 0 0 0 0 0 Eh ]
78 | //cv::setIdentity(kf.processNoiseCov, cv::Scalar(1e-2));
79 | kf.processNoiseCov.at(0) = 1e-2;
80 | kf.processNoiseCov.at(7) = 1e-2;
81 | kf.processNoiseCov.at(14) = 5.0f;
82 | kf.processNoiseCov.at(21) = 5.0f;
83 | kf.processNoiseCov.at(28) = 1e-2;
84 | kf.processNoiseCov.at(35) = 1e-2;
85 |
86 | // Measures Noise Covariance Matrix R
87 | cv::setIdentity(kf.measurementNoiseCov, cv::Scalar(1e-1));
88 | // <<<< Kalman Filter
89 |
90 | // Camera Index
91 | int idx = 0;
92 |
93 | // Camera Capture
94 | cv::VideoCapture cap;
95 |
96 | // >>>>> Camera Settings
97 | if (!cap.open(idx))
98 | {
99 | cout << "Webcam not connected.\n" << "Please verify\n";
100 | return EXIT_FAILURE;
101 | }
102 |
103 | cap.set(CV_CAP_PROP_FRAME_WIDTH, 1024);
104 | cap.set(CV_CAP_PROP_FRAME_HEIGHT, 768);
105 | // <<<<< Camera Settings
106 |
107 | cout << "\nHit 'q' to exit...\n";
108 |
109 | char ch = 0;
110 |
111 | double ticks = 0;
112 | bool found = false;
113 |
114 | int notFoundCount = 0;
115 |
116 | // >>>>> Main loop
117 | while (ch != 'q' && ch != 'Q')
118 | {
119 | double precTick = ticks;
120 | ticks = (double) cv::getTickCount();
121 |
122 | double dT = (ticks - precTick) / cv::getTickFrequency(); //seconds
123 |
124 | // Frame acquisition
125 | cap >> frame;
126 |
127 | cv::Mat res;
128 | frame.copyTo( res );
129 |
130 | if (found)
131 | {
132 | // >>>> Matrix A
133 | kf.transitionMatrix.at(2) = dT;
134 | kf.transitionMatrix.at(9) = dT;
135 | // <<<< Matrix A
136 |
137 | cout << "dT:" << endl << dT << endl;
138 |
139 | state = kf.predict();
140 | cout << "State post:" << endl << state << endl;
141 |
142 | cv::Rect predRect;
143 | predRect.width = state.at(4);
144 | predRect.height = state.at(5);
145 | predRect.x = state.at(0) - predRect.width / 2;
146 | predRect.y = state.at(1) - predRect.height / 2;
147 |
148 | cv::Point center;
149 | center.x = state.at(0);
150 | center.y = state.at(1);
151 | cv::circle(res, center, 2, CV_RGB(255,0,0), -1);
152 |
153 | cv::rectangle(res, predRect, CV_RGB(255,0,0), 2);
154 | }
155 |
156 | // >>>>> Noise smoothing
157 | cv::Mat blur;
158 | cv::GaussianBlur(frame, blur, cv::Size(5, 5), 3.0, 3.0);
159 | // <<<<< Noise smoothing
160 |
161 | // >>>>> HSV conversion
162 | cv::Mat frmHsv;
163 | cv::cvtColor(blur, frmHsv, CV_BGR2HSV);
164 | // <<<<< HSV conversion
165 |
166 | // >>>>> Color Thresholding
167 | // Note: change parameters for different colors
168 | cv::Mat rangeRes = cv::Mat::zeros(frame.size(), CV_8UC1);
169 | cv::inRange(frmHsv, cv::Scalar(MIN_H_BLUE / 2, 100, 80),
170 | cv::Scalar(MAX_H_BLUE / 2, 255, 255), rangeRes);
171 | // <<<<< Color Thresholding
172 |
173 | // >>>>> Improving the result
174 | cv::erode(rangeRes, rangeRes, cv::Mat(), cv::Point(-1, -1), 2);
175 | cv::dilate(rangeRes, rangeRes, cv::Mat(), cv::Point(-1, -1), 2);
176 | // <<<<< Improving the result
177 |
178 | // Thresholding viewing
179 | cv::imshow("Threshold", rangeRes);
180 |
181 | // >>>>> Contours detection
182 | vector > contours;
183 | cv::findContours(rangeRes, contours, CV_RETR_EXTERNAL,
184 | CV_CHAIN_APPROX_NONE);
185 | // <<<<< Contours detection
186 |
187 | // >>>>> Filtering
188 | vector > balls;
189 | vector ballsBox;
190 | for (size_t i = 0; i < contours.size(); i++)
191 | {
192 | cv::Rect bBox;
193 | bBox = cv::boundingRect(contours[i]);
194 |
195 | float ratio = (float) bBox.width / (float) bBox.height;
196 | if (ratio > 1.0f)
197 | ratio = 1.0f / ratio;
198 |
199 | // Searching for a bBox almost square
200 | if (ratio > 0.75 && bBox.area() >= 400)
201 | {
202 | balls.push_back(contours[i]);
203 | ballsBox.push_back(bBox);
204 | }
205 | }
206 | // <<<<< Filtering
207 |
208 | cout << "Balls found:" << ballsBox.size() << endl;
209 |
210 | // >>>>> Detection result
211 | for (size_t i = 0; i < balls.size(); i++)
212 | {
213 | cv::drawContours(res, balls, i, CV_RGB(20,150,20), 1);
214 | cv::rectangle(res, ballsBox[i], CV_RGB(0,255,0), 2);
215 |
216 | cv::Point center;
217 | center.x = ballsBox[i].x + ballsBox[i].width / 2;
218 | center.y = ballsBox[i].y + ballsBox[i].height / 2;
219 | cv::circle(res, center, 2, CV_RGB(20,150,20), -1);
220 |
221 | stringstream sstr;
222 | sstr << "(" << center.x << "," << center.y << ")";
223 | cv::putText(res, sstr.str(),
224 | cv::Point(center.x + 3, center.y - 3),
225 | cv::FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(20,150,20), 2);
226 | }
227 | // <<<<< Detection result
228 |
229 | // >>>>> Kalman Update
230 | if (balls.size() == 0)
231 | {
232 | notFoundCount++;
233 | cout << "notFoundCount:" << notFoundCount << endl;
234 | if( notFoundCount >= 100 )
235 | {
236 | found = false;
237 | }
238 | /*else
239 | kf.statePost = state;*/
240 | }
241 | else
242 | {
243 | notFoundCount = 0;
244 |
245 | meas.at(0) = ballsBox[0].x + ballsBox[0].width / 2;
246 | meas.at(1) = ballsBox[0].y + ballsBox[0].height / 2;
247 | meas.at(2) = (float)ballsBox[0].width;
248 | meas.at(3) = (float)ballsBox[0].height;
249 |
250 | if (!found) // First detection!
251 | {
252 | // >>>> Initialization
253 | kf.errorCovPre.at(0) = 1; // px
254 | kf.errorCovPre.at(7) = 1; // px
255 | kf.errorCovPre.at(14) = 1;
256 | kf.errorCovPre.at(21) = 1;
257 | kf.errorCovPre.at(28) = 1; // px
258 | kf.errorCovPre.at(35) = 1; // px
259 |
260 | state.at(0) = meas.at(0);
261 | state.at(1) = meas.at(1);
262 | state.at(2) = 0;
263 | state.at(3) = 0;
264 | state.at(4) = meas.at(2);
265 | state.at(5) = meas.at(3);
266 | // <<<< Initialization
267 |
268 | kf.statePost = state;
269 |
270 | found = true;
271 | }
272 | else
273 | kf.correct(meas); // Kalman Correction
274 |
275 | cout << "Measure matrix:" << endl << meas << endl;
276 | }
277 | // <<<<< Kalman Update
278 |
279 | // Final result
280 | cv::imshow("Tracking", res);
281 |
282 | // User key
283 | ch = cv::waitKey(1);
284 | }
285 | // <<<<< Main loop
286 |
287 | return EXIT_SUCCESS;
288 | }
289 |
--------------------------------------------------------------------------------