Skip to content

Instantly share code, notes, and snippets.

@selvypen
Last active March 24, 2024 15:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save selvypen/a27ca8368bde62f370effc634b4ed9de to your computer and use it in GitHub Desktop.
Save selvypen/a27ca8368bde62f370effc634b4ed9de to your computer and use it in GitHub Desktop.
Simple example source of handwriting recognition for Windows using C++
/*!
* @brief example source of handwriting recognition
* @date 2019/12/03
* @file handwriting_simple_example.cpp
* @author SELVAS AI
*
* Copyright 2019. SELVAS AI Inc. All Rights Reserved.
*/
#include <stdio.h>
#include <locale.h>
#include "dhwr.h"
#define log(...) printf(__VA_ARGS__)
// 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)
const 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'
const 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'
const 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 '안녕하세요'
const 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 '中囯'
const 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 'ありがとう'
const 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};
const int DHWR_MAX_CANDIDATES = 10;
DHWRInkObject ink_obj = NULL;
DHWRResultObject result_obj = NULL;
DHWRSettingObject setting_obj = NULL;
void make_input_event(DHWRInkObject ink, const int* inputs) {
DHWRInkClear(ink);
int i = 0;
while (true) {
if (inputs[i + 0] == -1 && inputs[i + 1] == -1) {
break;
} else if (inputs[i + 0] == -1 && inputs[i + 1] == 0) {
DHWREndStroke(ink);
} else {
DHWRAddPoint(ink, inputs[i + 0], inputs[i + 1]);
}
i += 2;
}
}
void print_wchar(const wchar_t* result, int size) {
int i;
for (i = 0; i < size; i++) {
log("%C", result[i]);
}
log(" (");
for (i = 0; i < size; i++) {
if (i != 0) {
log(" ");
}
log("0x%04x", result[i]);
}
log(")");
}
void print_candidate(DHWRResultObject result) {
bool exit = false;
unsigned int length;
int size_line = DHWRGetLineSize(result);
if (size_line == 0) {
log("result empty\n");
return;
}
for (unsigned int i = 0; i < DHWR_MAX_CANDIDATES; i++) {
for (int j = 0; j < size_line; j++) {
DHWRResultLine line = DHWRGetLine(result, j);
int size_block = DHWRGetBlockSize(line);
for (int k = 0; k < size_block; k++) {
DHWRResultBlock block = DHWRGetBlock(line, k);
if (DHWRGetCandidateSize(block) <= i) {
exit = true;
break;
}
log("[%02d] ", i + 1);
const wchar_t* current_cand = (const wchar_t*)(DHWRGetCandidate(block, i, &length));
print_wchar(current_cand, length);
if (k + 1 < size_block) {
log(", ");
}
}
if (exit) {
break;
}
if (j + 1 < size_line) {
log("\n");
}
}
if (exit) {
break;
}
log("\n");
}
}
void print_result(const char *title, int status) {
log("\n%s ", title);
if (status == ERR_SUCCESS) {
log("...Success\n");
print_candidate(result_obj);
} else {
log("...Failed (%d)\n", status);
}
}
int initEngine() {
unsigned long status = ERR_SUCCESS;
status = DHWRCreate("./license_key/license.key");
if (ink_obj == NULL) {
ink_obj = DHWRCreateInkObject();
}
if (result_obj == NULL) {
result_obj = DHWRCreateResultObject();
}
if (setting_obj == NULL) {
setting_obj = DHWRCreateSettingObject();
}
DHWRSetExternalResourcePath("./hdb/");
DHWRSetExternalLibraryPath("./lib/"); // for chinese & thai
DHWRSetRecognitionMode(setting_obj, MULTICHAR);
DHWRSetCandidateSize(setting_obj, DHWR_MAX_CANDIDATES);
log("\nInitialize Engine ...%s (%ld)\n", status == ERR_SUCCESS ? "Success" : "Failed", status);
return status;
}
int destroyEngine() {
unsigned long status = ERR_SUCCESS;
status = DHWRClose();
if (ink_obj != NULL) {
DHWRDestroyInkObject(ink_obj);
}
if (result_obj != NULL) {
DHWRDestroyResultObject(result_obj);
}
if (setting_obj != NULL) {
DHWRDestroySettingObject(setting_obj);
}
log("\nDestroy Engine ...%s (%ld)\n", status == ERR_SUCCESS ? "Success" : "Failed", status);
return status;
}
int testKorean() {
unsigned long status = ERR_SUCCESS;
DHWRClearLanguage(setting_obj);
DHWRAddLanguage(setting_obj, DLANG_KOREAN, (DHWR_TYPE)(DTYPE_KOREAN | DTYPE_SIGN | DTYPE_NUMERIC));
DHWRSetAttribute(setting_obj);
make_input_event(ink_obj, ink_annyeonghaseyo);
status = DHWRecognize(ink_obj, result_obj);
print_result("Korean Test", status);
return status;
}
int testEnglish() {
unsigned long status = ERR_SUCCESS;
DHWRClearLanguage(setting_obj);
DHWRAddLanguage(setting_obj, DLANG_ENGLISH, (DHWR_TYPE)(DTYPE_UPPERCASE | DTYPE_LOWERCASE));
DHWRSetAttribute(setting_obj);
make_input_event(ink_obj, ink_good_morning);
status = DHWRecognize(ink_obj, result_obj);
print_result("English Test", status);
return status;
}
int testChinese() {
unsigned long status = ERR_SUCCESS;
DHWRClearLanguage(setting_obj);
DHWRAddLanguage(setting_obj, DLANG_CHINA, DTYPE_SIMP);
DHWRSetAttribute(setting_obj);
make_input_event(ink_obj, ink_zhongguo);
status = DHWRecognize(ink_obj, result_obj);
print_result("Chinese Test", status);
return status;
}
int testJapanese() {
unsigned long status = ERR_SUCCESS;
DHWRClearLanguage(setting_obj);
DHWRAddLanguage(setting_obj, DLANG_JAPANESE, DTYPE_HIRAGANA);
DHWRSetAttribute(setting_obj);
make_input_event(ink_obj, ink_arigatou);
status = DHWRecognize(ink_obj, result_obj);
print_result("Japanese Test", status);
return status;
}
int main() {
setlocale(LC_ALL, "");
initEngine();
testKorean();
testEnglish();
testChinese();
testJapanese();
destroyEngine();
return 0;
}
@chavito706
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment