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

http://eyes-free.googlecode.com/ · Java · 294 lines · 170 code · 61 blank · 63 comment · 20 complexity · 508ecf25c2171b547bd4f16435702a66 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.Rect;
  21. import android.util.Log;
  22. import com.googlecode.eyesfree.env.Size;
  23. import com.googlecode.eyesfree.env.Stopwatch;
  24. import com.googlecode.eyesfree.opticflow.FrameProducer.Frame;
  25. import com.googlecode.eyesfree.opticflow.FrameProducer.FrameReceiver;
  26. import java.util.ArrayList;
  27. import java.util.Vector;
  28. /**
  29. * Handles looping of the preview frames. FrameProcessors may be registered, and
  30. * each will get access to the TimestampedFrame buffer wrapper in sequence.
  31. * FrameProcessors may access data that came before them, so care must be taken
  32. * that they are added in the right order. Modified by Alan Viverette from
  33. * Andrew Harp's original source.
  34. *
  35. * @author Andrew Harp
  36. * @author alanv@google.com (Alan Viverette)
  37. */
  38. public class FrameLooper implements FrameReceiver {
  39. private static final String TAG = "FrameLooper";
  40. private final FrameProducer frameProducer;
  41. private final DebugView debugView;
  42. private final Stopwatch stopwatch;
  43. // The thread that does the actual frame processing. Also acts as a flag for
  44. // whether the processing loop is active or not.
  45. private final ProcessingThread processingThreads[];
  46. // The number of preview frames that have been received from the camera.
  47. private int numPreviewFrames;
  48. private boolean firstRun;
  49. private boolean running;
  50. private Size size;
  51. ArrayList<FrameProcessor> allPreviewProcessors;
  52. public FrameLooper(
  53. final FrameProducer cameraManager, final DebugView debugView, final int[] delays) {
  54. this.frameProducer = cameraManager;
  55. this.debugView = debugView;
  56. this.stopwatch = new Stopwatch();
  57. this.firstRun = true;
  58. this.running = false;
  59. int width = frameProducer.getFrameWidth();
  60. int height = frameProducer.getFrameHeight();
  61. size = new Size(width, height);
  62. this.processingThreads = new ProcessingThread[delays.length];
  63. for (int level = 0; level < delays.length; ++level) {
  64. // The last processing thread should have priority
  65. // Thread.MIN_PRIORITY, every thread before it should have
  66. // incrementally more priority.
  67. final int priority = Thread.MIN_PRIORITY + delays.length - (level + 1);
  68. processingThreads[level] = new ProcessingThread(this, level, delays[level], priority);
  69. }
  70. }
  71. public void addPreviewProcessor(final FrameProcessor handler, final int level) {
  72. synchronized (processingThreads[level]) {
  73. processingThreads[level].addProcessor(handler);
  74. allPreviewProcessors = null;
  75. }
  76. }
  77. /**
  78. * @return A copy of the current processor list that is safe to modify.
  79. */
  80. public ArrayList<FrameProcessor> getAllProcessors() {
  81. if (allPreviewProcessors == null) {
  82. allPreviewProcessors = new ArrayList<FrameProcessor>();
  83. for (final ProcessingThread thread : processingThreads) {
  84. allPreviewProcessors.addAll(thread.getProcessors());
  85. }
  86. }
  87. return allPreviewProcessors;
  88. }
  89. final ArrayList<ProcessingThread> readyThreads = new ArrayList<ProcessingThread>();
  90. @Override
  91. public synchronized void onFrameReceived(final Frame frame) {
  92. ++numPreviewFrames;
  93. // Create a new TimestampedFrame to wrap the raw byte[].
  94. final TimestampedFrame previewFrame = new TimestampedFrame(frame);
  95. // TODO(alanv): Why does this run on the main thread?!
  96. processingThreads[0].preprocess(previewFrame);
  97. // readyThreads is a list of all threads that are done processing the
  98. // last
  99. // iteration.
  100. readyThreads.clear();
  101. for (int i = 1; i < processingThreads.length; ++i) {
  102. final ProcessingThread thread = processingThreads[i];
  103. if (thread.isReady()) {
  104. readyThreads.add(thread);
  105. thread.preprocess(previewFrame);
  106. }
  107. }
  108. // This is synchronized so that stopLoop is guaranteed to be called (if
  109. // desired) before preview frame processing finishes.
  110. processingThreads[0].processFrame(previewFrame);
  111. if (running) {
  112. frameProducer.requestFrame(this);
  113. }
  114. if (DebugView.isVisible) {
  115. debugView.invalidate();
  116. }
  117. for (final ProcessingThread thread : readyThreads) {
  118. thread.sendFrame(previewFrame);
  119. }
  120. }
  121. public synchronized void doneProcessing(final TimestampedFrame frame) {
  122. if (frame.allThreadsDone()) {
  123. frame.clearRawData();
  124. }
  125. }
  126. /**
  127. * Starts the loop for a finite number of frames.
  128. */
  129. public synchronized void startLoop() {
  130. stopwatch.start();
  131. running = true;
  132. numPreviewFrames = 0;
  133. Log.d(TAG, "Starting frame loop.");
  134. if (firstRun) {
  135. for (int i = 1; i < processingThreads.length; ++i) {
  136. processingThreads[i].start();
  137. }
  138. }
  139. firstRun = false;
  140. startAllProcessors();
  141. frameProducer.requestFrame(this);
  142. }
  143. /**
  144. * Stops the loop at its earliest possible convenience.
  145. */
  146. public synchronized void stopLoop() {
  147. if (running == false) {
  148. Log.w(TAG, "Stopping a FrameLooper that was already stoppped.");
  149. return;
  150. }
  151. // Reset the state of preview requests.
  152. frameProducer.requestFrame(null);
  153. stopAllProcessors();
  154. running = false;
  155. }
  156. /**
  157. * Guaranteed to be called at least once before the processors processes
  158. * anything and every time the preview size changes.
  159. */
  160. public void initAllProcessors() {
  161. int width = frameProducer.getFrameWidth();
  162. int height = frameProducer.getFrameHeight();
  163. size = new Size(width, height);
  164. for (final FrameProcessor processor : getAllProcessors()) {
  165. processor.init(size);
  166. }
  167. }
  168. private void startAllProcessors() {
  169. for (final FrameProcessor processor : getAllProcessors()) {
  170. processor.start();
  171. }
  172. }
  173. // TODO(xiaotao, andrewharp): Make sure that no frame slips through for
  174. // processing after onStop() is called on processor not running in the UI
  175. // thread
  176. private void stopAllProcessors() {
  177. for (final FrameProcessor processor : getAllProcessors()) {
  178. processor.stop();
  179. }
  180. }
  181. public void drawDebug(final Canvas canvas) {
  182. final ArrayList<FrameProcessor> processors = getAllProcessors();
  183. for (final FrameProcessor processor : processors) {
  184. synchronized (processor) {
  185. processor.drawDebug(canvas);
  186. }
  187. }
  188. // Draw the debug text in the bottom left.
  189. final Paint p = new Paint();
  190. final int xPos = 0;
  191. int startingYPos = canvas.getHeight();
  192. final int kLargeTextSize = 20;
  193. final int kSmallTextSize = 16;
  194. final int kTextBufferSize = 4;
  195. for (final FrameProcessor processor : processors) {
  196. Vector<String> lines;
  197. synchronized (processor) {
  198. lines = processor.getDebugText();
  199. }
  200. // TODO(andrewharp): Don't hardcode this, figure out the text
  201. // length.
  202. final int shadedWidth = 200;
  203. // Each block has one large header line followed by a buffer, then N
  204. // smaller lines each followed by a buffer, and then an additional
  205. // buffer.
  206. final int shadedHeight = kLargeTextSize + kTextBufferSize
  207. + (kSmallTextSize + kTextBufferSize) * lines.size() + kTextBufferSize;
  208. startingYPos -= shadedHeight;
  209. p.setColor(Color.BLACK);
  210. p.setAlpha(100);
  211. int yPos = startingYPos;
  212. canvas.drawRect(new Rect(xPos, yPos, xPos + shadedWidth, yPos + shadedHeight), p);
  213. // Header line.
  214. p.setAlpha(255);
  215. p.setAntiAlias(true);
  216. p.setColor(Color.CYAN);
  217. p.setTextSize(kLargeTextSize);
  218. yPos += kLargeTextSize + kTextBufferSize;
  219. canvas.drawText(processor.getName(), xPos, yPos, p);
  220. // Debug lines.
  221. p.setColor(Color.WHITE);
  222. p.setTextSize(kSmallTextSize);
  223. for (final String line : lines) {
  224. yPos += kSmallTextSize + kTextBufferSize;
  225. canvas.drawText(line, xPos, yPos, p);
  226. }
  227. yPos += kTextBufferSize;
  228. }
  229. }
  230. public boolean isRunning() {
  231. return running;
  232. }
  233. }