Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Simple example source of math recognition for Android using Java
/*!
* @brief simple example source of math recognition
* @date 2020/01/16
* @file MathSimpleExampleActivity.java
* @author SELVAS AI
*
* Copyright 2020. SELVAS AI Inc. All Rights Reserved.
*/
package com.selvasai.handwriting.math;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import com.selvy.spmath.DHWR;
public class MathSimpleExampleActivity extends Activity {
public final static String TAG = "HWR";
final int MAX_CANDIDATES = 10;
private DHWR.Ink mInk;
private DHWR.Setting mSetting;
private DHWR.Result mResult;
// points of '\frac{2x+4}{5y-2}'
// -1, 0 : end of stroke
// -1, -1 : end of ink
// (x0, y0), (x1, y1), ... (-1, 0) ... (xn, yn) .... (-1, 0), (-1, -1)
final int[] ink_fraction = {383, 614, 325, 755, 338, 759, 373, 759, 392, 772, 353, 808, 318,
825, 320, 805, -1, 0, 348, 691, 351, 688, 358, 676, 397, 668, 427, 667, 433, 663, -1, 0,
528, 642, 533, 647, 524, 654, 506, 696, 503, 729, 515, 726, 528, 711, 561, 671, 586,
648, 585, 657, 572, 701, 563, 736, 545, 810, 518, 860, 498, 865, 478, 849, 480, 822,
515, 783, 535, 769, 587, 744, 620, 735, 631, 732, 642, 732, 644, 733, -1, 0, 714, 691,
712, 685, 750, 684, 771, 679, 778, 678, -1, 0, 880, 665, 891, 660, 926, 655, 938, 659,
958, 680, 940, 706, 923, 719, 883, 743, 876, 750, 892, 752, 933, 749, 979, 744, 999,
743, 1023, 743, 1003, 744, -1, 0, 193, 531, 206, 526, 258, 529, 316, 528, 384, 524, 418,
522, 500, 520, 575, 519, 604, 518, 661, 517, 715, 516, 752, 515, 798, 512, 852, 508,
873, 508, 915, 505, 961, 502, 979, 501, 1023, 500, 1055, 500, 1070, 499, 1100, 497,
1135, 495, 1146, 493, 1163, 492, 1152, 490, 1131, 487, -1, 0, 302, 341, 340, 342, 365,
355, 369, 379, 327, 416, 313, 427, 304, 436, 337, 430, 362, 424, 414, 410, 430, 406,
423, 406, -1, 0, 444, 337, 450, 337, 479, 319, 509, 325, 511, 347, 492, 386, 481, 405,
465, 428, 472, 414, -1, 0, 537, 292, 534, 292, 512, 303, 498, 337, 500, 378, 520, 419,
529, 429, 548, 445, 555, 452, 558, 453, -1, 0, 607, 374, 613, 369, 660, 371, 728, 364,
761, 356, 760, 349, -1, 0, 688, 316, 686, 318, 681, 331, 690, 388, 693, 407, 696, 426,
697, 423, -1, 0, 842, 292, 851, 296, 849, 311, 830, 354, 821, 376, 827, 391, 861, 398,
926, 402, 985, 401, 995, 400, 989, 398, -1, 0, 959, 281, 941, 293, 916, 337, 890, 401,
869, 458, 864, 479, 866, 488, -1, 0, -1, -1};
// points of '\sqrt{144}+\sqrt{1728}'
final int[] ink_square_root = {376, 665, 375, 659, 383, 680, 394, 704, 405, 717, 410, 691, 411,
666, 408, 566, 404, 495, 410, 450, 413, 437, 419, 429, 449, 427, 498, 436, 602, 440,
759, 428, 824, 421, 859, 418, 857, 428, -1, 0, 516, 571, 509, 580, 513, 625, 519, 648,
526, 644, -1, 0, 624, 545, 627, 545, 627, 553, 594, 582, 562, 604, 567, 609, 606, 605,
657, 596, 670, 588, -1, 0, 613, 555, 612, 553, 620, 569, 624, 616, 630, 634, 635, 632,
-1, 0, 728, 547, 729, 548, 695, 580, 671, 597, 672, 601, 716, 601, 775, 594, 786, 590,
-1, 0, 739, 541, 728, 562, 729, 606, 733, 642, 743, 654, -1, 0, 1063, 519, 1054, 536,
1058, 577, 1060, 597, -1, 0, 1023, 563, 1032, 558, 1087, 553, 1117, 552, -1, 0, 1315,
620, 1319, 620, 1343, 652, 1360, 672, 1361, 650, 1359, 570, 1363, 484, 1365, 424, 1370,
413, 1391, 412, 1454, 415, 1519, 413, 1685, 397, 1816, 382, 1910, 377, 1980, 386, 1963,
394, -1, 0, 1506, 512, 1493, 512, 1499, 556, 1504, 592, 1509, 608, 1513, 602, -1, 0,
1586, 505, 1586, 511, 1573, 560, 1560, 549, 1564, 534, 1586, 506, 1614, 497, 1635, 525,
1637, 554, 1624, 617, 1618, 639, 1617, 641, 1623, 632, -1, 0, 1714, 542, 1731, 516,
1778, 519, 1792, 561, 1778, 578, 1735, 595, 1713, 593, 1733, 587, 1753, 584, 1811, 579,
1838, 578, -1, 0, 1925, 527, 1925, 520, 1906, 470, 1886, 476, 1877, 525, 1885, 576,
1873, 615, 1852, 617, 1862, 561, 1907, 535, 1942, 531, 1959, 542, -1, 0, -1, -1};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
copyResourceToStorage();
initEngine();
testFraction();
testSquareRoot();
destroyEngine();
}
private void copyResourceToStorage() {
String path = "hdb";
String[] fileList = null;
try {
fileList = getAssets().list(path);
} catch (IOException e) {
e.printStackTrace();
}
if (fileList == null) {
return;
}
for (String file : fileList) {
try {
String filePath = (path + "/" + file);
try {
FileInputStream fis = openFileInput(file);
fis.close();
} catch (FileNotFoundException e) {
InputStream is = getAssets().open(filePath);
final int bufferSize = is.available();
byte[] buffer = new byte[bufferSize];
int readSize = is.read(buffer);
is.close();
if (readSize > 0) {
FileOutputStream fos = openFileOutput(file, Activity.MODE_PRIVATE);
fos.write(buffer);
fos.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void makeInputEvent(DHWR.Ink ink, int[] inputs) {
DHWR.InkClear(ink.GetHandle());
int i = 0;
while (true) {
if (inputs[i] == -1 && inputs[i + 1] == -1) { // if end of inks
break;
} else if (inputs[i] == -1 && inputs[i + 1] == 0) { // if end of stroke
DHWR.EndStroke(ink.GetHandle());
} else {
DHWR.AddPoint(ink.GetHandle(), inputs[i], inputs[i + 1]);
}
i += 2;
}
}
private String getCandidates(DHWR.Result result) {
StringBuilder candidates = new StringBuilder();
boolean exit = false;
int resultSize = result.size();
if (resultSize < 1) {
return "result empty";
}
for (int i = 0; i < MAX_CANDIDATES; i++) {
for (int j = 0; j < resultSize; j++) {
DHWR.Line line = result.get(j);
for (int k = 0; k < line.size(); k++) {
DHWR.Block block = line.get(k);
if (block.candidates.size() <= i) {
exit = true;
break;
}
candidates.append(String.format(Locale.US, " [%d] ", i + 1));
candidates.append(block.candidates.get(i));
if (k + 1 < line.size()) {
candidates.append(" ");
}
}
if (exit) {
break;
}
if (j + 1 < result.size()) {
candidates.append("\n");
}
}
if (exit) {
break;
}
candidates.append("\n");
}
return candidates.toString();
}
private void printResult(String title, int status) {
if (status == DHWR.ERR_SUCCESS) {
Log.d(TAG, title + " ...Success");
Log.d(TAG, "Recognized Text:\n" + getCandidates(mResult));
} else {
Log.d(TAG, title + " ...Failed" + " (" + status + ")");
}
}
private int initEngine() {
final int MAX_VERSION_LENGTH = 64;
char[] version = new char[MAX_VERSION_LENGTH];
DHWR.GetRevision(version);
final String filesPath = getFilesDir().getAbsolutePath();
int status = DHWR.Create(filesPath + "/" + "license.key");
DHWR.SetExternalResourcePath(filesPath.toCharArray());
mInk = new DHWR.Ink();
mSetting = new DHWR.Setting();
mResult = new DHWR.Result();
DHWR.SetRecognitionMode(mSetting.GetHandle(), DHWR.MULTICHAR);
DHWR.SetCandidateSize(mSetting.GetHandle(), MAX_CANDIDATES);
Log.d(TAG, "SDK Version: " + String.valueOf(version).trim());
Log.d(TAG, "Initialize Engine ..." + (status == DHWR.ERR_SUCCESS ? "Success" : "Failed"));
return status;
}
private int destroyEngine() {
int status = DHWR.Close();
Log.d(TAG, "Destroy Engine ..." + (status == DHWR.ERR_SUCCESS ? "Success" : "Failed"));
return status;
}
private int testFraction() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_MATH_MIDDLE_EXPANSION, DHWR.DTYPE_MATH_EX);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_fraction);
int status = DHWR.Recognize(mInk, mResult);
printResult("Fraction Test", status);
return status;
}
private int testSquareRoot() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_MATH_MIDDLE_EXPANSION, DHWR.DTYPE_MATH_EX);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_square_root);
int status = DHWR.Recognize(mInk, mResult);
printResult("Square root Test", status);
return status;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.