PageRenderTime 28ms CodeModel.GetById 14ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

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