/*
* Copyright (C) 2008 ZXing authors
* Copyright 2011 Robert Theis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.sfsu.cs.orange.ocr;
import java.util.List;
import com.v.mypersonaltrainer.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import edu.sfsu.cs.orange.ocr.camera.CameraManager;
/**
* This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
* transparency outside it, as well as the result text.
*
* The code for this class was adapted from the ZXing project: http://code.google.com/p/zxing
*/
public final class ViewfinderView extends View {
//private static final long ANIMATION_DELAY = 80L;
/** Flag to draw boxes representing the results from TessBaseAPI::GetRegions(). */
static final boolean DRAW_REGION_BOXES = false;
/** Flag to draw boxes representing the results from TessBaseAPI::GetTextlines(). */
static final boolean DRAW_TEXTLINE_BOXES = true;
/** Flag to draw boxes representing the results from TessBaseAPI::GetStrips(). */
static final boolean DRAW_STRIP_BOXES = false;
/** Flag to draw boxes representing the results from TessBaseAPI::GetWords(). */
static final boolean DRAW_WORD_BOXES = true;
/** Flag to draw word text with a background varying from transparent to opaque. */
static final boolean DRAW_TRANSPARENT_WORD_BACKGROUNDS = false;
/** Flag to draw boxes representing the results from TessBaseAPI::GetCharacters(). */
static final boolean DRAW_CHARACTER_BOXES = false;
/** Flag to draw the text of words within their respective boxes from TessBaseAPI::GetWords(). */
static final boolean DRAW_WORD_TEXT = false;
/** Flag to draw each character in its respective box from TessBaseAPI::GetCharacters(). */
static final boolean DRAW_CHARACTER_TEXT = false;
private CameraManager cameraManager;
private final Paint paint;
private final int maskColor;
private final int frameColor;
private final int cornerColor;
private OcrResultText resultText;
private String[] words;
private List<Rect> regionBoundingBoxes;
private List<Rect> textlineBoundingBoxes;
private List<Rect> stripBoundingBoxes;
private List<Rect> wordBoundingBoxes;
private List<Rect> characterBoundingBoxes;
// Rect bounds;
private Rect previewFrame;
private Rect rect;
// This constructor is used when the class is built from an XML resource.
public ViewfinderView(Context context, AttributeSet attrs) {
super(context, attrs);
// Initialize these once for performance rather than calling them every time in onDraw().
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask);
frameColor = resources.getColor(R.color.viewfinder_frame);
cornerColor = resources.getColor(R.color.viewfinder_corners);
// bounds = new Rect();
previewFrame = new Rect();
rect = new Rect();
}
public void setCameraManager(CameraManager cameraManager) {
this.cameraManager = cameraManager;
}
@SuppressWarnings("unused")
@Override
public void onDraw(Canvas canvas) {
Rect frame = cameraManager.getFramingRect();
if (frame == null) {
return;
}
int width = canvas.getWidth();
int height = canvas.getHeight();
// Draw the exterior (i.e. outside the framing rect) darkened
paint.setColor(maskColor);
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
// If we have an OCR result, overlay its information on the viewfinder.
if (resultText != null) {
// Only draw text/bounding boxes on viewfinder if it hasn't been resized since the OCR was requested.
Point bitmapSize = resultText.getBitmapDimensions();
previewFrame = cameraManager.getFramingRectInPreview();
if (bitmapSize.x == previewFrame.width() && bitmapSize.y == previewFrame.height()) {
float scaleX = frame.width() / (float) previewFrame.width();
float scaleY = frame.height() / (float) previewFrame.height();
if (DRAW_REGION_BOXES) {
regionBoundingBoxes = resultText.getRegionBoundingBoxes();
for (int i = 0; i < regionBoundingBoxes.size(); i++) {
paint.setAlpha(0xA0);
paint.setColor(Color.MAGENTA);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
rect = regionBoundingBoxes.get(i);
canvas.drawRect(frame.left + rect.left * scaleX,
frame.top + rect.top * scaleY,
frame.left + rect.right * scaleX,
frame.top + rect.bottom * scaleY, paint);
}
}
if (DRAW_TEXTLINE_BOXES) {
// Draw each textline
textlineBoundingBoxes = resultText.getTextlineBoundingBoxes();
paint.setAlpha(0xA0);
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
for (int i = 0; i < textlineBoundingBoxes.size(); i++) {
rect = textlineBoundingBoxes.get(i);
canvas.drawRect(frame.left + rect.left * scaleX,
frame.top + rect.top * scaleY,
frame.left + rect.right * scaleX,
frame.top + rect.bottom * scaleY, paint);
}
}
if (DRAW_STRIP_BOXES) {
stripBoundingBoxes = resultText.getStripBoundingBoxes();
paint.setAlpha(0xFF);
paint.setColor(Color.YELLOW);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
for (int i = 0; i < stripBoundingBoxes.size(); i++) {
rect = stripBoundingBoxes.get(i);
canvas.drawRect(frame.left + rect.left * scaleX,
frame.top + rect.top * scaleY,
frame.left + rect.right * scaleX,
frame.top + rect.bottom * scaleY, paint);
}
}
if (DRAW_WORD_BOXES || DRAW_WORD_TEXT) {
// Split the text into words
wordBoundingBoxes = resultText.getWordBoundingBoxes();
// for (String w : words) {
// Log.e("ViewfinderView", "word: " + w);
// }
//Log.d("ViewfinderView", "There are " + words.length + " words in the string array.");
//Log.d("ViewfinderView", "There are " + wordBoundingBoxes.size() + " words with bounding boxes.");
}
if (DRAW_WORD_BOXES) {
paint.setAlpha(0xFF);
paint.setColor(0xFF00CCFF);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
for (int i = 0; i < wordBoundingBoxes.size(); i++) {
// Draw a bounding box around the word
rect = wordBoundingBoxes.get(i);
canvas.drawRect(
frame.left + rect.left * scaleX,
frame.top + rect.top * scaleY,
frame.left + rect.right * scaleX,
frame.top + rect.bottom * scaleY, paint);
}
}
if (DRAW_WORD_TEXT) {
words = resultText.getText().replace("\n"," ").split(" ");
int[] wordConfidences = resultText.getWordConfidences();
for (int i = 0; i < wordBoundingBoxes.size(); i++) {
boolean isWordBlank = true;
try {
if (!words[i].equals("")) {
isWordBlank = false;
}
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
// Only draw if word has characters
if (!isWordBlank) {
// Draw a white background around each word
rect = wordBoundingBoxes.get(i);
paint.setColor(Color.WHITE);
paint.setStyle(Style.FILL);
if (DRAW_TRANSPARENT_WORD_BACKGROUNDS) {
// Higher confidence = more opaque, less transparent background
paint.setAlpha(wordConfidences[i] * 255 / 100);
} else {
paint.setAlpha(255);
}
canvas.drawRect(frame.left + rect.left * scaleX,
frame.top + rect.top * scaleY,
frame.left + rect.right * scaleX,
frame.top + rect.bottom * scaleY, paint);
// Draw the word in black text
paint.setColor(Color.BLACK);
paint.setAlpha(0xFF);
paint.setAntiAlias(true);
paint.setTextAlign(Align.LEFT);
// Adjust text size to fill rect
paint.setTextSize(100);
paint.setTextScaleX(1.0f);
// ask the paint for the bounding rect if it were to draw this text
Rect bounds = new Rect();
paint.getTextBounds(words[i], 0, words[i].length(), bounds);
// get the height that would have been produced
int h = bounds.bottom - bounds.top;
// figure out what textSize setting would create that height of text
float size = (((float)(rect.height())/h)*100f);
// and set it into the paint
paint.setTextSize(size);
// Now set the scale.
// do calculation with scale of 1.0 (no scale)
paint.setTextScaleX(1.0f);
// ask the paint for the bounding rect if it were to draw this text.
paint.getTextBounds(words[i], 0, words[i].length(), bounds);
// determine the width
int w = bounds.right - bounds.left;
// calculate the baseline to use so that the entire text is visible including the descenders
int text_h = bounds.bottom-bounds.top;
int baseline =bounds.bottom+((rect.height()-text_h)/2);
// determine how much to scale the width to fit the view
float xscale = ((float) (rect.width())) / w;
// set the scale for the text paint
paint.setTextScaleX(xscale);
canvas.drawText(words[i], frame.left + rect.left * scaleX, frame.top + rect.bottom * scaleY - baseline, paint);
}
}
}
if (DRAW_CHARACTER_BOXES || DRAW_CHARACTER_TEXT) {
characterBoundingBoxes = resultText.getCharacterBoundingBoxes();
}
if (DRAW_CHARACTER_BOXES) {
// Draw bounding boxes around each character
paint.setAlpha(0xA0);
paint.setColor(0xFF00FF00);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
for (int c = 0; c < characterBoundingBoxes.size(); c++) {
Rect characterRect = characterBoundingBoxes.get(c);
canvas.drawRect(frame.left + characterRect.left * scaleX,
frame.top + characterRect.top * scaleY,
frame.left + characterRect.right * scaleX,
frame.top + characterRect.bottom * scaleY, paint);
}
}
if (DRAW_CHARACTER_TEXT) {
// Draw letters individually
for (int i = 0; i < characterBoundingBoxes.size(); i++) {
Rect r = characterBoundingBoxes.get(i);
// Draw a white background for every letter
int meanConfidence = resultText.getMeanConfidence();
paint.setColor(Color.WHITE);
paint.setAlpha(meanConfidence * (255 / 100));
paint.setStyle(Style.FILL);
canvas.drawRect(frame.left + r.left * scaleX,
frame.top + r.top * scaleY,
frame.left + r.right * scaleX,
frame.top + r.bottom * scaleY, paint);
// Draw each letter, in black
paint.setColor(Color.BLACK);
paint.setAlpha(0xFF);
paint.setAntiAlias(true);
paint.setTextAlign(Align.LEFT);
String letter = "";
try {
char c = resultText.getText().replace("\n","").replace(" ", "").charAt(i);
letter = Character.toString(c);
if (!letter.equals("-") && !letter.equals("_")) {
// Adjust text size to fill rect
paint.setTextSize(100);
paint.setTextScaleX(1.0f);
// ask the paint for the bounding rect if it were to draw this text
Rect bounds = new Rect();
paint.getTextBounds(letter, 0, letter.length(), bounds);
// get the height that would have been produced
int h = bounds.bottom - bounds.top;
// figure out what textSize setting would create that height of text
float size = (((float)(r.height())/h)*100f);
// and set it into the paint
paint.setTextSize(size);
// Draw the text as is. We don't really need to set the text scale, because the dimensions
// of the Rect should already be suited for drawing our letter.
canvas.drawText(letter, frame.left + r.left * scaleX, frame.top + r.bottom * scaleY, paint);
}
} catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// Draw a two pixel solid border inside the framing rect
paint.setAlpha(0);
paint.setStyle(Style.FILL);
paint.setColor(frameColor);
canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
// Draw the framing rect corner UI elements
paint.setColor(cornerColor);
canvas.drawRect(frame.left - 15, frame.top - 15, frame.left + 15, frame.top, paint);
canvas.drawRect(frame.left - 15, frame.top, frame.left, frame.top + 15, paint);
canvas.drawRect(frame.right - 15, frame.top - 15, frame.right + 15, frame.top, paint);
canvas.drawRect(frame.right, frame.top - 15, frame.right + 15, frame.top + 15, paint);
canvas.drawRect(frame.left - 15, frame.bottom, frame.left + 15, frame.bottom + 15, paint);
canvas.drawRect(frame.left - 15, frame.bottom - 15, frame.left, frame.bottom, paint);
canvas.drawRect(frame.right - 15, frame.bottom, frame.right + 15, frame.bottom + 15, paint);
canvas.drawRect(frame.right, frame.bottom - 15, frame.right + 15, frame.bottom + 15, paint);
// Request another update at the animation interval, but don't repaint the entire viewfinder mask.
//postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
}
public void drawViewfinder() {
invalidate();
}
/**
* Adds the given OCR results for drawing to the view.
*
* @param text Object containing OCR-derived text and corresponding data.
*/
public void addResultText(OcrResultText text) {
resultText = text;
}
/**
* Nullifies OCR text to remove it at the next onDraw() drawing.
*/
public void removeResultText() {
resultText = null;
}
}