1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - PRC - demo - characterRecognition-1
3 File: main.c
4
5 Copyright 2007-2008 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Date:: 2008-09-18#$
14 $Rev: 8573 $
15 $Author: okubata_ryoma $
16 *---------------------------------------------------------------------------*/
17
18 #include <nitro.h>
19 #include <nitro/prc/algo_fine.h>
20
21 #include "patterns.h"
22
23 //----- Specify how many places to output recognition result
24 #define RESULT_NUM 5
25
26 //----- The number of queue levels for recognition process wait
27 #define RECOGNITION_QUEUE_SIZE 2
28
29 //----- Maximum number of points per input
30 //----- * As a standard, 60 points are entered each second
31 #define POINT_PER_INPUT_MAX 1024
32 //----- Maximum stroke count per input
33 #define STROKE_PER_INPUT_MAX 20
34 //----- Maximum number of points after resampling
35 #define POINT_AFTER_RESAMPLING_MAX 40
36
37 #define VRAM_BASE_ADDR ((u16*)HW_LCDC_VRAM_C)
38 #define VRAM_LINE_SIZE 256
39 #define DRAW_COLOR 0
40
41 #define MEMORY_MAIN_MAX_HEAPS 1
42 #define MEMORY_MAIN_HEAP_SIZE 0x200000
43
44 static void InitOS(void);
45 static void InitTP(void);
46 static void InitDisplay(void);
47 static void DrawLine(int x1, int y1, int x2, int y2, u16 col);
48 static void PutDot(int x, int y, u16 col);
49 static void ClearScreen(void);
50 void DrawStrokes(const PRCStrokes *strokes, int sx, int sy);
51 static void PrintRecognitionResult(PRCPrototypeEntry **result, fx32 *scores);
52 static void VBlankIntr(void);
53
54
55 typedef struct RecognitionObject
56 {
57 BOOL recognized; // TRUE if variables such as results have valid values
58
59 PRCStrokes strokes;
60 PRCPoint points[POINT_PER_INPUT_MAX];
61
62 PRCPrototypeEntry *results[RESULT_NUM];
63 fx32 scores[RESULT_NUM];
64
65 PRCStrokes inputPatternStrokes;
66 PRCPoint inputPatternPoints[POINT_AFTER_RESAMPLING_MAX];
67 }
68 RecognitionObject;
69
70 void InitRecognition(void);
71 void DestroyRecognition(void);
72 BOOL RecognizePatternAsync(RecognitionObject * packet);
73
74
NitroMain(void)75 void NitroMain(void)
76 {
77 InitOS();
78 InitDisplay();
79 InitTP();
80
81 {
82 PRCPoint points[POINT_PER_INPUT_MAX];
83 PRCStrokes strokes;
84 RecognitionObject queue[RECOGNITION_QUEUE_SIZE]; // May be consuming a large amount of memory. Be aware of that.
85 int queueHead, queueTail, queueSize;
86 u16 prevKey, currKey, trgKey;
87 BOOL touching = FALSE;
88 TPData prevSample = { 0, 0, 0, 0 };
89
90 InitRecognition();
91 {
92 // Embed the entry number in entry.data of the sample database to facilitate subsequent reverse lookup
93 int iEntry;
94 for (iEntry = 0; iEntry < PrototypeList.entrySize; iEntry++)
95 {
96 ((PRCPrototypeEntry *)&PrototypeList.entries[iEntry])->data = (void *)iEntry;
97 }
98 }
99
100 PRC_InitStrokes(&strokes, points, POINT_PER_INPUT_MAX);
101
102 {
103 int i;
104 for (i = 0; i < RECOGNITION_QUEUE_SIZE; i++)
105 {
106 PRC_InitStrokes(&queue[i].strokes, queue[i].points, POINT_PER_INPUT_MAX);
107 PRC_InitStrokes(&queue[i].inputPatternStrokes, queue[i].inputPatternPoints,
108 POINT_AFTER_RESAMPLING_MAX);
109 }
110 }
111 queueHead = 0;
112 queueTail = 0;
113 queueSize = 0;
114
115 ClearScreen();
116
117 //---- Dummy READ workaround for pushing 'A' at IPL
118 currKey = PAD_Read();
119
120 while (TRUE)
121 {
122 BOOL fClearScreen = FALSE;
123 BOOL fRecognizeChar = FALSE;
124 TPData sample;
125
126 prevKey = currKey;
127 currKey = PAD_Read();
128 trgKey = (u16)(currKey & ~prevKey);
129
130 (void)TP_RequestCalibratedSampling(&sample);
131
132 if (sample.touch && (sample.validity == 0))
133 {
134 // Pen is touching the screen
135
136 if (!PRC_IsFull(&strokes))
137 {
138 if (touching)
139 {
140 // Writing
141 DrawLine(prevSample.x, prevSample.y, sample.x, sample.y, DRAW_COLOR);
142 }
143 else
144 {
145 // The moment pen touches the screen
146 PutDot(sample.x, sample.y, DRAW_COLOR);
147 touching = TRUE;
148 }
149 // Store stroke data
150 PRC_AppendPoint(&strokes, sample.x, sample.y);
151 }
152 }
153 else
154 {
155 // Pen is off the screen
156
157 if (touching)
158 {
159 // The moment the pen comes off the screen
160 if (!PRC_IsFull(&strokes))
161 {
162 PRC_AppendPenUpMarker(&strokes);
163 }
164 touching = FALSE;
165 }
166 }
167
168 // Recognition begins when A button is pressed
169 if (trgKey & PAD_BUTTON_A)
170 {
171 fRecognizeChar = TRUE;
172 }
173
174 // Clear screen when B button is pressed
175 if (trgKey & (PAD_BUTTON_START | PAD_BUTTON_B))
176 {
177 fClearScreen = TRUE;
178 }
179
180 // End when SELECT button is pressed
181 if (trgKey & PAD_BUTTON_SELECT)
182 {
183 break;
184 }
185
186 if (fRecognizeChar)
187 {
188 if (PRC_IsEmpty(&strokes))
189 {
190 OS_Printf("\n-----------------------------------------------\n");
191 OS_Printf("No input.\n");
192 }
193 else
194 {
195 // Want to start graphics recognition
196 if (queueSize < RECOGNITION_QUEUE_SIZE)
197 {
198 // Create data for request
199 (void)PRC_CopyStrokes(&strokes, &queue[queueTail].strokes);
200 queue[queueTail].recognized = FALSE;
201
202 // Send request for graphics recognition
203 if (RecognizePatternAsync(&queue[queueTail]))
204 {
205 // Put on queue
206 queueTail++;
207 queueSize++;
208 if (queueTail >= RECOGNITION_QUEUE_SIZE)
209 {
210 queueTail = 0;
211 }
212
213 // Clear screen and input stroke data
214 ClearScreen();
215 PRC_Clear(&strokes);
216 }
217 else
218 {
219 } // If message queue for other thread is full, do nothing
220 // This should not happen if the sizes of the two queues have been matched
221 }
222 else
223 {
224 } // If queue controlled by main is full, do nothing
225 }
226 }
227
228
229 // Wait for V-Blank interrupt
230 // While waiting with OS_WaitIrq, process moves to different thread.
231 OS_WaitIrq(1, OS_IE_V_BLANK);
232 // Return to main thread with V-Blank interrupt
233
234
235 // Is there currently data waiting for recognition?
236 if (queueSize > 0)
237 {
238 // Get and display any recognition results produced
239 if (queue[queueHead].recognized == TRUE)
240 {
241 // Recognition results were produced
242 PRCPrototypeEntry **results = queue[queueHead].results;
243 fx32 *scores = queue[queueHead].scores;
244
245 ClearScreen();
246 // Display again the stroke data currently being input
247 DrawStrokes(&strokes, 0, 0);
248 // In the upper-left portion of the screen, display the interpretation of the input
249 DrawStrokes(&queue[queueHead].inputPatternStrokes, 0, 0);
250
251 if (results[0] != NULL)
252 {
253 // If something could be recognized, it is pulled from the sample database and displayed on the upper-right corner of the screen
254 PRCStrokes drawingStrokes;
255 int dictIndex = (int)results[0]->data; // Entry number was stored in data in advance
256 PRC_GetEntryStrokes(&drawingStrokes, &PrototypeList,
257 &PrototypeList.entries[dictIndex]);
258 DrawStrokes(&drawingStrokes, 256 - PrototypeList.normalizeSize, 0);
259 }
260
261 // Display recognition results in debugging output
262 PrintRecognitionResult(results, scores);
263
264 // Get from queue
265 queueHead++;
266 queueSize--;
267 if (queueHead >= RECOGNITION_QUEUE_SIZE)
268 {
269 queueHead = 0;
270 }
271 }
272 }
273
274 if (fClearScreen)
275 {
276 ClearScreen();
277 PRC_Clear(&strokes);
278 }
279
280 prevSample = sample;
281 }
282 DestroyRecognition();
283 }
284 OS_Printf("Terminated.\n");
285 OS_Terminate();
286 }
287
InitOS(void)288 void InitOS(void)
289 {
290 void *nstart;
291 void *heapStart;
292 OSHeapHandle handle;
293
294 OS_Init();
295
296 nstart =
297 OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(),
298 MEMORY_MAIN_MAX_HEAPS);
299 OS_SetMainArenaLo(nstart);
300
301 heapStart = OS_AllocFromMainArenaLo(MEMORY_MAIN_HEAP_SIZE, 32);
302 handle =
303 OS_CreateHeap(OS_ARENA_MAIN, heapStart, (void *)((u32)heapStart + MEMORY_MAIN_HEAP_SIZE));
304
305 (void)OS_SetCurrentHeap(OS_ARENA_MAIN, handle);
306
307 OS_InitTick();
308 OS_InitAlarm();
309 OS_InitThread();
310 }
311
InitDisplay(void)312 void InitDisplay(void)
313 {
314
315 GX_Init();
316 FX_Init();
317
318 GX_DispOff();
319 GXS_DispOff();
320
321 GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);
322
323 OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
324 (void)OS_EnableIrqMask(OS_IE_V_BLANK);
325 (void)GX_VBlankIntr(TRUE); // To generate V-Blank interrupt request
326 (void)OS_EnableIrq();
327
328 GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
329 MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);
330
331 MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE); // Clear OAM
332 MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE); // Clear the standard palette
333
334 MI_CpuFillFast((void *)HW_DB_OAM, 192, HW_DB_OAM_SIZE); // Clear OAM
335 MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE); // Clear the standard palette
336 MI_DmaFill32(3, (void *)HW_LCDC_VRAM_C, 0x7FFF7FFF, 256 * 192 * sizeof(u16));
337
338 GX_SetGraphicsMode(GX_DISPMODE_VRAM_C, // VRAM mode
339 (GXBGMode)0, // Dummy
340 (GXBG0As)0); // Dummy
341
342 OS_WaitVBlankIntr(); // Waiting for the end of the V-Blank interrupt
343 GX_DispOn();
344 }
345
InitTP(void)346 void InitTP(void)
347 {
348 TPCalibrateParam calibrate;
349
350 TP_Init();
351
352 // Get CalibrateParameter from FlashMemory
353 if (!TP_GetUserInfo(&calibrate))
354 {
355 OS_Panic("Can't get Calibration Parameter from NVRAM\n");
356 }
357 else
358 {
359 OS_Printf("Get Calibration Parameter from NVRAM\n");
360 }
361
362 TP_SetCalibrateParam(&calibrate);
363 }
364
DrawLine(int x1,int y1,int x2,int y2,u16 col)365 void DrawLine(int x1, int y1, int x2, int y2, u16 col)
366 {
367 u16 sx, sy, ey, wx, wy;
368 u16 i;
369 int err, plus, minus;
370 u16 *point = VRAM_BASE_ADDR;
371
372 SDK_ASSERT(x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0);
373
374 // Swap (x1, y1) <-> (x2, y2) if x1 > x2
375 if (x1 <= x2)
376 {
377 sx = (u16)x1;
378 sy = (u16)y1;
379 ey = (u16)y2;
380 wx = (u16)(x2 - x1); // Width
381 }
382 else
383 {
384 sx = (u16)x2;
385 sy = (u16)y2;
386 ey = (u16)y1;
387 wx = (u16)(x1 - x2); // Width
388 }
389
390 point += sy * VRAM_LINE_SIZE + sx;
391 if (sy <= ey)
392 {
393 /* Line goes to upper */
394 wy = (u16)(ey - sy); // Height
395 if (wx > wy)
396 {
397 /* If (X size > Y size) draw a Dot per Xdot */
398 plus = wy * 2;
399 minus = -wx * 2;
400 err = -wx;
401 for (i = 0; i <= wx; i++)
402 {
403 *point = col; // PutDot
404 ++point;
405 err += plus;
406 if (err >= 0)
407 {
408 point += VRAM_LINE_SIZE;
409 err += minus;
410 }
411 }
412
413 }
414 else
415 {
416 /* If (X size <= Y size) draw a Dot per Ydot */
417 plus = wx * 2;
418 minus = -wy * 2;
419 err = -wy;
420 for (i = 0; i <= wy; i++)
421 {
422 *point = col; // PutDot
423 point += VRAM_LINE_SIZE;
424 err += plus;
425 if (err >= 0)
426 {
427 ++point;
428 err += minus;
429 }
430 }
431 }
432 }
433 else
434 {
435 /* line goes to lower */
436 wy = (u16)(sy - ey); // Height
437 if (wx > wy)
438 {
439 /* if (X size > Y size) draw a Dot per Xdot */
440 plus = wy * 2;
441 minus = -wx * 2;
442 err = -wx;
443 for (i = 0; i <= wx; i++)
444 {
445 *point = col; // PutDot
446 ++point;
447 err += plus;
448 if (err >= 0)
449 {
450 point -= VRAM_LINE_SIZE;
451 err += minus;
452 }
453 }
454
455 }
456 else
457 {
458 /* If (X size <= Y size) draw a Dot per Ydot */
459 plus = wx * 2;
460 minus = -wy * 2;
461 err = -wy;
462 for (i = 0; i <= wy; i++)
463 {
464 *point = col; // PutDot
465 point -= VRAM_LINE_SIZE;
466 err += plus;
467 if (err >= 0)
468 {
469 ++point;
470 err += minus;
471 }
472 }
473 }
474 }
475 }
476
PutDot(int x,int y,u16 col)477 void PutDot(int x, int y, u16 col)
478 {
479 *(u16 *)((u32)VRAM_BASE_ADDR + y * 256 * 2 + x * 2) = col;
480 }
481
ClearScreen(void)482 void ClearScreen(void)
483 {
484 MI_DmaFill32(3, (void *)HW_LCDC_VRAM_C, 0x7FFF7FFF, 256 * 192 * sizeof(u16));
485 }
486
DrawStrokes(const PRCStrokes * strokes,int sx,int sy)487 void DrawStrokes(const PRCStrokes *strokes, int sx, int sy)
488 {
489 int iPoint;
490 PRCPoint prev;
491 BOOL newFlag;
492 const PRCPoint *point;
493
494 newFlag = TRUE;
495 point = strokes->points;
496 for (iPoint = 0; iPoint < strokes->size; iPoint++, point++)
497 {
498 if (!PRC_IsPenUpMarker(point))
499 {
500 if (newFlag)
501 {
502 PutDot(sx + point->x, sy + point->y, 1);
503 }
504 else
505 {
506 DrawLine(sx + prev.x, sy + prev.y, sx + point->x, sy + point->y, 1);
507 }
508 prev = *point;
509 newFlag = FALSE;
510 }
511 else
512 {
513 newFlag = TRUE;
514 }
515 }
516 }
517
PrintRecognitionResult(PRCPrototypeEntry ** results,fx32 * scores)518 void PrintRecognitionResult(PRCPrototypeEntry **results, fx32 *scores)
519 {
520 int iResult;
521 (void)scores;
522
523 OS_Printf("\n-----------------------------------------------\n");
524
525 // Recognition results output
526 if (results[0] == NULL)
527 {
528 OS_Printf("Result: can't recognize as char\n");
529 }
530 else
531 {
532 OS_Printf("Result:\n");
533 for (iResult = 0; iResult < RESULT_NUM; iResult++)
534 {
535 int code;
536
537 if (results[iResult] == NULL)
538 break;
539
540 code = PRC_GetEntryCode(results[iResult]);
541 OS_Printf(" '%s' (score: %d.%03d)\n", PatternName[code], FX_Whole(scores[iResult]),
542 (scores[iResult] & FX16_DEC_MASK) * 1000 / (1 << FX16_DEC_SIZE));
543 }
544 }
545 }
546
VBlankIntr(void)547 void VBlankIntr(void)
548 {
549 // Set IRQ check flag
550 OS_SetIrqCheckFlag(OS_IE_V_BLANK);
551 }
552
553
554
555
556
557 /****************************************************************************
558 Figure recognition execution section
559 ****************************************************************************/
560 #define RECOGNITION_THREAD_STACK_SIZE 0x4000
561 #define RECOGNITION_THREAD_PRIO 20
562
563 #define RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT RECOGNITION_QUEUE_SIZE
564
565 PRCPrototypeDB gProtoDB;
566 PRCInputPattern gInputPattern;
567 void *gPrototypeListBuffer;
568 void *gPatternBuffer;
569 void *gRecognitionBuffer;
570
571 OSThread gRecognitionThread;
572 void *gRecognitionThreadStack;
573
574 OSMessageQueue gRecognitionRequestMessageQueue;
575 OSMessage gRecognitionRequestMessages[RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT];
576
577 void RecognizePattern(void *data);
578
InitRecognition(void)579 void InitRecognition(void)
580 {
581 PRC_Init();
582
583 // Expand the sample database
584 // PrototypeList is a list of the sample patterns defined in a separate file
585 {
586 u32 size;
587
588 size = PRC_GetPrototypeDBBufferSize(&PrototypeList);
589 OS_Printf("required buffer for InitPrototypeDB: %d bytes\n", size);
590 gPrototypeListBuffer = OS_Alloc(size);
591 SDK_ASSERT(gPrototypeListBuffer);
592 }
593 if (!PRC_InitPrototypeDB(&gProtoDB, gPrototypeListBuffer, &PrototypeList))
594 {
595 OS_Printf("cannot initialize the prototype DB.\n");
596 OS_Terminate();
597 }
598
599 {
600 u32 size;
601
602 // Allocate memory for expansion of input stroke data
603 size = PRC_GetInputPatternBufferSize(POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX);
604 OS_Printf("required buffer for InitInputPattern: %d bytes\n", size);
605 gPatternBuffer = OS_Alloc(size);
606 SDK_ASSERT(gPatternBuffer);
607
608 // Allocate memory for recognition algorithm
609 size =
610 PRC_GetRecognitionBufferSize(POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX,
611 &gProtoDB);
612 OS_Printf("required buffer for Recognition: %d bytes\n", size);
613 gRecognitionBuffer = OS_Alloc(size);
614 SDK_ASSERT(gRecognitionBuffer);
615 }
616
617 // Initialize the message queue used for communication with the recognition routine thread
618 OS_InitMessageQueue(&gRecognitionRequestMessageQueue, gRecognitionRequestMessages,
619 RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT);
620
621 // Allocate the stack region for the thread
622 gRecognitionThreadStack = OS_Alloc(RECOGNITION_THREAD_STACK_SIZE);
623 SDK_ASSERT(gRecognitionThreadStack);
624
625 // Create separate thread for recognition routine
626 OS_CreateThread(&gRecognitionThread,
627 RecognizePattern,
628 NULL,
629 (void *)((int)gRecognitionThreadStack + RECOGNITION_THREAD_STACK_SIZE),
630 RECOGNITION_THREAD_STACK_SIZE, RECOGNITION_THREAD_PRIO);
631
632 // Start separate thread
633 // However, not immediately executed, because main routine has higher priority.
634 OS_WakeupThreadDirect(&gRecognitionThread);
635 }
636
DestroyRecognition(void)637 void DestroyRecognition(void)
638 {
639 if (!OS_IsThreadTerminated(&gRecognitionThread))
640 {
641 // Terminate recognition thread
642
643 // Send termination command
644 (void)OS_JamMessage(&gRecognitionRequestMessageQueue, (OSMessage)NULL, OS_MESSAGE_BLOCK);
645
646 // Wait for thread to be terminated
647 OS_JoinThread(&gRecognitionThread);
648 }
649
650 OS_Free(gRecognitionThreadStack);
651 OS_Free(gRecognitionBuffer);
652 OS_Free(gPatternBuffer);
653 OS_Free(gPrototypeListBuffer);
654 }
655
RecognizePatternAsync(RecognitionObject * object)656 BOOL RecognizePatternAsync(RecognitionObject * object)
657 {
658 if (object == NULL)
659 {
660 return FALSE;
661 }
662 // Request recognition processing from a separate thread
663 return OS_SendMessage(&gRecognitionRequestMessageQueue, (OSMessage)object, OS_MESSAGE_NOBLOCK);
664 }
665
666
667 // Functions running in a separate thread
RecognizePattern(void * data)668 void RecognizePattern(void *data)
669 {
670 PRCInputPatternParam inputParam;
671
672 (void)data;
673
674 inputParam.normalizeSize = gProtoDB.normalizeSize;
675 // inputParam.resampleMethod = PRC_RESAMPLE_METHOD_DISTANCE;
676 // inputParam.resampleThreshold = 12;
677 // inputParam.resampleMethod = PRC_RESAMPLE_METHOD_ANGLE;
678 // inputParam.resampleThreshold = 4096;
679 inputParam.resampleMethod = PRC_RESAMPLE_METHOD_RECURSIVE;
680 inputParam.resampleThreshold = 2;
681
682 while (TRUE)
683 {
684 OSMessage msg;
685 RecognitionObject *obj;
686
687 // Wait for recognition to be requested
688 (void)OS_ReceiveMessage(&gRecognitionRequestMessageQueue, &msg, OS_MESSAGE_BLOCK);
689 if (msg == (OSMessage)NULL)
690 {
691 // Termination command
692 break;
693 }
694 obj = (RecognitionObject *) msg;
695
696 if (PRC_InitInputPatternEx(&gInputPattern, gPatternBuffer, &obj->strokes,
697 POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX, &inputParam))
698 {
699 // Compare prototypeList and inputPattern and perform recognition
700 (void)PRC_GetRecognizedEntriesEx(obj->results, obj->scores, RESULT_NUM,
701 gRecognitionBuffer, &gInputPattern, &gProtoDB,
702 PRC_KIND_ALL, NULL);
703
704 {
705 PRCStrokes tmpStrokes;
706 PRC_GetInputPatternStrokes(&tmpStrokes, &gInputPattern);
707 (void)PRC_CopyStrokes(&tmpStrokes, &obj->inputPatternStrokes);
708 }
709 }
710 else
711 {
712 // Failure in PRC_InitInputPattern due to incorrect input
713 int i;
714
715 PRC_Clear(&obj->inputPatternStrokes);
716 for (i = 0; i < RESULT_NUM; i++)
717 {
718 obj->results[i] = NULL;
719 obj->scores[i] = 0;
720 }
721 }
722
723 // Set flag indicating that recognition was completed
724 obj->recognized = TRUE;
725 }
726 }
727