1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 package org.tensorflow.demo.tracking;
17 
18 import android.graphics.Canvas;
19 import android.graphics.Color;
20 import android.graphics.Matrix;
21 import android.graphics.Paint;
22 import android.graphics.PointF;
23 import android.graphics.RectF;
24 import android.graphics.Typeface;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Vector;
31 import javax.microedition.khronos.opengles.GL10;
32 import org.tensorflow.demo.env.Logger;
33 import org.tensorflow.demo.env.Size;
34 
35 /**
36  * True object detector/tracker class that tracks objects across consecutive preview frames.
37  * It provides a simplified Java interface to the analogous native object defined by
38  * jni/client_vision/tracking/object_tracker.*.
39  *
40  * Currently, the ObjectTracker is a singleton due to native code restrictions, and so must
41  * be allocated by ObjectTracker.getInstance(). In addition, release() should be called
42  * as soon as the ObjectTracker is no longer needed, and before a new one is created.
43  *
44  * nextFrame() should be called as new frames become available, preferably as often as possible.
45  *
46  * After allocation, new TrackedObjects may be instantiated via trackObject(). TrackedObjects
47  * are associated with the ObjectTracker that created them, and are only valid while that
48  * ObjectTracker still exists.
49  */
50 public class ObjectTracker {
51   private static final Logger LOGGER = new Logger();
52 
53   private static boolean libraryFound = false;
54 
55   static {
56     try {
57       System.loadLibrary("tensorflow_demo");
58       libraryFound = true;
59     } catch (UnsatisfiedLinkError e) {
60       LOGGER.e("libtensorflow_demo.so not found, tracking unavailable");
61     }
62   }
63 
64   private static final boolean DRAW_TEXT = false;
65 
66   /**
67    * How many history points to keep track of and draw in the red history line.
68    */
69   private static final int MAX_DEBUG_HISTORY_SIZE = 30;
70 
71   /**
72    * How many frames of optical flow deltas to record.
73    * TODO(andrewharp): Push this down to the native level so it can be polled
74    * efficiently into a an array for upload, instead of keeping a duplicate
75    * copy in Java.
76    */
77   private static final int MAX_FRAME_HISTORY_SIZE = 200;
78 
79   private static final int DOWNSAMPLE_FACTOR = 2;
80 
81   private final byte[] downsampledFrame;
82 
83   protected static ObjectTracker instance;
84 
85   private final Map<String, TrackedObject> trackedObjects;
86 
87   private long lastTimestamp;
88 
89   private FrameChange lastKeypoints;
90 
91   private final Vector<PointF> debugHistory;
92 
93   private final LinkedList<TimestampedDeltas> timestampedDeltas;
94 
95   protected final int frameWidth;
96   protected final int frameHeight;
97   private final int rowStride;
98   protected final boolean alwaysTrack;
99 
100   private static class TimestampedDeltas {
101     final long timestamp;
102     final byte[] deltas;
103 
TimestampedDeltas(final long timestamp, final byte[] deltas)104     public TimestampedDeltas(final long timestamp, final byte[] deltas) {
105       this.timestamp = timestamp;
106       this.deltas = deltas;
107     }
108   }
109 
110   /**
111    * A simple class that records keypoint information, which includes
112    * local location, score and type. This will be used in calculating
113    * FrameChange.
114    */
115   public static class Keypoint {
116     public final float x;
117     public final float y;
118     public final float score;
119     public final int type;
120 
Keypoint(final float x, final float y)121     public Keypoint(final float x, final float y) {
122       this.x = x;
123       this.y = y;
124       this.score = 0;
125       this.type = -1;
126     }
127 
Keypoint(final float x, final float y, final float score, final int type)128     public Keypoint(final float x, final float y, final float score, final int type) {
129       this.x = x;
130       this.y = y;
131       this.score = score;
132       this.type = type;
133     }
134 
delta(final Keypoint other)135     Keypoint delta(final Keypoint other) {
136       return new Keypoint(this.x - other.x, this.y - other.y);
137     }
138   }
139 
140   /**
141    * A simple class that could calculate Keypoint delta.
142    * This class will be used in calculating frame translation delta
143    * for optical flow.
144    */
145   public static class PointChange {
146     public final Keypoint keypointA;
147     public final Keypoint keypointB;
148     Keypoint pointDelta;
149     private final boolean wasFound;
150 
PointChange(final float x1, final float y1, final float x2, final float y2, final float score, final int type, final boolean wasFound)151     public PointChange(final float x1, final float y1,
152                        final float x2, final float y2,
153                        final float score, final int type,
154                        final boolean wasFound) {
155       this.wasFound = wasFound;
156 
157       keypointA = new Keypoint(x1, y1, score, type);
158       keypointB = new Keypoint(x2, y2);
159     }
160 
getDelta()161     public Keypoint getDelta() {
162       if (pointDelta == null) {
163         pointDelta = keypointB.delta(keypointA);
164       }
165       return pointDelta;
166     }
167   }
168 
169   /** A class that records a timestamped frame translation delta for optical flow. */
170   public static class FrameChange {
171     public static final int KEYPOINT_STEP = 7;
172 
173     public final Vector<PointChange> pointDeltas;
174 
175     private final float minScore;
176     private final float maxScore;
177 
FrameChange(final float[] framePoints)178     public FrameChange(final float[] framePoints) {
179       float minScore = 100.0f;
180       float maxScore = -100.0f;
181 
182       pointDeltas = new Vector<PointChange>(framePoints.length / KEYPOINT_STEP);
183 
184       for (int i = 0; i < framePoints.length; i += KEYPOINT_STEP) {
185         final float x1 = framePoints[i + 0] * DOWNSAMPLE_FACTOR;
186         final float y1 = framePoints[i + 1] * DOWNSAMPLE_FACTOR;
187 
188         final boolean wasFound = framePoints[i + 2] > 0.0f;
189 
190         final float x2 = framePoints[i + 3] * DOWNSAMPLE_FACTOR;
191         final float y2 = framePoints[i + 4] * DOWNSAMPLE_FACTOR;
192         final float score = framePoints[i + 5];
193         final int type = (int) framePoints[i + 6];
194 
195         minScore = Math.min(minScore, score);
196         maxScore = Math.max(maxScore, score);
197 
198         pointDeltas.add(new PointChange(x1, y1, x2, y2, score, type, wasFound));
199       }
200 
201       this.minScore = minScore;
202       this.maxScore = maxScore;
203     }
204   }
205 
getInstance( final int frameWidth, final int frameHeight, final int rowStride, final boolean alwaysTrack)206   public static synchronized ObjectTracker getInstance(
207       final int frameWidth, final int frameHeight, final int rowStride, final boolean alwaysTrack) {
208     if (!libraryFound) {
209       LOGGER.e(
210           "Native object tracking support not found. "
211               + "See tensorflow/tools/android/test/README.md for details.");
212       return null;
213     }
214 
215     if (instance == null) {
216       instance = new ObjectTracker(frameWidth, frameHeight, rowStride, alwaysTrack);
217       instance.init();
218     } else {
219       throw new RuntimeException(
220           "Tried to create a new objectracker before releasing the old one!");
221     }
222     return instance;
223   }
224 
clearInstance()225   public static synchronized void clearInstance() {
226     if (instance != null) {
227       instance.release();
228     }
229   }
230 
ObjectTracker( final int frameWidth, final int frameHeight, final int rowStride, final boolean alwaysTrack)231   protected ObjectTracker(
232       final int frameWidth, final int frameHeight, final int rowStride, final boolean alwaysTrack) {
233     this.frameWidth = frameWidth;
234     this.frameHeight = frameHeight;
235     this.rowStride = rowStride;
236     this.alwaysTrack = alwaysTrack;
237     this.timestampedDeltas = new LinkedList<TimestampedDeltas>();
238 
239     trackedObjects = new HashMap<String, TrackedObject>();
240 
241     debugHistory = new Vector<PointF>(MAX_DEBUG_HISTORY_SIZE);
242 
243     downsampledFrame =
244         new byte
245             [(frameWidth + DOWNSAMPLE_FACTOR - 1)
246                 / DOWNSAMPLE_FACTOR
247                 * (frameHeight + DOWNSAMPLE_FACTOR - 1)
248                 / DOWNSAMPLE_FACTOR];
249   }
250 
init()251   protected void init() {
252     // The native tracker never sees the full frame, so pre-scale dimensions
253     // by the downsample factor.
254     initNative(frameWidth / DOWNSAMPLE_FACTOR, frameHeight / DOWNSAMPLE_FACTOR, alwaysTrack);
255   }
256 
257   private final float[] matrixValues = new float[9];
258 
259   private long downsampledTimestamp;
260 
261   @SuppressWarnings("unused")
drawOverlay(final GL10 gl, final Size cameraViewSize, final Matrix matrix)262   public synchronized void drawOverlay(final GL10 gl,
263       final Size cameraViewSize, final Matrix matrix) {
264     final Matrix tempMatrix = new Matrix(matrix);
265     tempMatrix.preScale(DOWNSAMPLE_FACTOR, DOWNSAMPLE_FACTOR);
266     tempMatrix.getValues(matrixValues);
267     drawNative(cameraViewSize.width, cameraViewSize.height, matrixValues);
268   }
269 
nextFrame( final byte[] frameData, final byte[] uvData, final long timestamp, final float[] transformationMatrix, final boolean updateDebugInfo)270   public synchronized void nextFrame(
271       final byte[] frameData, final byte[] uvData,
272       final long timestamp, final float[] transformationMatrix,
273       final boolean updateDebugInfo) {
274     if (downsampledTimestamp != timestamp) {
275       ObjectTracker.downsampleImageNative(
276           frameWidth, frameHeight, rowStride, frameData, DOWNSAMPLE_FACTOR, downsampledFrame);
277       downsampledTimestamp = timestamp;
278     }
279 
280     // Do Lucas Kanade using the fullframe initializer.
281     nextFrameNative(downsampledFrame, uvData, timestamp, transformationMatrix);
282 
283     timestampedDeltas.add(new TimestampedDeltas(timestamp, getKeypointsPacked(DOWNSAMPLE_FACTOR)));
284     while (timestampedDeltas.size() > MAX_FRAME_HISTORY_SIZE) {
285       timestampedDeltas.removeFirst();
286     }
287 
288     for (final TrackedObject trackedObject : trackedObjects.values()) {
289       trackedObject.updateTrackedPosition();
290     }
291 
292     if (updateDebugInfo) {
293       updateDebugHistory();
294     }
295 
296     lastTimestamp = timestamp;
297   }
298 
release()299   public synchronized void release() {
300     releaseMemoryNative();
301     synchronized (ObjectTracker.class) {
302       instance = null;
303     }
304   }
305 
drawHistoryDebug(final Canvas canvas)306   private void drawHistoryDebug(final Canvas canvas) {
307     drawHistoryPoint(
308         canvas, frameWidth * DOWNSAMPLE_FACTOR / 2, frameHeight * DOWNSAMPLE_FACTOR / 2);
309   }
310 
drawHistoryPoint(final Canvas canvas, final float startX, final float startY)311   private void drawHistoryPoint(final Canvas canvas, final float startX, final float startY) {
312     final Paint p = new Paint();
313     p.setAntiAlias(false);
314     p.setTypeface(Typeface.SERIF);
315 
316     p.setColor(Color.RED);
317     p.setStrokeWidth(2.0f);
318 
319     // Draw the center circle.
320     p.setColor(Color.GREEN);
321     canvas.drawCircle(startX, startY, 3.0f, p);
322 
323     p.setColor(Color.RED);
324 
325     // Iterate through in backwards order.
326     synchronized (debugHistory) {
327       final int numPoints = debugHistory.size();
328       float lastX = startX;
329       float lastY = startY;
330       for (int keypointNum = 0; keypointNum < numPoints; ++keypointNum) {
331         final PointF delta = debugHistory.get(numPoints - keypointNum - 1);
332         final float newX = lastX + delta.x;
333         final float newY = lastY + delta.y;
334         canvas.drawLine(lastX, lastY, newX, newY, p);
335         lastX = newX;
336         lastY = newY;
337       }
338     }
339   }
340 
floatToChar(final float value)341   private static int floatToChar(final float value) {
342     return Math.max(0, Math.min((int) (value * 255.999f), 255));
343   }
344 
drawKeypointsDebug(final Canvas canvas)345   private void drawKeypointsDebug(final Canvas canvas) {
346     final Paint p = new Paint();
347     if (lastKeypoints == null) {
348       return;
349     }
350     final int keypointSize = 3;
351 
352     final float minScore = lastKeypoints.minScore;
353     final float maxScore = lastKeypoints.maxScore;
354 
355     for (final PointChange keypoint : lastKeypoints.pointDeltas) {
356       if (keypoint.wasFound) {
357         final int r =
358             floatToChar((keypoint.keypointA.score - minScore) / (maxScore - minScore));
359         final int b =
360             floatToChar(1.0f - (keypoint.keypointA.score - minScore) / (maxScore - minScore));
361 
362         final int color = 0xFF000000 | (r << 16) | b;
363         p.setColor(color);
364 
365         final float[] screenPoints = {keypoint.keypointA.x, keypoint.keypointA.y,
366                                       keypoint.keypointB.x, keypoint.keypointB.y};
367         canvas.drawRect(screenPoints[2] - keypointSize,
368                         screenPoints[3] - keypointSize,
369                         screenPoints[2] + keypointSize,
370                         screenPoints[3] + keypointSize, p);
371         p.setColor(Color.CYAN);
372         canvas.drawLine(screenPoints[2], screenPoints[3],
373                         screenPoints[0], screenPoints[1], p);
374 
375         if (DRAW_TEXT) {
376           p.setColor(Color.WHITE);
377           canvas.drawText(keypoint.keypointA.type + ": " + keypoint.keypointA.score,
378               keypoint.keypointA.x, keypoint.keypointA.y, p);
379         }
380       } else {
381         p.setColor(Color.YELLOW);
382         final float[] screenPoint = {keypoint.keypointA.x, keypoint.keypointA.y};
383         canvas.drawCircle(screenPoint[0], screenPoint[1], 5.0f, p);
384       }
385     }
386   }
387 
getAccumulatedDelta(final long timestamp, final float positionX, final float positionY, final float radius)388   private synchronized PointF getAccumulatedDelta(final long timestamp, final float positionX,
389       final float positionY, final float radius) {
390     final RectF currPosition = getCurrentPosition(timestamp,
391         new RectF(positionX - radius, positionY - radius, positionX + radius, positionY + radius));
392     return new PointF(currPosition.centerX() - positionX, currPosition.centerY() - positionY);
393   }
394 
getCurrentPosition(final long timestamp, final RectF oldPosition)395   private synchronized RectF getCurrentPosition(final long timestamp, final RectF
396       oldPosition) {
397     final RectF downscaledFrameRect = downscaleRect(oldPosition);
398 
399     final float[] delta = new float[4];
400     getCurrentPositionNative(timestamp, downscaledFrameRect.left, downscaledFrameRect.top,
401         downscaledFrameRect.right, downscaledFrameRect.bottom, delta);
402 
403     final RectF newPosition = new RectF(delta[0], delta[1], delta[2], delta[3]);
404 
405     return upscaleRect(newPosition);
406   }
407 
updateDebugHistory()408   private void updateDebugHistory() {
409     lastKeypoints = new FrameChange(getKeypointsNative(false));
410 
411     if (lastTimestamp == 0) {
412       return;
413     }
414 
415     final PointF delta =
416         getAccumulatedDelta(
417             lastTimestamp, frameWidth / DOWNSAMPLE_FACTOR, frameHeight / DOWNSAMPLE_FACTOR, 100);
418 
419     synchronized (debugHistory) {
420       debugHistory.add(delta);
421 
422       while (debugHistory.size() > MAX_DEBUG_HISTORY_SIZE) {
423         debugHistory.remove(0);
424       }
425     }
426   }
427 
drawDebug(final Canvas canvas, final Matrix frameToCanvas)428   public synchronized void drawDebug(final Canvas canvas, final Matrix frameToCanvas) {
429     canvas.save();
430     canvas.setMatrix(frameToCanvas);
431 
432     drawHistoryDebug(canvas);
433     drawKeypointsDebug(canvas);
434 
435     canvas.restore();
436   }
437 
getDebugText()438   public Vector<String> getDebugText() {
439     final Vector<String> lines = new Vector<String>();
440 
441     if (lastKeypoints != null) {
442       lines.add("Num keypoints " + lastKeypoints.pointDeltas.size());
443       lines.add("Min score: " + lastKeypoints.minScore);
444       lines.add("Max score: " + lastKeypoints.maxScore);
445     }
446 
447     return lines;
448   }
449 
pollAccumulatedFlowData(final long endFrameTime)450   public synchronized List<byte[]> pollAccumulatedFlowData(final long endFrameTime) {
451     final List<byte[]> frameDeltas = new ArrayList<byte[]>();
452     while (timestampedDeltas.size() > 0) {
453       final TimestampedDeltas currentDeltas = timestampedDeltas.peek();
454       if (currentDeltas.timestamp <= endFrameTime) {
455         frameDeltas.add(currentDeltas.deltas);
456         timestampedDeltas.removeFirst();
457       } else {
458         break;
459       }
460     }
461 
462     return frameDeltas;
463   }
464 
downscaleRect(final RectF fullFrameRect)465   private RectF downscaleRect(final RectF fullFrameRect) {
466     return new RectF(
467         fullFrameRect.left / DOWNSAMPLE_FACTOR,
468         fullFrameRect.top / DOWNSAMPLE_FACTOR,
469         fullFrameRect.right / DOWNSAMPLE_FACTOR,
470         fullFrameRect.bottom / DOWNSAMPLE_FACTOR);
471   }
472 
upscaleRect(final RectF downsampledFrameRect)473   private RectF upscaleRect(final RectF downsampledFrameRect) {
474     return new RectF(
475         downsampledFrameRect.left * DOWNSAMPLE_FACTOR,
476         downsampledFrameRect.top * DOWNSAMPLE_FACTOR,
477         downsampledFrameRect.right * DOWNSAMPLE_FACTOR,
478         downsampledFrameRect.bottom * DOWNSAMPLE_FACTOR);
479   }
480 
481   /**
482    * A TrackedObject represents a native TrackedObject, and provides access to the
483    * relevant native tracking information available after every frame update. They may
484    * be safely passed around and accessed externally, but will become invalid after
485    * stopTracking() is called or the related creating ObjectTracker is deactivated.
486    *
487    * @author andrewharp@google.com (Andrew Harp)
488    */
489   public class TrackedObject {
490     private final String id;
491 
492     private long lastExternalPositionTime;
493 
494     private RectF lastTrackedPosition;
495     private boolean visibleInLastFrame;
496 
497     private boolean isDead;
498 
TrackedObject(final RectF position, final long timestamp, final byte[] data)499     TrackedObject(final RectF position, final long timestamp, final byte[] data) {
500       isDead = false;
501 
502       id = Integer.toString(this.hashCode());
503 
504       lastExternalPositionTime = timestamp;
505 
506       synchronized (ObjectTracker.this) {
507         registerInitialAppearance(position, data);
508         setPreviousPosition(position, timestamp);
509         trackedObjects.put(id, this);
510       }
511     }
512 
stopTracking()513     public void stopTracking() {
514       checkValidObject();
515 
516       synchronized (ObjectTracker.this) {
517         isDead = true;
518         forgetNative(id);
519         trackedObjects.remove(id);
520       }
521     }
522 
getCurrentCorrelation()523     public float getCurrentCorrelation() {
524       checkValidObject();
525       return ObjectTracker.this.getCurrentCorrelation(id);
526     }
527 
registerInitialAppearance(final RectF position, final byte[] data)528     void registerInitialAppearance(final RectF position, final byte[] data) {
529       final RectF externalPosition = downscaleRect(position);
530       registerNewObjectWithAppearanceNative(id,
531             externalPosition.left, externalPosition.top,
532             externalPosition.right, externalPosition.bottom,
533             data);
534     }
535 
setPreviousPosition(final RectF position, final long timestamp)536     synchronized void setPreviousPosition(final RectF position, final long timestamp) {
537       checkValidObject();
538       synchronized (ObjectTracker.this) {
539         if (lastExternalPositionTime > timestamp) {
540           LOGGER.w("Tried to use older position time!");
541           return;
542         }
543         final RectF externalPosition = downscaleRect(position);
544         lastExternalPositionTime = timestamp;
545 
546         setPreviousPositionNative(id,
547             externalPosition.left, externalPosition.top,
548             externalPosition.right, externalPosition.bottom,
549             lastExternalPositionTime);
550 
551         updateTrackedPosition();
552       }
553     }
554 
setCurrentPosition(final RectF position)555     void setCurrentPosition(final RectF position) {
556       checkValidObject();
557       final RectF downsampledPosition = downscaleRect(position);
558       synchronized (ObjectTracker.this) {
559         setCurrentPositionNative(id,
560             downsampledPosition.left, downsampledPosition.top,
561             downsampledPosition.right, downsampledPosition.bottom);
562       }
563     }
564 
updateTrackedPosition()565     private synchronized void updateTrackedPosition() {
566       checkValidObject();
567 
568       final float[] delta = new float[4];
569       getTrackedPositionNative(id, delta);
570       lastTrackedPosition = new RectF(delta[0], delta[1], delta[2], delta[3]);
571 
572       visibleInLastFrame = isObjectVisible(id);
573     }
574 
getTrackedPositionInPreviewFrame()575     public synchronized RectF getTrackedPositionInPreviewFrame() {
576       checkValidObject();
577 
578       if (lastTrackedPosition == null) {
579         return null;
580       }
581       return upscaleRect(lastTrackedPosition);
582     }
583 
getLastExternalPositionTime()584     synchronized long getLastExternalPositionTime() {
585       return lastExternalPositionTime;
586     }
587 
visibleInLastPreviewFrame()588     public synchronized boolean visibleInLastPreviewFrame() {
589       return visibleInLastFrame;
590     }
591 
checkValidObject()592     private void checkValidObject() {
593       if (isDead) {
594         throw new RuntimeException("TrackedObject already removed from tracking!");
595       } else if (ObjectTracker.this != instance) {
596         throw new RuntimeException("TrackedObject created with another ObjectTracker!");
597       }
598     }
599   }
600 
trackObject( final RectF position, final long timestamp, final byte[] frameData)601   public synchronized TrackedObject trackObject(
602       final RectF position, final long timestamp, final byte[] frameData) {
603     if (downsampledTimestamp != timestamp) {
604       ObjectTracker.downsampleImageNative(
605           frameWidth, frameHeight, rowStride, frameData, DOWNSAMPLE_FACTOR, downsampledFrame);
606       downsampledTimestamp = timestamp;
607     }
608     return new TrackedObject(position, timestamp, downsampledFrame);
609   }
610 
trackObject(final RectF position, final byte[] frameData)611   public synchronized TrackedObject trackObject(final RectF position, final byte[] frameData) {
612     return new TrackedObject(position, lastTimestamp, frameData);
613   }
614 
615   /** ********************* NATIVE CODE ************************************ */
616 
617   /** This will contain an opaque pointer to the native ObjectTracker */
618   private long nativeObjectTracker;
619 
initNative(int imageWidth, int imageHeight, boolean alwaysTrack)620   private native void initNative(int imageWidth, int imageHeight, boolean alwaysTrack);
621 
registerNewObjectWithAppearanceNative( String objectId, float x1, float y1, float x2, float y2, byte[] data)622   protected native void registerNewObjectWithAppearanceNative(
623       String objectId, float x1, float y1, float x2, float y2, byte[] data);
624 
setPreviousPositionNative( String objectId, float x1, float y1, float x2, float y2, long timestamp)625   protected native void setPreviousPositionNative(
626       String objectId, float x1, float y1, float x2, float y2, long timestamp);
627 
setCurrentPositionNative( String objectId, float x1, float y1, float x2, float y2)628   protected native void setCurrentPositionNative(
629       String objectId, float x1, float y1, float x2, float y2);
630 
forgetNative(String key)631   protected native void forgetNative(String key);
632 
getModelIdNative(String key)633   protected native String getModelIdNative(String key);
634 
haveObject(String key)635   protected native boolean haveObject(String key);
isObjectVisible(String key)636   protected native boolean isObjectVisible(String key);
getCurrentCorrelation(String key)637   protected native float getCurrentCorrelation(String key);
638 
getMatchScore(String key)639   protected native float getMatchScore(String key);
640 
getTrackedPositionNative(String key, float[] points)641   protected native void getTrackedPositionNative(String key, float[] points);
642 
nextFrameNative( byte[] frameData, byte[] uvData, long timestamp, float[] frameAlignMatrix)643   protected native void nextFrameNative(
644       byte[] frameData, byte[] uvData, long timestamp, float[] frameAlignMatrix);
645 
releaseMemoryNative()646   protected native void releaseMemoryNative();
647 
getCurrentPositionNative(long timestamp, final float positionX1, final float positionY1, final float positionX2, final float positionY2, final float[] delta)648   protected native void getCurrentPositionNative(long timestamp,
649       final float positionX1, final float positionY1,
650       final float positionX2, final float positionY2,
651       final float[] delta);
652 
getKeypointsPacked(float scaleFactor)653   protected native byte[] getKeypointsPacked(float scaleFactor);
654 
getKeypointsNative(boolean onlyReturnCorrespondingKeypoints)655   protected native float[] getKeypointsNative(boolean onlyReturnCorrespondingKeypoints);
656 
drawNative(int viewWidth, int viewHeight, float[] frameToCanvas)657   protected native void drawNative(int viewWidth, int viewHeight, float[] frameToCanvas);
658 
downsampleImageNative( int width, int height, int rowStride, byte[] input, int factor, byte[] output)659   protected static native void downsampleImageNative(
660       int width, int height, int rowStride, byte[] input, int factor, byte[] output);
661 }
662