From 695b63654a6440e2a988b8d741b1a078a7e89927 Mon Sep 17 00:00:00 2001 From: Carlos Sotelo Date: Fri, 22 Jul 2016 18:40:39 +0100 Subject: [PATCH] 1- First Commit --- .gitignore | 11 +- build.gradle | 17 ++ gradle.properties | 25 ++ ohcrapi/build.gradle | 29 +++ ohcrapi/proguard-rules.pro | 17 ++ .../com/csot/ohcrapi/ApplicationTest.java | 13 + .../main/java/com/csot/ohcrapi/OhCRapi.java | 103 ++++++++ .../com/csot/ohcrapi/OhCRapiListener.java | 16 ++ .../com/csot/ohcrapi/OhCRapiLocalTask.java | 168 +++++++++++++ .../com/csot/ohcrapi/OhCRapiRemoteTask.java | 224 ++++++++++++++++++ settings.gradle | 1 + 11 files changed, 619 insertions(+), 5 deletions(-) create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 ohcrapi/build.gradle create mode 100644 ohcrapi/proguard-rules.pro create mode 100644 ohcrapi/src/androidTest/java/com/csot/ohcrapi/ApplicationTest.java create mode 100644 ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapi.java create mode 100644 ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiListener.java create mode 100644 ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiLocalTask.java create mode 100644 ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiRemoteTask.java create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore index f6b286c..f0454c6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,14 +14,14 @@ gen/ out/ # Gradle files -.gradle/ -build/ +**/.gradle/ +**/build/ # Local configuration file (sdk path, etc) -local.properties +**/local.properties # Proguard folder generated by Eclipse -proguard/ +**/proguard/ # Log Files *.log @@ -30,11 +30,12 @@ proguard/ .navigation/ # Android Studio captures folder -captures/ +**/captures/ # Intellij *.iml .idea/workspace.xml +**/.idea/* # Keystore files *.jks diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..59c36f7 --- /dev/null +++ b/build.gradle @@ -0,0 +1,17 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + } +} + +allprojects { + repositories { + jcenter() + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..88469f7 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,25 @@ +## Project-wide Gradle settings. +# +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +#Fri Jul 22 18:38:34 BST 2016 +systemProp.http.proxyPort=8080 +systemProp.http.proxyUser=csotelo +systemProp.http.proxyPassword=User\#\#345\#\# +systemProp.https.proxyPassword=User\#\#345\#\# +systemProp.https.proxyHost=10.164.212.25 +systemProp.http.nonProxyHosts=svn.bancocetelem.local,localhost,127.0.0.1 +systemProp.http.proxyHost=10.164.212.25 +systemProp.https.proxyPort=8080 +systemProp.https.nonProxyHosts=svn.bancocetelem.local,localhost,127.0.0.1 +systemProp.https.proxyUser=csotelo diff --git a/ohcrapi/build.gradle b/ohcrapi/build.gradle new file mode 100644 index 0000000..738fc20 --- /dev/null +++ b/ohcrapi/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + minSdkVersion 15 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.0.0' + compile 'com.rmtheis:tess-two:6.0.2' +} diff --git a/ohcrapi/proguard-rules.pro b/ohcrapi/proguard-rules.pro new file mode 100644 index 0000000..3ab60b8 --- /dev/null +++ b/ohcrapi/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/priyankvex/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/ohcrapi/src/androidTest/java/com/csot/ohcrapi/ApplicationTest.java b/ohcrapi/src/androidTest/java/com/csot/ohcrapi/ApplicationTest.java new file mode 100644 index 0000000..40b9357 --- /dev/null +++ b/ohcrapi/src/androidTest/java/com/csot/ohcrapi/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.csot.ohcrapi; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapi.java b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapi.java new file mode 100644 index 0000000..69e8d65 --- /dev/null +++ b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapi.java @@ -0,0 +1,103 @@ +package com.csot.ohcrapi; + +import android.content.Context; +import android.content.res.AssetManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.googlecode.tesseract.android.TessBaseAPI; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +/** + * Description Created on 19-07-2016. + * + * @author csotelo + * @version $Revision : 1 $ + */ +public class OhCRapi { + final static String TAG = "OCR"; + final static Random random = new Random(); + final static String url = "https://api.ocr.space/parse/image"; // OCR API Endpoints + final static String url2 = "https://apifree2.ocr.space/parse/image"; // OCR API Endpoints + static OhCRapi mInstance; + static TessBaseAPI mLocalOcrEngine; + String trainedDataLanguage = "eng"; + String apiKey; + + private OhCRapi(String trainedDataLanguage, String apiKey) { + this.trainedDataLanguage = trainedDataLanguage; + this.apiKey = apiKey; + } + + public static void initRemote(String trainedDataLanguage, String apiKey) { + mInstance = new OhCRapi(trainedDataLanguage, apiKey); + } + + //ProgressNotifier + public static void initLocal(Context ctx, String tessDirectoryPath, String trainedDataLanguage, TessBaseAPI.ProgressNotifier progressNotifier) { + if (mLocalOcrEngine == null) { + mLocalOcrEngine = touchTesseract(ctx, tessDirectoryPath, trainedDataLanguage, progressNotifier); + } + } + + public static String getApiKey() { + return mInstance.apiKey; + } + + public static String getTrainedDataLanguage() { + return mInstance.trainedDataLanguage; + } + + public static String getOcrUrl() { + int num = random.nextInt(2); + return (num == 1) ? url : url2; + } + + @NonNull + static TessBaseAPI touchTesseract(@NonNull Context ctx, @NonNull String tessDirectoryPath, @NonNull String trainedDataLanguage, @Nullable TessBaseAPI.ProgressNotifier progressNotifier) { + File tessDir = new File(tessDirectoryPath + File.separator + "tessdata"); + tessDir.mkdir(); + File tessData = new File(tessDir.getAbsolutePath() + File.separator + trainedDataLanguage + ".traineddata"); + if (!tessData.exists()) { + try { + AssetManager assetManager = ctx.getAssets(); + InputStream in = assetManager.open("tessdata/" + trainedDataLanguage + ".traineddata"); + //GZIPInputStream gin = new GZIPInputStream(in); + // Output stream with the location where we have to write the eng.traineddata file. + OutputStream out = new FileOutputStream(tessData); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + //while ((lenf = gin.read(buff)) > 0) { + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + //gin.close(); + out.close(); +// mInstance.trainedDataLanguage = trainedDataLanguage; + Log.v(OhCRapi.TAG, "Copied " + tessData.getAbsolutePath()); + } catch (IOException e) { + Log.e(OhCRapi.TAG, "Was unable to copy " + tessData.getAbsolutePath() + " : " + e.toString()); + } + } else { + Log.d(OhCRapi.TAG, "TessData already present"); + } + TessBaseAPI tess; + if (progressNotifier != null) { + tess = new TessBaseAPI(progressNotifier); + } else { + tess = new TessBaseAPI(); + } + tess.init(tessDirectoryPath, trainedDataLanguage); + return tess; + } +} diff --git a/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiListener.java b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiListener.java new file mode 100644 index 0000000..888ddb1 --- /dev/null +++ b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiListener.java @@ -0,0 +1,16 @@ +package com.csot.ohcrapi; + +/** + * Description Created on 19-07-2016. + * + * @author csotelo + * @version $Revision : 1 $ + */ +public interface OhCRapiListener { + + void onOhCRapiStarted(); + +// void onOhCRapiFinished(Bitmap bitmap, String recognizedText); + + void onOhCRapiFinished(String recognizedText); +} diff --git a/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiLocalTask.java b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiLocalTask.java new file mode 100644 index 0000000..b7a7d37 --- /dev/null +++ b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiLocalTask.java @@ -0,0 +1,168 @@ +package com.csot.ohcrapi; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.media.ExifInterface; +import android.os.AsyncTask; +import android.util.Log; + +import com.googlecode.tesseract.android.TessBaseAPI; + +import java.io.IOException; +import java.util.Collection; + +/** + * Description Created on 19-07-2016. + * + * @author csotelo + * @version $Revision : 1 $ + */ +public class OhCRapiLocalTask extends AsyncTask { + protected final static String TAG = OhCRapiLocalTask.class.getName(); + private OhCRapiListener mOhCRapiListener; + private String filePath; + private Bitmap mBitmap; + private String scannedText; + private TessBaseAPI baseApi = OhCRapi.mLocalOcrEngine; + private Collection rectangles; + + public OhCRapiLocalTask(OhCRapiListener OhCRapiListener, String filePath) { + this.mOhCRapiListener = OhCRapiListener; + this.filePath = filePath; + } + + public OhCRapiLocalTask(OhCRapiListener OhCRapiListener, Bitmap bitmap) { + this.mOhCRapiListener = OhCRapiListener; + this.mBitmap = bitmap; + } + + public OhCRapiLocalTask(OhCRapiListener OhCRapiListener, Bitmap bitmap, Collection rectangles) { + this.mOhCRapiListener = OhCRapiListener; + this.mBitmap = bitmap; + this.rectangles = rectangles; + } + + @Override + protected Void doInBackground(Void... params) { + processImage(); + scannedText = scanImage(); + return null; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + mOhCRapiListener.onOhCRapiStarted(); + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + mOhCRapiListener.onOhCRapiFinished(scannedText); + } + + private void processImage() { + if (mBitmap == null) { + int imageOrientationCode = getImageOrientation(); + Bitmap rawBitmap = getBitmapFromPath(); + // Getting the bitmap in right orientation. + this.mBitmap = rotateBitmap(rawBitmap, imageOrientationCode); + } + } + + private Bitmap getBitmapFromPath() { + BitmapFactory.Options bmOptions = new BitmapFactory.Options(); + bmOptions.inSampleSize = 4; + return BitmapFactory.decodeFile(this.filePath, bmOptions); + } + + private int getImageOrientation() { + ExifInterface exif = null; + try { + exif = new ExifInterface(this.filePath); + } catch (IOException e) { + e.printStackTrace(); + } + + assert exif != null; + return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_UNDEFINED); + } + + private Bitmap rotateBitmap(Bitmap bitmap, int orientation) { + + Matrix matrix = new Matrix(); + switch (orientation) { + case ExifInterface.ORIENTATION_NORMAL: + return bitmap; + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: + matrix.setScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_180: + matrix.setRotate(180); + break; + case ExifInterface.ORIENTATION_FLIP_VERTICAL: + matrix.setRotate(180); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_TRANSPOSE: + matrix.setRotate(90); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_90: + matrix.setRotate(90); + break; + case ExifInterface.ORIENTATION_TRANSVERSE: + matrix.setRotate(-90); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_270: + matrix.setRotate(-90); + break; + default: + return bitmap; + } + try { + Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + bitmap.recycle(); + return bmRotated; + } catch (OutOfMemoryError e) { + e.printStackTrace(); + return null; + } + } + + private String scanImage() { + long startTime = System.nanoTime(); + String recognizedText; + baseApi.setImage(this.mBitmap); + if (rectangles == null || rectangles.isEmpty()) { + recognizedText = baseApi.getUTF8Text(); + baseApi.clear(); + } else { + StringBuilder sb = new StringBuilder(); + for (Rect r : rectangles) { + baseApi.setRectangle(r); + String s = baseApi.getUTF8Text(); + Log.d(TAG, s); + sb.append(s); + sb.append("\n"); + } + recognizedText = sb.toString(); + } + long endTime = System.nanoTime(); + + long duration = (endTime - startTime) / 1000000; //divide by 1000000 to get milliseconds. + Log.v(TAG, "Took: " + duration + "ms"); + return recognizedText; + } + +} + + + + + + diff --git a/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiRemoteTask.java b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiRemoteTask.java new file mode 100644 index 0000000..55b4a91 --- /dev/null +++ b/ohcrapi/src/main/java/com/csot/ohcrapi/OhCRapiRemoteTask.java @@ -0,0 +1,224 @@ +package com.csot.ohcrapi; + +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.util.Iterator; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Description Created on 19-07-2016. + * + * @author csotelo + * @version $Revision : 1 $ + */ +public class OhCRapiRemoteTask extends AsyncTask { + final static String TAG = OhCRapiRemoteTask.class.getName(); + private String mApiKey; + private boolean isOverlayRequired = false; + private Object image; + private String mLanguage; + private OhCRapiListener OhCRapiListener; + + public OhCRapiRemoteTask(Object image, boolean isOverlayRequired, OhCRapiListener listener) { + this.mApiKey = OhCRapi.getApiKey(); + this.mLanguage = OhCRapi.getTrainedDataLanguage(); + this.isOverlayRequired = isOverlayRequired; + this.image = image; + this.OhCRapiListener = listener; + } + + public OhCRapiRemoteTask(Object image, OhCRapiListener listener) { + this.mApiKey = OhCRapi.getApiKey(); + this.mLanguage = OhCRapi.getTrainedDataLanguage(); + this.image = image; + this.OhCRapiListener = listener; + } + + public static byte[] getByteArrayFromFile(File file) throws IOException { + byte[] buffer = new byte[(int) file.length()]; + InputStream ios = null; + try { + ios = new FileInputStream(file); + if (ios.read(buffer) == -1) { + throw new IOException( + "EOF reached while trying to read the whole file"); + } + } finally { + try { + if (ios != null) { + ios.close(); + } + } catch (IOException e) { + } + } + return buffer; + } + + public String getPostDataString(JSONObject params) throws Exception { + + StringBuilder result = new StringBuilder(); + boolean first = true; + + Iterator itr = params.keys(); + + while (itr.hasNext()) { + + String key = itr.next(); + Object value = params.get(key); + + if (first) { + first = false; + } else { + result.append("&"); + } + + result.append(URLEncoder.encode(key, "UTF-8")); + result.append("="); + result.append(URLEncoder.encode(value.toString(), "UTF-8")); + + } + return result.toString(); + } + + @Override + protected String doInBackground(Void... params) { + try { + return sendPost(mApiKey, isOverlayRequired, image, mLanguage); + } catch (Exception e) { + Log.e(TAG, "Failed to call service", e); + return null; + } + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + OhCRapiListener.onOhCRapiStarted(); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + OhCRapiListener.onOhCRapiFinished(result); + } + + private String sendPost(String apiKey, boolean isOverlayRequired, Object image, String language) throws Exception { + if (image instanceof Bitmap) { + return sendPost(apiKey, isOverlayRequired, (Bitmap) image, language); + } else if (image instanceof String) { + return sendPost(apiKey, isOverlayRequired, (String) image, language); + } else if (image instanceof File) { + return sendPost(apiKey, isOverlayRequired, (File) image, language); + } else { + return sendPost(apiKey, isOverlayRequired, (byte[]) image, language); + } + } + + private String sendPost(String apiKey, boolean isOverlayRequired, String imageUrl, String language) throws Exception { + + URL obj = new URL(OhCRapi.getOcrUrl()); // OCR API Endpoints + HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); + + //add request header + con.setRequestMethod("POST"); + con.setRequestProperty("User-Agent", "Mozilla/5.0"); + con.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); + + + JSONObject postDataParams = new JSONObject(); + + postDataParams.put("apikey", apiKey); + postDataParams.put("isOverlayRequired", isOverlayRequired); + postDataParams.put("url", imageUrl); + postDataParams.put("language", language); + + + // Send post request + con.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(con.getOutputStream()); + wr.writeBytes(getPostDataString(postDataParams)); + wr.flush(); + wr.close(); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + //return result + return String.valueOf(response); + } + + private String sendPost(String apiKey, boolean isOverlayRequired, Bitmap imageFile, String language) throws Exception { + ByteBuffer byteBuffer = ByteBuffer.allocate(imageFile.getByteCount()); + imageFile.copyPixelsToBuffer(byteBuffer); + return sendPost(apiKey, isOverlayRequired, byteBuffer.array(), language); + } + + private String sendPost(String apiKey, boolean isOverlayRequired, File imageFile, String language) throws Exception { + return sendPost(apiKey, isOverlayRequired, getByteArrayFromFile(imageFile), language); + } + + private String sendPost(String apiKey, boolean isOverlayRequired, byte[] data, String language) throws Exception { + + URL obj = new URL(OhCRapi.getOcrUrl()); // OCR API Endpoints + HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); + + //add request header + con.setRequestMethod("POST"); + con.setRequestProperty("User-Agent", "Mozilla/5.0"); + con.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); + + + JSONObject postDataParams = new JSONObject(); + + postDataParams.put("apikey", apiKey); + postDataParams.put("isOverlayRequired", isOverlayRequired); +// byte[] base64Encoded = Base64.encode(data, Base64.DEFAULT); + String dataString = new String(data); + postDataParams.put("file", dataString); + postDataParams.put("language", language); + + String logMsg = String.format("URL: " + OhCRapi.getOcrUrl() + " > posting the following JSONObject - %s", postDataParams.toString()); + Log.v(TAG, logMsg); +// logMsg = logMsg.replaceAll("\"file\":\"[^\"]+\"", "\"file\":\"FILE CONTENT NOT INCLUDED IN LOGS\""); +// Log.d(TAG, logMsg); + // Send post request + con.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(con.getOutputStream()); + wr.writeBytes(getPostDataString(postDataParams)); + wr.flush(); + wr.close(); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + //return result + return String.valueOf(response); + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e856d84 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':ohcrapi'