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