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

http://eyes-free.googlecode.com/ · Java · 237 lines · 126 code · 42 blank · 69 comment · 18 complexity · d9e5bd0afcda9e887fabac2af8fb9f61 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.hardware.Camera;
  18. import android.hardware.Camera.AutoFocusCallback;
  19. import com.googlecode.eyesfree.env.Size;
  20. import com.googlecode.eyesfree.env.Stopwatch;
  21. import com.googlecode.eyesfree.ocr.intent.CameraManager;
  22. import java.util.Vector;
  23. /**
  24. * An {@link FrameProcessor} to detect image blurriness for each preview frame.
  25. * Modified by Alan Viverette from Xiaotao Duan's original source.
  26. *
  27. * @author Xiaotao Duan
  28. * @author alanv@google.com (Alan Viverette)
  29. */
  30. public class ImageBlurProcessor extends FrameProcessor implements AutoFocusCallback {
  31. /**
  32. * Minimum period to wait between consecutive focus requests.
  33. */
  34. private static final long MIN_TIME_BETWEEN_FOCUS_REQUESTS_MS = 1500;
  35. /**
  36. * Period during which frames must be consistently blurry to trigger a focus
  37. * request.
  38. */
  39. private static final long FOCUS_DELAY_MS = 300;
  40. private static final int MIN_DIFF_PERCENT = 10;
  41. private final Stopwatch lastFocusTimer = new Stopwatch();
  42. private final Stopwatch consecutiveBlurTimer = new Stopwatch();
  43. private volatile int movingBits = 0;
  44. private final CameraManager cameraManager;
  45. private final Vector<String> debugText = new Vector<String>();
  46. private boolean lastFrameBlurred = false;
  47. private boolean focusing = false;
  48. private volatile boolean justFocused = false;
  49. private boolean hasFocusedWithSameImage = false;
  50. private int lastDiffPercent = 0;
  51. private long lastBlurDuration;
  52. private int[] focusedSignature;
  53. private int[] currFrameSignature;
  54. private boolean runSinceLastTime = false;
  55. private boolean movedSinceLastFocus = false;
  56. public ImageBlurProcessor(final CameraManager cameraManager) {
  57. this.cameraManager = cameraManager;
  58. }
  59. @Override
  60. protected void onInit(final Size size) {
  61. // We want to start timing as early as possible for our inter-focus
  62. // delays.
  63. lastFocusTimer.reset();
  64. lastFocusTimer.start();
  65. // Make sure this is zeroed and not running.
  66. consecutiveBlurTimer.stop();
  67. consecutiveBlurTimer.reset();
  68. justFocused = false;
  69. focusing = false;
  70. lastFrameBlurred = false;
  71. lastDiffPercent = 0;
  72. focusedSignature = null;
  73. }
  74. @Override
  75. protected void onStart() {
  76. onInit(null);
  77. }
  78. public void requestFocus() {
  79. focusing = true;
  80. cameraManager.requestFocus(this);
  81. }
  82. @Override
  83. public void onAutoFocus(final boolean success, Camera camera) {
  84. lastFocusTimer.reset();
  85. lastFocusTimer.start();
  86. movedSinceLastFocus = false;
  87. // Make sure we don't focus on the very next frame we get, just in case.
  88. consecutiveBlurTimer.stop();
  89. consecutiveBlurTimer.reset();
  90. justFocused = true;
  91. focusing = false;
  92. }
  93. /**
  94. * Tells blurriness of last preview frame processed by this processor.
  95. * Caller may use the return value to decide whether a focus action is
  96. * necessary at this moment.
  97. */
  98. public boolean isLastFrameBlurred() {
  99. return lastFrameBlurred;
  100. }
  101. @Override
  102. protected void onProcessFrame(final TimestampedFrame frame) {
  103. runSinceLastTime = true;
  104. frame.setTakenWhileFocusing(focusing);
  105. // No activity if we're moving or focusing.
  106. if (focusing) {
  107. return;
  108. }
  109. if (movingBits > 0) {
  110. // If we move, we will focus without considering
  111. // MIN_TIME_BETWEEN_FOCUS_REQUESTS_MS
  112. // after we stop.
  113. movedSinceLastFocus = true;
  114. return;
  115. }
  116. // TODO(xiaotao): Remove time evaluation and debugText.
  117. final long start = System.currentTimeMillis();
  118. // First check to see if the current image is blurred.
  119. lastFrameBlurred = ImageBlur.isBlurred(frame.getRawData(), frame.getWidth(), frame.getHeight());
  120. final long end = System.currentTimeMillis();
  121. lastBlurDuration = end - start;
  122. frame.setBlurred(lastFrameBlurred);
  123. // Aborts if camera does not support focus
  124. // if (!cameraManager.isFocusSupported()) {
  125. // return;
  126. // }
  127. // Make sure the blur timer is running if the frame was blurred,
  128. // otherwise reset it.
  129. if (lastFrameBlurred) {
  130. if (!consecutiveBlurTimer.isRunning()) {
  131. consecutiveBlurTimer.start();
  132. }
  133. } else {
  134. consecutiveBlurTimer.stop();
  135. consecutiveBlurTimer.reset();
  136. }
  137. // If we've gone more than FOCUS_DELAY_MS frames worth with a blurry
  138. // image, see if the image has changed enough, and if so, focus.
  139. if (consecutiveBlurTimer.getElapsedMilliseconds() > FOCUS_DELAY_MS) {
  140. // See if device has been moved or enough time has gone by since the
  141. // last focus attempt.
  142. if (movedSinceLastFocus) {
  143. focusedSignature = null;
  144. hasFocusedWithSameImage = false;
  145. requestFocus();
  146. } else if (lastFocusTimer.getElapsedMilliseconds() > MIN_TIME_BETWEEN_FOCUS_REQUESTS_MS) {
  147. // Only request a focus if we've changed significantly since the
  148. // last focus finished.
  149. currFrameSignature = ImageBlur.computeSignature(frame.getRawData(), frame.getWidth(), frame.getHeight(),
  150. currFrameSignature);
  151. // This logic computes the image difference from the last
  152. // post-focus event frame, if it exists.
  153. if (focusedSignature != null) {
  154. lastDiffPercent = ImageBlur.diffSignature(currFrameSignature, focusedSignature);
  155. } else {
  156. // If we've never focused before we're not going to have a
  157. // previous signature to compare to, so force the focus
  158. // event.
  159. lastDiffPercent = 100;
  160. }
  161. // If the difference is above a certain threshold, focus. If
  162. // not, it's probably the case that the environment is not
  163. // amenable to focusing anyway, so lets just wait till there is
  164. // a difference.
  165. if (lastDiffPercent > MIN_DIFF_PERCENT) {
  166. hasFocusedWithSameImage = false;
  167. requestFocus();
  168. } else if (!hasFocusedWithSameImage) {
  169. // If the image is blurry but it looks the same as last
  170. // time, try focusing once more.
  171. hasFocusedWithSameImage = true;
  172. requestFocus();
  173. }
  174. }
  175. } else if (justFocused) {
  176. // Take a snapshot of the image signature for reference when decided
  177. // whether to refocus or not.
  178. focusedSignature = ImageBlur.computeSignature(frame.getRawData(), frame.getWidth(), frame.getHeight(),
  179. focusedSignature);
  180. justFocused = false;
  181. }
  182. }
  183. @Override
  184. protected Vector<String> getDebugText() {
  185. if (runSinceLastTime) {
  186. debugText.clear();
  187. debugText.add((lastFrameBlurred ? "blurred" : "focused") + ": " + lastBlurDuration
  188. + " ms(s)");
  189. debugText.add("moving: " + movingBits);
  190. debugText.add("lastDiffPercent: " + lastDiffPercent);
  191. runSinceLastTime = false;
  192. }
  193. return debugText;
  194. }
  195. }