前边一篇文章介绍了班讯通中新加的一个根据拼音写汉字功能的第一版本算法实现,今天说一说改进之后的算法。
在班讯通3.2版本提交之后,我们对汉字识别算法进行了改进,并且将该模块抽离出来完善成了一个新的应用--写霸,现已经通过appstore审核上线, 欢迎大家下载体验。
在第一版本中,我们将整个字拆为若干笔画存入数据库,每条笔画记录都对应若干条点记录,模板和用户记录均如此,在对比模板和用户数据的时候,根据笔画id对比相应的记录,最后根据匹配结果计算分数。新的版本里,我们在数据库中为所有笔画建立模型,然后每个字对应若干笔画模型。用户写字提交之后,分析用户输入的每一个笔画,在模板表中匹配出到若干笔画,再看与模板是否一致,从而得出分数。以下是详细过程
1.提炼笔画模型
在网上查到有三十一个笔画,于是将三十一个笔画一一录入,然后观察其特点。最后决定再将每一个笔画细分成几个线段,比如横折钩,拆成三段。具体做法是:写某一个笔画的时候,计算相邻两个点所成直线与水平直线的角度, 当这个角度与前两个点成直线的角度相差大于某个值(比如15度)的时候,就认为该笔画又多了一段。最后把该笔画所有线段id和线段平均角度存入数据库,这样一个笔画就对应若干个线段了。
// 解析笔画模型 - (void)featurePoints { NSArray *array = self.gridArray; if ([array count] < 4) { self.gridArray = self.pointArray; array = self.pointArray; } CGPoint point; CGPoint point0; CGPoint point1; CGPoint point2; int tag = 0; double angle0 = 0.0; double angle1 = 0.0; double angle3 = 0.0; for (int i=0; i<[array count]; i++) { if (i > 0) { point1 = [array[i-1] CGPointValue]; point2 = [array[i] CGPointValue]; angle1 = atan2(point2.y-point1.y, point2.x-point1.x)*180/M_PI; if (angle1 < -180) { angle1 += 360; } if (i > 1) { point0 = [array[i-2] CGPointValue]; angle0 = atan2(point1.y-point0.y, point1.x-point0.x)*180/M_PI; if ((fabs(angle1-angle0)>180 && 360-fabs(angle1-angle0)>36) || fabs(angle1 - angle0) > 36) { tag += 1; } else if(i > 2) { point = [array[i-3] CGPointValue]; angle3 = atan2(point0.y-point.y, point0.x-point.x)*180/M_PI; if (fabs(angle3-angle1)>36 || (fabs(angle3-angle1)>180 && 360-fabs(angle3-angle1)>36)) { tag += 1; } } } } [self.angleArray addObject:[NSNumber numberWithDouble:angle1]]; [self.angleTagArray addObject:[NSNumber numberWithInt:tag]]; } }
2.录入模板
手工录入汉字模板,每录入一个字,按笔画进行比对,查找出相匹配的笔画,将笔画id存入该汉字记录,最后每个汉字就对应多个笔画了。
// 录入汉字模板 - (void)insertStandardStroke2:(CharacterModel *)model { NSMutableDictionary *dict = [NSMutableDictionary dictionary];// 记录重复的笔画的个数 NSUserDefaults *userdefaults = [NSUserDefaults standardUserDefaults]; NSNumber *widthNumber = [userdefaults objectForKey:wtDefaultsCanvasViewWidth]; NSNumber *heightNumber = [userdefaults objectForKey:wtDefaultsCanvasViewHeight]; float canvasViewWidth = widthNumber.floatValue; float canvasViewHeight = heightNumber.floatValue; /****************插入字**************/ [self.hanziDb executeUpdate:@"insert into s_character(chinese, pinyin) values(?,?)", model.chinese, model.pinyin]; /****************获取字的id**************/ FMResultSet* set0 = [self.hanziDb executeQuery:@"select max(id) as maxCharId from s_character"]; int charid = -1; if([set0 next]) { charid = [set0 intForColumn:@"maxCharId"]; } else { return; } NSArray *strokeModelArray = model.strokeModelArray; for (int strokeIndex=0; strokeIndex<strokeModelArray.count; strokeIndex++) { StrokeModel *strokeModel = strokeModelArray[strokeIndex]; [self clearUserTable]; /****************插入笔画**************/ CGPoint minPoint = [model.minPointValue CGPointValue]; CGPoint maxPoint = [model.maxPointValue CGPointValue]; [self.hanziDb executeUpdate:@"insert into u_stroke(minX, minY, maxX, maxY) values(?,?,?,?)", minPoint.x, minPoint.y, maxPoint.x,maxPoint.y]; /****************获取笔画的id**************/ NSString *queryStrokeid = [NSString stringWithFormat:@"select max(id) maxid from u_stroke"]; FMResultSet* setStrokeid = [self.hanziDb executeQuery:queryStrokeid]; int strokeid = -1; if([setStrokeid next]) { strokeid = [setStrokeid intForColumn:@"maxid"]; } else { return; } /****************插入点和角度**************/ NSArray *pointsArray1 = strokeModel.gridArray; NSArray *angleArray1 = strokeModel.angleArray; NSArray *angleTagArray1 = strokeModel.angleTagArray; for (int i=0; i<pointsArray1.count; i++) { NSValue *pointValue = pointsArray1[i]; NSNumber *angleTagNumber = angleTagArray1[i]; CGFloat angle = [angleArray1[i] floatValue]; CGPoint point = [pointValue CGPointValue]; NSString *ss = [NSString stringWithFormat:@"insert into u_angle(strokeid, pointX, pointY, angle, angleTag) values(%d,%.2f,%.2f,%.2f,%d)", strokeid,point.x,point.y,angle, angleTagNumber.intValue]; [self.hanziDb executeUpdate:ss]; } // 提炼 把每个笔画分解成线段 [self.hanziDb executeUpdate:@"insert into u_line (strokeid, pointcount, angle, angletag) select strokeid, count(*), sum(angle)/count(*),angletag from u_angle group by strokeid, angleTag"]; <span style="white-space:pre"> </span>// ...... }
3.判断用户输入
判断用户输入的时候只需要对比对应汉字模板的笔画id就可以了,具体代码就不再奉上(⊙﹏⊙b实在不敢公开)
等待了一周多,写霸上线,结果刚上线,就被朋友们玩儿坏了……
有这样的:
竟然还有这样的
不多说了,回去接着改算法
原文地址:http://blog.csdn.net/zwf_apple/article/details/46482617