/ocr/ocrservice/src/com/googlecode/eyesfree/opticflow/TimestampedFrame.java
Java | 210 lines | 116 code | 38 blank | 56 comment | 19 complexity | 288cfe683d2292d9377f5eeb4d21a58c 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.util.Log; 20 21import com.googlecode.eyesfree.env.Size; 22import com.googlecode.eyesfree.opticflow.FrameProducer.Frame; 23import com.googlecode.leptonica.android.Pix; 24import com.googlecode.leptonica.android.Pixa; 25import com.googlecode.leptonica.android.ReadFile; 26 27/** 28 * Class for interfacing efficiently with image data, and keeping track of all 29 * records associated with a frame. 30 * 31 * @author alanv@google.com (Alan Viverette) 32 */ 33public class TimestampedFrame { 34 private static final String TAG = "TimestampedFrame"; 35 36 private int threadsLeft; 37 38 // For caching PIX created from rawFrameData. 39 private Pix cachedPix; 40 41 // Whether this frame is thought to be blurred. May be null. 42 private Boolean isBlurred; 43 44 // Whether this frame was created while focus was occurring. May be null. 45 private Boolean takenWhileFocusing; 46 47 // The results of text detection. 48 private Pixa detectedText; 49 50 // The FDR value of detected text areas. 51 private float[] textConfidences; 52 53 // The estimated angle for text present in this frame. 54 // TODO(alanv): Make this a per-Pix setting? 55 private float angle; 56 57 private final Frame originalFrame; 58 59 // TODO(andrewharp): Create pool of TimestampedFrames so that used frames 60 // can be recycled. 61 protected TimestampedFrame(final Frame originalFrame) { 62 this.originalFrame = originalFrame; 63 } 64 65 public long getTimestamp() { 66 return originalFrame.timestamp; 67 } 68 69 public Size getSize() { 70 return new Size(originalFrame.width, originalFrame.height); 71 } 72 73 public int getWidth() { 74 return originalFrame.width; 75 } 76 77 public int getHeight() { 78 return originalFrame.height; 79 } 80 81 public void setDetectedText(Pixa detectedText, float[] textAreaQuality, float angle) { 82 if (detectedText == null) { 83 throw new IllegalArgumentException("Detected text must be non-null"); 84 } 85 86 this.detectedText = detectedText.copy(); 87 this.textConfidences = textAreaQuality; 88 this.angle = angle; 89 } 90 91 public Pixa getDetectedText() { 92 if (detectedText != null) { 93 return detectedText.copy(); 94 } else { 95 return null; 96 } 97 } 98 99 public float[] getTextConfidences() { 100 return textConfidences.clone(); 101 } 102 103 public float getAngle() { 104 return angle; 105 } 106 107 public void recycleDetectedText() { 108 if (detectedText != null) { 109 detectedText.recycle(); 110 detectedText = null; 111 } 112 } 113 114 /** 115 * @return A Pix containing the data for a PIX representation of this frame. 116 */ 117 public synchronized Pix getPixData() { 118 if (cachedPix == null) { 119 cachedPix = ReadFile.readBytes8( 120 originalFrame.data, originalFrame.width, originalFrame.height); 121 } 122 123 return cachedPix.clone(); 124 } 125 126 /** 127 * @return Whether or not rawFrameData is null. 128 */ 129 protected synchronized boolean hasRawData() { 130 return originalFrame.data != null; 131 } 132 133 /** 134 * @return The raw frame data. Raw data may have already been released, in 135 * which case an error is logged. 136 */ 137 public synchronized byte[] getRawData() { 138 if (!hasRawData()) { 139 Log.e(TAG, "Frame data for frame is no longer available."); 140 } 141 return originalFrame.data; 142 } 143 144 /** 145 * Raw frame data is expensive to keep around, so we need to provide a way 146 * to remove it from this frame after the smaller JPEG is created. 147 * 148 * @return The byte[] this frame was holding. 149 */ 150 protected synchronized byte[] clearRawData() { 151 final byte[] tmpData = getRawData(); // So we get the implicit check. 152 originalFrame.recycle(); 153 // Unblock any threads that are wait()ing in releaesBitmap(true). 154 notify(); 155 return tmpData; 156 } 157 158 public boolean isBlurred() { 159 if (isBlurred == null) { 160 Log.w(TAG, "isBlurred() called without value having been set!"); 161 162 // If focusing we can assume it's blurred, otherwise default to 163 // unblurred. 164 return takenWhileFocusing(); 165 } 166 167 return isBlurred; 168 } 169 170 public void setBlurred(final boolean blurred) { 171 if (isBlurred != null) { 172 Log.w(TAG, "Blurred already set!"); 173 } 174 175 isBlurred = blurred; 176 } 177 178 public void setTakenWhileFocusing(final boolean takenWhileFocusing) { 179 this.takenWhileFocusing = takenWhileFocusing; 180 } 181 182 public boolean takenWhileFocusing() { 183 if (takenWhileFocusing == null) { 184 return false; 185 } 186 return takenWhileFocusing.booleanValue(); 187 } 188 189 /** 190 * Used by a ProcessingThread to signify that it's done processing this 191 * frame. 192 */ 193 public void threadDone() { 194 --threadsLeft; 195 if (threadsLeft < 0) { 196 Log.w(TAG, "Negative number of threads remaining."); 197 } 198 } 199 200 /** 201 * @return true iff all threads have finished processing this frame. 202 */ 203 public boolean allThreadsDone() { 204 return threadsLeft == 0; 205 } 206 207 public void threadStart() { 208 ++threadsLeft; 209 } 210}