Skip to content

Instantly share code, notes, and snippets.

@selvypen
Last active May 13, 2020 05:25
Show Gist options
  • Save selvypen/787fe7d13e8fd14b08ec1234e8e9bdb7 to your computer and use it in GitHub Desktop.
Save selvypen/787fe7d13e8fd14b08ec1234e8e9bdb7 to your computer and use it in GitHub Desktop.
Simple example source of handwriting recognition for Android using Java
/*!
* @brief example source of handwriting recognition
* @date 2019/12/10
* @file HandwritingExampleActivity.java
* @author SELVAS AI
*
* Copyright 2019. SELVAS AI Inc. All Rights Reserved.
*/
package com.selvasai.handwriting;
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.diotek.dhwr.DHWR;
public class HandwritingExampleActivity 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 'a'
// -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_a = {271, 97, 270, 95, 266, 94, 262, 94, 256, 94, 249, 94, 243, 96, 234, 100, 226,
107, 216, 118, 208, 127, 203, 137, 195, 147, 191, 156, 188, 163, 188, 169, 188, 172, 191, 175,
195, 176, 202, 176, 210, 176, 218, 176, 227, 173, 237, 170, 246, 166, 253, 161, 258, 156, 261,
153, 264, 148, 265, 144, 267, 139, 267, 135, 268, 131, 268, 128, 268, 123, 268, 122, 268, 120,
268, 117, 268, 116, 268, 119, 273, 126, 278, 133, 284, 140, 291, 145, 294, 150, 299, 153, 301,
156, 303, 159, 306, 160, 307, 163, 308, 163, -1, 0, -1, -1
};
// points of 'good morning'
final int[] ink_good_morning = {242, 382, 233, 372, 209, 385, 176, 425, 174, 438, 195, 447, 216,
427, 223, 411, 231, 379, 233, 382, 233, 405, 234, 456, 232, 512, 227, 534, 200, 569, 167, 569, 154,
543, 161, 513, 173, 500, 220, 476, 266, 464, 278, 458, 282, 458, -1, 0, 303, 398, 292, 403, 285,
426, 307, 437, 319, 430, 330, 397, 311, 385, 291, 391, 297, 401, -1, 0, 369, 383, 363, 386, 358,
415, 385, 421, 411, 402, 418, 388, 406, 370, 372, 380, 362, 402, 368, 406, -1, 0, 496, 385, 497,
384, 495, 377, 471, 383, 457, 391, 438, 418, 463, 424, 482, 416, 510, 386, 516, 343, 511, 315,
508, 302, 510, 306, 513, 336, 518, 384, 528, 419, 534, 425, 533, 419, -1, 0, 626, 365, 629, 370,
633, 392, 635, 405, 634, 421, 630, 420, 638, 389, 644, 378, 658, 371, 671, 393, 673, 412, 673, 416,
673, 409, 678, 389, 684, 378, 704, 366, 715, 377, 720, 397, 723, 409, 723, 411, 725, 409, 730, 402,
-1, 0, 756, 374, 750, 375, 749, 401, 776, 400, 788, 391, 797, 373, 766, 366, 750, 373, 763, 375,
-1, 0, 814, 350, 814, 347, 823, 358, 836, 390, 838, 406, 835, 409, 833, 397, 837, 389, 855, 368,
875, 356, 894, 354, 900, 355, 899, 365, -1, 0, 911, 342, 917, 357, 916, 370, 918, 398, 917, 401,
917, 384, 929, 351, 946, 341, 955, 354, 958, 366, 961, 389, 964, 401, 959, 380, -1, 0, 983, 327,
991, 326, 1005, 330, 1005, 338, -1, 0, 991, 362, 992, 366, 999, 393, 1001, 400, -1, 0, 1032, 344,
1039, 350, 1045, 376, 1046, 396, 1049, 388, 1053, 378, 1067, 355, 1079, 352, 1086, 374, 1092, 398,
1095, 404, 1097, 403, 1099, 398, -1, 0, 1146, 345, 1149, 350, 1140, 356, 1127, 375, 1142, 384,
1160, 359, 1155, 342, 1151, 343, 1164, 366, 1180, 403, 1181, 439, 1176, 457, 1153, 486, 1113, 488,
1089, 463, 1101, 443, 1116, 434, 1162, 417, 1219, 409, 1229, 416, 1228, 417, -1, 0, -1, -1
};
// points of '4'
final int[] ink_4 = {273, 69, 270, 72, 267, 78, 261, 88, 253, 103, 245, 115, 236, 127, 231, 137, 227,
144, 226, 146, 230, 146, 238, 146, 252, 146, 271, 145, 291, 143, 313, 141, 329, 139, 341, 137,
352, 137, 358, 137, 362, 137, 364, 136, -1, 0, 300, 105, 300, 106, 300, 112, 298, 121, 298, 132,
296, 143, 296, 154, 296, 163, 296, 169, 296, 177, 296, 181, 295, 186, 295, 189, 295, 190, -1, 0,
-1, -1
};
// points of '안녕하세요'
final int[] ink_annyeonghaseyo = {182, 315, 185, 316, 183, 316, 182, 318, 180, 320, 173, 330, 159,
356, 149, 384, 149, 406, 157, 426, 175, 440, 200, 451, 230, 446, 250, 431, 259, 411, 268, 384,
266, 357, 258, 341, 244, 330, 229, 327, 215, 326, 204, 328, 195, 332, 192, 339, -1, 0, 357,
269, 355, 268, 355, 271, 354, 281, 355, 317, 359, 360, 363, 403, 365, 430, 364, 443, 362, 443,
-1, 0, 363, 377, 368, 370, 371, 370, 381, 370, 400, 371, 424, 376, 431, 377, -1, 0, 275, 502,
270, 504, 268, 508, 267, 515, 268, 527, 272, 541, 281, 556, 299, 571, 324, 575, 357, 569, 387,
555, 408, 533, -1, 0, 506, 301, 498, 296, 497, 302, 492, 327, 484, 364, 479, 401, 478, 432,
482, 453, 489, 467, 502, 470, 518, 471, 551, 468, 571, 454, 574, 450, -1, 0, 552, 334, 554,
319, 559, 323, 574, 320, 604, 317, 635, 315, 650, 316, 659, 320, 656, 322, -1, 0, 591, 377,
589, 377, 598, 375, 622, 371, 650, 363, 678, 353, 683, 348, -1, 0, 701, 300, 698, 300, 697,
301, 696, 307, 694, 318, 692, 347, 688, 378, 682, 412, 678, 442, 678, 449, -1, 0, 621, 515,
606, 520, 603, 526, 600, 536, 602, 546, 616, 558, 649, 562, 674, 556, 682, 546, 679, 538, 666,
531, 621, 518, 594, 526, -1, 0, 790, 282, 789, 278, 789, 279, 790, 282, 799, 288, 829, 304,
854, 318, 871, 330, 872, 331, -1, 0, 793, 386, 787, 383, 786, 383, 785, 384, 792, 384, 827,
385, 861, 385, 878, 387, -1, 0, 828, 436, 807, 448, 796, 457, 783, 471, 777, 487, 778, 501,
790, 506, 825, 507, 847, 496, 851, 487, 850, 477, 844, 471, 830, 465, 804, 460, 792, 459, -1,
0, 937, 294, 936, 290, 935, 292, 933, 298, 931, 315, 930, 359, 931, 407, 935, 453, 939, 488,
942, 518, 942, 536, 939, 550, 936, 554, -1, 0, 939, 408, 946, 399, 947, 402, 967, 401, 997,
402, 1013, 405, -1, 0, 1145, 319, 1134, 315, 1135, 318, 1129, 326, 1105, 362, 1080, 403, 1058,
446, 1041, 476, 1030, 496, 1030, 494, -1, 0, 1069, 430, 1071, 425, 1073, 427, 1079, 433, 1092,
449, 1106, 471, 1114, 486, -1, 0, 1106, 416, 1104, 404, 1116, 407, 1146, 403, 1179, 399, 1206,
391, 1209, 389, -1, 0, 1228, 352, 1225, 357, 1224, 361, 1222, 374, 1222, 404, 1223, 431, 1226,
452, 1226, 466, 1225, 463, -1, 0, 1269, 345, 1268, 343, 1267, 347, 1265, 360, 1265, 398, 1267,
434, 1269, 466, 1272, 484, 1276, 490, -1, 0, 1373, 342, 1369, 342, 1368, 345, 1368, 354, 1372,
366, 1382, 376, 1404, 385, 1431, 377, 1445, 360, 1451, 334, 1446, 321, 1432, 321, 1400, 326,
1369, 341, 1358, 359, 1353, 380, 1357, 402, 1363, 421, 1370, 440, 1376, 459, 1377, 479, 1374,
497, 1369, 509, 1363, 516, 1361, 514, -1, 0, 1426, 413, 1432, 407, 1432, 408, 1434, 416, 1429,
442, 1413, 476, 1388, 503, 1358, 526, 1334, 534, 1319, 536, 1318, 533, 1342, 523, 1417, 503,
1477, 495, 1525, 497, -1, 0, -1, -1
};
// points of '中囯'
final int[] ink_zhongguo = {54, 72, 53, 72, 52, 73, 52, 75, 52, 80, 52, 83, 52, 87, 52, 89, 52,
95, 52, 98, 52, 101, 51, 104, 51, 106, 51, 109, 51, 111, 51, 114, 51, 116, 51, 118, 51,
121, 51, 122, 51, 123, 51, 120, 51, 117, 50, 114, -1, 0, 51, 81, 52, 81, 53, 81, 54, 81,
55, 81, 56, 81, 59, 81, 60, 81, 64, 80, 68, 79, 71, 78, 76, 78, 81, 76, 86, 75, 96, 74,
99, 74, 104, 73, 110, 72, 113, 72, 117, 71, 119, 71, 123, 70, 125, 70, 127, 70, 127, 69,
128, 69, 129, 69, 129, 70, 129, 71, 129, 73, 129, 75, 129, 78, 129, 81, 129, 84, 129, 87,
129, 90, 129, 94, 129, 95, 129, 97, 129, 98, 129, 101, 129, 102, 129, 103, 129, 104, 129,
105, 128, 106, 127, 106, 124, 106, -1, 0, 47, 114, 48, 114, 49, 114, 50, 114, 53, 114, 55,
114, 56, 114, 58, 114, 61, 114, 66, 114, 70, 113, 75, 112, 82, 112, 87, 111, 96, 108, 104,
108, 111, 107, 119, 106, 123, 106, 126, 105, 130, 105, 131, 104, 132, 104, 133, 104, 133,
103, 132, 101, -1, 0, 82, 30, 82, 31, 82, 32, 82, 34, 82, 38, 82, 39, 82, 41, 82, 46, 82,
48, 82, 52, 82, 54, 82, 58, 82, 63, 82, 67, 82, 69, 82, 75, 82, 78, 82, 83, 82, 87, 82,
91, 82, 95, 82, 98, 82, 102, 82, 105, 82, 110, 82, 113, 82, 116, 82, 120, 82, 124, 82,
128, 82, 131, 82, 134, 82, 137, 82, 139, 82, 141, 82, 146, 82, 147, 82, 148, 82, 149, 82,
150, 82, 151, 82, 152, 82, 153, 82, 154, 82, 155, 82, 156, 82, 157, 81, 157, 81, 155, 81,
152, 81, 149, -1, 0, 165, 51, 166, 54, 166, 59, 167, 64, 167, 71, 167, 76, 167, 83, 167,
90, 167, 96, 167, 105, 167, 110, 167, 114, 167, 118, 167, 122, 167, 125, 167, 130, 167,
133, 167, 135, 167, 137, 167, 138, 167, 140, 167, 138, 167, 132, 167, 129, -1, 0, 168,
60, 169, 60, 170, 60, 171, 60, 174, 60, 175, 60, 176, 60, 177, 60, 178, 60, 180, 60, 181,
60, 185, 59, 189, 59, 193, 59, 199, 58, 205, 55, 209, 54, 211, 53, 217, 52, 220, 51, 222,
51, 224, 51, 225, 51, 226, 51, 227, 51, 228, 50, 229, 50, 231, 50, 232, 50, 233, 50, 234,
50, 235, 51, 235, 53, 235, 55, 235, 56, 237, 57, 237, 60, 237, 63, 237, 64, 238, 67, 238,
69, 238, 73, 238, 75, 238, 80, 238, 82, 238, 86, 238, 88, 238, 94, 238, 96, 238, 99, 238,
103, 238, 106, 238, 110, 238, 112, 238, 116, 238, 117, 238, 119, 238, 123, 238, 125, 238,
126, 238, 129, 238, 130, 238, 132, 238, 134, 238, 135, 238, 136, 238, 137, 237, 135, 234,
131, 231, 129, 225, 124, -1, 0, 176, 90, 177, 90, 178, 90, 179, 90, 180, 90, 181, 90, 182,
89, 183, 89, 185, 87, 188, 87, 190, 86, 192, 85, 194, 84, 196, 84, 197, 83, 198, 83, 199,
83, 200, 83, 201, 83, 202, 83, 204, 83, 205, 83, 206, 83, 206, 82, -1, 0, 180, 109, 181,
109, 182, 109, 183, 109, 184, 109, 186, 109, 188, 109, 190, 109, 192, 109, 197, 107, 199,
107, 200, 107, 202, 107, 203, 106, 205, 106, 205, 105, -1, 0, 191, 86, 192, 86, 192, 89,
192, 90, 193, 92, 193, 93, 193, 96, 193, 98, 193, 100, 194, 103, 194, 105, 194, 106, 194,
108, 194, 110, 194, 111, 194, 114, 194, 115, 194, 116, 194, 118, 194, 120, 194, 122, 194,
123, 194, 124, 194, 125, -1, 0, 183, 129, 184, 129, 185, 129, 186, 129, 187, 129, 188,
129, 190, 128, 193, 127, 195, 127, 198, 126, 201, 125, 202, 124, 206, 123, 207, 123, 209,
123, 210, 123, 211, 123, 212, 123, 213, 123, 213, 122, -1, 0, 218, 93, 218, 94, 219, 94,
220, 96, 221, 96, 221, 98, 221, 99, 223, 99, 223, 101, 224, 102, 225, 103, 225, 105, 226,
106, 227, 107, 227, 108, 227, 109, -1, 0, 169, 146, 170, 146, 171, 146, 172, 146, 176, 146,
181, 145, 187, 145, 192, 143, 197, 143, 203, 142, 206, 142, 208, 141, 210, 141, 211, 141,
212, 141, 213, 141, 213, 140, 214, 140, 215, 140, 216, 140, 217, 140, 218, 140, 219, 140,
221, 140, 223, 140, 224, 140, 225, 140, 226, 140, -1, 0, -1, -1
};
// points of 'ありがとう'
final int[] ink_arigatou = {272, 331, 265, 338, 311, 340, 400, 328, 455, 306, 470, 282, -1, 0, 354,
236, 342, 243, 342, 305, 357, 394, 374, 462, 386, 499, 384, 479, -1, 0, 444, 350, 435, 388, 416,
412, 349, 454, 299, 459, 278, 439, 325, 391, 411, 373, 457, 392, 456, 452, 451, 463, 455, 454, -1,
0, 569, 310, 570, 327, 571, 365, 570, 380, 569, 370, -1, 0, 619, 280, 620, 280, 632, 309, 639, 357,
638, 386, 620, 447, 591, 505, 585, 516, 596, 489, -1, 0, 706, 315, 705, 310, 735, 308, 794, 308,
827, 327, 826, 388, 808, 437, 790, 459, 785, 441, -1, 0, 816, 251, 803, 248, 772, 302, 739, 364,
723, 426, 723, 468, 740, 450, 747, 441, -1, 0, 839, 294, 851, 303, 859, 330, 861, 344, 858, 338,
-1, 0, 869, 235, 874, 240, 876, 249, 878, 263, 872, 259, -1, 0, 887, 223, 892, 227, 902, 249, 906,
264, -1, 0, 970, 266, 979, 288, 994, 314, 1015, 320, -1, 0, 1075, 272, 1078, 276, 1021, 338, 962,
391, 940, 432, 946, 458, 971, 482, 1029, 487, 1069, 475, 1090, 463, -1, 0, 1181, 234, 1223, 256,
1237, 262, 1249, 270, -1, 0, 1151, 336, 1146, 340, 1193, 320, 1246, 320, 1285, 337, 1290, 387,
1246, 448, 1203, 494, 1204, 486, -1, 0, -1, -1
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
copyResourceToStorage();
initEngine();
testKorean();
testEnglish();
testChinese();
testJapanese();
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());
DHWR.SetExternalLibraryPath(getApplicationInfo().nativeLibraryDir.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 testKorean() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_KOREAN, DHWR.DTYPE_KOREAN);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_annyeonghaseyo);
int status = DHWR.Recognize(mInk, mResult);
printResult("Korean Test", status);
return status;
}
private int testEnglish() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_ENGLISH, DHWR.DTYPE_UPPERCASE | DHWR.DTYPE_LOWERCASE);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_good_morning);
int status = DHWR.Recognize(mInk, mResult);
printResult("English Test", status);
return status;
}
private int testChinese() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_CHINA, DHWR.DTYPE_SIMP);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_zhongguo);
int status = DHWR.Recognize(mInk, mResult);
printResult("Chinese Test", status);
return status;
}
private int testJapanese() {
DHWR.ClearLanguage(mSetting.GetHandle());
DHWR.AddLanguage(mSetting.GetHandle(), DHWR.DLANG_JAPANESE, DHWR.DTYPE_HIRAGANA);
DHWR.SetAttribute(mSetting.GetHandle());
makeInputEvent(mInk, ink_arigatou);
int status = DHWR.Recognize(mInk, mResult);
printResult("Japanese Test", status);
return status;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment