IOS simple handwritten Chinese Character Recognition and ios Chinese Character Recognition
Introduction
Some time ago, I added a small feature to banxun: I wrote Chinese characters according to the pinyin prompts. After the submission, the software scored the scores. The interface is as follows:
The following describes the implementation of the first version recognition algorithm:
- Recording Chinese Character Input tracks
In iOS, The UIView inherits the UIResponder class. The four methods in this class need to be called:
-(Void) touchesBegan :( NSSet *) touches withEvent :( UIEvent *) event;
-(Void) touchesMoved :( NSSet *) touches withEvent :( UIEvent *) event;
-(Void) touchesEnded :( NSSet *) touches withEvent :( UIEvent *) event;
-(Void) touchesCancelled :( NSSet *) touches withEvent :( UIEvent *) event;
- In the first three methods, we record the starting point, process point, and end point of a stroke, add the vertex to the array of saved points, and in the touchesEnded method, add the array of saved points to the array of saved strokes. In this way, after writing a word, the array that saves the strokes is the point set of the entire word we want.
- Save the set to the database template table as a template.
- After the template is complete, record the user-Input Point Set and record the data in the User table.
// Collection point set-(void) touchesBegan :( NSSet *) touches withEvent :( UIEvent *) event {for (UITouch * touch in touches) {NSMutableArray * vertex = [NSMutableArray array]; [newStackOfPoints addObject: [NSValue valueWithCGPoint: [touch locationInView: self]; [self. strokes addObject: newStackOfPoints] ;}}-(void) touchesMoved :( NSSet *) touches withEvent :( UIEvent *) event {for (UITouch * touch in touches) {[self. strokes lastObject] addObject: [NSValue valueWithCGPoint: [touch locationInView: self]; }}- (void) touchesEnded :( NSSet *) touches withEvent :( UIEvent *) event {for (UITouch * touch in touches) {[self. strokes lastObject] addObject: [NSValue valueWithCGPoint: [touch locationInView: self];}
// Insert the database-(int) insertChar :( CharacterModel *) model {int charId = 0; [self. hanziDb executeUpdate: @ "insert into characters (chinese, pinyin, pointsString) values (?,?,?) ", Model. chinese, model. pinyin, model. pointsString]; NSString * queryString = [NSString stringWithFormat: @ "select id from characters where chinese = '% @'", model. chinese]; FMResultSet * set = [self. hanziDb executeQuery: queryString]; if ([set next]) {charId = [set intForColumn: @ "id"] ;}for (int I = 0; I <model. inputPointGrids. count; I ++) {NSArray * aStroke = model. inputPointGrids [I]; for (NSValue * aPoin TValue in aStroke) {CGPoint aPoint = [aPointValue CGPointValue]; [self. hanziDb executeUpdate: @ "insert into points (id, pointX, pointY, strokeid) values (?,?,?,?) ", [NSNumber numberWithInt: charId], [NSNumber numberWithInt :( int) aPoint. x], [NSNumber numberWithInt :( int) aPoint. y], [NSNumber numberWithInt: I + 1] ;}} return charId ;}
- Compare user input data and template data
- With the data, the rest is to use the select statement of the database for comparison. However, in actual comparison, we will find that ~~ There are only a few vertices to match. The reason is simple: in the first step, the absolute coordinates of the points are recorded. For example, if we write "1", when entering the template, the recorded coordinate set is {(50,200), (100,200), (150,200), (200,200), (250,200 )}, when the user inputs the information {(60,220), (90,220), (130,220), (160,220), (200,220), (230,220)}, the result is Zero match. Another important thing is the screen resolution ......
- In this case, the input area is divided into 50 grids. Each time a grid passes through, it is recorded as relative coordinates, so that the two above data becomes {(10, 40), (20, 40), (30, 40), (40, 40), (50, 40)} and {(10, 40), (20, 40), (30, 40), (40, 40), (50, 40), (60, 40)}, the matching degree is improved (of course, this is just an example of data, not accurate ).
- After being changed to the relative coordinate storage, although the matching degree is improved a lot, the identification is still inaccurate when the upper and lower strokes or the left and right positions are relatively different. For example, a horizontal template data is {(10, 40), (20, 40), (30, 40), (40, 40), (50, 40)}, user input data is {(10, 41 ), (20, 41), (30, 41), (40, 41), (50, 41)}, so that the query fails. Therefore, during the query, You need to offset the stroke you have entered by {(10, 40), (20, 40), (30, 40), (40, 40), (50, 40)} and {(10, 41), (20, 41), (30, 41), (40, 41), (50, 41)} match.
// Obtain the relative coordinate-(void) turnToGrids {self. strokeCount = self. inputStrokes. count; // float gridWidth = kScreenWidth/10; for (int k = 0; k <self. inputStrokes. count; k ++) {// store all points of a stroke to an array NSMutableArray * strokPointGrids = [NSMutableArray array]; NSArray * inputStrokesArray = self. inputStrokes [k]; for (int l = 0; l <inputStrokesArray. count; l ++) {NSValue * value = inputStrokesArray [l]; if (l = 0) {[self. StrokeStartPoints addObject: value];} if (l = self. inputStrokes. count-1) {[self. strokeEndPoints addObject: value];} CGPoint point = [value CGPointValue]; CGPoint grid = CGPointMake (ceil (point. x/gridWidth), ceil (point. y/gridWidth); BOOL shouldAdd = NO; if (strokPointGrids. count = 0) {shouldAdd = YES;} else {NSValue * lastValue = strokPointGrids. lastObject; CGPoint lastGrid = [lastValue CGPoi NtValue]; if (lastGrid. x! = Grid. x | lastGrid. y! = Grid. y) {shouldAdd = YES ;}} if (shouldAdd) {[strokPointGrids addObject: [NSValue valueWithCGPoint: grid]; if (! [Self. pointsString isw.tostring: @ ""] &! [Self. pointsString hasSuffix: @ "*"]) {[self. pointsString appendString: [NSString stringWithFormat: @ "|"];} [self. pointsString appendString: [NSString stringWithFormat: @ "% d, % d", (int) grid. x, (int) grid. y] ;}} if (k! = Self. inputStrokes. count-1) {[self. pointsString appendString: @ "*"];} [self. inputPointGrids addObject: strokPointGrids];}
// The final query statement NSString * queryString4 = [NSString stringWithFormat: @ "select. strokeid as strokeid,. samecount as samecount,. ucount as ucount,. pcount as pcount from (select strokeid, count (*) as samecount, (select count (*) from input_points where id = p. id and strokeid = % d) as ucount, (select count (*) from points where id = p. id and strokeid = p. strokeid) as pcount from points p where exists (select * from input_points u where u. id = p. id and u. strokeid = % d and (abs (u. pointX-p.pointX) + abs (u. pointY-p.pointY) <2) and p. strokeid not in (% @) and p. id = % d group by strokeid) a order by abs (. pcount-. samecount) asc ", j, j, hasStrokeid, model. charID];
At this point, this function module can easily identify the Chinese characters written from the mobile phone screen, because if the template input and user input style are significantly different or the User write is on the top (down, right or left), the recognition rate will decrease linearly, in version 2, we completely changed the recognition algorithm, greatly improving the recognition rate.
For the second version of handwriting recognition, see the next blog