├── README.md
├── android
├── .gitignore
├── .idea
│ ├── .name
│ ├── compiler.xml
│ ├── copyright
│ │ └── profiles_settings.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── libraries
│ │ ├── android_0_17.xml
│ │ ├── calibration_0_17.xml
│ │ ├── ddogleg_0_5.xml
│ │ ├── ejml_0_25.xml
│ │ ├── feature_0_17.xml
│ │ ├── geo_0_17.xml
│ │ ├── georegression_0_6.xml
│ │ ├── ip_0_17.xml
│ │ ├── recognition_0_17.xml
│ │ └── sfm_0_17.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── scopes
│ │ └── scope_settings.xml
│ └── vcs.xml
├── android.iml
├── app
│ ├── .gitignore
│ ├── app.iml
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── org
│ │ │ └── boofcv
│ │ │ └── objecttracking
│ │ │ └── ApplicationTest.java
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── org
│ │ │ └── boofcv
│ │ │ └── objecttracking
│ │ │ ├── ObjectTrackerActivity.java
│ │ │ └── UtilVarious.java
│ │ └── res
│ │ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── layout
│ │ └── objecttrack_controls.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
└── settings.gradle
├── copyright.txt
└── desktop
├── build.gradle
├── desktop.iml
└── src
└── ExampleObjectTracking.java
/README.md:
--------------------------------------------------------------------------------
1 | TutorialObjectTracking
2 | ======================
3 |
4 | Demonstration on how to perform object tracking from live video streams on the desktop and Android devices. Object tracking is the process of tracking objects inside of video streams, often selected by the user or an automated algorithm. BoofCV (http://boofcv.org) is an open source Java based computer vision library and is used to provide the tracking algorithms. As of version 0.17 has following built in:
5 |
6 | - Circulant
7 | * Simple and robust, but can't recover tracks
8 | * http://home.isr.uc.pt/~henriques/circulant/
9 | - Track-Learning-Detect (TLD)
10 | * Only long term tracking algorithm in BoofCV
11 | * More computationally expensive and can be finicky
12 | * http://personal.ee.surrey.ac.uk/Personal/Z.Kalal/tld.html
13 | - Sparse-Flow
14 | * Only tracker in BoofCV which can estimate rotations
15 | * KLT based tracker
16 | * Can be brittle
17 | * http://boofcv.org/javadoc/boofcv/alg/tracker/sfot/SparseFlowObjectTracker.html
18 | - Mean-Shift
19 | * Matches the histogram of a local neighborhood
20 | * Comaniciu, et. al. ,"Kernel-Based Object Tracking" 2003
21 | - Mean-Shift Likelihood
22 | * Extremely fast but only works well when a single color dominates
23 |
24 | Desktop Instructions
25 | ======================
26 |
27 | The Desktop example should run without difficulty on Linux, Windows, and MacOS. Make sure you have webcam connect to your computer before you run.
28 |
29 | 1. Install Gradle if you don't already have it
30 | 2. Switch to desktop directory
31 | * cd TutorialObjectTracking/desktop
32 | 3. Compile and run the example:
33 | * gradle webcamRun
34 | 4. Select an object to track in the window which popped up
35 |
36 | Android Instructions
37 | ======================
38 |
39 | Just load it into Android studio and you will be good to go.
40 |
41 | ======================
42 |
43 | Author: Peter Abeles
44 | Date: July 15, 2014
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | .DS_Store
5 | /build
6 |
--------------------------------------------------------------------------------
/android/.idea/.name:
--------------------------------------------------------------------------------
1 | ObjectTracking
--------------------------------------------------------------------------------
/android/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/android/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/.idea/libraries/android_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/calibration_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/ddogleg_0_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/ejml_0_25.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/feature_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/geo_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/georegression_0_6.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/ip_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/recognition_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/libraries/sfm_0_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/android/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/android/app/app.iml:
--------------------------------------------------------------------------------
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 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 14
5 | buildToolsVersion '20.0.0'
6 |
7 | defaultConfig {
8 | applicationId "org.boofcv.objecttracking"
9 | minSdkVersion 14
10 | targetSdkVersion 19
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | runProguard false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 |
25 | ['recognition','android'].each { String a -> compile group: 'org.boofcv', name: a, version: '0.17' }
26 | }
27 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /opt/android-studio/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/android/app/src/androidTest/java/org/boofcv/objecttracking/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package org.boofcv.objecttracking;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/boofcv/objecttracking/ObjectTrackerActivity.java:
--------------------------------------------------------------------------------
1 | package org.boofcv.objecttracking;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.hardware.Camera;
8 | import android.os.Bundle;
9 | import android.view.LayoutInflater;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.widget.AdapterView;
13 | import android.widget.ArrayAdapter;
14 | import android.widget.FrameLayout;
15 | import android.widget.LinearLayout;
16 | import android.widget.Spinner;
17 | import android.widget.Toast;
18 |
19 | import java.util.List;
20 |
21 | import boofcv.abst.tracker.ConfigComaniciu2003;
22 | import boofcv.abst.tracker.ConfigTld;
23 | import boofcv.abst.tracker.MeanShiftLikelihoodType;
24 | import boofcv.abst.tracker.TrackerObjectQuad;
25 | import boofcv.alg.tracker.sfot.SfotConfig;
26 | import boofcv.android.ConvertBitmap;
27 | import boofcv.android.gui.VideoDisplayActivity;
28 | import boofcv.android.gui.VideoImageProcessing;
29 | import boofcv.core.image.ConvertImage;
30 | import boofcv.factory.tracker.FactoryTrackerObjectQuad;
31 | import boofcv.struct.image.ImageBase;
32 | import boofcv.struct.image.ImageType;
33 | import boofcv.struct.image.ImageUInt8;
34 | import boofcv.struct.image.MultiSpectral;
35 | import georegression.struct.point.Point2D_F64;
36 | import georegression.struct.point.Point2D_I32;
37 | import georegression.struct.shapes.Quadrilateral_F64;
38 |
39 | /**
40 | * Activity which opens a camera and lets the user select objects to track and switch between
41 | * different trackers. The user selects an object by clicking and dragging until the drawn
42 | * rectangle fills the object. To select a new object click reset.
43 | */
44 | public class ObjectTrackerActivity extends VideoDisplayActivity
45 | implements AdapterView.OnItemSelectedListener, View.OnTouchListener
46 | {
47 |
48 | Spinner spinnerView;
49 |
50 | int mode = 0;
51 |
52 | // size of the minimum square which the user can select
53 | final static int MINIMUM_MOTION = 20;
54 |
55 | Point2D_I32 click0 = new Point2D_I32();
56 | Point2D_I32 click1 = new Point2D_I32();
57 |
58 | @Override
59 | public void onCreate(Bundle savedInstanceState) {
60 | super.onCreate(savedInstanceState);
61 |
62 | LayoutInflater inflater = getLayoutInflater();
63 | LinearLayout controls = (LinearLayout)inflater.inflate(R.layout.objecttrack_controls,null);
64 |
65 | LinearLayout parent = getViewContent();
66 | parent.addView(controls);
67 |
68 | FrameLayout iv = getViewPreview();
69 | iv.setOnTouchListener(this);
70 |
71 | spinnerView = (Spinner)controls.findViewById(R.id.spinner_algs);
72 | ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
73 | R.array.tracking_objects, android.R.layout.simple_spinner_item);
74 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
75 | spinnerView.setAdapter(adapter);
76 | spinnerView.setOnItemSelectedListener(this);
77 | }
78 |
79 | @Override
80 | protected void onResume() {
81 | super.onResume();
82 | startObjectTracking(spinnerView.getSelectedItemPosition());
83 |
84 | // uncomment the line below to visually show the FPS.
85 | // The FPS is affected by the camera speed and processing power. The camera speed
86 | // is often determined by ambient lighting conditions
87 | // setShowFPS(true);
88 | }
89 |
90 | @Override
91 | protected Camera openConfigureCamera( Camera.CameraInfo cameraInfo )
92 | {
93 | Camera mCamera = UtilVarious.selectAndOpenCamera(cameraInfo, this);
94 | Camera.Parameters param = mCamera.getParameters();
95 |
96 | // Select the preview size closest to 320x240
97 | // Smaller images are recommended because some computer vision operations are very expensive
98 | List sizes = param.getSupportedPreviewSizes();
99 | Camera.Size s = sizes.get(UtilVarious.closest(sizes, 320, 240));
100 | param.setPreviewSize(s.width,s.height);
101 | mCamera.setParameters(param);
102 |
103 | return mCamera;
104 | }
105 |
106 | @Override
107 | public void onItemSelected(AdapterView> adapterView, View view, int pos, long id ) {
108 | startObjectTracking(pos);
109 | }
110 |
111 | private void startObjectTracking(int pos) {
112 | TrackerObjectQuad tracker = null;
113 | ImageType imageType = null;
114 |
115 | switch (pos) {
116 | case 0:
117 | imageType = ImageType.single(ImageUInt8.class);
118 | tracker = FactoryTrackerObjectQuad.circulant(null, ImageUInt8.class);
119 | break;
120 |
121 | case 1:
122 | imageType = ImageType.ms(3, ImageUInt8.class);
123 | tracker = FactoryTrackerObjectQuad.meanShiftComaniciu2003(new ConfigComaniciu2003(false),imageType);
124 | break;
125 |
126 | case 2:
127 | imageType = ImageType.ms(3, ImageUInt8.class);
128 | tracker = FactoryTrackerObjectQuad.meanShiftComaniciu2003(new ConfigComaniciu2003(true),imageType);
129 | break;
130 |
131 | case 3:
132 | imageType = ImageType.ms(3, ImageUInt8.class);
133 | tracker = FactoryTrackerObjectQuad.meanShiftLikelihood(30,5,256, MeanShiftLikelihoodType.HISTOGRAM,imageType);
134 | break;
135 |
136 | case 4:{
137 | imageType = ImageType.single(ImageUInt8.class);
138 | SfotConfig config = new SfotConfig();
139 | config.numberOfSamples = 10;
140 | config.robustMaxError = 30;
141 | tracker = FactoryTrackerObjectQuad.sparseFlow(config,ImageUInt8.class,null);
142 | }break;
143 |
144 | case 5:
145 | imageType = ImageType.single(ImageUInt8.class);
146 | tracker = FactoryTrackerObjectQuad.tld(new ConfigTld(false),ImageUInt8.class);
147 | break;
148 |
149 | default:
150 | throw new RuntimeException("Unknown tracker: "+pos);
151 | }
152 | setProcessing(new TrackingProcessing(tracker,imageType) );
153 | }
154 |
155 | @Override
156 | public void onNothingSelected(AdapterView> adapterView) {}
157 |
158 | @Override
159 | public boolean onTouch(View view, MotionEvent motionEvent) {
160 | if( mode == 0 ) {
161 | if(MotionEvent.ACTION_DOWN == motionEvent.getActionMasked()) {
162 | click0.set((int) motionEvent.getX(), (int) motionEvent.getY());
163 | click1.set((int) motionEvent.getX(), (int) motionEvent.getY());
164 | mode = 1;
165 | }
166 | } else if( mode == 1 ) {
167 | if(MotionEvent.ACTION_MOVE == motionEvent.getActionMasked()) {
168 | click1.set((int)motionEvent.getX(),(int)motionEvent.getY());
169 | } else if(MotionEvent.ACTION_UP == motionEvent.getActionMasked()) {
170 | click1.set((int)motionEvent.getX(),(int)motionEvent.getY());
171 | mode = 2;
172 | }
173 | }
174 | return true;
175 | }
176 |
177 | public void resetPressed( View view ) {
178 | mode = 0;
179 | }
180 |
181 | protected class TrackingProcessing extends VideoImageProcessing>
182 | {
183 |
184 | T input;
185 | ImageType inputType;
186 |
187 | TrackerObjectQuad tracker;
188 | boolean visible;
189 |
190 | Quadrilateral_F64 location = new Quadrilateral_F64();
191 |
192 | Paint paintSelected = new Paint();
193 | Paint paintLine0 = new Paint();
194 | Paint paintLine1 = new Paint();
195 | Paint paintLine2 = new Paint();
196 | Paint paintLine3 = new Paint();
197 | private Paint textPaint = new Paint();
198 |
199 | protected TrackingProcessing(TrackerObjectQuad tracker , ImageType inputType) {
200 | super(ImageType.ms(3,ImageUInt8.class));
201 | this.inputType = inputType;
202 |
203 | if( inputType.getFamily() == ImageType.Family.SINGLE_BAND ) {
204 | input = inputType.createImage(1,1);
205 | }
206 |
207 | mode = 0;
208 | this.tracker = tracker;
209 |
210 | paintSelected.setColor(Color.argb(0xFF / 2, 0xFF, 0, 0));
211 |
212 | paintLine0.setColor(Color.RED);
213 | paintLine0.setStrokeWidth(3f);
214 | paintLine1.setColor(Color.MAGENTA);
215 | paintLine1.setStrokeWidth(3f);
216 | paintLine2.setColor(Color.BLUE);
217 | paintLine2.setStrokeWidth(3f);
218 | paintLine3.setColor(Color.GREEN);
219 | paintLine3.setStrokeWidth(3f);
220 |
221 | // Create out paint to use for drawing
222 | textPaint.setARGB(255, 200, 0, 0);
223 | textPaint.setTextSize(60);
224 |
225 | }
226 |
227 | @Override
228 | protected void process(MultiSpectral input, Bitmap output, byte[] storage)
229 | {
230 | updateTracker(input);
231 | visualize(input, output, storage);
232 | }
233 |
234 | private void updateTracker(MultiSpectral color) {
235 | if( inputType.getFamily() == ImageType.Family.SINGLE_BAND ) {
236 | input.reshape(color.width,color.height);
237 | ConvertImage.average(color, (ImageUInt8) input);
238 | } else {
239 | input = (T)color;
240 | }
241 |
242 | if( mode == 2 ) {
243 | imageToOutput(click0.x, click0.y, location.a);
244 | imageToOutput(click1.x, click1.y, location.c);
245 |
246 | // make sure the user selected a valid region
247 | makeInBounds(location.a);
248 | makeInBounds(location.c);
249 |
250 | if( movedSignificantly(location.a,location.c) ) {
251 | // use the selected region and start the tracker
252 | location.b.set(location.c.x, location.a.y);
253 | location.d.set( location.a.x, location.c.y );
254 |
255 | tracker.initialize(input, location);
256 | visible = true;
257 | mode = 3;
258 | } else {
259 | // the user screw up. Let them know what they did wrong
260 | runOnUiThread(new Runnable() {
261 | @Override
262 | public void run() {
263 | Toast.makeText(ObjectTrackerActivity.this, "Drag a larger region", Toast.LENGTH_SHORT).show();
264 | }
265 | });
266 | mode = 0;
267 | }
268 | } else if( mode == 3 ) {
269 | visible = tracker.process(input,location);
270 | }
271 | }
272 |
273 | private void visualize(MultiSpectral color, Bitmap output, byte[] storage) {
274 | ConvertBitmap.multiToBitmap(color, output, storage);
275 | Canvas canvas = new Canvas(output);
276 |
277 | if( mode == 1 ) {
278 | Point2D_F64 a = new Point2D_F64();
279 | Point2D_F64 b = new Point2D_F64();
280 |
281 | imageToOutput(click0.x, click0.y, a);
282 | imageToOutput(click1.x, click1.y, b);
283 |
284 | canvas.drawRect((int)a.x,(int)a.y,(int)b.x,(int)b.y,paintSelected);
285 | } else if( mode >= 2 ) {
286 | if( visible ) {
287 | Quadrilateral_F64 q = location;
288 |
289 | drawLine(canvas,q.a,q.b,paintLine0);
290 | drawLine(canvas,q.b,q.c,paintLine1);
291 | drawLine(canvas,q.c,q.d,paintLine2);
292 | drawLine(canvas,q.d,q.a,paintLine3);
293 | } else {
294 | canvas.drawText("?",color.width/2,color.height/2,textPaint);
295 | }
296 | }
297 | }
298 |
299 | private void drawLine( Canvas canvas , Point2D_F64 a , Point2D_F64 b , Paint color ) {
300 | canvas.drawLine((int)a.x,(int)a.y,(int)b.x,(int)b.y,color);
301 | }
302 |
303 | private void makeInBounds( Point2D_F64 p ) {
304 | if( p.x < 0 ) p.x = 0;
305 | else if( p.x >= input.width )
306 | p.x = input.width - 1;
307 |
308 | if( p.y < 0 ) p.y = 0;
309 | else if( p.y >= input.height )
310 | p.y = input.height - 1;
311 |
312 | }
313 |
314 | private boolean movedSignificantly( Point2D_F64 a , Point2D_F64 b ) {
315 | if( Math.abs(a.x-b.x) < MINIMUM_MOTION )
316 | return false;
317 | if( Math.abs(a.y-b.y) < MINIMUM_MOTION )
318 | return false;
319 |
320 | return true;
321 | }
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/boofcv/objecttracking/UtilVarious.java:
--------------------------------------------------------------------------------
1 | package org.boofcv.objecttracking;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.hardware.Camera;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Various generic funtions.
12 | *
13 | * @author Peter Abeles
14 | */
15 | public class UtilVarious {
16 |
17 | /**
18 | * Step through the camera list and select a camera. It is also possible that there is no camera.
19 | * The camera hardware requirement in AndroidManifest.xml was turned off so that devices with just
20 | * a front facing camera can be found. Newer SDK's handle this in a more sane way, but with older devices
21 | * you need this work around.
22 | */
23 | public static Camera selectAndOpenCamera(Camera.CameraInfo info , Context context ) {
24 | int numberOfCameras = Camera.getNumberOfCameras();
25 |
26 | int selected = -1;
27 |
28 | for (int i = 0; i < numberOfCameras; i++) {
29 | Camera.getCameraInfo(i, info);
30 |
31 | if( info.facing == Camera.CameraInfo.CAMERA_FACING_BACK ) {
32 | selected = i;
33 | break;
34 | } else {
35 | // default to a front facing camera if a back facing one can't be found
36 | selected = i;
37 | }
38 | }
39 |
40 | if( selected == -1 ) {
41 | dialogNoCamera(context);
42 | return null; // won't ever be called
43 | } else {
44 | return Camera.open(selected);
45 | }
46 | }
47 |
48 | /**
49 | * Gracefully handle the situation where a camera could not be found
50 | */
51 | public static void dialogNoCamera( Context context ) {
52 | AlertDialog.Builder builder = new AlertDialog.Builder(context);
53 | builder.setMessage("Your device has no cameras!")
54 | .setCancelable(false)
55 | .setPositiveButton("OK", new DialogInterface.OnClickListener() {
56 | public void onClick(DialogInterface dialog, int id) {
57 | System.exit(0);
58 | }
59 | });
60 | AlertDialog alert = builder.create();
61 | alert.show();
62 | }
63 |
64 |
65 | /**
66 | * From the list of image sizes, select the one which is closest to the specified size.
67 | */
68 | public static int closest( List sizes , int width , int height ) {
69 | int best = -1;
70 | int bestScore = Integer.MAX_VALUE;
71 |
72 | for( int i = 0; i < sizes.size(); i++ ) {
73 | Camera.Size s = sizes.get(i);
74 |
75 | int dx = s.width-width;
76 | int dy = s.height-height;
77 |
78 | int score = dx*dx + dy*dy;
79 | if( score < bestScore ) {
80 | best = i;
81 | bestScore = score;
82 | }
83 | }
84 |
85 | return best;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lessthanoptimal/TutorialObjectTracking/a85b16884efa4d6782953148c06da6dd552ab0f0/android/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lessthanoptimal/TutorialObjectTracking/a85b16884efa4d6782953148c06da6dd552ab0f0/android/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lessthanoptimal/TutorialObjectTracking/a85b16884efa4d6782953148c06da6dd552ab0f0/android/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lessthanoptimal/TutorialObjectTracking/a85b16884efa4d6782953148c06da6dd552ab0f0/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/objecttrack_controls.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ObjectTracking
5 |
6 |
7 | - Circulant
8 | - Mean-Shift
9 | - Mean-Shift Scale
10 | - Mean-Shift Likelihood
11 | - Sparse Flow
12 | - TLD
13 |
14 |
15 | ObjectTrackerActivity
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:0.12.+'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/copyright.txt:
--------------------------------------------------------------------------------
1 | Copyright Notice
2 |
3 | This software is written by Peter Abeles and has been released to the public domain. Peter Abeles does not assumes any responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic.
4 |
5 | July 15, 2014
6 |
--------------------------------------------------------------------------------
/desktop/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'idea'
2 | apply plugin: 'eclipse'
3 | apply plugin: 'java'
4 | apply plugin: 'maven'
5 |
6 | sourceCompatibility = 1.6
7 |
8 | repositories {
9 | mavenCentral()
10 | mavenLocal()
11 | }
12 |
13 | sourceSets {
14 | main {
15 | java {
16 | srcDir 'src'
17 | }
18 | resources {
19 | srcDir 'resources/src'
20 | }
21 | }
22 |
23 | test {
24 | java {
25 | srcDir 'test'
26 | }
27 | resources {
28 | srcDir 'resources/test'
29 | }
30 | }
31 | }
32 |
33 | dependencies {
34 | ['recognition','WebcamCapture'].each { String a -> compile group: 'org.boofcv', name: a, version: '0.17' }
35 | }
36 |
37 | // Open a webcam and runs the object tracking example
38 | task webcamRun(dependsOn: [classes,testClasses] ) << {
39 | javaexec {
40 | main = "ExampleObjectTracking"
41 | classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath
42 | }
43 | }
44 |
45 | idea {
46 | project {
47 | jdkName = '1.6 (64bit)'
48 | languageLevel = '1.6'
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/desktop/desktop.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/desktop/src/ExampleObjectTracking.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014, Peter Abeles. All Rights Reserved.
3 | *
4 | * This file is part of BoofCV (http://boofcv.org).
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import boofcv.abst.tracker.TrackerObjectQuad;
20 | import boofcv.core.image.ConvertBufferedImage;
21 | import boofcv.factory.tracker.FactoryTrackerObjectQuad;
22 | import boofcv.io.webcamcapture.UtilWebcamCapture;
23 | import boofcv.struct.image.ImageBase;
24 | import boofcv.struct.image.ImageType;
25 | import boofcv.struct.image.ImageUInt8;
26 | import boofcv.struct.image.MultiSpectral;
27 | import com.github.sarxos.webcam.Webcam;
28 | import georegression.geometry.UtilPolygons2D_F64;
29 | import georegression.struct.point.Point2D_I32;
30 | import georegression.struct.shapes.Quadrilateral_F64;
31 | import georegression.struct.shapes.RectangleCorner2D_F64;
32 |
33 | import javax.swing.*;
34 | import java.awt.*;
35 | import java.awt.event.MouseEvent;
36 | import java.awt.event.MouseListener;
37 | import java.awt.event.MouseMotionListener;
38 | import java.awt.image.BufferedImage;
39 |
40 | /**
41 | * Demonstration for how to open a webcam and track an object in the video feed selected by the user. To select an object
42 | * simply click and drag a rectangle across. To change which tracker is used comment/uncomment the code in main().
43 | *
44 | * Code originally from BoofCV's WebcamCapture examples.
45 | *
46 | * @author Peter Abeles
47 | */
48 | public class ExampleObjectTracking extends JPanel
49 | implements MouseListener, MouseMotionListener {
50 |
51 | TrackerObjectQuad tracker;
52 |
53 | // location of the target being tracked
54 | Quadrilateral_F64 target = new Quadrilateral_F64();
55 |
56 | // location selected by the mouse
57 | Point2D_I32 point0 = new Point2D_I32();
58 | Point2D_I32 point1 = new Point2D_I32();
59 |
60 | int desiredWidth,desiredHeight;
61 | volatile int mode = 0;
62 |
63 | BufferedImage workImage;
64 |
65 | JFrame window;
66 |
67 | /**
68 | * Configures the tracking application
69 | *
70 | * @param tracker The object tracker
71 | * @param desiredWidth Desired size of the input stream
72 | * @param desiredHeight Desired height of the input stream
73 | */
74 | public ExampleObjectTracking(TrackerObjectQuad tracker, int desiredWidth, int desiredHeight)
75 | {
76 | this.tracker = tracker;
77 | this.desiredWidth = desiredWidth;
78 | this.desiredHeight = desiredHeight;
79 |
80 | addMouseListener(this);
81 | addMouseMotionListener(this);
82 |
83 | window = new JFrame("Object Tracking");
84 | window.setContentPane(this);
85 | window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
86 | }
87 |
88 | /**
89 | * Invoke to start the main processing loop.
90 | */
91 | public void process() {
92 | Webcam webcam = UtilWebcamCapture.openDefault(desiredWidth, desiredHeight);
93 |
94 | // adjust the window size and let the GUI know it has changed
95 | Dimension actualSize = webcam.getViewSize();
96 | setPreferredSize(actualSize);
97 | setMinimumSize(actualSize);
98 | window.setMinimumSize(actualSize);
99 | window.setPreferredSize(actualSize);
100 | window.setVisible(true);
101 |
102 | // create
103 | T input = tracker.getImageType().createImage(actualSize.width,actualSize.height);
104 |
105 | workImage = new BufferedImage(input.getWidth(),input.getHeight(),BufferedImage.TYPE_INT_RGB);
106 |
107 | while( true ) {
108 | BufferedImage buffered = webcam.getImage();
109 | ConvertBufferedImage.convertFrom(webcam.getImage(), input, true);
110 |
111 | // mode is read/written to by the GUI also
112 | int mode = this.mode;
113 |
114 | boolean success = false;
115 | if( mode == 2 ) {
116 | RectangleCorner2D_F64 rect = new RectangleCorner2D_F64();
117 | rect.set(point0.x, point0.y, point1.x, point1.y);
118 | UtilPolygons2D_F64.convert(rect, target);
119 | success = tracker.initialize(input,target);
120 | this.mode = success ? 3 : 0;
121 | } else if( mode == 3 ) {
122 | success = tracker.process(input,target);
123 | }
124 |
125 | synchronized( workImage ) {
126 | // copy the latest image into the work buffered
127 | Graphics2D g2 = workImage.createGraphics();
128 | g2.drawImage(buffered,0,0,null);
129 |
130 | // visualize the current results
131 | if (mode == 1) {
132 | drawSelected(g2);
133 | } else if (mode == 3) {
134 | if( success ) {
135 | drawTrack(g2);
136 | }
137 | }
138 | }
139 |
140 | repaint();
141 | }
142 | }
143 |
144 | @Override
145 | public void paint (Graphics g) {
146 | if( workImage != null ) {
147 | // draw the work image and be careful to make sure it isn't being manipulated at the same time
148 | synchronized (workImage) {
149 | g.drawImage(workImage, 0, 0, null);
150 | }
151 | }
152 | }
153 |
154 | private void drawSelected( Graphics2D g2 ) {
155 | g2.setColor(Color.RED);
156 | g2.setStroke( new BasicStroke(3));
157 | g2.drawLine(point0.getX(),point0.getY(),point1.getX(),point0.getY());
158 | g2.drawLine(point1.getX(),point0.getY(),point1.getX(),point1.getY());
159 | g2.drawLine(point1.getX(),point1.getY(),point0.getX(),point1.getY());
160 | g2.drawLine(point0.getX(),point1.getY(),point0.getX(),point0.getY());
161 | }
162 |
163 | private void drawTrack( Graphics2D g2 ) {
164 | g2.setStroke(new BasicStroke(3));
165 | g2.setColor(Color.RED);
166 | g2.drawLine((int)target.a.getX(),(int)target.a.getY(),(int)target.b.getX(),(int)target.b.getY());
167 | g2.setColor(Color.BLUE);
168 | g2.drawLine((int)target.b.getX(),(int)target.b.getY(),(int)target.c.getX(),(int)target.c.getY());
169 | g2.setColor(Color.GREEN);
170 | g2.drawLine((int)target.c.getX(),(int)target.c.getY(),(int)target.d.getX(),(int)target.d.getY());
171 | g2.setColor(Color.DARK_GRAY);
172 | g2.drawLine((int)target.d.getX(),(int)target.d.getY(),(int)target.a.getX(),(int)target.a.getY());
173 | }
174 |
175 | private void drawTarget( Graphics2D g2 ) {
176 | g2.setColor(Color.RED);
177 | g2.setStroke( new BasicStroke(2));
178 | g2.drawLine(point0.getX(),point0.getY(),point1.getX(),point0.getY());
179 | g2.drawLine(point1.getX(),point0.getY(),point1.getX(),point1.getY());
180 | g2.drawLine(point1.getX(),point1.getY(),point0.getX(),point1.getY());
181 | g2.drawLine(point0.getX(),point1.getY(),point0.getX(),point0.getY());
182 | }
183 |
184 | @Override
185 | public void mousePressed(MouseEvent e) {
186 | point0.set(e.getX(),e.getY());
187 | point1.set(e.getX(),e.getY());
188 | mode = 1;
189 | }
190 |
191 | @Override
192 | public void mouseReleased(MouseEvent e) {
193 | point1.set(e.getX(),e.getY());
194 | mode = 2;
195 | }
196 |
197 | @Override public void mouseClicked(MouseEvent e) {mode = 0;}
198 |
199 | @Override public void mouseEntered(MouseEvent e) {}
200 |
201 | @Override public void mouseExited(MouseEvent e) {}
202 |
203 | @Override public void mouseDragged(MouseEvent e) {
204 | if( mode == 1 ) {
205 | point1.set(e.getX(),e.getY());
206 | }
207 | }
208 |
209 | @Override
210 | public void mouseMoved(MouseEvent e) {}
211 |
212 | public static void main(String[] args) {
213 |
214 | ImageType> colorType = ImageType.ms(3, ImageUInt8.class);
215 |
216 | TrackerObjectQuad tracker =
217 | FactoryTrackerObjectQuad.circulant(null, ImageUInt8.class);
218 | // FactoryTrackerObjectQuad.sparseFlow(null,ImageUInt8.class,null);
219 | // FactoryTrackerObjectQuad.tld(null,ImageUInt8.class);
220 | // FactoryTrackerObjectQuad.meanShiftComaniciu2003(new ConfigComaniciu2003(), colorType);
221 | // FactoryTrackerObjectQuad.meanShiftComaniciu2003(new ConfigComaniciu2003(true),colorType);
222 | // FactoryTrackerObjectQuad.meanShiftLikelihood(30,5,255, MeanShiftLikelihoodType.HISTOGRAM,colorType);
223 |
224 |
225 | ExampleObjectTracking app = new ExampleObjectTracking(tracker,640,480);
226 |
227 | app.process();
228 | }
229 | }
230 |
--------------------------------------------------------------------------------