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