├── .gitignore
├── LICENSE
├── README
├── tld-main
├── .classpath
├── .project
├── AndroidManifest.xml
├── ic_launcher-web.png
├── proguard-project.txt
├── project.properties
├── res
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ ├── layout
│ │ └── activity_main.xml
│ ├── menu
│ │ └── activity_main.xml
│ ├── raw
│ │ └── parameters.properties
│ ├── values-large
│ │ └── dimens.xml
│ └── values
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
└── src
│ └── com
│ └── trandi
│ └── opentld
│ ├── MainActivity.java
│ ├── TLDView.java
│ └── tld
│ ├── BoundingBox.java
│ ├── FernEnsembleClassifier.java
│ ├── Grid.java
│ ├── LKTracker.java
│ ├── NNClassifier.java
│ ├── Parameters.java
│ ├── PatchGenerator.java
│ ├── Tld.java
│ └── Util.java
└── tld-test
├── .classpath
├── .project
├── AndroidManifest.xml
├── proguard-project.txt
├── project.properties
├── res
└── raw
│ ├── parameters.properties
│ └── test_grid_frame.png
└── src
└── com
└── trandi
└── opentld
└── tld
├── FerNNClassifierTest.java
├── GridTest.java
├── LKTrackerTest.java
├── OpenCVTestCase.java
├── TldTest.java
└── UtilTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | gen
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | OpenTLDAndroid
2 | ==============
3 |
4 | Open TLD (Predator) algorithm port to Android
5 |
--------------------------------------------------------------------------------
/tld-main/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/tld-main/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenTLD
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tld-main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tld-main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-main/ic_launcher-web.png
--------------------------------------------------------------------------------
/tld-main/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/tld-main/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-16
15 | android.library=false
16 | android.library.reference.1=../../../OpenCV-2.4.6-android-sdk/sdk/java
17 |
--------------------------------------------------------------------------------
/tld-main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/tld-main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/tld-main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/tld-main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/tld-main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tld-main/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/tld-main/res/raw/parameters.properties:
--------------------------------------------------------------------------------
1 | min_win=15
2 | patch_size=15
3 | ncc_thesame=0.95
4 | valid=0.5
5 | num_ferns=10
6 | num_features_per_fern=13
7 | pos_thr_fern=0.5
8 | pos_thr_nn=0.65
9 | pos_thr_nn_valid=0.7
10 |
11 | num_closest_init=10
12 | num_warps_init=20
13 | noise_init=5
14 | angle_init=20
15 | shift_init=0.02
16 | scale_init=0.02
17 |
18 | num_closest_update=10
19 | num_warps_update=10
20 | noise_update=5
21 | angle_update=10
22 | shift_update=0.02
23 | scale_update=0.02
24 | overlap=0.2
25 | num_bad_patches=100
26 |
27 | tracker_stability_FBerrMax=10
--------------------------------------------------------------------------------
/tld-main/res/values-large/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/tld-main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 8dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/tld-main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenTLD
4 | Settings
5 | OpenTLD
6 |
7 |
--------------------------------------------------------------------------------
/tld-main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/MainActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld;
18 |
19 | import org.opencv.android.BaseLoaderCallback;
20 | import org.opencv.android.LoaderCallbackInterface;
21 | import org.opencv.android.OpenCVLoader;
22 |
23 | import android.app.Activity;
24 | import android.os.Bundle;
25 | import android.util.Log;
26 | import android.view.Menu;
27 | import android.view.SurfaceView;
28 | import android.view.Window;
29 |
30 | import com.trandi.opentld.tld.Util;
31 |
32 | public class MainActivity extends Activity {
33 | private TLDView _tldView;
34 |
35 | private BaseLoaderCallback _openCVCallBack = new BaseLoaderCallback(this) {
36 | @Override
37 | public void onManagerConnected(int status) {
38 | switch (status) {
39 | case LoaderCallbackInterface.SUCCESS:
40 | {
41 | Log.i(Util.TAG, "OpenCV loaded successfully");
42 |
43 | setContentView(R.layout.activity_main);
44 |
45 | _tldView = (TLDView) findViewById(R.id.tld_view);
46 | _tldView.setVisibility(SurfaceView.VISIBLE);
47 | _tldView.enableView();
48 | } break;
49 | default:
50 | {
51 | Log.e(Util.TAG, "OpenCV can NOT be loaded");
52 | super.onManagerConnected(status);
53 | } break;
54 | }
55 | }
56 | };
57 |
58 |
59 | /** Called when the activity is first created. */
60 | @Override
61 | public void onCreate(Bundle savedInstanceState) {
62 | super.onCreate(savedInstanceState);
63 | requestWindowFeature(Window.FEATURE_NO_TITLE);
64 |
65 | Log.i(Util.TAG, "Trying to load OpenCV library");
66 | if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, _openCVCallBack)) {
67 | Log.e(Util.TAG, "Cannot connect to OpenCV Manager");
68 | }
69 | }
70 |
71 | @Override
72 | public boolean onCreateOptionsMenu(Menu menu) {
73 | getMenuInflater().inflate(R.menu.activity_main, menu);
74 | return true;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/TLDView.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld;
18 |
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.util.List;
22 | import java.util.Properties;
23 | import java.util.concurrent.atomic.AtomicReference;
24 |
25 | import org.opencv.android.CameraBridgeViewBase;
26 | import org.opencv.android.JavaCameraView;
27 | import org.opencv.core.Core;
28 | import org.opencv.core.Mat;
29 | import org.opencv.core.Point;
30 | import org.opencv.core.Rect;
31 | import org.opencv.core.Scalar;
32 | import org.opencv.core.Size;
33 | import org.opencv.imgproc.Imgproc;
34 |
35 | import android.content.Context;
36 | import android.graphics.Canvas;
37 | import android.graphics.Color;
38 | import android.graphics.Paint;
39 | import android.graphics.Paint.Style;
40 | import android.graphics.PorterDuff;
41 | import android.util.AttributeSet;
42 | import android.util.Log;
43 | import android.view.MotionEvent;
44 | import android.view.SurfaceHolder;
45 | import android.view.View;
46 |
47 | import com.trandi.opentld.tld.Tld;
48 | import com.trandi.opentld.tld.Tld.ProcessFrameStruct;
49 | import com.trandi.opentld.tld.Util;
50 |
51 |
52 | public class TLDView extends JavaCameraView implements CameraBridgeViewBase.CvCameraViewListener {
53 | final private SurfaceHolder _holder;
54 | private int _canvasImgYOffset;
55 | private int _canvasImgXOffset;
56 |
57 | private Mat _currentGray = new Mat();
58 | private Mat _lastGray = new Mat();
59 | private Tld _tld = null;
60 | private Rect _trackedBox = null;
61 | private ProcessFrameStruct _processFrameStruct = null;
62 | private Properties _tldProperties;
63 |
64 | private static final Size WORKING_FRAME_SIZE = new Size(144, 80);
65 | private Mat _workingFrame = new Mat();
66 | private String _errMessage;
67 |
68 | public TLDView(Context context, AttributeSet attrs) {
69 | super(context, attrs);
70 | _holder = getHolder();
71 |
72 | // Init the PROPERTIES
73 | InputStream propsIS = null;
74 | try{
75 | propsIS = context.getResources().openRawResource(R.raw.parameters);
76 | _tldProperties = new Properties();
77 | _tldProperties.load(propsIS);
78 | } catch (IOException e) {
79 | Log.e(Util.TAG, "Can't load properties", e);
80 | }finally{
81 | if(propsIS != null){
82 | try {
83 | propsIS.close();
84 | } catch (IOException e) {
85 | Log.e(Util.TAG, "Can't close props", e);
86 | }
87 | }
88 | }
89 |
90 | // listens to its own events
91 | setCvCameraViewListener(this);
92 |
93 |
94 | // DEBUG
95 | //_trackedBox = new BoundingBox(165,93,51,54, 0, 0);
96 |
97 | // LISTEN for touches of the screen, to define the BOX to be tracked
98 | final AtomicReference trackedBox1stCorner = new AtomicReference();
99 | final Paint rectPaint = new Paint();
100 | rectPaint.setColor(Color.rgb(0, 255, 0));
101 | rectPaint.setStrokeWidth(5);
102 | rectPaint.setStyle(Style.STROKE);
103 |
104 | setOnTouchListener(new OnTouchListener() {
105 | @Override
106 | public boolean onTouch(View v, MotionEvent event) {
107 | // re-init
108 | _errMessage = null;
109 | _tld = null;
110 |
111 | final Point corner = new Point(event.getX() - _canvasImgXOffset, event.getY() - _canvasImgYOffset);
112 | switch(event.getAction()){
113 | case MotionEvent.ACTION_DOWN:
114 | trackedBox1stCorner.set(corner);
115 | Log.i(Util.TAG, "1st corner: " + corner);
116 | break;
117 | case MotionEvent.ACTION_UP:
118 | _trackedBox = new Rect(trackedBox1stCorner.get(), corner);
119 | Log.i(Util.TAG, "Tracked box DEFINED: " + _trackedBox);
120 | break;
121 | case MotionEvent.ACTION_MOVE:
122 | final android.graphics.Rect rect = new android.graphics.Rect(
123 | (int)trackedBox1stCorner.get().x + _canvasImgXOffset, (int)trackedBox1stCorner.get().y + _canvasImgYOffset,
124 | (int)corner.x + _canvasImgXOffset, (int)corner.y + _canvasImgYOffset);
125 | final Canvas canvas =_holder.lockCanvas(rect);
126 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // remove old rectangle
127 | canvas.drawRect(rect, rectPaint);
128 | _holder.unlockCanvasAndPost(canvas);
129 | break;
130 | }
131 |
132 | return true;
133 | }
134 | });
135 | }
136 |
137 | @Override
138 | public Mat onCameraFrame(Mat originalFrame) {
139 | try{
140 | // Image is too big and this requires too much CPU for a phone, so scale everything down...
141 | Imgproc.resize(originalFrame, _workingFrame, WORKING_FRAME_SIZE);
142 | final Size workingRatio = new Size(originalFrame.width() / WORKING_FRAME_SIZE.width, originalFrame.height() / WORKING_FRAME_SIZE.height);
143 | // usefull to see what we're actually working with...
144 | _workingFrame.copyTo(originalFrame.submat(originalFrame.rows() - _workingFrame.rows(), originalFrame.rows(), 0, _workingFrame.cols()));
145 |
146 | if(_trackedBox != null){
147 | if(_tld == null){ // run the 1st time only
148 | Imgproc.cvtColor(_workingFrame, _lastGray, Imgproc.COLOR_RGB2GRAY);
149 | _tld = new Tld(_tldProperties);
150 | final Rect scaledDownTrackedBox = scaleDown(_trackedBox, workingRatio);
151 | Log.i(Util.TAG, "Working Ration: " + workingRatio + " / Tracking Box: " + _trackedBox + " / Scaled down to: " + scaledDownTrackedBox);
152 | try {
153 | _tld.init(_lastGray, scaledDownTrackedBox);
154 | }catch(Exception eInit){
155 | // start from scratch, you have to select an init box again !
156 | _trackedBox = null;
157 | _tld = null;
158 | throw eInit; // re-throw it as it will be dealt with later
159 | }
160 | }else{
161 | Imgproc.cvtColor(_workingFrame, _currentGray, Imgproc.COLOR_RGB2GRAY);
162 |
163 | _processFrameStruct = _tld.processFrame(_lastGray, _currentGray);
164 | drawPoints(originalFrame, _processFrameStruct.lastPoints, workingRatio, new Scalar(255, 0, 0));
165 | drawPoints(originalFrame, _processFrameStruct.currentPoints, workingRatio, new Scalar(0, 255, 0));
166 | drawBox(originalFrame, scaleUp(_processFrameStruct.currentBBox, workingRatio), new Scalar(0, 0, 255));
167 |
168 | _currentGray.copyTo(_lastGray);
169 |
170 | // overlay the current positive examples on the real image(needs converting at the same time !)
171 | //copyTo(_tld.getPPatterns(), originalFrame);
172 | }
173 | }
174 | } catch(Exception e) {
175 | _errMessage = e.getClass().getSimpleName() + " / " + e.getMessage();
176 | Log.e(Util.TAG, "TLDView PROBLEM", e);
177 | }
178 |
179 |
180 | if(_errMessage != null){
181 | Core.putText(originalFrame, _errMessage, new Point(0, 300), Core.FONT_HERSHEY_PLAIN, 1.3d, new Scalar(255, 0, 0), 2);
182 | }
183 |
184 | return originalFrame;
185 | }
186 |
187 | private static void copyTo(List patterns, Mat dest) {
188 | if(patterns == null || patterns.isEmpty() || dest == null) return;
189 |
190 | final int patternRows = patterns.get(0).rows();
191 | final int patternCols = patterns.get(0).cols();
192 | final int vertCount = dest.rows() / patternRows;
193 | final int horizCount = patterns.size() / vertCount + 1;
194 |
195 | int patchIdx = 0;
196 | for(int col = dest.cols() - horizCount * patternCols - 1; col < dest.cols() && patchIdx < patterns.size(); col += patternCols){
197 | for(int row = 0; row < dest.rows() && patchIdx < patterns.size(); row += patternRows) {
198 | Imgproc.cvtColor(patterns.get(patchIdx), dest.submat(row, row + patternRows, col, col + patternCols), Imgproc.COLOR_GRAY2RGBA);
199 | patchIdx++;
200 | }
201 | }
202 | }
203 |
204 | @Override
205 | public void onCameraViewStarted(int width, int height) {
206 | _canvasImgXOffset = (getWidth() - width) / 2;
207 | _canvasImgYOffset = (getHeight() - height) / 2;
208 | }
209 |
210 | @Override
211 | public void onCameraViewStopped() {
212 | // TODO Auto-generated method stub
213 | }
214 |
215 |
216 | private static void drawPoints(Mat image, final Point[] points, final Size scale, final Scalar colour){
217 | if(points != null){
218 | for(Point point : points){
219 | Core.circle(image, scaleUp(point, scale), 2, colour);
220 | }
221 | }
222 | }
223 |
224 | private static void drawBox(Mat image, final Rect box, final Scalar colour){
225 | if(box != null){
226 | Core.rectangle(image, box.tl(), box.br(), colour);
227 | }
228 | }
229 |
230 |
231 | /* SCALING */
232 |
233 | private static Point scaleUp(Point point, Size scale){
234 | if(point == null || scale == null) return null;
235 | return new Point(point.x * scale.width, point.y * scale.height);
236 | }
237 |
238 | private static Point scaleDown(Point point, Size scale){
239 | if(point == null || scale == null) return null;
240 | return new Point(point.x / scale.width, point.y / scale.height);
241 | }
242 |
243 | private static Rect scaleUp(Rect rect, Size scale) {
244 | if(rect == null || scale == null) return null;
245 | return new Rect(scaleUp(rect.tl(), scale), scaleUp(rect.br(), scale));
246 | }
247 |
248 | private static Rect scaleDown(Rect rect, Size scale) {
249 | if(rect == null || scale == null) return null;
250 | return new Rect(scaleDown(rect.tl(), scale), scaleDown(rect.br(), scale));
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/BoundingBox.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | import org.opencv.core.Mat;
23 | import org.opencv.core.Point;
24 | import org.opencv.core.Rect;
25 |
26 | import android.util.Log;
27 |
28 |
29 | public class BoundingBox extends Rect{
30 | private static final int POINTS_MAX_COUNT = 10;
31 | private static final int POINTS_MARGIN_H = 0;
32 | private static final int POINTS_MARGIN_V = 0;
33 |
34 | public float overlap = -1; // overlap with current(main) BoundingBox
35 | int scaleIdx = -1;
36 |
37 |
38 | public BoundingBox(){
39 | super();
40 | }
41 |
42 | public BoundingBox(final Point ul, final Point br){
43 | super(ul, br);
44 | }
45 |
46 | public BoundingBox(int x, int y, int width, int height, float overlap, int scaleIdx){
47 | super(x, y, width, height);
48 | this.overlap = overlap;
49 | this.scaleIdx = scaleIdx;
50 | }
51 |
52 | float calcOverlap(final Rect other){
53 | if(x > other.x + other.width || y > other.y + other.height || x + width < other.x || y + height < other.y){
54 | // obvious case where these 2 boxes do not overlap at all !
55 | return 0f;
56 | }else{
57 | final float colIntersection = Math.min(x + width, other.x + other.width) - Math.max(x, other.x);
58 | final float rowIntersection = Math.min(y + height, other.y + other.height) - Math.max(y, other.y);
59 |
60 | final float intersection = colIntersection * rowIntersection;
61 | final float myArea = width * height;
62 | final float otherArea = other.width * other.height;
63 |
64 | return intersection / (myArea + otherArea - intersection);
65 | }
66 | }
67 |
68 |
69 | Point[] points(){
70 | final List result = new ArrayList();
71 | final int stepx = (int) Math.ceil((width - 2 * POINTS_MARGIN_H) / POINTS_MAX_COUNT);
72 | final int stepy = (int) Math.ceil((height - 2 * POINTS_MARGIN_V) / POINTS_MAX_COUNT);
73 | for(int j = y + POINTS_MARGIN_V; j < y + height - POINTS_MARGIN_V; j += stepy){
74 | for(int i = x + POINTS_MARGIN_H; i < x + width - POINTS_MARGIN_H; i += stepx){
75 | result.add(new Point(i, j));
76 | }
77 | }
78 | Log.i(Util.TAG, "Points in BB: " + this + " stepx=" + stepx + " stepy=" + stepy + " RES size=" + result.size());
79 | return result.toArray(new Point[result.size()]);
80 | }
81 |
82 |
83 | BoundingBox predict(final Point[] points1, final Point[] points2){
84 | if(points1.length != points2.length) throw new IllegalArgumentException("The 2 arrays of points must be of the same lenght ! (" + points1.length + ", " + points2.length + ")");
85 |
86 | final int npoints = points1.length;
87 | Log.i(Util.TAG, "Tracked points: " + npoints);
88 |
89 | final float[] xoff = new float[npoints];
90 | final float[] yoff = new float[npoints];
91 | for(int i = 0; i < npoints; i++){
92 | xoff[i] = (float) (points2[i].x - points1[i].x);
93 | yoff[i] = (float) (points2[i].y - points1[i].y);
94 | }
95 | final float dx = Util.median(xoff);
96 | final float dy = Util.median(yoff);
97 |
98 | float s = 1f;
99 | if(npoints > 1){
100 | final float[] d = new float[npoints * (npoints - 1) / 2];
101 | int idx = 0;
102 | for(int i = 0; i < npoints; i++){
103 | for(int j = i + 1; j < npoints; j++){
104 | d[idx++] = Util.norm(points2[i], points2[j]) / Util.norm(points1[i], points1[j]);
105 | }
106 | }
107 | s = Util.median(d);
108 | }
109 |
110 | final float s1 = 0.5f * (s - 1) * width;
111 | final float s2 = 0.5f * (s - 1) * height;
112 | final BoundingBox result = new BoundingBox();
113 | result.x = Math.max(Math.round(x + dx - s1), 0);
114 | result.y = Math.max(Math.round(y + dy - s2), 0);
115 | result.width = Math.round(width * s);
116 | result.height = Math.round(height * s);
117 |
118 | Log.i(Util.TAG, "Current BB: " + this + ", Predicted BB: " + result);
119 |
120 | return result;
121 | }
122 |
123 |
124 | BoundingBox intersect(final Mat img){
125 | final BoundingBox result = new BoundingBox();
126 | result.x = Math.max(x, 0);
127 | result.y = Math.max(y, 0);
128 | result.width = (int) Math.min(Math.min(img.cols() - x, width), Math.min(width, br().x));
129 | result.height = (int) Math.min(Math.min(img.rows() - y, height), Math.min(height, br().y));
130 | return result;
131 | }
132 |
133 | @Override
134 | public String toString(){
135 | return "(" + x + ", " + y + ", " + width + ", " + height + " / " + overlap + ", " + scaleIdx + ")";
136 | }
137 |
138 | @Override
139 | public int hashCode() {
140 | final int prime = 31;
141 | int result = super.hashCode();
142 | result = prime * result + Float.floatToIntBits(overlap);
143 | result = prime * result + scaleIdx;
144 | return result;
145 | }
146 |
147 | @Override
148 | public boolean equals(Object obj) {
149 | if (this == obj)
150 | return true;
151 | if (!super.equals(obj))
152 | return false;
153 | if (getClass() != obj.getClass())
154 | return false;
155 | BoundingBox other = (BoundingBox) obj;
156 | if (Float.floatToIntBits(overlap) != Float
157 | .floatToIntBits(other.overlap))
158 | return false;
159 | if (scaleIdx != other.scaleIdx)
160 | return false;
161 | return true;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/FernEnsembleClassifier.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.List;
20 | import java.util.Properties;
21 |
22 | import org.opencv.core.Mat;
23 | import org.opencv.core.Size;
24 |
25 | import android.util.Log;
26 |
27 | import com.trandi.opentld.tld.Parameters.ParamsClassifiers;
28 | import com.trandi.opentld.tld.Util.Pair;
29 | import com.trandi.opentld.tld.Util.RNG;
30 |
31 |
32 | class FernEnsembleClassifier {
33 | ParamsClassifiers params;
34 | private Fern[] ferns;
35 |
36 | // final List pExamples = new ArrayList();
37 | // final List nExamples = new ArrayList();
38 |
39 | FernEnsembleClassifier(){
40 | }
41 |
42 | FernEnsembleClassifier(Properties props) {
43 | params = new ParamsClassifiers(props);
44 | }
45 |
46 |
47 | void init(Size[] scales, RNG rng){
48 | ferns = new Fern[params.numFerns];
49 | for(int i=0; i to the average of negative posteriors
58 | */
59 | void evaluateThreshold(final List> nFernsTest){
60 | for(Pair fern : nFernsTest){
61 | // here we know/hope that fern.second is always FALSE, as they are all NEGATIVE examples
62 | final double averagePosterior = averagePosterior(fern.first);
63 | if(averagePosterior > params.pos_thr_fern){
64 | params.pos_thr_fern = averagePosterior;
65 | }
66 | }
67 | }
68 |
69 |
70 | void trainF(final List> ferns, int resample){
71 | for(int i = 0; i < resample; i++){
72 | for(Pair fern : ferns){
73 | // the THRESHOLDS are here to make sure we don't increase/decrease the probabilities beyond given limits, to give other hashCodes a chance
74 | if(fern.second){ // if it's a positive fern
75 | if(averagePosterior(fern.first) <= params.pos_thr_fern){
76 | updatePosteriors(fern.first, true);
77 | }
78 | }else if(averagePosterior(fern.first) >= params.neg_thr_fern){
79 | updatePosteriors(fern.first, false);
80 | }
81 | }
82 | }
83 | }
84 |
85 | private void updatePosteriors(final int[] fernsHashCodes, boolean positive){
86 | assert(params.numFerns == fernsHashCodes.length);
87 |
88 | for(int fern = 0; fern < fernsHashCodes.length; fern++){
89 | ferns[fern].addCountUpdatePosteriors(fernsHashCodes[fern], positive);
90 | }
91 | }
92 |
93 |
94 | /**
95 | * @return conf
96 | */
97 | double averagePosterior(final int[] fernsHashCodes){
98 | assert(params.numFerns == fernsHashCodes.length);
99 |
100 | double result = 0;
101 | for(int fern = 0; fern < fernsHashCodes.length; fern++){
102 | result += ferns[fern].posteriorProbabilities[fernsHashCodes[fern]];
103 | }
104 | return result / fernsHashCodes.length;
105 | }
106 |
107 |
108 | /**
109 | * The numbers in this array can be up to 2^params.structSize as we shift left once of each feature
110 | */
111 | int[] getAllFernsHashCodes(final Mat patch, int scaleIdx){
112 | final int[] result = new int[ferns.length];
113 | final byte[] imageData = Util.getByteArray(patch);
114 | final int cols = patch.cols();
115 | for(int fern = 0; fern < ferns.length; fern++){
116 | result[fern] = ferns[fern].calculateHashCode(scaleIdx, imageData, cols);
117 | }
118 |
119 | return result;
120 | }
121 |
122 |
123 |
124 | static class Fern {
125 | private final Feature[][] features; // per scaleIdx
126 | // per HASHCODE
127 | final double[] posteriorProbabilities; // the probability that it's our image
128 | final long[] nCounter; // the number of NEGATIVE patches
129 | final long[] pCounter; // the number of POSITIVE patches
130 |
131 |
132 | Fern(int featuresPerFern, Size[] scales, RNG rng) {
133 | // 1. Define random features
134 | features = new Feature[scales.length][featuresPerFern];
135 | for (int i=0; i= patch.length || pos2 >= patch.length) {
200 | Log.w(Util.TAG, "Bad patch of size: " + patch.length + " cols: " + cols + " to compare Feature: " + this.toString());
201 | return 0;
202 | }
203 |
204 | final boolean boolRes = patch[pos1] > patch[pos2];
205 | return boolRes ? 1 : 0;
206 | }
207 |
208 | @Override
209 | public String toString(){
210 | return x1 + ", " + y1 + ", " + x2 + ", " + y2;
211 | }
212 | }
213 |
214 |
215 | int getNumFerns(){
216 | return params.numFerns;
217 | }
218 |
219 | double getFernPosThreshold(){
220 | return params.pos_thr_fern;
221 | }
222 |
223 |
224 |
225 | // TODO use to display the positive examples used by learning...
226 | // public Mat getPosExamples(){
227 | // if(pExamples == null || pExamples.size() == 0) return null;
228 | //
229 | // final int exRows = pExamples.get(0).rows();
230 | // final int exCols = pExamples.get(0).cols();
231 | //
232 | // // create a Matrix that can contain vertically all the positive examples
233 | // final Mat result = new Mat(pExamples.size() * exRows, exCols, CvType.CV_8U);
234 | // Imgproc.
235 | // }
236 | }
237 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/Grid.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Comparator;
21 | import java.util.Iterator;
22 | import java.util.List;
23 |
24 | import org.opencv.core.Mat;
25 | import org.opencv.core.Rect;
26 | import org.opencv.core.Size;
27 |
28 | import android.util.Log;
29 |
30 |
31 |
32 | class Grid implements Iterable{
33 | static final float GOOD_OVERLAP = 0.6f;
34 | static final float BAD_OVERLAP = 0.2f;
35 |
36 | private static final float SHIFT = 0.1f;
37 | private static final float[] SCALES = {
38 | 0.16151f, 0.19381f, 0.23257f, 0.27908f, 0.33490f, 0.40188f, 0.48225f,
39 | 0.57870f, 0.69444f, 0.83333f, 1f, 1.20000f, 1.44000f, 1.72800f,
40 | 2.07360f, 2.48832f, 2.98598f, 3.58318f, 4.29982f, 5.15978f, 6.19174f};
41 |
42 |
43 | final List grid = new ArrayList();
44 | private final List trackedBoxScales = new ArrayList();
45 | final List goodBoxes = new ArrayList(); //bboxes with overlap > GOOD_OVERLAP
46 | final private List badBoxes = new ArrayList(); //bboxes with overlap < BAD_OVERLAP
47 | BoundingBox bbHull = new BoundingBox(); // hull of good_boxes
48 | BoundingBox bestBox; // maximum overlapping bbox
49 |
50 | Grid(){
51 | }
52 |
53 |
54 | Grid(Mat img, Rect trackedBox, int minWinSide){
55 | // TODO why do we generate so many BAD boxes, only to remove them later on !?
56 | // OR do we need them to re-asses which ones are bad later on ?
57 | for(int s=0; s= minWinSide && width <= img.cols() && height <= img.rows()){
64 | trackedBoxScales.add(new Size(width, height));
65 | final int shift = Math.round(SHIFT * minBbSide);
66 |
67 | for(int row=1; row<(img.rows() - height); row+=shift){
68 | for(int col=1; col<(img.cols() - width); col+=shift){
69 | final BoundingBox bbox = new BoundingBox();
70 | bbox.x = col;
71 | bbox.y = row;
72 | bbox.width = width;
73 | bbox.height = height;
74 | bbox.scaleIdx = trackedBoxScales.size() - 1; // currently last one in this list
75 |
76 | grid.add(bbox);
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
83 |
84 | /**
85 | * goodBoxes OUTPUT
86 | * badBoxes OUTPUT
87 | *
88 | * This should be called AFTER updateOverlap(lastBox) so that the overlap numbers are relative to this lastBox, NOT the initial one...
89 | */
90 | void updateGoodBadBoxes(final Rect trackedBox, final int numClosest) {
91 | // start by updating the overlap numbers
92 | for(BoundingBox box : grid){
93 | box.overlap = box.calcOverlap(trackedBox);
94 | }
95 |
96 | goodBoxes.clear();
97 | badBoxes.clear();
98 |
99 | float maxOverlap = 0f;
100 | for(BoundingBox box : grid){
101 | if(box.overlap > maxOverlap){
102 | maxOverlap = box.overlap;
103 | bestBox = box;
104 | }
105 |
106 | if(box.overlap > GOOD_OVERLAP){
107 | goodBoxes.add(box);
108 | }else if(box.overlap < BAD_OVERLAP){
109 | badBoxes.add(box);
110 | }
111 | }
112 |
113 | // keep only the best numClosest (10) items in goodBoxes
114 | Util.keepBestN(goodBoxes, numClosest, new Comparator(){
115 | @Override
116 | public int compare(BoundingBox bb1, BoundingBox bb2) {
117 | return Float.valueOf(bb1.overlap).compareTo(bb2.overlap);
118 | }
119 | });
120 |
121 | Log.i(Util.TAG, "Found " + goodBoxes.size() + " good boxes, " + badBoxes.size() + " bad boxes.");
122 | Log.i(Util.TAG, "Best Box: " + bestBox);
123 |
124 | updateBBHull();
125 | Log.i(Util.TAG, "Bounding box hull " + bbHull);
126 | }
127 |
128 |
129 | private void updateBBHull(){
130 | //if(goodBoxes.isEmpty()) throw new IllegalStateException("Can't Calculate the BBHull without at least 1 good box !");
131 | int x1 = Integer.MAX_VALUE, x2 = 0;
132 | int y1 = Integer.MAX_VALUE, y2 = 0;
133 | for (BoundingBox goodBox : goodBoxes) {
134 | x1 = Math.min(goodBox.x, x1);
135 | y1 = Math.min(goodBox.y, y1);
136 | x2 = Math.max(goodBox.x + goodBox.width, x2);
137 | y2 = Math.max(goodBox.y + goodBox.height, y2);
138 | }
139 |
140 | bbHull.x = x1;
141 | bbHull.y = y1;
142 | bbHull.width = x2 - x1;
143 | bbHull.height = y2 - y1;
144 | }
145 |
146 |
147 | BoundingBox[] getGoodBoxes(){
148 | return goodBoxes.toArray(new BoundingBox[goodBoxes.size()]);
149 | }
150 |
151 | BoundingBox[] getBadBoxes(){
152 | return badBoxes.toArray(new BoundingBox[badBoxes.size()]);
153 | }
154 |
155 | BoundingBox getBestBox(){
156 | return bestBox;
157 | }
158 |
159 | BoundingBox getBBhull(){
160 | return bbHull;
161 | }
162 |
163 | Size[] getTrackedBoxScales(){
164 | return trackedBoxScales.toArray(new Size[trackedBoxScales.size()]);
165 | }
166 |
167 | public int getSize(){
168 | return grid.size();
169 | }
170 |
171 | BoundingBox getBox(int idx){
172 | return grid.get(idx);
173 | }
174 |
175 |
176 | @Override
177 | public Iterator iterator() {
178 | return grid.iterator();
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/LKTracker.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | import org.opencv.core.CvType;
23 | import org.opencv.core.Mat;
24 | import org.opencv.core.MatOfByte;
25 | import org.opencv.core.MatOfFloat;
26 | import org.opencv.core.MatOfPoint2f;
27 | import org.opencv.core.Point;
28 | import org.opencv.core.Size;
29 | import org.opencv.core.TermCriteria;
30 | import org.opencv.imgproc.Imgproc;
31 | import org.opencv.video.Video;
32 |
33 | import android.util.Log;
34 |
35 | import com.trandi.opentld.tld.Util.Pair;
36 |
37 | class LKTracker {
38 | private static final int MAX_COUNT = 20;
39 | private static final double EPSILON = 0.03;
40 | private static final Size WINDOW_SIZE = new Size(4, 4);
41 | private static final int MAX_LEVEL = 5;
42 | private static final float LAMBDA = 0f; // minEigenThreshold
43 | private static final Size CROSS_CORR_PATCH_SIZE = new Size(10, 10);
44 |
45 | private final TermCriteria termCriteria;
46 | float errFBMed;
47 |
48 |
49 |
50 | LKTracker(){
51 | termCriteria = new TermCriteria(TermCriteria.COUNT + TermCriteria.EPS, MAX_COUNT, EPSILON);
52 | }
53 |
54 |
55 | /**
56 | * @return Pair of new, FILTERED, last and current POINTS, or null if it hasn't managed to track anything.
57 | */
58 | Pair track(final Mat lastImg, final Mat currentImg, Point[] lastPoints){
59 | final int size = lastPoints.length;
60 | final MatOfPoint2f currentPointsMat = new MatOfPoint2f();
61 | final MatOfPoint2f pointsFBMat = new MatOfPoint2f();
62 | final MatOfByte statusMat = new MatOfByte();
63 | final MatOfFloat errSimilarityMat = new MatOfFloat();
64 | final MatOfByte statusFBMat = new MatOfByte();
65 | final MatOfFloat errSimilarityFBMat = new MatOfFloat();
66 |
67 | //Forward-Backward tracking
68 | Video.calcOpticalFlowPyrLK(lastImg, currentImg, new MatOfPoint2f(lastPoints), currentPointsMat,
69 | statusMat, errSimilarityMat, WINDOW_SIZE, MAX_LEVEL, termCriteria, 0, LAMBDA);
70 | Video.calcOpticalFlowPyrLK(currentImg, lastImg, currentPointsMat, pointsFBMat,
71 | statusFBMat, errSimilarityFBMat, WINDOW_SIZE, MAX_LEVEL, termCriteria, 0, LAMBDA);
72 |
73 | final byte[] status = statusMat.toArray();
74 | float[] errSimilarity = new float[lastPoints.length];
75 | //final byte[] statusFB = statusFBMat.toArray();
76 | final float[] errSimilarityFB = errSimilarityFBMat.toArray();
77 |
78 | // compute the real FB error (relative to LAST points not the current ones...
79 | final Point[] pointsFB = pointsFBMat.toArray();
80 | for(int i = 0; i < size; i++){
81 | errSimilarityFB[i] = Util.norm(pointsFB[i], lastPoints[i]);
82 | }
83 |
84 | final Point[] currPoints = currentPointsMat.toArray();
85 | // compute real similarity error
86 | errSimilarity = normCrossCorrelation(lastImg, currentImg, lastPoints, currPoints, status);
87 |
88 |
89 | //TODO errSimilarityFB has problem != from C++
90 | // filter out points with fwd-back error > the median AND points with similarity error > median
91 | return filterPts(lastPoints, currPoints, errSimilarity, errSimilarityFB, status);
92 | }
93 |
94 |
95 | /**
96 | * @return real similarities errors
97 | */
98 | private float[] normCrossCorrelation(final Mat lastImg, final Mat currentImg, final Point[] lastPoints, final Point[] currentPoints, final byte[] status){
99 | final float[] similarity = new float[lastPoints.length];
100 |
101 | final Mat lastPatch = new Mat(CROSS_CORR_PATCH_SIZE, CvType.CV_8U);
102 | final Mat currentPatch = new Mat(CROSS_CORR_PATCH_SIZE, CvType.CV_8U);
103 | final Mat res = new Mat(new Size(1, 1), CvType.CV_32F);
104 |
105 | for(int i = 0; i < lastPoints.length; i++){
106 | if(status[i] == 1){
107 | Imgproc.getRectSubPix(lastImg, CROSS_CORR_PATCH_SIZE, lastPoints[i], lastPatch);
108 | Imgproc.getRectSubPix(currentImg, CROSS_CORR_PATCH_SIZE, currentPoints[i], currentPatch);
109 | Imgproc.matchTemplate(lastPatch, currentPatch, res, Imgproc.TM_CCOEFF_NORMED);
110 |
111 | similarity[i] = Util.getFloat(0, 0, res);
112 | }else{
113 | similarity[i] = 0f;
114 | }
115 | }
116 |
117 | return similarity;
118 | }
119 |
120 |
121 | /**
122 | * @return Pair of new, FILTERED, last and current POINTS. Null if none were valid (with similarity > median and FB error <= median)
123 | */
124 | private Pair filterPts(final Point[] lastPoints, final Point[] currentPoints, final float[] similarity, final float[] errFB, final byte[] status){
125 | final List filteredLastPoints = new ArrayList();
126 | final List filteredCurrentPoints = new ArrayList();
127 | final List filteredErrFB = new ArrayList();
128 |
129 | final float similarityMed = Util.median(similarity);
130 | Log.i(Util.TAG, "Filter points MED SIMILARITY: " + similarityMed);
131 |
132 | for(int i = 0; i < currentPoints.length; i++){
133 | if(status[i] == 1 && similarity[i] > similarityMed){
134 | filteredLastPoints.add(lastPoints[i]);
135 | filteredCurrentPoints.add(currentPoints[i]);
136 | filteredErrFB.add(errFB[i]);
137 | }
138 | }
139 |
140 | final List filteredLastPoints2 = new ArrayList();
141 | final List filteredCurrentPoints2 = new ArrayList();
142 | if(filteredErrFB.size() > 0){
143 | errFBMed = Util.median(filteredErrFB);
144 |
145 | for(int i = 0; i < filteredErrFB.size(); i++){
146 | // status has already been checked
147 | if(filteredErrFB.get(i) <= errFBMed){
148 | filteredLastPoints2.add(filteredLastPoints.get(i));
149 | filteredCurrentPoints2.add(filteredCurrentPoints.get(i));
150 | }
151 | }
152 |
153 | Log.i(Util.TAG, "Filter points MED ErrFB: " + errFBMed + " K count=" + filteredLastPoints2.size());
154 | }
155 |
156 | final int size = filteredLastPoints2.size();
157 | return size > 0 ? new Pair(filteredLastPoints2.toArray(new Point[size]), filteredCurrentPoints2.toArray(new Point[size])) : null;
158 | }
159 |
160 | float getMedianErrFB(){
161 | return errFBMed;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/NNClassifier.java:
--------------------------------------------------------------------------------
1 | package com.trandi.opentld.tld;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Properties;
8 |
9 | import org.opencv.core.CvType;
10 | import org.opencv.core.Mat;
11 | import org.opencv.imgproc.Imgproc;
12 |
13 | import android.util.Log;
14 |
15 | import com.trandi.opentld.tld.Parameters.ParamsClassifiers;
16 | import com.trandi.opentld.tld.Tld.DetectionStruct;
17 | import com.trandi.opentld.tld.Util.IsinStruct;
18 | import com.trandi.opentld.tld.Util.NNConfStruct;
19 |
20 | /**
21 | *
22 | * Nearest Neighbour classifier
23 | *
24 | */
25 | public class NNClassifier {
26 | ParamsClassifiers params;
27 |
28 | final List pExamples = new ArrayList();
29 | final List nExamples = new ArrayList();
30 |
31 | NNClassifier(Properties props) {
32 | params = new ParamsClassifiers(props);
33 | }
34 |
35 | /**
36 | * OUTPUT (updates) : pExamples, nExamples
37 | */
38 | void trainNN(final Mat pExampleIn, final List nExamplesIn){
39 | NNConfStruct nnConf = nnConf(pExampleIn);
40 | if(nnConf.relativeSimilarity <= params.pos_thr_nn){
41 | if(nnConf.isin == null || nnConf.isin.idxPosSet < 0){
42 | pExamples.clear();
43 | }
44 | pExamples.add(pExampleIn);
45 | }
46 |
47 | for(Mat nEx : nExamplesIn){
48 | nnConf = nnConf(nEx);
49 | if(nnConf.relativeSimilarity > params.neg_thr_nn){
50 | nExamples.add(nEx);
51 | }
52 | }
53 |
54 | Log.i(Util.TAG, "Trained NN examples: " + pExamples.size() + " positive " + nExamples.size() + " negative");
55 | }
56 |
57 |
58 | /**
59 | * INPUTs : pExamples, nExamples
60 | * @param example NN patch
61 | * @return Relative Similarity (rsconf), Conservative Similarity (csconf), In pos. set|Id pos set|In neg. set (isin)
62 | */
63 | NNConfStruct nnConf(final Mat example) {
64 | if(example == null){
65 | Log.e(Util.TAG, "NNClass.nnConf() - Null example received, stop here");
66 | return new NNConfStruct(null, 0, 0);
67 | }
68 | if(pExamples.isEmpty()){
69 | // IF positive examples in the model are not defined THEN everything is negative
70 | return new NNConfStruct(null, 0, 0);
71 | }
72 |
73 | if(nExamples.isEmpty()){
74 | // IF negative examples in the model are not defined THEN everything is positive
75 | return new NNConfStruct(null, 1, 1);
76 | }
77 |
78 | final Mat ncc = new Mat(1, 1, CvType.CV_32F);
79 | float nccP=0, csmaxP=0, maxP=0;
80 | boolean anyP = false;
81 | int maxPidx = 0;
82 | final int validatedPart = (int) Math.ceil(pExamples.size() * params.valid);
83 | for(int i = 0; i < pExamples.size(); i++){
84 | Imgproc.matchTemplate(pExamples.get(i), example, ncc, Imgproc.TM_CCORR_NORMED); // measure NCC to positive examples
85 | nccP = (Util.getFloat(0, 0, ncc) + 1) * 0.5f;
86 | if(nccP > params.ncc_thesame){
87 | anyP = true;
88 | }
89 | if(nccP > maxP){
90 | maxP = nccP;
91 | maxPidx = i;
92 | if(i < validatedPart){
93 | csmaxP = maxP;
94 | }
95 | }
96 | }
97 |
98 | float nccN=0, maxN = 0;
99 | boolean anyN = false;
100 | for(int i = 0; i < nExamples.size(); i++){
101 | Imgproc.matchTemplate(nExamples.get(i), example, ncc, Imgproc.TM_CCORR_NORMED); //measure NCC to negative examples
102 | nccN = (Util.getFloat(0, 0, ncc) + 1) * 0.5f;
103 | if(nccN > params.ncc_thesame){
104 | anyN = true;
105 | }
106 | if(nccN > maxN){
107 | maxN = nccN;
108 | }
109 | }
110 |
111 |
112 | //Log.i(Util.TAG, "nccP=" + nccP + ", nccN=" + nccN + ", csmaxP=" + csmaxP + ", maxP="+ maxP + ", maxN=" + maxN);
113 |
114 | // put together the result
115 | final float dN = 1 - maxN;
116 | final float dPrelative = 1 - maxP;
117 | final float dPconservative = 1 - csmaxP;
118 | return new NNConfStruct(new IsinStruct(anyP, maxPidx, anyN), dN / (dN + dPrelative), dN / (dN + dPconservative));
119 | }
120 |
121 |
122 |
123 | /**
124 | * Updates NN threshold in case the negative threshold are above them.
125 | * The Pos threshold has to be > to the negative one.
126 | */
127 | void evaluateThreshold(final List nExamplesTest){
128 | for(Mat ex : nExamplesTest){
129 | final NNConfStruct nnConf = nnConf(ex);
130 | if(nnConf.relativeSimilarity > params.pos_thr_nn){
131 | params.pos_thr_nn = nnConf.relativeSimilarity;
132 | }
133 | }
134 | if(params.pos_thr_nn > params.pos_thr_nn_valid){
135 | params.pos_thr_nn_valid = params.pos_thr_nn;
136 | }
137 | }
138 |
139 |
140 |
141 |
142 | float getNNThreshold(){
143 | return params.pos_thr_nn;
144 | }
145 |
146 | float getNNThresholdValid(){
147 | return params.pos_thr_nn_valid;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/Parameters.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.Properties;
20 |
21 | class Parameters {
22 | protected final Properties props;
23 |
24 | static class ParamsTld extends Parameters{
25 | int min_win;
26 | int patch_size;
27 |
28 | // initial parameters for positive examples
29 | int num_closest_init;
30 | int num_warps_init;
31 | int noise_init;
32 | float angle_init;
33 | float shift_init;
34 | float scale_init;
35 |
36 | // update parameters for positive examples
37 | int num_closest_update;
38 | int num_warps_update;
39 | int noise_update;
40 | float angle_update;
41 | float shift_update;
42 | float scale_update;
43 |
44 | // parameters for negative examples
45 | float num_bad_patches;
46 |
47 |
48 | float tracker_stability_FBerrMax;
49 |
50 | protected ParamsTld(){
51 | super(null);
52 | }
53 |
54 | ParamsTld(Properties props) {
55 | super(props);
56 |
57 | // Bounding Box Parameters
58 | min_win = getInt("min_win");
59 |
60 | // Generator Parameters
61 | // initial parameters for positive examples
62 | patch_size = getInt("patch_size");
63 | num_closest_init = getInt("num_closest_init");
64 | num_warps_init = getInt("num_warps_init");
65 | noise_init = getInt("noise_init");
66 | angle_init = getFloat("angle_init");
67 | shift_init = getFloat("shift_init");
68 | scale_init = getFloat("scale_init");
69 | // update parameters for positive examples
70 | num_closest_update = getInt("num_closest_update");
71 | num_warps_update = getInt("num_warps_update");
72 | noise_update = getInt("noise_update");
73 | angle_update = getFloat("angle_update");
74 | shift_update = getFloat("shift_update");
75 | scale_update = getFloat("scale_update");
76 | // parameters for negative examples
77 | num_bad_patches = getInt("num_bad_patches");
78 |
79 | tracker_stability_FBerrMax = getFloat("tracker_stability_FBerrMax");
80 | }
81 | }
82 |
83 |
84 | static class ParamsClassifiers extends Parameters {
85 | double pos_thr_fern;
86 | float neg_thr_fern;
87 | int numFeaturesPerFern;
88 | int numFerns;
89 | float valid;
90 | float ncc_thesame;
91 | float pos_thr_nn;
92 | float pos_thr_nn_valid;
93 | float neg_thr_nn;
94 |
95 | ParamsClassifiers(){
96 | super(null);
97 | }
98 |
99 | ParamsClassifiers(Properties props) {
100 | super(props);
101 |
102 | valid = getFloat("valid");
103 | ncc_thesame = getFloat("ncc_thesame");
104 | numFerns = getInt("num_ferns");
105 | numFeaturesPerFern = getInt("num_features_per_fern");
106 | pos_thr_fern = getFloat("pos_thr_fern");
107 | neg_thr_fern = getFloat("neg_thr_fern", 0.3f);
108 | pos_thr_nn = getFloat("pos_thr_nn");
109 | pos_thr_nn_valid = getFloat("pos_thr_nn_valid");
110 | neg_thr_nn = getFloat("neg_thr_nn", 0.5f);
111 | }
112 | }
113 |
114 |
115 | Parameters(Properties props) {
116 | this.props = props;
117 | }
118 |
119 | protected int getInt(String propName){
120 | if(props.containsKey(propName)){
121 | return Integer.valueOf(props.getProperty(propName));
122 | }
123 |
124 | throw new IllegalArgumentException("Parameter " + propName + " has NOT been provided.");
125 | }
126 |
127 | protected float getFloat(String propName){
128 | if(props.containsKey(propName)){
129 | return Float.valueOf(props.getProperty(propName));
130 | }
131 |
132 | throw new IllegalArgumentException("Parameter " + propName + " has NOT been provided.");
133 | }
134 |
135 | protected float getFloat(String propName, float defaultValue){
136 | if(props.containsKey(propName)){
137 | return Float.valueOf(props.getProperty(propName));
138 | }
139 |
140 | return defaultValue;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/PatchGenerator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import org.opencv.core.Core;
20 | import org.opencv.core.CvType;
21 | import org.opencv.core.Mat;
22 | import org.opencv.core.MatOfDouble;
23 | import org.opencv.core.Point;
24 | import org.opencv.core.Scalar;
25 | import org.opencv.core.Size;
26 | import org.opencv.imgproc.Imgproc;
27 |
28 | import com.trandi.opentld.tld.Util.RNG;
29 |
30 | class PatchGenerator {
31 | final double backgroundMin;
32 | final double backgroundMax;
33 | final double noiseRange;
34 | final boolean randomBlur;
35 | final double lambdaMin;
36 | final double lambdaMax;
37 | final double thetaMin;
38 | final double thetaMax;
39 | final double phiMin;
40 | final double phiMax;
41 |
42 |
43 | PatchGenerator(double backgroundMin, double backgroundMax, double noiseRange, boolean randomBlur,
44 | double lambdaMin, double lambdaMax, double thetaMin, double thetaMax, double phiMin, double phiMax )
45 | {
46 | this.backgroundMin = backgroundMin;
47 | this.backgroundMax = backgroundMax;
48 | this.noiseRange = noiseRange;
49 | this.randomBlur = randomBlur;
50 | this.lambdaMin = lambdaMin;
51 | this.lambdaMax = lambdaMax;
52 | this.thetaMin = thetaMin;
53 | this.thetaMax = thetaMax;
54 | this.phiMin = phiMin;
55 | this.phiMax = phiMax;
56 | }
57 |
58 | void generate(final Mat image, Point pt, Mat patch, Size patchSize, final RNG rng) {
59 | final Mat T = new MatOfDouble();
60 |
61 | // TODO why is inverse not specified in the original C++ code
62 | generateRandomTransform(pt, new Point((patchSize.width - 1) * 0.5, (patchSize.height - 1) * 0.5), T, false);
63 |
64 | generate(image, T, patch, patchSize, rng);
65 | }
66 |
67 |
68 | /**
69 | *
70 | * @param image
71 | * @param T
72 | * @param patch OUTPUT
73 | * @param patchSize
74 | */
75 | void generate(final Mat image, final Mat T, Mat patch, Size patchSize, final RNG rng){
76 | patch.create( patchSize, image.type() );
77 | if( backgroundMin != backgroundMax ) {
78 | Core.randu(patch, backgroundMin, backgroundMax);
79 | // TODO if that null scalar OK or should it be new Scalar(0) ?
80 | Imgproc.warpAffine(image, patch, T, patchSize, Imgproc.INTER_LINEAR, Imgproc.BORDER_TRANSPARENT, null);
81 | } else {
82 | Imgproc.warpAffine(image, patch, T, patchSize, Imgproc.INTER_LINEAR, Imgproc.BORDER_CONSTANT, new Scalar(backgroundMin));
83 | }
84 |
85 | int ksize = randomBlur ? rng.nextInt() % 9 - 5 : 0;
86 | if( ksize > 0 ) {
87 | ksize = ksize * 2 + 1;
88 | Imgproc.GaussianBlur(patch, patch, new Size(ksize, ksize), 0, 0);
89 | }
90 |
91 | if( noiseRange > 0 ) {
92 | final Mat noise = new Mat(patchSize, image.type());
93 | int delta = (image.depth() == CvType.CV_8U ? 128 : (image.depth() == CvType.CV_16U ? 32768 : 0));
94 | Core.randn(noise, delta, noiseRange);
95 |
96 | // TODO this was different !!
97 | Core.addWeighted(patch, 1, noise, 1, -delta, patch);
98 |
99 | // if( backgroundMin != backgroundMax )
100 | // addWeighted(patch, 1, noise, 1, -delta, patch);
101 | // else
102 | // {
103 | // for( int i = 0; i < patchSize.height; i++ )
104 | // {
105 | // uchar* prow = patch.ptr(i);
106 | // const uchar* nrow = noise.ptr(i);
107 | // for( int j = 0; j < patchSize.width; j++ )
108 | // if( prow[j] != backgroundMin )
109 | // prow[j] = saturate_cast(prow[j] + nrow[j] - delta);
110 | // }
111 | // }
112 | }
113 | }
114 |
115 |
116 |
117 | /**
118 | *
119 | * @param srcCenter
120 | * @param dstCenter
121 | * @param transform OUTPUT
122 | * @param inverse
123 | */
124 | private void generateRandomTransform(Point srcCenter, Point dstCenter, Mat transform, boolean inverse) {
125 | MatOfDouble tempRand = new MatOfDouble(0d, 0d);
126 | Core.randu(tempRand, lambdaMin, lambdaMax);
127 | final double[] rands = tempRand.toArray();
128 | final double lambda1 = rands[0];
129 | final double lambda2 = rands[1];
130 | Core.randu(tempRand, thetaMin, thetaMax);
131 | final double theta = tempRand.toArray()[0];
132 | Core.randu(tempRand, phiMin, phiMax);
133 | final double phi = tempRand.toArray()[0];
134 |
135 |
136 | // Calculate random parameterized affine transformation A,
137 | // A = T(patch center) * R(theta) * R(phi)' * S(lambda1, lambda2) * R(phi) * T(-pt)
138 | final double st = Math.sin(theta);
139 | final double ct = Math.cos(theta);
140 | final double sp = Math.sin(phi);
141 | final double cp = Math.cos(phi);
142 | final double c2p = cp*cp;
143 | final double s2p = sp*sp;
144 |
145 | final double A = lambda1*c2p + lambda2*s2p;
146 | final double B = (lambda2 - lambda1)*sp*cp;
147 | final double C = lambda1*s2p + lambda2*c2p;
148 |
149 | final double Ax_plus_By = A*srcCenter.x + B*srcCenter.y;
150 | final double Bx_plus_Cy = B*srcCenter.x + C*srcCenter.y;
151 |
152 | transform.create(2, 3, CvType.CV_64F);
153 | transform.put(0, 0, A*ct - B*st);
154 | transform.put(0, 1, B*ct - C*st);
155 | transform.put(0, 2, -ct*Ax_plus_By + st*Bx_plus_Cy + dstCenter.x);
156 | transform.put(1, 0, A*st + B*ct);
157 | transform.put(1, 1, B*st + C*ct);
158 | transform.put(1, 2, -st*Ax_plus_By - ct*Bx_plus_Cy + dstCenter.y);
159 |
160 | if( inverse ){
161 | Imgproc.invertAffineTransform(transform, transform);
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/Tld.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Arrays;
21 | import java.util.Collections;
22 | import java.util.Comparator;
23 | import java.util.HashMap;
24 | import java.util.Iterator;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Properties;
28 |
29 | import org.opencv.core.Core;
30 | import org.opencv.core.CvType;
31 | import org.opencv.core.Mat;
32 | import org.opencv.core.MatOfDouble;
33 | import org.opencv.core.Point;
34 | import org.opencv.core.Rect;
35 | import org.opencv.core.Scalar;
36 | import org.opencv.core.Size;
37 | import org.opencv.imgproc.Imgproc;
38 |
39 | import android.util.Log;
40 |
41 | import com.trandi.opentld.tld.Parameters.ParamsTld;
42 | import com.trandi.opentld.tld.Util.DefaultRNG;
43 | import com.trandi.opentld.tld.Util.NNConfStruct;
44 | import com.trandi.opentld.tld.Util.Pair;
45 | import com.trandi.opentld.tld.Util.RNG;
46 |
47 |
48 | public class Tld {
49 | private static final int MAX_DETECTED = 100;
50 |
51 |
52 | ParamsTld _params;
53 | FernEnsembleClassifier _classifierFern;
54 | NNClassifier _classifierNN;
55 | private final LKTracker _tracker = new LKTracker();
56 | private PatchGenerator _patchGenerator; // FIXME UNUSED, why !?
57 | private final RNG _rng = new DefaultRNG();
58 |
59 |
60 | // Integral Images
61 | private int _iiRows;
62 | private int _iiCols;
63 | private final Mat _iisum = new Mat();
64 | private final Mat _iisqsum = new Mat();
65 | // for performance reasons, duplicate the data directly in Java, to avoid too many native code invocations
66 | private int[] _iisumJava;
67 | private double[] _iisqsumJava;
68 | private float _var; // variance of the initial patch/box. Will be used by the 1st stage of the classifier.
69 |
70 | // Training data
71 | Mat _pExample = new Mat(); // positive NN example
72 | final List> _pFerns = new ArrayList>(); //positive ferns
73 | private final List _pPatterns = new ArrayList(); //positive patches to display
74 | private List _nExamples;
75 |
76 | // Last frame data
77 | private BoundingBox _lastbox;
78 | private boolean _learn = true;
79 |
80 | // Detector data
81 | private Map _fernDetectionNegDataForLearning = new HashMap(); // all ferns hash codes for a given bounding box
82 | final Map _boxClusterMap = new HashMap(); // the cluster to which each detected box belongs
83 |
84 | // Bounding Boxes Grid
85 | Grid _grid;
86 |
87 |
88 | public Tld(Properties parameters){
89 | _params = new ParamsTld(parameters);
90 | _classifierFern = new FernEnsembleClassifier(parameters);
91 | _classifierNN = new NNClassifier(parameters);
92 | _patchGenerator = new PatchGenerator(0, 0, _params.noise_init, true, 1 - _params.scale_init, 1 + _params.scale_init,
93 | -_params.angle_init * Math.PI / 180f, _params.angle_init * Math.PI / 180f,
94 | -_params.angle_init * Math.PI / 180f, _params.angle_init * Math.PI / 180f);
95 |
96 | _pExample.create(_params.patch_size, _params.patch_size, CvType.CV_64F);
97 | }
98 |
99 | protected Tld() {
100 | // for TESTING only
101 | }
102 |
103 | public void init(Mat frame1, Rect trackedBox) {
104 | // get Bounding boxes
105 | if(Math.min(trackedBox.width, trackedBox.height) < _params.min_win) {
106 | throw new IllegalArgumentException("Provided trackedBox: " + trackedBox + " is too small (min " + _params.min_win + ")");
107 | }
108 | _grid = new Grid(frame1, trackedBox, _params.min_win);
109 | Log.i(Util.TAG, "Init Created " + _grid.getSize() + " bounding boxes.");
110 | _grid.updateGoodBadBoxes(trackedBox, _params.num_closest_init);
111 |
112 | _iiRows = frame1.rows();
113 | _iiCols = frame1.cols();
114 | _iisum.create(_iiRows, _iiCols, CvType.CV_32F);
115 | _iisqsum.create(_iiRows, _iiCols, CvType.CV_64F);
116 |
117 | // correct bounding box
118 | _lastbox = _grid.getBestBox();
119 |
120 | _classifierFern.init(_grid.getTrackedBoxScales(), _rng);
121 |
122 |
123 | // generate DATA
124 | // generate POSITIVE DATA
125 | generatePositiveData(frame1, _params.num_warps_init, _grid);
126 |
127 | // Set variance threshold
128 | MatOfDouble stddev = new MatOfDouble();
129 | Core.meanStdDev(frame1.submat(_grid.getBestBox()), new MatOfDouble(), stddev);
130 | updateIntegralImgs(frame1);
131 | // this is directly half of the variance of the initial box, which will be used the the 1st stage of the classifier
132 | _var = (float)Math.pow(stddev.toArray()[0], 2d) * 0.5f;
133 | // check variance
134 | final double checkVar = Util.getVar(_grid.getBestBox(), _iisumJava, _iisqsumJava, _iiCols) * 0.5;
135 | Log.i(Util.TAG, "Variance: " + _var + " / Check variance: " + checkVar);
136 |
137 |
138 | // generate NEGATIVE DATA
139 | final Pair>, List> negData = generateNegativeData(frame1);
140 |
141 | // Split Negative Ferns into Training and Testing sets (they are already shuffled)
142 | final int nFernsSize = negData.first.size();
143 | final List> nFernsTest = new ArrayList>(negData.first.subList(0, nFernsSize/2));
144 | final List> nFerns = new ArrayList>(negData.first.subList(nFernsSize/2, nFernsSize));
145 |
146 | // Split Negative NN Examples into Training and Testing sets
147 | final int nExSize = negData.second.size();
148 | final List nExamplesTest = new ArrayList(negData.second.subList(0, nExSize/2));
149 | _nExamples = new ArrayList(negData.second.subList(nExSize/2, nExSize));
150 |
151 |
152 | //MERGE Negative Data with Positive Data and shuffle it
153 | final List> fernsData = new ArrayList>(_pFerns);
154 | fernsData.addAll(nFerns);
155 | Collections.shuffle(fernsData);
156 |
157 | // TRAINING
158 | Log.i(Util.TAG, "Init Start Training with " + fernsData.size() + " ferns, "
159 | + _nExamples.size() + " nExamples, " + nFernsTest.size() + " nFernsTest, " + nExamplesTest.size() + " nExamplesTest");
160 | _classifierFern.trainF(fernsData, 10);
161 | _classifierNN.trainNN(_pExample, _nExamples);
162 | // Threshold evaluation on testing sets
163 | _classifierFern.evaluateThreshold(nFernsTest);
164 | _classifierNN.evaluateThreshold(nExamplesTest);
165 | }
166 |
167 | private void updateIntegralImgs(Mat frame) {
168 | Imgproc.integral2(frame, _iisum, _iisqsum);
169 | // duplicate the data for performance reasons
170 | _iisumJava = Arrays.copyOf(Util.getIntArray(_iisum), _iiRows * _iiCols);
171 | _iisqsumJava = Arrays.copyOf(Util.getDoubleArray(_iisqsum), _iiRows * _iiCols);
172 | }
173 |
174 | public ProcessFrameStruct processFrame(final Mat lastImg, final Mat currentImg){
175 | // 1. TRACK
176 | TrackingStruct trackingStruct = null;
177 | if(_lastbox != null){
178 | trackingStruct = track(lastImg, currentImg, _lastbox);
179 | }
180 |
181 |
182 |
183 | // 2. DETECT
184 | final Pair, List> detStructs = detect(currentImg);
185 |
186 | // 3. INTEGRATION tracking with detection
187 | if(trackingStruct != null){
188 | _lastbox = trackingStruct.predictedBB;
189 | if(trackingStruct.conf > _classifierNN.getNNThresholdValid()){
190 | Log.i(Util.TAG, "Tracking confidence: " + trackingStruct.conf + " > " + " Threshold: " + _classifierNN.getNNThresholdValid() + " ===> WILL LEARN");
191 | _learn = true;
192 | }else{
193 | Log.i(Util.TAG, "Tracking confidence: " + trackingStruct.conf + " < " + " Threshold: " + _classifierNN.getNNThresholdValid() + " ===> WILL NOT LEARN");
194 | }
195 |
196 | Log.i(Util.TAG, "Tracked");
197 | if(detStructs != null){
198 | final Map clusters = clusterConfidentIndices(detStructs.second);// cluster detections
199 | Log.i(Util.TAG, "Found " + clusters.size() + " clusters");
200 | final Map confidentClusters = new HashMap();
201 | for(BoundingBox clusterBox : clusters.keySet()){
202 | // Get clusters that are far from tracker and with better confidence
203 | if(trackingStruct.predictedBB.calcOverlap(clusterBox) < 0.5 && clusters.get(clusterBox) > trackingStruct.conf){
204 | confidentClusters.put(clusterBox, clusters.get(clusterBox));
205 | }
206 | }
207 |
208 | if(confidentClusters.size() == 0){
209 | Log.i(Util.TAG, "NO NN confident cluster !");
210 | }else if(confidentClusters.size() == 1){
211 | Log.i(Util.TAG, "Detected better match (1 confident cluster), re-initialising tracker");
212 | _lastbox = confidentClusters.keySet().iterator().next(); //bbnext
213 | _learn = false;
214 | }else{
215 | Log.i(Util.TAG, "Plenty of confident clusters detected. Get mean of close detections (use nnMatches)");
216 | int cx=0,cy=0,cw=0,ch=0, close_detections=0;
217 | for(DetectionStruct detStruct : detStructs.second){
218 | if(trackingStruct.predictedBB.calcOverlap(detStruct.detectedBB) > 0.7){
219 | cx += detStruct.detectedBB.x;
220 | cy += detStruct.detectedBB.y;
221 | cw += detStruct.detectedBB.width;
222 | ch += detStruct.detectedBB.height;
223 | close_detections++;
224 | }
225 | }
226 |
227 | if(close_detections > 0){
228 | // weighted average (10 to 1 in favour of the tracked) trackers trajectory with the close detections
229 | _lastbox.x = Math.round((float)(10*trackingStruct.predictedBB.x+cx)/(float)(10+close_detections));
230 | _lastbox.y = Math.round((float)(10*trackingStruct.predictedBB.y+cy)/(float)(10+close_detections));
231 | _lastbox.width = Math.round((float)(10*trackingStruct.predictedBB.width+cw)/(float)(10+close_detections));
232 | _lastbox.height = Math.round((float)(10*trackingStruct.predictedBB.height+ch)/(float)(10+close_detections));
233 | }
234 | }
235 | }
236 | }else{ // IF NOT Tracking
237 | Log.w(Util.TAG, "NOT Tracking");
238 | _lastbox = null;
239 | _learn = false;
240 | if(detStructs != null){ // and detector is defined
241 | final Map clusters = clusterConfidentIndices(detStructs.second);// cluster detections
242 | if(clusters.size() == 1){
243 | // not tracking but detected exactly 1 cluster -> use this one as the best option
244 | _lastbox = clusters.keySet().iterator().next();
245 | }
246 | }
247 | }
248 |
249 |
250 | // 4. LEARN
251 | if(_learn){
252 | _learn = learn(currentImg, detStructs != null ? detStructs.first : null); // use the Fern classifier detected
253 | }else{
254 | Log.i(Util.TAG, "NOT Learning");
255 | }
256 |
257 |
258 |
259 |
260 | final Point[] lastPoints = (trackingStruct == null ? null : trackingStruct.lastPoints);
261 | final Point[] currentPoints = (trackingStruct == null ? null : trackingStruct.currentPoints);
262 | return new ProcessFrameStruct(lastPoints, currentPoints, _lastbox);
263 | }
264 |
265 |
266 |
267 |
268 |
269 | private TrackingStruct track(final Mat lastImg, final Mat currentImg, final BoundingBox lastBox) {
270 | Log.i(Util.TAG, "[TRACK]");
271 |
272 | // Generate points
273 | final Point[] lastPoints = lastBox.points();
274 | if(lastPoints.length == 0){
275 | Log.e(Util.TAG, "Points not generated from lastBox: " + lastBox);
276 | return null;
277 | }
278 |
279 |
280 | // Frame-to-frame tracking with forward-backward error checking
281 | final Pair trackedPoints = _tracker.track(lastImg, currentImg, lastPoints);
282 | if(trackedPoints == null){
283 | Log.e(Util.TAG, "No points could be tracked.");
284 | return null;
285 | }
286 | if(_tracker.getMedianErrFB() > _params.tracker_stability_FBerrMax){
287 | Log.w(Util.TAG, "TRACKER too unstable. FB Median error: " + _tracker.getMedianErrFB() + " > " + _params.tracker_stability_FBerrMax);
288 | // return null; // we hope the detection will find the pattern again
289 | }
290 |
291 | // bounding box prediction
292 | final BoundingBox predictedBB = lastBox.predict(trackedPoints.first, trackedPoints.second);
293 | if(predictedBB.x > currentImg.cols() || predictedBB.y > currentImg.rows()
294 | || predictedBB.br().x < 1 || predictedBB.br().y < 1)
295 | {
296 | Log.e(Util.TAG, "TRACKER Predicted bounding box out of range !");
297 | return null;
298 | }
299 |
300 | // estimate Confidence
301 | Mat pattern = new Mat();
302 | try{
303 | resizeZeroMeanStdev(currentImg.submat(predictedBB.intersect(currentImg)), pattern, _params.patch_size);
304 | }catch(Throwable t){
305 | Log.e(Util.TAG, "PredBB when failed: " + predictedBB);
306 | }
307 | //Log.i(Util.TAG, "Confidence " + pattern.dump());
308 |
309 | //Conservative Similarity
310 | final NNConfStruct nnConf = _classifierNN.nnConf(pattern);
311 | Log.i(Util.TAG, "Tracking confidence: " + nnConf.conservativeSimilarity);
312 |
313 | Log.i(Util.TAG, "[TRACK END]");
314 | return new TrackingStruct(nnConf.conservativeSimilarity, predictedBB, trackedPoints.first, trackedPoints.second);
315 | }
316 |
317 |
318 | /**
319 | * Structure the classifier into 3 stages:
320 | * a) patch variance
321 | * b) ensemble of ferns classifier
322 | * c) nearest neighbour
323 | */
324 | private Pair, List> detect(final Mat frame){
325 | Log.i(Util.TAG, "[DETECT]");
326 |
327 | final List fernClassDetected = new ArrayList(); //dt
328 | final List nnMatches = new ArrayList(); //dbb
329 |
330 |
331 | // 0. Cleaning
332 | _boxClusterMap.clear();
333 |
334 | // 1. DETECTION
335 | final Mat img = new Mat(frame.rows(), frame.cols(), CvType.CV_8U);
336 | updateIntegralImgs(frame);
337 | Imgproc.GaussianBlur(frame, img, new Size(9, 9), 1.5);
338 |
339 | // Apply the Variance filter TODO : Bottleneck
340 | int a=0;
341 | for(BoundingBox box : _grid){
342 | // a) speed up by doing the features/ferns check ONLY if the variance is high enough !
343 | if(Util.getVar(box, _iisumJava, _iisqsumJava, _iiCols) >= _var ){
344 | a++;
345 | final Mat patch = img.submat(box);
346 | final int[] allFernsHashCodes = _classifierFern.getAllFernsHashCodes(patch, box.scaleIdx);
347 | final double averagePosterior = _classifierFern.averagePosterior(allFernsHashCodes);
348 | _fernDetectionNegDataForLearning.put(box, allFernsHashCodes);// store for later use in learning
349 |
350 | // b)
351 | if(averagePosterior > _classifierFern.getFernPosThreshold()){
352 | fernClassDetected.add(new DetectionStruct(box, allFernsHashCodes, averagePosterior, patch));
353 | }
354 | }
355 | }
356 |
357 | Log.i(Util.TAG, a + " Bounding boxes passed the variance filter (" + _var + ")");
358 | Log.i(Util.TAG, fernClassDetected.size() + " Initial detected from Fern Classifier");
359 | if(fernClassDetected.size() == 0){
360 | Log.i(Util.TAG, "[DETECT END]");
361 | return null;
362 | }
363 |
364 | // keep only the best
365 | Util.keepBestN(fernClassDetected, MAX_DETECTED, new Comparator() {
366 | @Override
367 | public int compare(DetectionStruct detS1, DetectionStruct detS2) {
368 | return Double.compare(detS1.averagePosterior, detS2.averagePosterior);
369 | }
370 | });
371 |
372 |
373 | // 2. MATCHING using the NN classifier c)
374 | for(DetectionStruct detStruct : fernClassDetected){
375 | // update detStruct.patch to params.patch_size and normalise it
376 | Mat pattern = new Mat();
377 | resizeZeroMeanStdev(detStruct.patch, pattern, _params.patch_size);
378 | detStruct.nnConf = _classifierNN.nnConf(pattern);
379 |
380 | Log.i(Util.TAG, "NNConf: " + detStruct.nnConf.relativeSimilarity + " / " + detStruct.nnConf.conservativeSimilarity + " Threshold: " + _classifierNN.getNNThreshold());
381 | // only keep valid boxes
382 | if(detStruct.nnConf.relativeSimilarity > _classifierNN.getNNThreshold()){
383 | nnMatches.add(detStruct);
384 | }
385 | }
386 |
387 | Log.i(Util.TAG, "[DETECT END]");
388 | return new Pair, List>(fernClassDetected, nnMatches);
389 | }
390 |
391 |
392 | private boolean learn(final Mat img, final List fernClassDetected){
393 | Log.i(Util.TAG, "[LEARN]");
394 | Mat pattern = new Mat();
395 | final double stdev = resizeZeroMeanStdev(img.submat(_lastbox.intersect(img)), pattern, _params.patch_size);
396 | final NNConfStruct confStruct = _classifierNN.nnConf(pattern);
397 |
398 | if(confStruct.relativeSimilarity < 0.5){
399 | Log.w(Util.TAG, "Fast change, NOT learning");
400 | return false;
401 | }
402 | if(Math.pow(stdev, 2) < _var){
403 | Log.w(Util.TAG, "Low variance, NOT learning");
404 | return false;
405 | }
406 | if(confStruct.isin.inNegSet){
407 | Log.w(Util.TAG, "Patch in negative data, NOT learning");
408 | return false;
409 | }
410 |
411 | // Data generation
412 | _grid.updateGoodBadBoxes(_lastbox, _params.num_closest_update);
413 | if(_grid.getGoodBoxes().length > 0){
414 | generatePositiveData(img, _params.num_warps_update, _grid);
415 | }else{
416 | Log.w(Util.TAG, "NO good boxes, NOT learning.");
417 | return false;
418 | }
419 |
420 | // TODO why don't we learn from the GOOD boxes too !?
421 | final List> fernExamples = new ArrayList>(_pFerns);
422 | for(BoundingBox badBox : _grid.getBadBoxes()){
423 | final int[] allFernsHashCodes = _fernDetectionNegDataForLearning.get(badBox);
424 | if(allFernsHashCodes != null){
425 | // these are NEGATIVE examples !
426 | fernExamples.add(new Pair(allFernsHashCodes, false));
427 | }
428 | }
429 |
430 | final List nnExamples = new ArrayList();
431 | if(fernClassDetected != null){
432 | for(DetectionStruct detStruct : fernClassDetected){
433 | if(_lastbox.calcOverlap(detStruct.detectedBB) < Grid.BAD_OVERLAP){
434 | nnExamples.add(detStruct.patch);
435 | }
436 | }
437 | }
438 |
439 | // Classifiers update
440 | _classifierFern.trainF(fernExamples, 2);
441 | _classifierNN.trainNN(_pExample, _nExamples);
442 |
443 | Log.i(Util.TAG, "[LEARN END]");
444 | return true;
445 | }
446 |
447 |
448 |
449 | /**
450 | *
451 | * @param conservativeSimilarities
452 | * @return Map of clusters' boxes and their confidence
453 | */
454 | private Map clusterConfidentIndices(final List conservativeSimilarities){
455 | final int numbb = conservativeSimilarities.size();
456 | if(numbb == 0){
457 | Log.i(Util.TAG, "NO conservative similarities provided, NOTHING to cluster.");
458 | return new HashMap(); // empty result
459 | }
460 |
461 |
462 | // by default there is only 1 cluster, and ALL boxes are in it (0)
463 | int clusters = 1;
464 | for(DetectionStruct detStruct : conservativeSimilarities){
465 | _boxClusterMap.put(detStruct, 0);
466 | }
467 |
468 |
469 | if(numbb == 1){
470 | return Collections.singletonMap(conservativeSimilarities.get(0).detectedBB, conservativeSimilarities.get(0).nnConf.conservativeSimilarity);
471 | }else if(numbb == 2){
472 | if(conservativeSimilarities.get(0).detectedBB.calcOverlap(conservativeSimilarities.get(1).detectedBB) < 0.5){
473 | // 2nd box is in its own cluster, update
474 | _boxClusterMap.put(conservativeSimilarities.get(1), 1);
475 | clusters = 2;
476 | }
477 | }else {
478 | clusters = clusterBB();
479 | }
480 |
481 | final Map result = new HashMap();
482 |
483 | for(int cluster = 0; cluster < clusters; cluster++){
484 | float avgConservativeSimilarity = 0f;
485 | int clusterBoxCount = 0, mx=0, my=0, mw=0, mh=0;
486 |
487 | for(DetectionStruct detStruct : _boxClusterMap.keySet()){
488 | if(_boxClusterMap.get(detStruct) == cluster){
489 | avgConservativeSimilarity += detStruct.nnConf.conservativeSimilarity;
490 | mx += detStruct.detectedBB.x;
491 | my += detStruct.detectedBB.y;
492 | mw += detStruct.detectedBB.width;
493 | mh += detStruct.detectedBB.height;
494 | clusterBoxCount++;
495 | }
496 | }
497 |
498 | if(clusterBoxCount > 0){
499 | final BoundingBox clusterBox = new BoundingBox();
500 | clusterBox.x = mx / clusterBoxCount;
501 | clusterBox.y = my / clusterBoxCount;
502 | clusterBox.width = mw / clusterBoxCount;
503 | clusterBox.height = mh / clusterBoxCount;
504 |
505 | result.put(clusterBox, avgConservativeSimilarity / clusterBoxCount);
506 | }
507 | }
508 |
509 | return result;
510 | }
511 |
512 |
513 | /**
514 | * @param boxClusterMap INPUT / OUTPUT
515 | * @return Total clusters count
516 | */
517 | private int clusterBB(){
518 | final int size = _boxClusterMap.size();
519 | // need the data in arrays
520 | final DetectionStruct[] dbb = _boxClusterMap.keySet().toArray(new DetectionStruct[size]);
521 | final int[] indexes = new int[size];
522 | for(int i = 0; i < size; i++){
523 | indexes[i] = _boxClusterMap.get(dbb[i]);
524 | }
525 |
526 | // 1. Build proximity matrix
527 | final float[] data = new float[size * size];
528 | for(int i = 0; i < size; i++){
529 | for(int j = 0; j < size; j++){
530 | final float d = 1 - dbb[i].detectedBB.calcOverlap(dbb[j].detectedBB);
531 | data[i * size + j] = d;
532 | data[j * size + i] = d;
533 | }
534 | }
535 | Mat D = new Mat(size, size, CvType.CV_32F);
536 | D.put(0, 0, data);
537 |
538 | // 2. Initialise disjoint clustering
539 | final int[] belongs = new int[size];
540 | int m = size;
541 | for(int i = 0; i < size; i++){
542 | belongs[i] = i;
543 | }
544 |
545 |
546 | for(int it = 0; it < size - 1; it++){
547 | //3. Find nearest neighbour
548 | float min_d = 1;
549 | int node_a = -1, node_b = -1;
550 | for (int i = 0; i < D.rows(); i++){
551 | for (int j = i + 1 ;j < D.cols(); j++){
552 | if (data[i * size + j] < min_d && belongs[i] != belongs[j]){
553 | min_d = data[i * size + j];
554 | node_a = i;
555 | node_b = j;
556 | }
557 | }
558 | }
559 |
560 | // are we done ?
561 | if (min_d > 0.5){
562 | int max_idx =0;
563 | for (int j = 0; j < size; j++){
564 | boolean visited = false;
565 | for(int i = 0; i < 2 * size - 1; i++){
566 | if (belongs[j] == i){
567 | // populate the correct / aggregated cluster
568 | indexes[j] = max_idx;
569 | visited = true;
570 | }
571 | }
572 |
573 | if (visited){
574 | max_idx++;
575 | }
576 | }
577 |
578 | // update the main map before going back
579 | for(int i = 0; i < size; i++){
580 | _boxClusterMap.put(dbb[i], indexes[i]);
581 | }
582 | return max_idx;
583 | }
584 |
585 | //4. Merge clusters and assign level
586 | if(node_a >= 0 && node_b >= 0){ // this should always BE true, otherwise we would have returned
587 | for (int k = 0; k < size; k++){
588 | if (belongs[k] == belongs[node_a] || belongs[k] == belongs[node_b])
589 | belongs[k] = m;
590 | }
591 | m++;
592 | }
593 | }
594 |
595 | // there seem to be only 1 cluster
596 | for(int i = 0; i < size; i++){
597 | _boxClusterMap.put(dbb[i], 0);
598 | }
599 | return 1;
600 | }
601 |
602 |
603 | /** Inputs:
604 | * - Image
605 | * - bad_boxes (Boxes far from the bounding box)
606 | * - variance (pEx variance)
607 | * Outputs
608 | * - Negative fern features (nFerns)
609 | * - Negative NN examples (nExample)
610 | */
611 | private Pair>, List> generateNegativeData(final Mat frame){
612 | final List> negFerns = new ArrayList>();
613 | final List negExamples = new ArrayList();
614 |
615 |
616 | final List badBoxes = Arrays.asList(_grid.getBadBoxes());
617 | Collections.shuffle(badBoxes);
618 | Log.w(Util.TAG, "ST");
619 | // Get Fern Features of the boxes with big variance (calculated using integral images)
620 | for(BoundingBox badBox : badBoxes){
621 | if(Util.getVar(badBox, _iisumJava, _iisqsumJava, _iiCols) >= _var * 0.5f){
622 | final Mat patch = frame.submat(badBox);
623 | final int[] allFernsHashCodes = _classifierFern.getAllFernsHashCodes(patch, badBox.scaleIdx);
624 | negFerns.add(new Pair(allFernsHashCodes, false));
625 | }
626 | }
627 |
628 | // select a hard coded number of negative examples
629 | Iterator bbIt = badBoxes.iterator();
630 | for(int i = 0; i < _params.num_bad_patches && bbIt.hasNext(); i++){
631 | final Mat pattern = new Mat();
632 | final Mat patch = frame.submat(bbIt.next());
633 | resizeZeroMeanStdev(patch, pattern, _params.patch_size);
634 | negExamples.add(pattern);
635 | }
636 |
637 | Log.i(Util.TAG, "Negative examples generated. Ferns count: " + negFerns.size() + ". negEx count: " + negExamples.size());
638 |
639 | return new Pair>, List>(negFerns, negExamples);
640 | }
641 |
642 | /**
643 | * Generate Positive data
644 | * Inputs:
645 | * - good_boxes
646 | * - best_box
647 | * - bbhull
648 | * Outputs:
649 | * - Positive fern features (pFerns)
650 | * - Positive NN examples (pExample)
651 | */
652 | void generatePositiveData(final Mat frame, final int numWarps, final Grid aGrid) {
653 | resizeZeroMeanStdev(frame.submat(aGrid.getBestBox()), _pExample, _params.patch_size);
654 | //Get Fern features on warped patches
655 | final Mat img = new Mat();
656 | Imgproc.GaussianBlur(frame, img, new Size(9, 9), 1.5);
657 | final BoundingBox bbhull = aGrid.getBBhull();
658 | final Mat warped = img.submat(bbhull);
659 | // centre of the hull
660 | final Point pt = new Point(bbhull.x + (bbhull.width - 1) * 0.5f, bbhull.y + (bbhull.height - 1) * 0.5f);
661 |
662 | _pFerns.clear();
663 | _pPatterns.clear();
664 |
665 | for(int i = 0; i < numWarps; i++){
666 | if(i > 0){
667 | // this is important as it introduces the necessary noise / fuziness in the initial examples such that the Fern classifier recognises similar shapes not only Exact ones !
668 | // warped is a reference to a subset of the img data, so this will affect the img object
669 | _patchGenerator.generate(frame, pt, warped, bbhull.size(), _rng);
670 | }
671 |
672 | final BoundingBox[] goodBoxes = aGrid.getGoodBoxes();
673 | for(BoundingBox goodBox : goodBoxes){
674 | final Mat patch = img.submat(goodBox);
675 | final int[] allFernsHashCodes = _classifierFern.getAllFernsHashCodes(patch, goodBox.scaleIdx);
676 | _pFerns.add(new Pair(allFernsHashCodes, true));
677 |
678 | // // this will be used for display only
679 | // final Mat tempPattern = new Mat();
680 | // Imgproc.resize(patch, tempPattern, new Size(_params.patch_size, _params.patch_size));
681 | // _pPatterns.add(tempPattern);
682 | }
683 | }
684 |
685 | Log.i(Util.TAG, "Positive examples generated( ferns: " + _pFerns.size() + " NN: 1/n )");
686 | }
687 |
688 |
689 | /**
690 | * Output: resized zero-mean patch/pattern
691 | * @param inImg INPUT, outPattern OUTPUT
692 | * @return stdev
693 | */
694 | private static double resizeZeroMeanStdev(final Mat inImg, Mat outPattern, int patternSize){
695 | if(inImg == null || outPattern == null){
696 | return -1;
697 | }
698 |
699 | Imgproc.resize(inImg, outPattern, new Size(patternSize, patternSize));
700 | final MatOfDouble mean = new MatOfDouble();
701 | final MatOfDouble stdev = new MatOfDouble();
702 | Core.meanStdDev(outPattern, mean, stdev);
703 | outPattern.convertTo(outPattern, CvType.CV_32F);
704 | Core.subtract(outPattern, new Scalar(mean.toArray()[0]), outPattern);
705 |
706 | return stdev.toArray()[0];
707 | }
708 |
709 | public List getPPatterns(){
710 | return _pPatterns;
711 | }
712 |
713 |
714 | static final class DetectionStruct {
715 | public final BoundingBox detectedBB;
716 | public final int[] pattern;
717 | public final double averagePosterior;
718 | public final Mat patch;
719 | public NNConfStruct nnConf;
720 |
721 | DetectionStruct(BoundingBox detectedBB, int[] pattern, double averagePosterior, Mat patch) {
722 | this.detectedBB = detectedBB;
723 | this.pattern = pattern;
724 | this.averagePosterior = averagePosterior;
725 | this.patch = patch;
726 | }
727 | }
728 |
729 | private static final class TrackingStruct {
730 | public final float conf;
731 | public final BoundingBox predictedBB;
732 | public final Point[] lastPoints;
733 | public final Point[] currentPoints;
734 |
735 | TrackingStruct(float conf, BoundingBox predictedBB, Point[] trackedLastPoints, Point[] trackedCurrentPoints) {
736 | this.conf = conf;
737 | this.predictedBB = predictedBB;
738 | this.lastPoints = trackedLastPoints;
739 | this.currentPoints = trackedCurrentPoints;
740 | }
741 | }
742 |
743 |
744 | public static final class ProcessFrameStruct {
745 | public final Point[] lastPoints;
746 | public final Point[] currentPoints;
747 | public final BoundingBox currentBBox;
748 |
749 | ProcessFrameStruct(Point[] lastPoints, Point[] currentPoints, BoundingBox currentBBox) {
750 | this.lastPoints = lastPoints;
751 | this.currentPoints = currentPoints;
752 | this.currentBBox = currentBBox;
753 | }
754 | }
755 |
756 | }
757 |
--------------------------------------------------------------------------------
/tld-main/src/com/trandi/opentld/tld/Util.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.Arrays;
20 | import java.util.Collections;
21 | import java.util.Comparator;
22 | import java.util.List;
23 | import java.util.Random;
24 |
25 | import org.opencv.core.CvType;
26 | import org.opencv.core.Mat;
27 | import org.opencv.core.Point;
28 |
29 | public class Util {
30 | public final static String TAG = "OpenTLD";
31 |
32 | private final static byte[] _byteBuff1 = new byte[1];
33 | private final static int[] _intBuff1 = new int[1];
34 | private final static float[] _floatBuff1 = new float[1];
35 | private final static double[] _doubleBuff1 = new double[1];
36 | // this is for a whole matrix !
37 | private static byte[] _byteBuff = new byte[1];
38 | private static int[] _intBuff = new int[1];
39 | private static float[] _floatBuff = new float[1];
40 | private static double[] _doubleBuff = new double[1];
41 |
42 |
43 | static int unsignedChar(int val){
44 | return Math.min(Math.max(val, 0), 255);
45 | }
46 |
47 | static int unsignedChar(double val){
48 | return unsignedChar((int)val);
49 | }
50 |
51 |
52 | /**
53 | * This is actually quite slow, as it makes a couple of native calls for each of the Util.getXXX methods...
54 | */
55 | @Deprecated
56 | static double getVar(final BoundingBox box, final Mat sum, final Mat sqsum){
57 | final int brs = Util.getInt(box.y + box.height, box.x + box.width, sum);
58 | final int bls = Util.getInt(box.y + box.height, box.x, sum);
59 | final int trs = Util.getInt(box.y, box.x + box.width, sum);
60 | final int tls = Util.getInt(box.y, box.x, sum);
61 | final double brsq = Util.getDouble(box.y + box.height, box.x + box.width, sqsum);
62 | final double blsq = Util.getDouble(box.y + box.height, box.x, sqsum);
63 | final double trsq = Util.getDouble(box.y, box.x + box.width, sqsum);
64 | final double tlsq = Util.getDouble(box.y, box.x, sqsum);
65 |
66 | final double boxArea = box.area();
67 | final double mean = (brs + tls - trs - bls) / boxArea;
68 | final double sqmean = (brsq + tlsq - trsq - blsq) / boxArea;
69 |
70 | return sqmean - mean * mean;
71 | }
72 |
73 | /**
74 | * Preferred for performance !
75 | * Here we get both the SUM and SQuaredSUM Matrices already in Java, rather than call native methods for each
76 | * element that we need.
77 | *
78 | * For a 320x240 frame the improvement is close to 5X !!!
79 | */
80 | static double getVar(final BoundingBox box, final int[] sum, final double[] sqsum, final int colCount) {
81 | final int brs = sum[(box.y + box.height) * colCount + box.x + box.width];
82 | final int bls = sum[(box.y + box.height) * colCount + box.x];
83 | final int trs = sum[box.y * colCount + box.x + box.width];
84 | final int tls = sum[box.y * colCount + box.x];
85 | final double brsq = sqsum[(box.y + box.height) * colCount + box.x + box.width];
86 | final double blsq = sqsum[(box.y + box.height) * colCount + box.x];
87 | final double trsq = sqsum[box.y * colCount + box.x + box.width];
88 | final double tlsq = sqsum[box.y * colCount + box.x];
89 |
90 | final double boxArea = box.area();
91 | final double mean = (brs + tls - trs - bls) / boxArea;
92 | final double sqmean = (brsq + tlsq - trsq - blsq) / boxArea;
93 |
94 | return sqmean - mean * mean;
95 | }
96 |
97 | static float median(float[] vals){
98 | final float[] newVals = Arrays.copyOf(vals, vals.length);
99 | Arrays.sort(newVals);
100 | return newVals[newVals.length / 2];
101 | }
102 |
103 | static float median(List vals){
104 | final Float[] newVals = vals.toArray(new Float[vals.size()]);
105 | Arrays.sort(newVals);
106 | return newVals[(int) Math.floor(newVals.length / 2d)];
107 | }
108 |
109 | static float norm(final Point p1, final Point p2){
110 | final double dX = p1.x - p2.x;
111 | final double dY = p1.y - p2.y;
112 | return (float)Math.sqrt(dX * dX + dY * dY);
113 | }
114 |
115 | /**
116 | * no std::nth_element in Java so we'll sort the list. Less performant but we don't really care for the small lists we have
117 | */
118 | static void keepBestN(List list, final int n, final Comparator comparator){
119 | final int size = list.size();
120 | if(size <= n) {
121 | // nothing to do, sorting is not a requirement
122 | return;
123 | }
124 |
125 | // sorts in ASCENDING ORDER
126 | Collections.sort(list, comparator);
127 | // we want the best / highest n so remote at the queue
128 | while(list.size() > n){
129 | list.remove(0);
130 | }
131 | }
132 |
133 |
134 | static byte getByte(final int row, final int col, final Mat mat){
135 | if(CvType.CV_8UC1 != mat.type()) throw new IllegalArgumentException("Expected type is CV_8UC1, we found: " + CvType.typeToString(mat.type()));
136 |
137 | mat.get(row, col, _byteBuff1);
138 | return _byteBuff1[0];
139 | }
140 |
141 | /**
142 | * The corresponding Java primitive array type depends on the Mat type:
143 | * CV_8U and CV_8S -> byte[]
144 | * CV_16U and CV_16S -> short[]
145 | * CV_32S -> int[]
146 | * CV_32F -> float[]
147 | * CV_64F-> double[]
148 | */
149 | static byte[] getByteArray(final Mat mat){
150 | if(CvType.CV_8UC1 != mat.type()) throw new IllegalArgumentException("Expected type is CV_8UC1, we found: " + CvType.typeToString(mat.type()));
151 |
152 | final int size = (int) (mat.total() * mat.channels());
153 | if(_byteBuff.length != size){
154 | _byteBuff = new byte[size];
155 | }
156 | mat.get(0, 0, _byteBuff); // 0 for row and col means the WHOLE Matrix
157 | return _byteBuff;
158 | }
159 |
160 | static int[] getIntArray(final Mat mat){
161 | if(CvType.CV_32SC1 != mat.type()) throw new IllegalArgumentException("Expected type is CV_32SC1, we found: " + CvType.typeToString(mat.type()));
162 |
163 | final int size = (int) (mat.total() * mat.channels());
164 | if(_intBuff.length != size){
165 | _intBuff = new int[size];
166 | }
167 | mat.get(0, 0, _intBuff); // 0 for row and col means the WHOLE Matrix
168 | return _intBuff;
169 | }
170 |
171 | static float[] getFloatArray(final Mat mat){
172 | if(CvType.CV_32FC1 != mat.type()) throw new IllegalArgumentException("Expected type is CV_32FC1, we found: " + CvType.typeToString(mat.type()));
173 |
174 | final int size = (int) (mat.total() * mat.channels());
175 | if(_floatBuff.length != size){
176 | _floatBuff = new float[size];
177 | }
178 | mat.get(0, 0, _floatBuff); // 0 for row and col means the WHOLE Matrix
179 | return _floatBuff;
180 | }
181 |
182 | static double[] getDoubleArray(final Mat mat){
183 | if(CvType.CV_64F != mat.type()) throw new IllegalArgumentException("Expected type is CV_64F, we found: " + CvType.typeToString(mat.type()));
184 |
185 | final int size = (int) (mat.total() * mat.channels());
186 | if(_doubleBuff.length != size){
187 | _doubleBuff = new double[size];
188 | }
189 | mat.get(0, 0, _doubleBuff); // 0 for row and col means the WHOLE Matrix
190 | return _doubleBuff;
191 | }
192 |
193 |
194 | static int getInt(final int row, final int col, final Mat mat){
195 | if(CvType.CV_32SC1 != mat.type()) throw new IllegalArgumentException("Expected type is CV_32SC1, we found: " + CvType.typeToString(mat.type()));
196 |
197 | mat.get(row, col, _intBuff1);
198 | return _intBuff1[0];
199 | }
200 |
201 | static float getFloat(final int row, final int col, final Mat mat){
202 | if(CvType.CV_32F != mat.type()) throw new IllegalArgumentException("Expected type is CV_32F, we found: " + CvType.typeToString(mat.type()));
203 |
204 | mat.get(row, col, _floatBuff1);
205 | return _floatBuff1[0];
206 | }
207 |
208 | static double getDouble(final int row, final int col, final Mat mat){
209 | if(CvType.CV_64F != mat.type()) throw new IllegalArgumentException("Expected type is CV_64F, we found: " + CvType.typeToString(mat.type()));
210 |
211 | mat.get(row, col, _doubleBuff1);
212 | return _doubleBuff1[0];
213 | }
214 |
215 |
216 | static final class NNConfStruct {
217 | final IsinStruct isin;
218 | final float relativeSimilarity;
219 | final float conservativeSimilarity;
220 |
221 |
222 | NNConfStruct(IsinStruct isin, float relativeSimilarity, float conservativeSimilarity) {
223 | this.isin = isin;
224 | this.relativeSimilarity = relativeSimilarity;
225 | this.conservativeSimilarity = conservativeSimilarity;
226 | }
227 | }
228 |
229 | static final class IsinStruct {
230 | final boolean inPosSet;
231 | final int idxPosSet;
232 | final boolean inNegSet;
233 |
234 |
235 | IsinStruct(boolean inPosSet, int idxPosSet, boolean inNegSet) {
236 | this.inPosSet = inPosSet;
237 | this.idxPosSet = idxPosSet;
238 | this.inNegSet = inNegSet;
239 | }
240 | }
241 |
242 |
243 | static final class Pair{
244 | final U first;
245 | final V second;
246 |
247 | Pair(U first, V second){
248 | this.first = first;
249 | this.second = second;
250 | }
251 |
252 | @Override
253 | public String toString(){
254 | return "{" + first + ", " + second + "}";
255 | }
256 | }
257 |
258 |
259 | static interface RNG {
260 | float nextFloat();
261 | int nextInt();
262 | }
263 |
264 | static class DefaultRNG implements RNG{
265 | private final Random rnd = new Random();
266 |
267 | @Override
268 | public float nextFloat() {
269 | return rnd.nextFloat();
270 | }
271 |
272 | @Override
273 | public int nextInt() {
274 | return rnd.nextInt();
275 | }
276 |
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/tld-test/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tld-test/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenTLD-test
4 |
5 |
6 | OpenTLD
7 |
8 |
9 |
10 | com.android.ide.eclipse.adt.ResourceManagerBuilder
11 |
12 |
13 |
14 |
15 | com.android.ide.eclipse.adt.PreCompilerBuilder
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javabuilder
21 |
22 |
23 |
24 |
25 | com.android.ide.eclipse.adt.ApkBuilder
26 |
27 |
28 |
29 |
30 |
31 | com.android.ide.eclipse.adt.AndroidNature
32 | org.eclipse.jdt.core.javanature
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tld-test/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tld-test/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/tld-test/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-10
15 |
--------------------------------------------------------------------------------
/tld-test/res/raw/parameters.properties:
--------------------------------------------------------------------------------
1 | min_win=15
2 | patch_size=15
3 | ncc_thesame=0.95
4 | valid=0.5
5 | num_trees=10
6 | num_features=13
7 | pos_thr_fern=0.6
8 | pos_thr_nn=0.65
9 | pos_thr_nn_valid=0.7
10 | num_closest_init=10
11 | num_warps_init=20
12 | noise_init=5
13 | angle_init=20
14 | shift_init=0.02
15 | scale_init=0.02
16 | num_closest_update=10
17 | num_warps_update=10
18 | noise_update=5
19 | angle_update=10
20 | shift_update=0.02
21 | scale_update=0.02
22 | overlap=0.2
23 | num_bad_patches=100
24 | bb_x=288
25 | bb_y=36
26 | bb_w=25
27 | bb_h=42
--------------------------------------------------------------------------------
/tld-test/res/raw/test_grid_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trandi/OpenTLDAndroid/5eea97c2e1fc3918132396d86699d8f5c18b96d9/tld-test/res/raw/test_grid_frame.png
--------------------------------------------------------------------------------
/tld-test/src/com/trandi/opentld/tld/FerNNClassifierTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | import org.opencv.core.Mat;
23 | import org.opencv.core.MatOfFloat;
24 | import org.opencv.core.Size;
25 |
26 | import com.trandi.opentld.tld.Util.NNConfStruct;
27 |
28 | public class FerNNClassifierTest extends OpenCVTestCase{
29 | static int[] EXPECTED_FERN = new int[]{6278, 6386, 2241, 1139, 3291, 3927, 7902, 6144, 256, 14};
30 |
31 | public void testGetFeatures(){
32 | final Size[] scales = new Size[]{new Size(2, 2)};
33 | FerNNClassifier classifier = new FerNNClassifier();
34 | classifier.params = new DummyParamsClassifier(10, 13);
35 | classifier.prepare(scales, new DummyRNG());
36 |
37 | final int[] fern = classifier.getFeatures(getSimpleMat(), 0);
38 | for(int i=0; i nExamples = new ArrayList();
115 | nExamples.add(pEx2);
116 | nExamples.add(pEx3);
117 | nExamples.add(nEx1);
118 | nExamples.add(nEx2);
119 | classifier.trainNN(pEx1, nExamples);
120 | assertEquals(1, classifier.pExamples.size());
121 | assertEquals(3, classifier.nExamples.size());
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tld-test/src/com/trandi/opentld/tld/GridTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import org.opencv.core.Rect;
20 |
21 |
22 |
23 | public class GridTest extends OpenCVTestCase {
24 |
25 | public void testGrid(){
26 | final Grid grid = new Grid(getTestMat(), new Rect(165, 93, 51, 54), 15);
27 | assertEquals(58901, grid.getSize());
28 |
29 | grid.updateGoodBadBoxes(10);
30 | assertEquals(10, grid.getGoodBoxes().length);
31 | assertEquals(57855, grid.getBadBoxes().length);
32 | assertEquals(new BoundingBox(166, 91, 51, 54, 0.8940853f, 6), grid.getBestBox());
33 | assertEquals(new BoundingBox(157,86,67,70,-1,-1), grid.getBBhull());
34 |
35 | assertEquals(new BoundingBox(157, 91, 61, 65, 0.6945776f, 7), grid.getGoodBoxes()[0]);
36 | assertEquals(new BoundingBox(163, 91, 61, 65, 0.6945776f, 7), grid.getGoodBoxes()[1]);
37 | assertEquals(new BoundingBox(171, 96, 51, 54, 0.71428573f, 6), grid.getGoodBoxes()[2]);
38 | assertEquals(new BoundingBox(166, 101, 51, 54, 0.7169576f, 6), grid.getGoodBoxes()[3]);
39 | assertEquals(new BoundingBox(171, 91, 51, 54, 0.7386364f, 6), grid.getGoodBoxes()[4]);
40 | assertEquals(new BoundingBox(166, 86, 51, 54, 0.7441419f, 6), grid.getGoodBoxes()[5]);
41 | assertEquals(new BoundingBox(161, 96, 51, 54, 0.7704918f, 6), grid.getGoodBoxes()[6]);
42 | assertEquals(new BoundingBox(161, 91, 51, 54, 0.79765016f, 6), grid.getGoodBoxes()[7]);
43 | assertEquals(new BoundingBox(166, 96, 51, 54, 0.86206895f, 6), grid.getGoodBoxes()[8]);
44 | assertEquals(new BoundingBox(166, 91, 51, 54, 0.8940853f, 6), grid.getGoodBoxes()[9]);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tld-test/src/com/trandi/opentld/tld/LKTrackerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Arrays;
21 | import java.util.List;
22 |
23 | import org.opencv.core.Mat;
24 | import org.opencv.core.Point;
25 | import org.opencv.imgproc.Imgproc;
26 |
27 | import android.util.Log;
28 |
29 | import com.trandi.opentld.tld.Util.Pair;
30 |
31 | public class LKTrackerTest extends OpenCVTestCase{
32 | private static final double [][] LAST_POINTS_IN = new double[][]{{165, 93}, {186, 93}, {207, 93}, {228, 93}, {249, 93}, {270, 93}, {291, 93}, {312, 93}, {333, 93}, {354, 93}, {375, 93}, {165, 107}, {186, 107}, {207, 107}, {228, 107}, {249, 107}, {270, 107}, {291, 107}, {312, 107}, {333, 107}, {354, 107}, {375, 107}, {165, 121}, {186, 121}, {207, 121}, {228, 121}, {249, 121}, {270, 121}, {291, 121}, {312, 121}, {333, 121}, {354, 121}, {375, 121}, {165, 135}, {186, 135}, {207, 135}, {228, 135}, {249, 135}, {270, 135}, {291, 135}, {312, 135}, {333, 135}, {354, 135}, {375, 135}, {165, 149}, {186, 149}, {207, 149}, {228, 149}, {249, 149}, {270, 149}, {291, 149}, {312, 149}, {333, 149}, {354, 149}, {375, 149}, {165, 163}, {186, 163}, {207, 163}, {228, 163}, {249, 163}, {270, 163}, {291, 163}, {312, 163}, {333, 163}, {354, 163}, {375, 163}, {165, 177}, {186, 177}, {207, 177}, {228, 177}, {249, 177}, {270, 177}, {291, 177}, {312, 177}, {333, 177}, {354, 177}, {375, 177}, {165, 191}, {186, 191}, {207, 191}, {228, 191}, {249, 191}, {270, 191}, {291, 191}, {312, 191}, {333, 191}, {354, 191}, {375, 191}, {165, 205}, {186, 205}, {207, 205}, {228, 205}, {249, 205}, {270, 205}, {291, 205}, {312, 205}, {333, 205}, {354, 205}, {375, 205}, {165, 219}, {186, 219}, {207, 219}, {228, 219}, {249, 219}, {270, 219}, {291, 219}, {312, 219}, {333, 219}, {354, 219}, {375, 219}, {165, 233}, {186, 233}, {207, 233}, {228, 233}, {249, 233}, {270, 233}, {291, 233}, {312, 233}, {333, 233}, {354, 233}, {375, 233}};
33 | private static final double[][] LAST_POINTS_OUT = new double[][]{{186.0, 93.0}, {291.0, 93.0}, {228.0, 121.0}, {312.0, 121.0}, {165.0, 135.0}, {207.0, 135.0}, {312.0, 135.0}, {228.0, 149.0}, {270.0, 149.0}, {291.0, 149.0}, {312.0, 149.0}, {207.0, 163.0}, {312.0, 163.0}, {165.0, 177.0}, {207.0, 177.0}, {249.0, 177.0}, {270.0, 177.0}, {291.0, 177.0}, {165.0, 191.0}, {207.0, 191.0}, {228.0, 191.0}, {249.0, 191.0}, {270.0, 191.0}, {291.0, 191.0}, {165.0, 205.0}, {186.0, 205.0}, {165.0, 219.0}, {186.0, 219.0}, {207.0, 219.0}, {228.0, 219.0}, {312.0, 219.0}};
34 | private static final double [][] CURR_POINTS = new double[][]{{183.86514282226563, 93.55406188964844}, {291.14886474609375, 100.68718719482422}, {226.94879150390625, 121.74057006835938}, {310.65032958984375, 121.42726135253906}, {162.57179260253906, 135.29327392578125}, {207.0057830810547, 134.99842834472656}, {309.7173156738281, 134.33848571777344}, {228.00534057617188, 148.99209594726563}, {269.8294677734375, 148.4680633544922}, {291.8500061035156, 147.63864135742188}, {310.7330627441406, 149.5430450439453}, {206.3493194580078, 165.28530883789063}, {309.9791259765625, 162.94647216796875}, {165.558837890625, 176.97889709472656}, {206.78158569335938, 177.51510620117188}, {246.7549591064453, 175.75962829589844}, {274.6474609375, 179.42388916015625}, {296.5911560058594, 178.86265563964844}, {164.9466552734375, 191.55120849609375}, {206.85003662109375, 191.38241577148438}, {227.9065704345703, 191.2167510986328}, {244.27536010742188, 190.4522247314453}, {268.47705078125, 191.3587646484375}, {299.8081970214844, 194.59164428710938}, {165.43556213378906, 208.11083984375}, {185.843017578125, 205.44772338867188}, {164.3252716064453, 219.3542022705078}, {186.7926483154297, 218.0596160888672}, {209.62525939941406, 217.36581420898438}, {227.74319458007813, 218.33566284179688}, {311.57666015625, 218.7012176513672}};
35 |
36 |
37 |
38 | /**
39 | * This does NOT give the same exact results as the C++ version on a PC.
40 | * The very calcOpticalFlowPyrLK, called with the exact same params returns different set of points with different values !
41 | */
42 | public void testTrack() {
43 | Mat img1 = readMatFromFile("track_frame_1");
44 | Mat img2 = readMatFromFile("track_frame_2");
45 |
46 | Imgproc.cvtColor(img1, img1, Imgproc.COLOR_RGB2GRAY);
47 | Imgproc.cvtColor(img2, img2, Imgproc.COLOR_RGB2GRAY);
48 |
49 | Pair result = new LKTracker().track(img1, img2, toPoints(LAST_POINTS_IN));
50 | Log.i(UtilTest.TAG, Arrays.asList(result.first).toString());
51 | Log.i(UtilTest.TAG, Arrays.asList(result.second).toString());
52 | assertNotNull("The tracking is broken, can't even return a non null result !", result);
53 | for(int i=0; i toList(Point[] points){
69 | List result = new ArrayList();
70 | for(Point p : points){
71 | result.add(p);
72 | }
73 | return result;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tld-test/src/com/trandi/opentld/tld/OpenCVTestCase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import java.util.List;
20 | import java.util.concurrent.CountDownLatch;
21 | import java.util.concurrent.atomic.AtomicBoolean;
22 |
23 | import org.opencv.android.InstallCallbackInterface;
24 | import org.opencv.android.LoaderCallbackInterface;
25 | import org.opencv.android.OpenCVLoader;
26 | import org.opencv.android.Utils;
27 | import org.opencv.core.CvType;
28 | import org.opencv.core.Mat;
29 | import org.opencv.core.Point;
30 | import org.opencv.core.Size;
31 |
32 | import android.graphics.Bitmap;
33 | import android.graphics.BitmapFactory;
34 | import android.test.AndroidTestCase;
35 | import android.util.Log;
36 |
37 | import com.trandi.opentld.tld.Parameters.ParamsClassifier;
38 | import com.trandi.opentld.tld.Util.Pair;
39 | import com.trandi.opentld.tld.Util.RNG;
40 |
41 | public abstract class OpenCVTestCase extends AndroidTestCase{
42 | protected static final String TAG = "TLD_TEST";
43 |
44 | protected static final byte[] SIMPLE_MAT = new byte[]{4, 65, 8, 23, 6, 98, 7, 2, 65, 44, 36, 74, 5, 12, 47, 86, 33, 4, 18, 51, 21, 36, 42, 78, 1};
45 |
46 | protected final static CountDownLatch _openCVLoaded = new CountDownLatch(1);
47 | protected final static AtomicBoolean _openCVLoadStarted = new AtomicBoolean(false);
48 |
49 | public void setUp(){
50 | if(!_openCVLoadStarted.getAndSet(true)){
51 | Log.i(TAG, "Trying to load OpenCV library");
52 | if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this.getContext(), _openCVCallBack)) {
53 | Log.e(TAG, "Cannot connect to OpenCV Manager");
54 | }
55 | }
56 |
57 | // we do need the native OpenCV lib, make sure it's been loaded...
58 | try {
59 | _openCVLoaded.await();
60 | } catch (InterruptedException e) {
61 | Log.e(TAG, "Can't wait on the CountDownLatch: " + e.getMessage());
62 | }
63 | }
64 |
65 |
66 |
67 | private LoaderCallbackInterface _openCVCallBack = new LoaderCallbackInterface() {
68 | @Override
69 | public void onPackageInstall(InstallCallbackInterface callback) {
70 | // nothing to do...
71 | }
72 |
73 | @Override
74 | public void onManagerConnected(int status) {
75 | Log.i(TAG, "OpenCV lib LOADED.");
76 | _openCVLoaded.countDown();
77 | }
78 | };
79 |
80 |
81 | protected static Mat getTestMat(){
82 | return readMatFromFile("test_grid_frame");
83 | }
84 |
85 | protected static Mat readMatFromFile(final String name){
86 | // BitmapFactory.decodeResource scales the image which is not great... Geting the image from resources is a BIG PAIN !
87 | //final Bitmap image = new BitmapDrawable(getContext().getResources(), getContext().getResources().openRawResource(R.drawable.test_grid_frame)).getBitmap();
88 | final Bitmap image = BitmapFactory.decodeFile("/mnt/sdcard/TLDtest/" + name + ".png");
89 | final Mat img = new Mat();
90 | Utils.bitmapToMat(image, img);
91 |
92 | return img;
93 | }
94 |
95 | protected Mat getSimpleMat(){
96 | final Mat result = new Mat(5, 5, CvType.CV_8UC1);
97 | result.put(0, 0, SIMPLE_MAT);
98 | return result;
99 | }
100 |
101 | protected static class DummyParamsClassifier extends ParamsClassifier{
102 | DummyParamsClassifier(int nstructs, int structSize) {
103 | this.nstructs = nstructs;
104 | this.structSize = structSize;
105 | }
106 |
107 | DummyParamsClassifier(float valid, float ncc_thesame, float pos_thr_nn, float neg_thr_nn){
108 | this.valid = valid;
109 | this.ncc_thesame = ncc_thesame;
110 | this.pos_thr_nn = pos_thr_nn;
111 | this.neg_thr_nn = neg_thr_nn;
112 | }
113 | }
114 |
115 |
116 |
117 | protected void log(List> ferns){
118 | final StringBuilder sb = new StringBuilder();
119 | for(Pair elem : ferns){
120 | sb.append("{");
121 | for(int num : elem.first){
122 | sb.append(num + ", ");
123 | }
124 | sb.append("}, " + elem.second + "\n");
125 | }
126 |
127 | Log.i(TAG, sb.toString());
128 | }
129 |
130 | protected void logFloat(Mat mat){
131 | StringBuilder sb = new StringBuilder();
132 | int cols = 0;
133 | for(float cell : Util.getFloatArray(mat)){
134 | sb.append(cell);
135 | sb.append(", ");
136 |
137 | cols++;
138 | if(cols == mat.cols()){
139 | sb.append("\n");
140 | cols = 0;
141 | }
142 | }
143 |
144 | Log.i(TAG, sb.toString());
145 | }
146 |
147 |
148 | /**
149 | * We want a dummy random number generator that is predictable for testing (so not random at all :)) !
150 | *
151 | * Simulating the constant sequence from theRNG in Linux OpenCV, so that I can compare my results.
152 | */
153 | protected static class DummyRNG implements RNG {
154 | private static final float[] FLOAT_VALS = new float[]{0.0302828f, 0.699259f, 0.901059f, 0.314385f, 0.937133f, 0.74879f, 0.273105f, 0.262094f, 0.807794f, 0.202432f, 0.978447f, 0.29219f, 0.585843f, 0.57506f, 0.663423f, 0.799779f, 0.405654f, 0.209686f, 0.649712f, 0.26544f, 0.624281f, 0.503729f, 0.0515136f, 0.498199f, 0.658992f, 0.661178f, 0.538189f, 0.925861f, 0.346128f, 0.387604f, 0.764712f, 0.770783f, 0.452666f, 0.175131f, 0.310216f, 0.68396f, 0.594456f, 0.315903f, 0.0245177f, 0.880839f, 0.911995f, 0.755535f, 0.444896f, 0.400123f, 0.957892f, 0.341307f, 0.7803f, 0.885436f, 0.951901f, 0.375434f, 0.659415f, 0.149287f, 0.925457f, 0.17995f, 0.849529f, 0.100717f, 0.777865f, 0.14982f, 0.935759f, 0.287623f, 0.803898f, 0.57758f, 0.135896f, 0.167349f, 0.247754f, 0.826119f, 0.874476f, 0.0406494f, 0.53514f, 0.813524f, 0.315994f, 0.16987f, 0.146549f, 0.59302f, 0.306879f, 0.318403f, 0.37031f, 0.515844f, 0.321763f, 0.773414f, 0.242869f, 0.378337f, 0.753198f, 0.710547f, 0.970446f, 0.685035f, 0.101767f, 0.0504552f, 0.0236324f, 0.711951f, 0.27889f, 0.501652f, 0.914189f, 0.278173f, 0.833208f, 0.298738f, 0.39429f, 0.34828f, 0.43545f, 0.966717f, 0.344438f, 0.53786f, 0.224755f, 0.0581553f, 0.359186f, 0.638634f, 0.0450694f, 0.33291f, 0.673552f, 0.60346f, 0.767849f, 0.627657f, 0.724334f, 0.0270216f, 0.126572f, 0.860088f, 0.969015f, 0.117603f, 0.489088f, 0.603721f, 0.191574f, 0.062358f, 0.136207f, 0.0405149f, 0.247364f, 0.0150434f, 0.539409f, 0.701493f, 0.572341f, 0.944394f, 0.230132f, 0.0916598f, 0.821941f, 0.0774442f, 0.0992629f, 0.638536f, 0.0827001f, 0.725325f, 0.402736f, 0.948108f, 0.19839f, 0.0184167f, 0.122276f, 0.170035f, 0.680386f, 0.267609f, 0.98092f, 0.64777f, 0.587737f, 0.295127f, 0.260091f, 0.390463f, 0.291739f, 0.780122f, 0.506764f, 0.947792f, 0.753794f, 0.476753f, 0.113941f, 0.943724f, 0.107921f, 0.841604f, 0.300448f, 0.79657f, 0.564746f, 0.886269f, 0.430645f, 0.125808f, 0.0533489f, 0.686669f, 0.573361f, 0.591694f, 0.354617f, 0.108885f, 0.31028f, 0.333034f, 0.943051f, 0.324802f, 0.0260598f, 0.989725f, 0.226796f, 0.890065f, 0.816288f, 0.738183f, 0.405064f, 0.540368f, 0.833171f, 0.877357f, 0.426997f, 0.154156f, 0.0950605f, 0.809311f, 0.445242f, 0.526991f, 0.246292f, 0.11575f, 0.578381f, 0.475285f, 0.188153f, 0.483129f, 0.312423f, 0.134111f, 0.856919f, 0.00151018f, 0.759387f, 0.679776f, 0.0529576f, 0.0303695f, 0.13697f, 0.9674f, 0.0539295f, 0.2081f, 0.169082f, 0.874383f, 0.73296f, 0.614883f, 0.934025f, 0.145489f, 0.788591f, 0.98322f, 0.991262f, 0.570196f, 0.327684f, 0.898323f, 0.414741f, 0.906338f, 0.954782f, 0.225884f, 0.439195f, 0.124569f, 0.271098f, 0.629456f, 0.129719f, 0.784967f, 0.0170592f, 0.241716f, 0.845276f, 0.139705f, 0.709373f, 0.275135f, 0.335275f, 0.586723f, 0.875136f, 0.327443f, 0.471408f, 0.749663f, 0.234007f, 0.00851168f, 0.0766955f, 0.664636f, 0.650937f, 0.386351f, 0.0650123f, 0.8498f, 0.565603f, 0.512429f, 0.260927f, 0.348782f, 0.344782f, 0.388152f, 0.234347f, 0.960589f, 0.745488f, 0.577699f, 0.319033f, 0.72123f, 0.888841f, 0.407346f, 0.0613338f, 0.263451f, 0.671283f, 0.46799f, 0.20903f, 0.586368f, 0.202341f, 0.0325399f, 0.801981f, 0.26099f, 0.991967f, 0.557405f, 0.158999f, 0.69862f, 0.933116f, 0.234261f, 0.115818f, 0.882687f, 0.781425f, 0.850389f, 0.983693f, 0.396448f, 0.799234f, 0.426167f, 0.982419f, 0.0414847f, 0.813397f, 0.139889f, 0.441356f, 0.0402882f, 0.567139f, 0.857216f, 0.0114054f, 0.532736f, 0.325343f, 0.71115f, 0.705264f, 0.647196f, 0.237083f, 0.502193f, 0.383696f, 0.0106499f, 0.469297f, 0.522604f, 0.741165f, 0.105133f, 0.176296f, 0.969175f, 0.840313f, 0.934355f, 0.372619f, 0.999991f, 0.236618f, 0.446304f, 0.286588f, 0.218999f, 0.806877f, 0.193574f, 0.531575f, 0.457617f, 0.240126f, 0.110324f, 0.266245f, 0.69946f, 0.998599f, 0.346799f, 0.500744f, 0.297095f, 0.181458f, 0.720223f, 0.65924f, 0.0888237f, 0.392388f, 0.27332f, 0.495657f, 0.734254f, 0.778615f, 0.702097f, 0.314488f, 0.939356f, 0.404341f, 0.802247f, 0.0779452f, 0.990597f, 0.456745f, 0.925541f, 0.151764f, 0.898865f, 0.817201f, 0.931203f, 0.907412f, 0.367884f, 0.548636f, 0.336718f, 0.0818088f, 0.929679f, 0.570782f, 0.718197f, 0.819274f, 0.933471f, 0.408198f, 0.202787f, 0.648993f, 0.416095f, 0.625034f, 0.830859f, 0.90524f, 0.595954f, 0.539348f, 0.518243f, 0.462666f, 0.887289f, 0.941242f, 0.131919f, 0.442463f, 0.539303f, 0.469079f, 0.0388665f, 0.0178558f, 0.603161f, 0.384191f, 0.339097f, 0.419493f, 0.829171f, 0.691604f, 0.340418f, 0.438553f, 0.572016f, 0.160621f, 0.621064f, 0.23444f, 0.373164f, 0.37187f, 0.572377f, 0.75106f, 0.0651843f, 0.726156f, 0.419805f, 0.0637989f, 0.474118f, 0.12387f, 0.345375f, 0.805612f, 0.869972f, 0.497212f, 0.728096f, 0.496649f, 0.476879f, 0.0398521f, 0.51132f, 0.0250764f, 0.966541f, 0.205904f, 0.584359f, 0.923995f, 0.911257f, 0.0290562f, 0.61019f, 0.902079f, 0.831151f, 0.279263f, 0.16341f, 0.484302f, 0.72933f, 0.387216f, 0.569566f, 0.45344f, 0.0102607f, 0.825243f, 0.83024f, 0.979228f, 0.442771f, 0.310725f, 0.402858f, 0.461778f, 0.14812f, 0.0317997f, 0.717051f, 0.0604186f, 0.618468f, 0.0492072f, 0.599871f, 0.312727f, 0.382221f, 0.951812f, 0.624513f, 0.393165f, 0.513231f, 0.0888002f, 0.575316f, 0.981212f, 0.804795f, 0.232609f, 0.217389f, 0.598159f, 0.867742f, 0.73384f, 0.0526724f, 0.260079f, 0.090166f, 0.762985f, 0.505438f, 0.688391f, 0.288719f, 0.941857f, 0.337076f, 0.22697f, 0.546474f, 0.941129f, 0.0224484f, 0.935954f, 0.433329f, 0.704276f, 0.786624f, 0.330062f, 0.473554f, 0.87676f, 0.736697f, 0.446794f, 0.747896f, 0.549209f, 0.153091f, 0.605677f, 0.146957f, 0.339015f, 0.726374f, 0.794175f, 0.244086f, 0.740843f, 0.69477f, 0.258599f, 0.730722f, 0.770583f, 0.335409f, 0.767071f, 0.49522f, 0.823759f, 0.672325f, 0.218518f, 0.165381f, 0.926261f, 0.609653f, 0.814846f, 0.25484f, 0.50592f, 0.276402f, 0.305092f, 0.928709f, 0.464274f, 0.0429572f, 0.72875f, 0.637547f, 0.276336f, 0.960395f, 0.825131f, 0.673549f, 0.74663f, 0.668333f, 0.814704f, 0.531843f, 0.671311f, 0.517659f, 0.642449f, 0.330058f, 0.982237f, 0.22965f, 0.80466f, 0.286679f, 0.856804f, 0.467595f, 0.161684f, 0.283397f, 0.200054f, 0.248434f, 0.501641f, 0.103115f, 0.441998f, 0.282585f, 0.842862f, 0.85912f, 0.787201f, 0.918203f, 0.434491f, 0.794399f, 0.96039f, 0.863135f, 0.96714f, 0.194211f, 0.207324f, 0.791053f, 0.72079f, 0.817721f, 0.372375f, 0.464473f, 0.0271096f, 0.891279f, 0.262555f, 0.254244f, 0.979254f, 0.736216f, 0.433567f, 0.257552f, 0.291293f, 0.669188f, 0.853558f, 0.383845f, 0.830721f, 0.130974f, 0.592171f, 0.512846f, 0.0574251f, 0.294685f, 0.152698f, 0.377627f, 0.369082f, 0.49346f, 0.118749f, 0.691865f, 0.72017f, 0.725873f, 0.605833f, 0.190278f, 0.640364f, 0.387849f, 0.988137f, 0.965823f, 0.660953f, 0.557181f, 0.462348f, 0.683348f, 0.634576f, 0.00828866f, 0.383596f, 0.281685f, 0.225558f, 0.867167f, 0.837191f, 0.984686f, 0.962235f, 0.414891f, 0.768726f, 0.889075f, 0.365235f, 0.0193771f, 0.785464f, 0.689f, 0.118207f, 0.809213f, 0.644715f, 0.0895678f, 0.68734f, 0.938325f, 0.968847f, 0.222221f, 0.583679f, 0.0886811f, 0.896622f, 0.493602f, 0.0946965f, 0.895153f, 0.151909f, 0.0903177f, 0.878638f, 0.459014f, 0.326202f, 0.677977f, 0.412225f, 0.472516f, 0.359737f, 0.932372f, 0.688556f, 0.485646f, 0.138031f, 0.0843526f, 0.477332f, 0.527066f, 0.180172f, 0.190952f, 0.377464f, 0.222186f, 0.807485f, 0.156527f, 0.88742f, 0.620464f, 0.898782f, 0.902413f, 0.645157f, 0.706947f, 0.496106f, 0.774231f, 0.767642f, 0.0555941f, 0.180441f, 0.441941f, 0.661246f, 0.722948f, 0.722415f, 0.190972f, 0.496007f, 0.919428f, 0.864577f, 0.688838f, 0.852379f, 0.193949f, 0.687383f, 0.49037f, 0.249515f, 0.0840979f, 0.725509f, 0.407476f, 0.651491f, 0.964721f, 0.553528f, 0.324657f, 0.47439f, 0.540082f, 0.40971f, 0.65927f, 0.983206f, 0.838786f, 0.593293f, 0.701832f, 0.833532f, 0.287805f, 0.701958f, 0.351841f, 0.780056f, 0.537599f, 0.165685f, 0.632443f, 0.435529f, 0.211979f, 0.880507f, 0.385021f, 0.0536233f, 0.0290887f, 0.840358f, 0.461469f, 0.370987f, 0.0374959f, 0.917434f, 0.472945f, 0.405675f, 0.685636f, 0.534187f, 0.0667923f, 0.157026f, 0.39071f, 0.0452388f, 0.992444f, 0.406344f, 0.395951f, 0.381161f, 0.517792f, 0.0534366f, 0.444435f, 0.798599f, 0.786415f, 0.462585f, 0.351598f, 0.437231f, 0.56999f, 0.449992f, 0.064277f, 0.335519f, 0.685746f, 0.882709f, 0.797853f, 0.860123f, 0.406511f, 0.73633f, 0.62617f, 0.914445f, 0.45455f, 0.414489f, 0.929975f, 0.52839f, 0.890448f, 0.995366f, 0.0369483f, 0.622663f, 0.428134f, 0.946527f, 0.486138f, 0.999117f, 0.707297f, 0.811334f, 0.318182f, 0.890789f, 0.519228f, 0.687279f, 0.801478f, 0.966357f, 0.871907f, 0.719913f, 0.846193f, 0.0688309f, 0.263962f, 0.816755f, 0.110815f, 0.0177471f, 0.105377f, 0.344074f, 0.328131f, 0.490521f, 0.453925f, 0.962191f, 0.474573f, 0.94997f, 0.141027f, 0.556769f, 0.458985f, 0.410196f, 0.314396f, 0.0364757f, 0.337141f, 0.697773f, 0.141182f, 0.557394f, 0.509235f, 0.653931f, 0.190407f, 0.480778f, 0.451213f, 0.742876f, 0.420231f, 0.472908f, 0.974704f, 0.494564f, 0.663517f, 0.15869f, 0.575425f, 0.172545f, 0.756486f, 0.19783f, 0.205187f, 0.693371f, 0.987892f, 0.130968f, 0.139507f, 0.174616f, 0.0285297f, 0.57986f, 0.242783f, 0.946789f, 0.294873f, 0.286289f, 0.842276f, 0.495292f, 0.667497f, 0.673508f, 0.247564f, 0.492272f, 0.28271f, 0.351011f, 0.0595105f, 0.907666f, 0.681135f, 0.853171f, 0.926189f, 0.228839f, 0.0609327f, 0.197458f, 0.900772f, 0.917371f, 0.850818f, 0.159414f, 0.543723f, 0.0175441f, 0.669857f, 0.601804f, 0.110368f, 0.0595272f, 0.860969f, 0.280732f, 0.403566f, 0.545047f, 0.197876f, 0.404363f, 0.382416f, 0.0226324f, 0.720408f, 0.620136f, 0.00837592f, 0.711183f, 0.133732f, 0.154031f, 0.449703f, 0.708456f, 0.989872f, 0.668515f, 0.27845f, 0.197887f, 0.826491f, 0.897574f, 0.351038f, 0.913449f, 0.322435f, 0.811453f, 0.301645f, 0.591089f, 0.633521f, 0.0802442f, 0.620754f, 0.313418f, 0.078788f, 0.10971f, 0.307704f, 0.302105f, 0.369336f, 0.0445973f, 0.97052f, 0.155584f, 0.360939f, 0.645088f, 0.535253f, 0.115192f, 0.408823f, 0.95773f, 0.940367f, 0.58823f, 0.471225f, 0.41175f, 0.73397f, 0.195805f, 0.874543f, 0.607823f, 0.64851f, 0.283897f, 0.975832f, 0.828873f, 0.186f, 0.726743f, 0.251955f, 0.777001f, 0.527693f, 0.341379f, 0.189806f, 0.278871f, 0.856124f, 0.453466f, 0.289112f, 0.477751f, 0.955666f, 0.963604f, 0.597168f, 0.915867f, 0.136414f, 0.0122043f, 0.479116f, 0.5356f, 0.00623677f, 0.364859f, 0.163833f, 0.812174f, 0.50395f, 0.582941f, 0.999247f, 0.728507f, 0.676842f, 0.756731f, 0.575742f, 0.808933f, 0.0493407f, 0.686227f, 0.741418f, 0.798913f, 0.838792f, 0.171739f, 0.0747538f, 0.112081f, 0.46376f, 0.556256f, 0.412f, 0.256525f, 0.503305f, 0.0688311f, 0.00604754f, 0.222702f, 0.275686f, 0.763512f, 0.822978f, 0.073417f, 0.578545f, 0.745573f, 0.466233f, 0.661539f, 0.493129f, 0.435046f, 0.876807f, 0.739943f, 0.809791f, 0.325968f, 0.702377f, 0.365711f, 0.0501611f, 0.536783f, 0.504106f, 0.660144f, 0.456292f, 0.569096f, 0.693448f, 0.933345f, 0.351839f, 0.884895f, 0.236782f, 0.970231f, 0.959083f, 0.116632f, 0.0252782f, 0.336278f, 0.293289f, 0.346459f, 0.36785f, 0.640265f, 0.87248f, 0.308247f, 0.655107f, 0.434537f, 0.151455f, 0.951242f, 0.116024f, 0.17915f, 0.549114f, 0.112509f, 0.894878f, 0.528591f, 0.993906f, 0.001859f, 0.786975f, 0.353469f, 0.71474f, 0.404946f, 0.649241f, 0.370904f, 0.090338f, 0.426768f, 0.98137f, 0.0955701f, 0.419284f, 0.703323f, 0.294611f, 0.381708f, 0.29351f, 0.615267f, 0.734035f, 0.459824f, 0.270827f, 0.636759f, 0.363248f, 0.410709f, 0.448557f, 0.177182f, 0.185375f, 0.488887f, 0.107553f, 0.532059f, 0.681823f, 0.419149f, 0.84382f, 0.447118f, 0.329585f, 0.485629f, 0.667868f, 0.622959f, 0.12827f, 0.451185f, 0.91336f, 0.544708f, 0.514912f, 0.852557f, 0.389978f, 0.793144f, 0.0883296f, 0.119997f, 0.406851f, 0.418578f, 0.165114f, 0.322287f, 0.0561834f, 0.849824f, 0.937688f, 0.289658f, 0.737153f, 0.306842f, 0.364817f, 0.977543f, 0.287433f, 0.330581f, 0.00949264f, 0.372674f, 0.94445f, 0.15235f, 0.561174f, 0.0287323f, 0.387638f, 0.591322f, 0.0456074f, 0.739486f, 0.65856f, 0.0697645f, 0.138903f, 0.201824f, 0.443179f, 0.457832f, 0.381976f, 0.670797f, 0.490463f, 0.0585273f, 0.131421f, 0.582455f, 0.259635f, 0.926849f, 0.620655f, 0.871655f, 0.248124f, 0.169097f, 0.364202f, 0.782716f, 0.678271f, 0.865586f, 0.996689f, 0.475687f, 0.497345f, 0.88648f, 0.412128f, 0.781793f, 0.732189f, 0.558032f, 0.572376f, 0.780656f, 0.850672f, 0.0597889f, 0.566659f, 0.927589f, 0.846964f, 0.456838f, 0.754753f, 0.719845f, 0.0291798f, 0.136967f, 0.697342f, 0.9079f, 0.953422f, 0.609963f, 0.849033f, 0.081894f, 0.225697f, 0.110682f, 0.275933f, 0.780181f, 0.894441f, 0.446063f, 0.862058f, 0.618956f, 0.828161f, 0.717655f, 0.0985824f, 0.440302f, 0.648364f, 0.18779f, 0.591757f, 0.230007f, 0.45618f, 0.428414f, 0.605482f, 0.738587f, 0.317486f, 0.819181f, 0.469752f, 0.252858f, 0.397668f, 0.871022f, 0.519701f, 0.336924f, 0.728867f, 0.754736f, 0.128085f, 0.799174f, 0.664963f, 0.980482f, 0.330307f, 0.590498f, 0.289799f, 0.157877f, 0.791543f, 0.52898f, 0.947847f, 0.582822f, 0.282132f, 0.619892f, 0.9265f, 0.833659f, 0.449337f, 0.735893f, 0.400665f, 0.771264f, 0.922145f, 0.575518f, 0.263008f, 0.359458f, 0.810388f, 0.0378245f, 0.415982f, 0.334204f, 0.563992f, 0.026879f, 0.732293f, 0.247788f, 0.757495f, 0.350912f, 0.667814f, 0.166229f, 0.634985f, 0.841521f, 0.311337f, 0.275023f, 0.185421f, 0.0436381f, 0.0335705f, 0.836143f, 0.395044f, 0.863799f, 0.269073f, 0.537374f, 0.441947f, 0.912603f, 0.751847f, 0.619459f, 0.99016f, 0.586118f, 0.277018f, 0.0735598f, 0.796478f, 0.378006f, 0.337891f, 0.462994f, 0.443753f, 0.369374f, 0.171287f, 0.435018f, 0.0525496f, 0.628859f, 0.543722f, 0.0924772f, 0.379519f, 0.0780981f, 0.326687f, 0.832182f, 0.645698f, 0.328377f, 0.32004f, 0.742632f, 0.0109052f, 0.0382239f, 0.0871899f, 0.814253f, 0.736762f, 0.162475f, 0.617694f, 0.0498015f, 0.740115f, 0.297816f, 0.961835f, 0.608739f, 0.659744f, 0.840528f, 0.900367f, 0.809166f, 0.475074f, 0.244182f, 0.199202f, 0.301044f, 0.0422551f, 0.024888f, 0.384151f, 0.874764f, 0.899893f, 0.378208f, 0.0198433f, 0.0492818f, 0.987838f, 0.357313f, 0.81329f, 0.333485f, 0.0034893f, 0.424941f, 0.427751f, 0.426392f, 0.529557f, 0.932441f, 0.707156f, 0.229742f, 0.127445f, 0.444714f, 0.213146f, 0.51593f, 0.491603f, 0.995046f, 0.085205f, 0.910668f, 0.989715f, 0.75172f, 0.298998f, 0.485693f, 0.948471f, 0.762442f, 0.403124f, 0.314823f, 0.99807f, 0.844695f, 0.0442568f, 0.995461f, 0.482983f, 0.456438f, 0.226087f, 0.920809f, 0.696301f, 0.0548892f, 0.39318f, 0.402514f, 0.140304f, 0.893837f, 0.527382f, 0.352871f, 0.776351f, 0.240449f, 0.73134f, 0.167542f, 0.0075921f, 0.65933f, 0.540461f, 0.35797f, 0.729003f, 0.0123079f, 0.513657f, 0.782139f, 0.312166f, 0.761906f, 0.403939f, 0.503056f, 0.956257f, 0.884943f, 0.4947f, 0.624594f, 0.138647f, 0.391437f, 0.705802f, 0.905201f, 0.761903f, 0.358508f, 0.711942f, 0.909775f, 0.83069f, 0.86636f, 0.232384f, 0.552084f, 0.292675f, 0.651875f, 0.581f, 0.578593f, 0.565356f, 0.704186f, 0.302158f, 0.0271689f, 0.155376f, 0.643945f, 0.640294f, 0.687746f, 0.899344f, 0.170393f, 0.657124f, 0.707385f, 0.39553f, 0.896437f, 0.268983f, 0.17986f, 0.231909f, 0.398636f, 0.316518f, 0.785666f, 0.067839f, 0.714167f, 0.693223f, 0.097405f, 0.0248107f, 0.810133f, 0.478633f, 0.374025f, 0.162413f, 0.331021f, 0.807649f, 0.558047f, 0.0956828f, 0.895993f, 0.960058f, 0.836049f, 0.407607f, 0.314822f, 0.899134f, 0.710449f, 0.637691f, 0.305183f, 0.773992f, 0.0987311f, 0.663668f, 0.427594f, 0.878348f, 0.357822f, 0.893242f, 0.557479f, 0.449238f, 0.282402f, 0.0729389f, 0.23156f, 0.909461f, 0.355742f, 0.525915f, 0.192253f, 0.282961f, 0.912528f, 0.397635f, 0.238035f, 0.352071f, 0.309582f, 0.0254188f, 0.680727f, 0.580528f, 0.226076f, 0.0834766f, 0.99638f, 0.092587f, 0.207641f, 0.568604f, 0.105056f, 0.449811f, 0.993625f, 0.968602f, 0.578929f, 0.888577f, 0.0297789f, 0.126847f, 0.318984f, 0.966938f, 0.598548f, 0.45569f, 0.272808f, 0.487651f, 0.0857943f, 0.976802f, 0.322319f, 0.959471f, 0.875184f, 0.747474f, 0.0323492f, 0.543811f, 0.758507f, 0.203439f, 0.813299f, 0.640533f, 0.580237f, 0.431538f, 0.628574f, 0.141506f, 0.557522f, 0.128557f, 0.336873f, 0.845508f, 0.496566f, 0.480398f, 0.985445f, 0.596746f, 0.0165834f, 0.249056f, 0.356097f, 0.999878f, 0.554944f, 0.247444f, 0.118408f, 0.398287f, 0.20773f, 0.499191f, 0.175335f, 0.920718f, 0.35812f, 0.0419942f, 0.189497f, 0.875807f, 0.648631f, 0.592449f, 0.631741f, 0.251013f, 0.747971f, 0.134103f, 0.151428f, 0.769865f, 0.539513f, 0.179694f, 0.489352f, 0.95986f, 0.760867f, 0.0862049f, 0.416983f, 0.28194f, 0.284409f, 0.17071f, 0.778147f, 0.57643f, 0.675305f, 0.361706f, 0.623922f, 0.93304f, 0.670333f, 0.364057f, 0.29479f, 0.34523f, 0.0802066f, 0.436239f, 0.904354f, 0.453449f, 0.217134f, 0.690289f, 0.227549f, 0.29241f, 0.104502f, 0.844798f, 0.0197631f, 0.501341f, 0.309698f, 0.630886f, 0.375497f, 0.355144f, 0.0789696f, 0.113972f, 0.577538f, 0.0786076f, 0.861591f, 0.444031f, 0.986486f, 0.548007f, 0.368182f, 0.697011f, 0.574514f, 0.300031f, 0.859686f, 0.18623f, 0.22011f, 0.929404f, 0.353108f, 0.122265f, 0.947966f, 0.581157f, 0.769062f, 0.476407f, 0.762758f, 0.379025f, 0.993169f, 0.406245f, 0.0617744f, 0.070429f, 0.0716642f, 0.654016f, 0.149589f, 0.665272f, 0.789747f, 0.22467f, 0.802917f, 0.135386f, 0.757977f, 0.44448f, 0.364761f, 0.424476f, 0.958102f, 0.928369f, 0.0567048f, 0.786621f, 0.594191f, 0.307051f, 0.456337f, 0.499783f, 0.553886f, 0.984526f, 0.419944f, 0.0435374f, 0.81127f, 0.330844f, 0.177709f, 0.516573f, 0.065382f, 0.986808f, 0.957733f, 0.792253f, 0.546296f, 0.866316f, 0.394275f, 0.967583f, 0.349592f, 0.785577f, 0.605621f, 0.80655f, 0.921511f, 0.115602f, 0.666941f, 0.0162641f, 0.258044f, 0.514019f, 0.999275f, 0.397627f, 0.761203f, 0.623303f, 0.887412f, 0.328683f, 0.162693f, 0.86742f, 0.729498f, 0.497044f, 0.269715f, 0.543416f, 0.783704f, 0.937763f, 0.847075f, 0.000150703f, 0.859629f, 0.277036f, 0.224368f, 0.93096f, 0.214922f, 0.579817f, 0.933972f, 0.024961f, 0.770249f, 0.254487f, 0.348464f, 0.198163f, 0.988963f, 0.857047f, 0.928872f, 0.167685f, 0.290298f, 0.627398f, 0.056714f, 0.539087f, 0.757458f, 0.655387f, 0.279938f, 0.166857f, 0.103837f, 0.116049f, 0.250189f, 0.0954196f, 0.568939f, 0.498053f, 0.462467f, 0.220062f, 0.390086f, 0.575033f, 0.610268f, 0.560678f, 0.739254f, 0.171565f, 0.319325f, 0.342327f, 0.88511f, 0.913376f, 0.966034f, 0.0165156f, 0.832042f, 0.196697f, 0.828866f, 0.462681f, 0.539315f, 0.787014f, 0.292822f, 0.929346f, 0.77743f, 0.990875f, 0.110681f, 0.603815f, 0.0694197f, 0.722705f, 0.119308f, 0.215838f, 0.854947f, 0.994762f, 0.786714f, 0.758008f, 0.567701f, 0.718539f, 0.458692f, 0.480216f, 0.283969f, 0.940137f, 0.644423f, 0.0900196f, 0.50175f, 0.914273f, 0.322007f, 0.855845f, 0.248038f, 0.0408285f, 0.187233f, 0.682203f, 0.691125f, 0.00797575f, 0.755959f, 0.230402f, 0.0673265f, 0.995006f, 0.274327f, 0.379287f, 0.000769862f, 0.403929f, 0.64495f, 0.396822f, 0.820504f, 0.570652f, 0.82839f, 0.703895f, 0.646681f, 0.140596f, 0.648353f, 0.999623f, 0.307982f, 0.431947f, 0.977372f, 0.294972f, 0.398959f, 0.91964f, 0.0646844f, 0.136599f, 0.182582f, 0.395355f, 0.352332f, 0.731837f, 0.997498f, 0.58474f, 0.94612f, 0.425731f, 0.776968f, 0.68292f, 0.413f, 0.0523979f, 0.759704f, 0.500116f, 0.206328f, 0.321243f, 0.825128f, 0.875235f, 0.470629f, 0.188371f, 0.896929f, 0.953573f, 0.557368f, 0.50405f, 0.303513f, 0.104388f, 0.849017f, 0.303644f, 0.367789f, 0.224046f, 0.183725f, 0.706195f, 0.449891f, 0.0984459f, 0.233854f, 0.994162f, 0.618259f, 0.20771f, 0.151375f, 0.583477f, 0.671431f, 0.217394f, 0.760815f, 0.0605553f, 0.0583726f, 0.817587f, 0.407531f, 0.350748f, 0.247787f, 0.784774f, 0.777238f, 0.27566f, 0.852514f, 0.42037f, 0.575159f, 0.373788f, 0.849715f, 0.0519931f, 0.0296441f, 0.294397f, 0.469656f, 0.283464f, 0.547492f, 0.765641f, 0.876838f, 0.68951f, 0.200328f, 0.212925f, 0.0421688f, 0.708939f, 0.261481f, 0.0727068f, 0.0934254f, 0.310433f, 0.672616f, 0.851943f, 0.019142f, 0.944373f, 0.217421f, 0.82427f, 0.363243f, 0.589759f, 0.77299f, 0.279507f, 0.748085f, 0.367985f, 0.465101f, 0.526916f, 0.747114f, 0.483725f, 0.68636f, 0.204161f, 0.0154765f, 0.479409f, 0.0406713f, 0.899257f, 0.311771f, 0.288404f, 0.751046f, 0.539867f, 0.0692004f, 0.173594f, 0.157303f, 0.328307f, 0.208754f, 0.703703f, 0.0121021f, 0.877912f, 0.996462f, 0.549136f, 0.13571f, 0.595115f, 0.0199654f, 0.145283f, 0.253164f, 0.49289f, 0.0554075f, 0.935406f, 0.956951f, 0.309987f, 0.10866f, 0.440692f, 0.944965f, 0.870117f, 0.57584f, 0.434586f, 0.665538f, 0.562851f, 0.168194f, 0.25867f, 0.452927f, 0.229477f, 0.798993f, 0.202924f, 0.698402f, 0.312233f, 0.344296f, 0.83783f, 0.915106f, 0.458815f, 0.796959f, 0.477005f, 0.618763f, 0.0222967f, 0.832104f, 0.538453f, 0.792362f, 0.68076f, 0.934944f, 0.87048f, 0.701026f, 0.201056f, 0.6455f, 0.583038f, 0.838607f, 0.231355f, 0.333748f, 0.99008f, 0.297537f, 0.0948004f, 0.858526f, 0.215819f, 0.496051f, 0.5593f, 0.578594f, 0.0118885f, 0.194321f, 0.166925f, 0.931318f, 0.272411f, 0.994863f, 0.488485f, 0.0807225f, 0.629759f, 0.943775f, 0.827614f, 0.198074f, 0.537356f, 0.116548f, 0.34368f, 0.323439f, 0.6756f, 0.918383f, 0.156711f, 0.77568f, 0.985563f, 0.017991f, 0.338861f, 0.600807f, 0.749086f, 0.322985f, 0.577408f, 0.362404f, 0.302229f, 0.981125f, 0.203732f, 0.951463f, 0.977094f, 0.908191f, 0.889876f, 0.612276f, 0.0671729f, 0.860688f, 0.243544f, 0.221602f, 0.0761352f, 0.985573f, 0.728187f, 0.0547594f, 0.930508f, 0.834965f, 0.433701f, 0.164098f, 0.837718f, 0.134149f, 0.388824f, 0.889434f, 0.503706f, 0.34923f, 0.937185f, 0.794077f, 0.0465971f, 0.874813f, 0.0198799f, 0.894076f, 0.49266f, 0.658546f, 0.426154f, 0.346393f, 0.570111f, 0.33153f, 0.751645f, 0.310625f, 0.287591f, 0.463839f, 0.922595f, 0.757236f, 0.771138f, 0.610565f, 0.993458f, 0.922766f, 0.123742f, 0.618135f, 0.971909f, 0.667206f, 0.440029f, 0.238731f, 0.576817f, 0.46517f, 0.648818f, 0.9544f, 0.189489f, 0.261763f, 0.305165f, 0.957384f, 0.0174079f, 0.837398f, 0.820163f, 0.840213f, 0.4588f, 0.975611f, 0.588482f, 0.852806f, 0.752008f, 0.0688132f, 0.815652f, 0.689978f, 0.0190042f, 0.602518f, 0.383025f, 0.363462f, 0.218776f, 0.920785f, 0.0865443f, 0.493966f, 0.554017f, 0.113529f, 0.742798f, 0.907957f, 0.0754802f, 0.0815597f, 0.849693f, 0.548453f, 0.91118f, 0.196587f, 0.861798f, 0.371588f, 0.749149f, 0.549719f, 0.168729f, 0.481784f, 0.310422f, 0.714568f, 0.640989f, 0.582094f, 0.157668f, 0.0519072f, 0.0685954f, 0.0557621f, 0.43973f, 0.360242f, 0.353566f};
155 | private int pos = -1;
156 |
157 | @Override
158 | public float nextFloat() {
159 | pos++;
160 | pos = pos % FLOAT_VALS.length;
161 | return FLOAT_VALS[pos];
162 | }
163 |
164 | @Override
165 | public int nextInt() {
166 | throw new UnsupportedOperationException();
167 | }
168 | }
169 |
170 |
171 | /**
172 | * This generator doesn't do anything to the provided inputs.
173 | *
174 | */
175 | protected static class DummyPatchGenerator extends PatchGenerator {
176 | DummyPatchGenerator() {
177 | super(0, 0, 0, false, 0, 0, 0, 0, 0, 0);
178 | }
179 |
180 | void generate(final Mat image, Point pt, Mat patch, Size patchSize, final RNG rng) {
181 | // do NOTHING
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/tld-test/src/com/trandi/opentld/tld/TldTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 Dan Oprescu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.trandi.opentld.tld;
18 |
19 | import org.opencv.core.CvType;
20 | import org.opencv.core.Mat;
21 | import org.opencv.core.Size;
22 |
23 | import com.trandi.opentld.tld.Parameters.ParamsTld;
24 |
25 |
26 | public class TldTest extends OpenCVTestCase {
27 | private static final float[] P_EXAMPLE = new float[]
28 | {-17.6667f, -3.66667f, 13.3333f,
29 | 12.3333f, -22.6667f, 10.3333f,
30 | 2.33333f, 19.3333f, -13.6667f};
31 |
32 | private static final float[] PATTERN = new float[]
33 | {10.2222f, -22.7778f, -3.77778f,
34 | 20.2222f, -23.7778f, 6.22222f,
35 | 11.2222f, 0.222222f, 2.22222f};
36 | private static final int[] EXPECTED_FERN = new int[]{4384, 84, 4290, 2065, 1237, 5719, 7926, 6400, 265, 1037};
37 |
38 |
39 |
40 |
41 | public void testGetPattern(){
42 | final int patch_size = 3;
43 |
44 | final Mat img = getSimpleMat();
45 | // manual init
46 | final Tld tld = new Tld();
47 | tld._params = new DummyParamsTld(1, patch_size);
48 | Mat pattern = new Mat(patch_size, patch_size, CvType.CV_64F);
49 | final double stdev = tld.getPattern(img, pattern);
50 |
51 | final float[] patternF = Util.getFloatArray(pattern);
52 | for(int i=0; i boxes = new ArrayList();
36 | for(int i=1; i<=100; i++){
37 | final BoundingBox box = new BoundingBox(new Point(10 * i, i), new Point(20 * i, i + 10));
38 | box.overlap = i;
39 | boxes.add(box);
40 | }
41 |
42 | Util.keepBestN(boxes, 10, new Comparator(){
43 | @Override
44 | public int compare(BoundingBox box1, BoundingBox box2) {
45 | return Float.valueOf(box1.overlap).compareTo(box2.overlap);
46 | }
47 | });
48 |
49 | assertEquals(10, boxes.size());
50 | assertEquals(91f, boxes.get(0).overlap);
51 | assertEquals(100f, boxes.get(9).overlap);
52 | }
53 |
54 |
55 | public void testToByteArray(){
56 | final Mat greyMat = new Mat();
57 | Imgproc.cvtColor(getTestMat(), greyMat, Imgproc.COLOR_RGB2GRAY);
58 | // make sure we have some extreme values
59 | greyMat.put(0, 1, new byte[]{-128});
60 | greyMat.put(0, 2, new byte[]{-0});
61 | greyMat.put(0, 3, new byte[]{127});
62 | final byte[] array = Util.getByteArray(greyMat);
63 |
64 | assertEquals(2, array[0]);
65 | assertEquals(-128, array[1]);
66 | assertEquals(0, array[2]);
67 | assertEquals(127, array[3]);
68 | assertEquals(8, array[500]);
69 | assertEquals(9, array[1000]);
70 | assertEquals(9, array[1500]);
71 | assertEquals(17, array[2000]);
72 | assertEquals(18, array[3000]);
73 | assertEquals(3, array[4000]);
74 | assertEquals(8, array[5000]);
75 | assertEquals(9, array[6000]);
76 | assertEquals(3, array[7000]);
77 | assertEquals(5, array[8000]);
78 | assertEquals(15, array[9000]);
79 | assertEquals(9, array[12000]);
80 | assertEquals(4, array[15000]);
81 |
82 | final int cols = greyMat.cols();
83 | for(int row=0; row