/ocr/ocrservice/src/com/googlecode/eyesfree/opticflow/OpticalFlowProcessor.java

http://eyes-free.googlecode.com/ · Java · 303 lines · 198 code · 74 blank · 31 comment · 12 complexity · a640f20304d81b2b57d9a3bea66ae6fe MD5 · raw file

  1. /*
  2. * Copyright (C) 2011 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.googlecode.eyesfree.opticflow;
  17. import android.graphics.Canvas;
  18. import android.graphics.Color;
  19. import android.graphics.Paint;
  20. import android.graphics.PointF;
  21. import android.graphics.Typeface;
  22. import android.os.SystemClock;
  23. import com.googlecode.eyesfree.env.Size;
  24. import java.util.Vector;
  25. /**
  26. * Frame processor that tracks optical flow.
  27. *
  28. * Modified by Alan Viverette from Andrew Harp's original source.
  29. *
  30. * @author Andrew Harp
  31. * @author alanv@google.com (Alan Viverette)
  32. */
  33. public class OpticalFlowProcessor extends FrameProcessor {
  34. private static final boolean DRAW_TEXT = false;
  35. // How many history points to keep track of and draw.
  36. private static final int MAX_HISTORY_SIZE = 30;
  37. // The reduction factor in image dimensions for optical flow processing.
  38. private static final int DOWNSAMPLE_FACTOR = 4;
  39. private int frameWidth;
  40. private int frameHeight;
  41. private final Paint p;
  42. private final Vector<PointF> history;
  43. private FrameChange lastFeatures;
  44. private long lastTimestamp;
  45. private OpticalFlow mOpticalFlow;
  46. public OpticalFlowProcessor() {
  47. history = new Vector<PointF>(MAX_HISTORY_SIZE);
  48. p = new Paint();
  49. p.setAntiAlias(false);
  50. p.setTypeface(Typeface.SERIF);
  51. mOpticalFlow = new OpticalFlow();
  52. }
  53. public static class Feature {
  54. public final float x;
  55. public final float y;
  56. public final float score;
  57. public final int type;
  58. public Feature(final float x, final float y) {
  59. this.x = x;
  60. this.y = y;
  61. this.score = 0;
  62. this.type = -1;
  63. }
  64. public Feature(final float x, final float y, final float score, final int type) {
  65. this.x = x;
  66. this.y = y;
  67. this.score = score;
  68. this.type = type;
  69. }
  70. Feature delta(final Feature other) {
  71. return new Feature(this.x - other.x, this.y - other.y);
  72. }
  73. }
  74. public static class PointChange {
  75. public final Feature featureA;
  76. public final Feature featureB;
  77. Feature pointDelta;
  78. public PointChange(final float x1, final float y1, final float x2, final float y2,
  79. final float score, final int type) {
  80. featureA = new Feature(x1, y1, score, type);
  81. featureB = new Feature(x2, y2);
  82. }
  83. public Feature getDelta() {
  84. if (pointDelta == null) {
  85. pointDelta = featureB.delta(featureA);
  86. }
  87. return pointDelta;
  88. }
  89. }
  90. /**
  91. * A class that records a timestamped frame translation delta for optical
  92. * flow.
  93. */
  94. public static class FrameChange {
  95. public final Vector<PointChange> pointDeltas;
  96. private final float minScore;
  97. private final float maxScore;
  98. public FrameChange(final float[] framePoints) {
  99. final int featureStep = 7;
  100. float minScore = 100.0f;
  101. float maxScore = -100.0f;
  102. pointDeltas = new Vector<PointChange>(framePoints.length / featureStep);
  103. float totalChangeX = 0.0f;
  104. float totalChangeY = 0.0f;
  105. for (int i = 0; i < framePoints.length; i += featureStep) {
  106. final float x1 = framePoints[i + 0];
  107. final float y1 = framePoints[i + 1];
  108. final float x2 = framePoints[i + 3];
  109. final float y2 = framePoints[i + 4];
  110. final float score = framePoints[i + 5];
  111. final int type = (int) framePoints[i + 6];
  112. minScore = Math.min(minScore, score);
  113. maxScore = Math.max(maxScore, score);
  114. final PointChange pointDelta = new PointChange(x1, y1, x2, y2, score, type);
  115. final Feature change = pointDelta.getDelta();
  116. totalChangeX += change.x;
  117. totalChangeY += change.y;
  118. pointDeltas.add(pointDelta);
  119. }
  120. this.minScore = minScore;
  121. this.maxScore = maxScore;
  122. }
  123. }
  124. @Override
  125. protected synchronized void onPreprocess(final TimestampedFrame frame) {
  126. mOpticalFlow.setImage(frame.getRawData(), frame.getTimestamp());
  127. }
  128. @Override
  129. protected synchronized void onProcessFrame(final TimestampedFrame frame) {
  130. mOpticalFlow.computeOpticalFlow();
  131. if (DebugView.isVisible) {
  132. updateHistory();
  133. }
  134. }
  135. public OpticalFlow getOpticalFlow() {
  136. return mOpticalFlow;
  137. }
  138. @Override
  139. protected synchronized void onInit(final Size size) {
  140. this.frameWidth = size.width;
  141. this.frameHeight = size.height;
  142. mOpticalFlow.initialize(frameWidth, frameHeight, DOWNSAMPLE_FACTOR);
  143. }
  144. @Override
  145. protected synchronized void onShutdown() {
  146. mOpticalFlow = null;
  147. }
  148. private void drawHistory(final Canvas canvas) {
  149. drawHistoryPoint(canvas, canvas.getWidth() / 2, canvas.getHeight() / 2);
  150. }
  151. private void drawHistoryPoint(final Canvas canvas, final float startX, final float startY) {
  152. // Draw the center circle.
  153. p.setColor(Color.GREEN);
  154. canvas.drawCircle(startX, startY, 3.0f, p);
  155. p.setColor(Color.RED);
  156. p.setStrokeWidth(2.0f);
  157. float x1 = startX;
  158. float y1 = startY;
  159. // Iterate through in backwards order.
  160. synchronized (history) {
  161. final int numPoints = history.size();
  162. for (int featureNum = numPoints - 1; featureNum >= 0; --featureNum) {
  163. final PointF delta = history.get(featureNum);
  164. final float x2 = x1 + delta.x;
  165. final float y2 = y1 + delta.y;
  166. canvas.drawLine(x1, y1, x2, y2, p);
  167. x1 = x2;
  168. y1 = y2;
  169. }
  170. }
  171. }
  172. private static int floatToChar(final float value) {
  173. return Math.max(0, Math.min((int) (value * 255.999f), 255));
  174. }
  175. private void drawFeatures(final Canvas canvas) {
  176. if (lastFeatures == null) {
  177. return;
  178. }
  179. final int featureSize = 3;
  180. final float minScore = lastFeatures.minScore;
  181. final float maxScore = lastFeatures.maxScore;
  182. for (final PointChange feature : lastFeatures.pointDeltas) {
  183. final int r = floatToChar((feature.featureA.score - minScore) / (maxScore - minScore));
  184. final int b = floatToChar(
  185. 1.0f - (feature.featureA.score - minScore) / (maxScore - minScore));
  186. final int color = 0xFF000000 | (r << 16) | b;
  187. p.setColor(color);
  188. canvas.drawRect(feature.featureB.x - featureSize, feature.featureB.y - featureSize,
  189. feature.featureB.x + featureSize, feature.featureB.y + featureSize, p);
  190. p.setColor(Color.CYAN);
  191. canvas.drawLine(feature.featureB.x, feature.featureB.y, feature.featureA.x,
  192. feature.featureA.y, p);
  193. if (DRAW_TEXT) {
  194. p.setColor(Color.WHITE);
  195. canvas.drawText(feature.featureA.type + ": " + feature.featureA.score,
  196. feature.featureA.x, feature.featureA.y, p);
  197. }
  198. }
  199. }
  200. private void updateHistory() {
  201. final float[] features = mOpticalFlow.getFeatures(true);
  202. lastFeatures = new FrameChange(features);
  203. final long timestamp = SystemClock.uptimeMillis();
  204. final PointF delta = mOpticalFlow.getAccumulatedDelta(
  205. lastTimestamp, frameWidth / 2, frameHeight / 2, 100);
  206. lastTimestamp = timestamp;
  207. synchronized (history) {
  208. history.add(delta);
  209. while (history.size() > MAX_HISTORY_SIZE) {
  210. history.remove(0);
  211. }
  212. }
  213. }
  214. @Override
  215. protected synchronized void onDrawDebug(final Canvas canvas) {
  216. drawHistory(canvas);
  217. drawFeatures(canvas);
  218. }
  219. @Override
  220. protected Vector<String> getDebugText() {
  221. final Vector<String> lines = new Vector<String>();
  222. if (lastFeatures != null) {
  223. lines.add("Num features " + lastFeatures.pointDeltas.size());
  224. lines.add("Min score: " + lastFeatures.minScore);
  225. lines.add("Max score: " + lastFeatures.maxScore);
  226. }
  227. return lines;
  228. }
  229. }