iOS Tesseract 中文字库训练

by admin on 2019年11月7日

要求:原操作系统代码里只是支持了日语显示,需要做的是实现对这个系统的汉字全角支持。

在首次安装RHEL5时,如果选择的是英文,那么系统将不安装中文支持包,这样就导致了中文显示为乱码(小方框)…….

项目中需要用到汉字识别,先前的研究中,我已经弄出了所要识别汉字的范围,并将这些汉字的像素弄成二值0或者255,文字色值为0,背景色值为255。

一、修改编译配置文件

目的:让PRODUCT_LOCALES := 后面有我们需要添加的语言。

一般原生安卓代码是修改这两个文件

Android/build/target/product/languages_full.mk
Android/build/target/product/languages_small.mk

为OLED屏添加GUI支持3:字库

hzk16的介绍以及简单的使用方法

有很多人说vi /etc/sysconfig/i18n文件,其实根本就没有必要。

我尝试使用像素点的值来识别汉字,但这其实是比较困难的,这需要有强大的算法基础支撑,短时间内完成对我来说是一项艰巨的挑战。

二、添加系统资源文件

上面添加好了但是还发现Resources.getSystem().getAssets().getLocales()没有我们添加的语言,这时候需要检查一下在Android/frameworks/base/core/res/res/目录添加values-xxx相应的资源

本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.

HZK16字库是符合GB2312标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个。其中一级汉字有3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。我们在一些应用场合根本用不到这么多汉字字模,所以在应用时就可以只提取部分字体作为己用。

解决方法:

为此,我选择了Tesseract,事实证明它是我想要的。

三、添加字库

Step1:  
Copy custom font .ttf into frameworks/base/data/fonts

Step2:  
Modify framworks/base/data/fonts/Android.mk ,Add your custom font into list of ‘font_src_files’  

Step3:  
Modify frameworks/base/data/fonts/fonts.mk ,Add your custom font into list of PRODUCT_PACKAGES  

完成上面三步之后在文件系统下的/system/fonts/就有添加的字库了,也可以通过mm命令然后在out/下检查。

 

HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。

安装 1.fonts-chinese-3.02-9.6.el5.noarch.rpm.
如果无法安装,则加个–force

我在百度上输入“tesseract
中文”,出现了很多关于字库训练的文章,这对我帮助很大,其中有一篇是这个:《Tesseract-OCR的简单使用与训练》,我跟着他说的步骤一步步做下来。

四、字库资源载入

Android4.0+ 和之前版本方式不一样

Modify frameworks/base/data/fonts/fallback_fonts.xml,Add your custom font like below :

    <family>
        <fileset>
            <file>Himalaya.ttf</file>
        </fileset>
    </family>

环境:

我们知道一个GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。A1-A9为符号区,B0到F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)。下面以汉字“我”为例,介绍如何在HZK16文件中找到它对应的32个字节的字模数据。

2.fonts-ISO8859-2-75dpi-1.0-17.1.noarch.rpm

首先,我需要有一台windows电脑,系统是10,然后安装Tesseract
3.02,很不幸,下载链接跳转到了CSDN,需要收费才能下载软件,幸好我的小伙伴中有人是CSDN的会员,可以免费下载。安装过程也没有障碍,你只需要跟随系统提示一步步安装就可以了。

五、可能遇到的问题

主机:WIN10

前面说到一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。所以要找到“我”在hzk16库中的位置就必须得到它的区码和位码。(为了区别使用了区码和区号,其实是一个东西,别被我误导了)

多数人到了这里就没有做其它操作了,导致仍然无法正确显示中文。

一切准备就绪,我按照教程的步骤一步步走下来,没有任何问题,我希望所有人都按照教程里面所写的命名操作,不要自作聪明或者画蛇添足,如果没什么问题的话,你将看到最终正确的结果。

1、在setting里面没有语言选项。

  • languages_full.mk没有添加到语言
  • framework res和settings res没有需添加语言的资源
  • 语言的名称不是规定的。如缅甸语的名称是my_MM,可以在维基查找。
  • ICU文件没有相应语言

开发环境:MDK5.13

区码:区号(汉字的第一个字节)-0xa0
(因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)

3.cd /usr/share/fonts/

得到正确的结果,这让我异常兴奋,但战争才刚开始。我把自己训练好的字库放进了iOS工程中,然后使用这个字库来识别教程中的png图片。奇迹出现了,数字被正确地识别出来,我非常高兴。但还没结束……

2、在settings的语言栏有选项,但是空的。

  • 检查安卓文件系统下system/etc/fallback_fonts.xml下面是否有添加新的字库
  • 检查安卓文件系统下system/fonts/是否有新的字库文件

MCU:STM32F103

位码:位号(汉字的第二个字节)-0xa0

4.fc-cache -fv #这两步才是成功的关键阿,相信还有无数人困扰在这里

再继续讲下去之前,我需要提到一个非常重要的命令:

六、参考文章:

  • Android系统应用开发(四)系统语言以及添加字体库
  • android添加新语言之缅甸语
  • Unicode字符检查

说明:

这样我们就可以得到汉字在HZK16中的绝对偏移位置:

以上就安装好了中文支持,下面的包是安装中文输入法:

tesseract test.png output_1 –l eng

      
GUI中有字库方能显示汉字。英文,数字等。英文数字等ASCII码128个字符保存显示easy。但汉字数万,假设不用字库芯片,用软件保存。则flash明显不够。所以仅仅加入须要显示的汉字。

offset=(94*(区码-1)+(位码-1))*32

scim-libs-*

是的,这个是教程中的命令,这个命令可以用来检测所提供图片是否可以用来训练,我提供了一张白底黑字图,黑字充满着整个图片,就像这样子:图片 17

      
本GUI用的显示方法,能够easy的显示不同字体,不同字号的汉字和ASCII码。

注解:1、区码减1是因为数组是以0为开始而区号位号是以1为开始的

scim-1.4.4-*

打入上面的命令后,控制台命令提示:

源码及步骤:

2、(94*(区号-1)+位号-1)是一个汉字字模占用的字节数

scim-chinese-standard-*

empty page!

1.用工具FontCvt.exe(emwin自带工具)选择须要的字体,字号。以及须要的字符(汉字。ASCII)并生成相应的.c文件

3、最后乘以32是因为汉字库文应从该位置起的32字节信息记录该字的字模信息(前面提到一个汉字要有32个字节显示)

scim-tables-*

空页面?只要出现这个提示,后续步骤就难以进行下去,而我换成教程中的图片就正常。我观察了两张图片的异同,发现教程中的图片有较大的留白,于是,我把图片改成这样:图片 27

 有了偏移地址就可以从HZK16中读取汉字编码了

scim-pinyin-*

终于正常了。所以在即将训练之前,请先打一下上面的命令试一试。

2.将此.c文件转换为指定格式。下面为微软雅黑20号字体的转换后源文件:

实现思路:

图片 3

这个Tesseract提供的是eng英文字库,而我需要识别汉字,这就意味着我需要有个中文字库,以前在研究Tesseract-OCR-iOS的时候,我使用的TesseractOCRiOS.framework是4.0.0最新版本,那个时候我为了寻找中文字库,花了好多时间,因为版本不同,运行的时候会报错,幸运的是,最后还是找到一个可以正常运行的,但字库的效果肯定是不理想的。

FHZ20.c

  1. 了解HZK编码,理解一下符合GB2312标准的中文点阵字库文件HZK16;
  2. 下载中文GB2312的二进制点阵文件;
  3. 将HZK16.fnt文件放入nihongo文件夹中;
  4. 修改主makefile文件和app_make.txt文件,将原来装载nihongo.fnt的语句替换成装载HZK16.fnt即可;
  5. 修改bootpack.c文件,将之前分配的装载日语字体的内存扩大,载入字库的文件名;
  6. 在haribote/graphic.c中添加支持汉字的代码,增加一个函数用于显示汉字;
  7. 修改putfonts8_asc函数里if (task->langmode == 3)语句块;
  8. 测试程序。
  9. 注意:日文的编码是分为左半部分和右半部分,而我们使用的HZK16是分为上半部分和下半部分的。

当然,你也可以在安装Tesseract
3.02的时候,选择一个简体中文字库,怎么做?看这个图:

/**
* Copyright (c), 2015-2025
* @file FHZ20.c
* @brief 20号汉字字库,字体微软雅黑
* @author jdh
* @date 2015/9/6
* @date 2015/9/7
* @date 2015/9/9
*/

/*********************************************************************
*                           头文件
**********************************************************************/

#include "font.h"

/*********************************************************************
*                           静态变量
**********************************************************************/

/**
* @brief 字符:空
*/

unsigned char Hz20_0020[ 20] = { /* code 0020, SPACE */
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_002E[ 20] = { /* code 002E, FULL STOP */
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  ________,
  _XX_____,
  _XX_____,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0030[ 20] = { /* code 0030, DIGIT ZERO */
  ________,
  ________,
  ________,
  ________,
  ________,
  ___XXX__,
  __X__X__,
  _X____X_,
  _X____X_,
  _X____X_,
  _X____X_,
  _X____X_,
  _X____X_,
  _X____X_,
  __X__X__,
  __XXX___,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0031[ 20] = { /* code 0031, DIGIT ONE */
  ________,
  ________,
  ________,
  ________,
  ________,
  ____X___,
  __XXX___,
  _XX_X___,
  ____X___,
  ____X___,
  ____X___,
  ____X___,
  ____X___,
  ____X___,
  ____X___,
  ____X___,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0032[ 20] = { /* code 0032, DIGIT TWO */
  ________,
  ________,
  ________,
  ________,
  ________,
  __XXXX__,
  _X___XX_,
  ______X_,
  ______X_,
  ______X_,
  _____X__,
  ___XX___,
  __XX____,
  _XX_____,
  _X______,
  _XXXXXX_,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0033[ 20] = { /* code 0033, DIGIT THREE */
  ________,
  ________,
  ________,
  ________,
  ________,
  __XXX___,
  _X___X__,
  _____X__,
  _____X__,
  ____XX__,
  __XX____,
  ____XX__,
  _____X__,
  _____X__,
  _X__XX__,
  __XXX___,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0034[ 20] = { /* code 0034, DIGIT FOUR */
  ________,
  ________,
  ________,
  ________,
  ________,
  _____X__,
  ____XX__,
  ___XXX__,
  ___X_X__,
  __X__X__,
  _XX__X__,
  XX___X__,
  XXXXXXXX,
  _____X__,
  _____X__,
  _____X__,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0035[ 20] = { /* code 0035, DIGIT FIVE */
  ________,
  ________,
  ________,
  ________,
  ________,
  _XXXXX__,
  _X______,
  _X______,
  _X______,
  _X______,
  _XXXXX__,
  _____XX_,
  ______X_,
  ______X_,
  _____XX_,
  _XXXX___,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0036[ 20] = { /* code 0036, DIGIT SIX */
  ________,
  ________,
  ________,
  ________,
  ________,
  ___XXX__,
  __X_____,
  _X______,
  _X______,
  _X_XXX__,
  _XX__XX_,
  _X____X_,
  _X____X_,
  _X____X_,
  __X__X__,
  ___XXX__,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0037[ 20] = { /* code 0037, DIGIT SEVEN */
  ________,
  ________,
  ________,
  ________,
  ________,
  XXXXXXX_,
  _____X__,
  _____X__,
  ____X___,
  ____X___,
  ___X____,
  ___X____,
  ___X____,
  __X_____,
  __X_____,
  __X_____,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0038[ 20] = { /* code 0038, DIGIT EIGHT */
  ________,
  ________,
  ________,
  ________,
  ________,
  __XXXX__,
  _XX__XX_,
  _X____X_,
  _X____X_,
  __X__X__,
  __XXXX__,
  _XX__XX_,
  _X____X_,
  _X____X_,
  _XX__XX_,
  __XXXX__,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_0039[ 20] = { /* code 0039, DIGIT NINE */
  ________,
  ________,
  ________,
  ________,
  ________,
  __XXXX__,
  __X__X__,
  _X____X_,
  _X____X_,
  _X____X_,
  _XX__XX_,
  __XXX_X_,
  ______X_,
  _____X__,
  __X__X__,
  __XXX___,
  ________,
  ________,
  ________,
  ________};

unsigned char Hz20_006D[ 40] = { /* code 006D, LATIN SMALL LETTER M */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  _X_XXX__,XXX_____,
  _XX__XXX,___X____,
  _X____X_,___X____,
  _X____X_,___X____,
  _X____X_,___X____,
  _X____X_,___X____,
  _X____X_,___X____,
  _X____X_,___X____,
  ________,________,
  ________,________,
  ________,________,
  ________,________};

unsigned char Hz20_CBD1[ 40] = { /* code 641C */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  __X___XX,X_______,
  __X__XX_,XXXXX___,
  XXXXXX__,X___X___,
  __X__XXX,X_XXX___,
  __X__X__,X___X___,
  __XX_XXX,XXXXX___,
  _XXXX___,X_______,
  X_X__XXX,XXXX____,
  __X___X_,__XX____,
  __X____X,_XX_____,
  __X____X,XX______,
  XXX_XXX_,__XXXX__,
  ________,________,
  ________,________,
  ________,________};

unsigned char Hz20_CBF7[ 40] = { /* code 7D22 */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  _______X,________,
  _XXXXXXX,XXXXX___,
  _______X,________,
  _XXXXXXX,XXXXX___,
  _X___X__,XX__X___,
  _X_XXXXX,X___X___,
  ______X_,_XX_____,
  ____XX__,__XX____,
  __XXXXXX,XXXXX___,
  ____X__X,_X______,
  __XX___X,__XX____,
  XX__XXXX,____X___,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:正
*/

unsigned char Hz20_D5FD[ 40] = { /* code 6B63 */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  _XXXXXXX,XXXXX___,
  _______X,________,
  _______X,________,
  __X____X,________,
  __X____X,________,
  __X____X,XXXXX___,
  __X____X,________,
  __X____X,________,
  __X____X,________,
  __X____X,________,
  __X____X,________,
  XXXXXXXX,XXXXXX__,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:常
*/

unsigned char Hz20_B3A3[ 40] = { /* code 5E38 */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  __XX__X_,__XX____,
  ___XX_X_,_XX_____,
  _XXXXXXX,XXXXX___,
  _X______,____X___,
  _X_XXXXX,XXX_X___,
  ___X____,__X_____,
  ___XXXXX,XXX_____,
  _______X,________,
  __XXXXXX,XXXX____,
  __X____X,___X____,
  __X____X,_XXX____,
  _______X,________,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:超
*/

unsigned char Hz20_B3AC[ 40] = { /* code 8D85 */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ___X__XX,XXXXX___,
  ___X____,X___X___,
  XXXXXX__,X___X___,
  ___X___X,____X___,
  ___X__X_,_XXX____,
  XXXXXX__,________,
  ___X___X,XXXXX___,
  _X_X___X,____X___,
  _X_XXX_X,____X___,
  _X_X___X,XXXXX___,
  X_XX____,________,
  X__XXXXX,XXXXXX__,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:距
*/

unsigned char Hz20_BEE0[ 40] = { /* code 8DDD */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  _XXXX__X,XXXXXX__,
  _X__X__X,________,
  _X__X__X,________,
  _XXXX__X,________,
  _X_X___X,XXXXX___,
  ___X___X,____X___,
  _X_XXX_X,____X___,
  _X_X___X,XXXXX___,
  _X_X___X,____X___,
  _X_X___X,________,
  _X_XXX_X,________,
  XXX____X,XXXXXX__,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:失
*/

unsigned char Hz20_CAA7[ 40] = { /* code 5931 */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ___X___X,________,
  ___X___X,________,
  __XXXXXX,XXXXX___,
  __X____X,________,
  _X_____X,________,
  _______X,________,
  _XXXXXXX,XXXXXX__,
  ______XX,________,
  ______X_,X_______,
  ____XX__,_X______,
  __XX____,__XX____,
  XX______,____XX__,
  ________,________,
  ________,________,
  ________,________};

/**
* @brief 字符:踪
*/

unsigned char Hz20_D7D9[ 40] = { /* code 8E2A */
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  ________,________,
  _XXXX___,_XX_____,
  _X__X_XX,XXXXXX__,
  _X__X_X_,_____X__,
  _XXXX___,________,
  _X_X___X,XXXXX___,
  ___X____,________,
  _X_XX_XX,XXXXXX__,
  _X_X____,_X______,
  _X_X___X,_X_X____,
  _X_X__X_,_X__X___,
  XXX_XX__,_X___X__,
  _______X,XX______,
  ________,________,
  ________,________,
  ________,________};

/*********************************************************************
*                           函数
**********************************************************************/

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t GB18030_20X20_get_height(void)
{
    return 20;
}

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t GB18030_20X20_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr) 
{ 
    switch (ch)
    {
        //空
        case 0x0020:
        {
            font_type->width = 4;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0020;
            break;
        }
        //.
        case 0x002E:
        {
            font_type->width = 3;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_002E;
            break;
        }
        //0
        case 0x0030:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0030;
            break;
        }
        //1
        case 0x0031:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0031;
            break;
        }
        //2
        case 0x0032:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0032;
            break;
        }
        //3
        case 0x0033:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0033;
            break;
        }
        //4
        case 0x0034:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0034;
            break;
        }
        //5
        case 0x0035:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0035;
            break;
        }
        //6
        case 0x0036:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0036;
            break;
        }
        //7
        case 0x0037:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0037;
            break;
        }
        //8
        case 0x0038:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0038;
            break;
        }
        //9
        case 0x0039:
        {
            font_type->width = 8;
            font_type->height = 20;
            font_type->size = 20;
            *addr = (uint32_t)Hz20_0039;
            break;
        }
        //m
        case 0x006D:
        {
            font_type->width = 13;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_006D;
            break;
        }
        //搜
        case 0xCBD1:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_CBD1;
            break;
        }
        //索
        case 0xCBF7:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_CBF7;
            break;
        }
        //正
        case 0xD5FD:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_D5FD;
            break;
        }
        //常
        case 0xB3A3:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_B3A3;
            break;
        }
        //超
        case 0xB3AC:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_B3AC;
            break;
        }
        //距
        case 0xBEE0:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_BEE0;
            break;
        }
        //失
        case 0xCAA7:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_CAA7;
            break;
        }
        //踪
        case 0xD7D9:
        {
            font_type->width = 14;
            font_type->height = 20;
            font_type->size = 40;
            *addr = (uint32_t)Hz20_D7D9;
            break;
        }
        default:
        {
            return 0;
        }
    }

    return 1;
}

这里其他的地方比较弄,第5步将大小修改一下,我的是nihongo = (unsigned
char *) memman_alloc_4k(memman, 55*94*32);

图片 4简体中文字库

第6步,要注意,HZK16是上下两部分,不同于日文的左右两部分的结构。

勾选 additional language
data,滑动滚动条,找到“简体中文”并勾选就可以了。内嵌下载可能比较慢,实在不行,你可以去网上随便下载一个试试,多研究几次,失败是成功之母。

3.将相应的字体宏定义填入字库文件头文件里

代码如下:

假设你根据上面的步骤已经弄好了中文字库,并确保Tesseract的版本是3.02后,还有最后一步,就是更换教程,因为之前的教程讲的是英文,所以我要换个中文的,我选择了这篇文章《验证码识别》。事实证明它给我带来了灾难。

/**
* @brief 17*17点阵GB18030汉字
*/

void putfont32(char *vram, int xsize, int x, int y, char c, char *font1, char *font2)
{
    int i,k,j,f;
    char *p, d ;
    j=0;
    p=vram+(y+j)*xsize+x;
    j++;
    //上半部分
    for(i=0;i<16;i++)
    {
        for(k=0;k<8;k++)
        {
            if(font1[i]&(0x80>>k))
            {
                p[k+(i%2)*8]=c;
            }
        }
        if(i%2==0){
            for(k=0;k<4;k++){
                f=p[k];
                p[k]=p[7-k];
                p[7-k]=f;
            }
        }else{
            for(k=0;k<4;k++){
                f=p[k+8];
                p[k+8]=p[15-k];
                p[15-k]=f;
            }
        }
       /* for(k=0;k<8/2;k++)
        {
            f=p[k+(i%2)*8];
            p[k+(i%2)*8]=p[8-1-k+(i%2)*8];
            p[8-1-k+(i%2)*8]=f;
        }*/
        if(i%2)
        {
            p=vram+(y+j)*xsize+x;
            j++;
        }
    }
    //下半部分
    for(i=0;i<16;i++)
    {
        for(k=0;k<8;k++)
        {
            if(font2[i]&(0x80>>k))
            {
                p[k+(i%2)*8]=c;
            }
        }
        if(i%2==0){
            for(k=0;k<4;k++){
                f=p[k];
                p[k]=p[7-k];
                p[7-k]=f;
            }
        }else{
            for(k=0;k<4;k++){
                f=p[k+8];
                p[k+8]=p[15-k];
                p[15-k]=f;
            }
        }
        /*for(k=0;k<8/2;k++)
        {
            f=p[k+(i%2)*8];
            p[k+(i%2)*8]=p[8-1-k+(i%2)*8];
            p[8-1-k+(i%2)*8]=f;
        }*/
        if(i%2)
        {
            p=vram+(y+j)*xsize+x;
            j++;
        }
    }
    return;
}

训练开始了,我使用PS创建了300X300像素的白色图(PS是很重要的工具,我建议人人都应该懂点它),里面弄上几个黑色字:“浙江皮革厂”。我还是按照原教程操作下来,发现中文文字并不能在图中红色区域中正常显示,我觉得这是个问题,但我还是继续往下走。图片 5汉字不能正常显示

#define GB18030_20X20    2

 

然而后续进展并不顺利,甚至出现了一些我在StackOverflow上也找不到很好解决方案的问题。关于“汉字不能正常显示”,我以为是jTessBoxEditor的问题,因为我没有使用最新版本,所以我换成了jTessBoxEditor
2.0版本。而其他问题呢,我在StackOverflow找不到满意的答案,我也不知道如何解决。

运行结果,我们在euc.txt中加入一些汉字。

我决定更换成这个教程《Tesseract训练中文字体识别》,因为教程中使用的是Tesseract
4.0.0,所以我决定也更换Tesseract版本,你可以到这里下载Tesseract各个版本下载地址。

图片 6

安装完Tesseract 4.0.0后,在控制台输入命令 tesseract,却提示

font.h

参考资料:

“tesseract”不是内部或外部命令,也不是可运行的程序
/**
* Copyright (c), 2015-2025
* @file font.h
* @brief 字库文件头文件
* @author jdh
* @date 2015/9/6
* @date 2015/9/7
*/

#ifndef _FONT_H_
#define _FONT_H_

/*********************************************************************
*                           头文件
**********************************************************************/

#include "inf_lcd.h"

/*********************************************************************
*                           宏定义
**********************************************************************/

/**
* @brief 点阵字符宏定义
*/

#define ________    0x0
#define _______X    0x1
#define ______X_    0x2
#define ______XX    0x3
#define _____X__    0x4
#define _____X_X    0x5
#define _____XX_    0x6
#define _____XXX    0x7
#define ____X___    0x8
#define ____X__X    0x9
#define ____X_X_    0xa
#define ____X_XX    0xb
#define ____XX__    0xc
#define ____XX_X    0xd
#define ____XXX_    0xe
#define ____XXXX    0xf
#define ___X____    0x10
#define ___X___X    0x11
#define ___X__X_    0x12
#define ___X__XX    0x13
#define ___X_X__    0x14
#define ___X_X_X    0x15
#define ___X_XX_    0x16
#define ___X_XXX    0x17
#define ___XX___    0x18
#define ___XX__X    0x19
#define ___XX_X_    0x1a
#define ___XX_XX    0x1b
#define ___XXX__    0x1c
#define ___XXX_X    0x1d
#define ___XXXX_    0x1e
#define ___XXXXX    0x1f
#define __X_____    0x20
#define __X____X    0x21
#define __X___X_    0x22
#define __X___XX    0x23
#define __X__X__    0x24
#define __X__X_X    0x25
#define __X__XX_    0x26
#define __X__XXX    0x27
#define __X_X___    0x28
#define __X_X__X    0x29
#define __X_X_X_    0x2a
#define __X_X_XX    0x2b
#define __X_XX__    0x2c
#define __X_XX_X    0x2d
#define __X_XXX_    0x2e
#define __X_XXXX    0x2f
#define __XX____    0x30
#define __XX___X    0x31
#define __XX__X_    0x32
#define __XX__XX    0x33
#define __XX_X__    0x34
#define __XX_X_X    0x35
#define __XX_XX_    0x36
#define __XX_XXX    0x37
#define __XXX___    0x38
#define __XXX__X    0x39
#define __XXX_X_    0x3a
#define __XXX_XX    0x3b
#define __XXXX__    0x3c
#define __XXXX_X    0x3d
#define __XXXXX_    0x3e
#define __XXXXXX    0x3f
#define _X______    0x40
#define _X_____X    0x41
#define _X____X_    0x42
#define _X____XX    0x43
#define _X___X__    0x44
#define _X___X_X    0x45
#define _X___XX_    0x46
#define _X___XXX    0x47
#define _X__X___    0x48
#define _X__X__X    0x49
#define _X__X_X_    0x4a
#define _X__X_XX    0x4b
#define _X__XX__    0x4c
#define _X__XX_X    0x4d
#define _X__XXX_    0x4e
#define _X__XXXX    0x4f
#define _X_X____    0x50
#define _X_X___X    0x51
#define _X_X__X_    0x52
#define _X_X__XX    0x53
#define _X_X_X__    0x54
#define _X_X_X_X    0x55
#define _X_X_XX_    0x56
#define _X_X_XXX    0x57
#define _X_XX___    0x58
#define _X_XX__X    0x59
#define _X_XX_X_    0x5a
#define _X_XX_XX    0x5b
#define _X_XXX__    0x5c
#define _X_XXX_X    0x5d
#define _X_XXXX_    0x5e
#define _X_XXXXX    0x5f
#define _XX_____    0x60
#define _XX____X    0x61
#define _XX___X_    0x62
#define _XX___XX    0x63
#define _XX__X__    0x64
#define _XX__X_X    0x65
#define _XX__XX_    0x66
#define _XX__XXX    0x67
#define _XX_X___    0x68
#define _XX_X__X    0x69
#define _XX_X_X_    0x6a
#define _XX_X_XX    0x6b
#define _XX_XX__    0x6c
#define _XX_XX_X    0x6d
#define _XX_XXX_    0x6e
#define _XX_XXXX    0x6f
#define _XXX____    0x70
#define _XXX___X    0x71
#define _XXX__X_    0x72
#define _XXX__XX    0x73
#define _XXX_X__    0x74
#define _XXX_X_X    0x75
#define _XXX_XX_    0x76
#define _XXX_XXX    0x77
#define _XXXX___    0x78
#define _XXXX__X    0x79
#define _XXXX_X_    0x7a
#define _XXXX_XX    0x7b
#define _XXXXX__    0x7c
#define _XXXXX_X    0x7d
#define _XXXXXX_    0x7e
#define _XXXXXXX    0x7f
#define X_______    0x80
#define X______X    0x81
#define X_____X_    0x82
#define X_____XX    0x83
#define X____X__    0x84
#define X____X_X    0x85
#define X____XX_    0x86
#define X____XXX    0x87
#define X___X___    0x88
#define X___X__X    0x89
#define X___X_X_    0x8a
#define X___X_XX    0x8b
#define X___XX__    0x8c
#define X___XX_X    0x8d
#define X___XXX_    0x8e
#define X___XXXX    0x8f
#define X__X____    0x90
#define X__X___X    0x91
#define X__X__X_    0x92
#define X__X__XX    0x93
#define X__X_X__    0x94
#define X__X_X_X    0x95
#define X__X_XX_    0x96
#define X__X_XXX    0x97
#define X__XX___    0x98
#define X__XX__X    0x99
#define X__XX_X_    0x9a
#define X__XX_XX    0x9b
#define X__XXX__    0x9c
#define X__XXX_X    0x9d
#define X__XXXX_    0x9e
#define X__XXXXX    0x9f
#define X_X_____    0xa0
#define X_X____X    0xa1
#define X_X___X_    0xa2
#define X_X___XX    0xa3
#define X_X__X__    0xa4
#define X_X__X_X    0xa5
#define X_X__XX_    0xa6
#define X_X__XXX    0xa7
#define X_X_X___    0xa8
#define X_X_X__X    0xa9
#define X_X_X_X_    0xaa
#define X_X_X_XX    0xab
#define X_X_XX__    0xac
#define X_X_XX_X    0xad
#define X_X_XXX_    0xae
#define X_X_XXXX    0xaf
#define X_XX____    0xb0
#define X_XX___X    0xb1
#define X_XX__X_    0xb2
#define X_XX__XX    0xb3
#define X_XX_X__    0xb4
#define X_XX_X_X    0xb5
#define X_XX_XX_    0xb6
#define X_XX_XXX    0xb7
#define X_XXX___    0xb8
#define X_XXX__X    0xb9
#define X_XXX_X_    0xba
#define X_XXX_XX    0xbb
#define X_XXXX__    0xbc
#define X_XXXX_X    0xbd
#define X_XXXXX_    0xbe
#define X_XXXXXX    0xbf
#define XX______    0xc0
#define XX_____X    0xc1
#define XX____X_    0xc2
#define XX____XX    0xc3
#define XX___X__    0xc4
#define XX___X_X    0xc5
#define XX___XX_    0xc6
#define XX___XXX    0xc7
#define XX__X___    0xc8
#define XX__X__X    0xc9
#define XX__X_X_    0xca
#define XX__X_XX    0xcb
#define XX__XX__    0xcc
#define XX__XX_X    0xcd
#define XX__XXX_    0xce
#define XX__XXXX    0xcf
#define XX_X____    0xd0
#define XX_X___X    0xd1
#define XX_X__X_    0xd2
#define XX_X__XX    0xd3
#define XX_X_X__    0xd4
#define XX_X_X_X    0xd5
#define XX_X_XX_    0xd6
#define XX_X_XXX    0xd7
#define XX_XX___    0xd8
#define XX_XX__X    0xd9
#define XX_XX_X_    0xda
#define XX_XX_XX    0xdb
#define XX_XXX__    0xdc
#define XX_XXX_X    0xdd
#define XX_XXXX_    0xde
#define XX_XXXXX    0xdf
#define XXX_____    0xe0
#define XXX____X    0xe1
#define XXX___X_    0xe2
#define XXX___XX    0xe3
#define XXX__X__    0xe4
#define XXX__X_X    0xe5
#define XXX__XX_    0xe6
#define XXX__XXX    0xe7
#define XXX_X___    0xe8
#define XXX_X__X    0xe9
#define XXX_X_X_    0xea
#define XXX_X_XX    0xeb
#define XXX_XX__    0xec
#define XXX_XX_X    0xed
#define XXX_XXX_    0xee
#define XXX_XXXX    0xef
#define XXXX____    0xf0
#define XXXX___X    0xf1
#define XXXX__X_    0xf2
#define XXXX__XX    0xf3
#define XXXX_X__    0xf4
#define XXXX_X_X    0xf5
#define XXXX_XX_    0xf6
#define XXXX_XXX    0xf7
#define XXXXX___    0xf8
#define XXXXX__X    0xf9
#define XXXXX_X_    0xfa
#define XXXXX_XX    0xfb
#define XXXXXX__    0xfc
#define XXXXXX_X    0xfd
#define XXXXXXX_    0xfe
#define XXXXXXXX    0xff

/*********************************************************************
*                           字体定义
**********************************************************************/

/**
* @brief 17*17点阵GB18030汉字
*/

#define GB18030_17X17               1

/**
* @brief 17*17点阵GB18030汉字
*/

#define GB18030_20X20               2

/**
* @brief 24*24点阵GB18030汉字
*/

#define GB18030_24X24               3

/**
* @brief 30*30点阵GB18030汉字
*/

#define GB18030_30X30               4

/**
* @brief 37*37点阵粗体GB18030汉字
*/

#define GB18030_37X37B              5

/**
* @brief 40*40点阵粗体GB18030汉字
*/

#define GB18030_40X40B              6

/**
* @brief 48*48点阵GB18030汉字
*/

#define GB18030_48X48               7

/*********************************************************************
*                           数据结构
**********************************************************************/

/**
* @brief 字体结构
*/

struct _Font_Type
{
    uint8_t width;
    uint8_t height;
    uint8_t size;
};

/*********************************************************************
*                           函数
**********************************************************************/

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t font_get_height(uint8_t font);

/**
* @brief 读取字库
* @param font:字体
* @param c:待读取的字符
* @param font_type:返回的字体信息
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t font_read(uint8_t font,uint16_t c,struct _Font_Type *font_type,uint32_t *addr);

#endif

1. 30天操作系统支持中文。

在windows上报这个错,基本就是环境变量的问题了,于是我将Tesseract
4.0.0的安装路径加入到windows10的环境变量中,然后重启,就恢复正常了。

4.将相应的接口填入字库文件源文件里

我仍然按照教程一步步走下来,我发现即使换成jTessBoxEditor
2.0版本,汉字依旧无法正常显示,这个问题和版本无关,我开始在jTessBoxEditor的界面菜单随便点一点,我发现菜单Setting中有个Font,可以修改字体,我改成黑体后,汉字终于正常显示了,总算是解决了一个问题。

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

继续往前走,在进行到教程步骤五的时候,出现了问题,事实上需要修改的有5个文件,而不是4个文件,还有一个文件unicharset也需要进行修改,改成chi.unicharset就没问题了。每个步骤都完成之后,会生成chi.traineddata文件,然后跟着他的步骤,应该不会有什么问题了。

extern uint8_t GB18030_20X20_get_height(void);

我把这个训练好的字库放进iOS工程中,很不幸的,程序在识别过程中报错了,错误提示为:

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

actual_tessdata_num_entries <= TESSDATA_NUM_ENTRIES:Error:Assert failed:in file ..\ccutil\tessdatamanager.cpp, line 55

extern uint8_t GB18030_20X20_get_address(uint16_t ch,struct
_Font_Type *font_type,uint32_t *addr);

我在网上找到了这个问题的答案,他们说字库和Tesseract的版本对应不上。基于此我首先想到了,应该是Tesseract
4.0.0的问题,因为至少使用3.02版本是正常的。带着这个疑问,我把Tesseract
4.0.0卸载了,重新安装3.02版本,将刚才进行的操作重新做一遍,生成了新的chi.traineddata文件。我把文件放入iOS工程中,正常了!Oh
my god!识别成功了,这让我欣喜若狂!

工作总算有点进展了,我了解我还有很长的路得走,我也会继续走下去!

font_get_height函数与font_read函数也须要做相应改动。

——————————-我是分割线——————————-

font.c:

/**
* Copyright (c), 2015-2025
* @file font.c
* @brief 字库文件主文件
* @author jdh
* @date 2015/9/6
* @date 2015/9/7
*/

/*********************************************************************
*                           头文件
**********************************************************************/

#include "font.h"
#include "string.h"

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

extern uint8_t GB18030_17X17_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

extern uint8_t GB18030_17X17_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

extern uint8_t GB18030_20X20_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

extern uint8_t GB18030_20X20_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

extern uint8_t GB18030_24X24_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

extern uint8_t GB18030_24X24_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

extern uint8_t GB18030_30X30_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

extern uint8_t GB18030_30X30_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t GB18030_37X37B_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t GB18030_37X37B_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t GB18030_40X40B_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t GB18030_40X40B_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t GB18030_48X48_get_height(void);

/**
* @brief 得到地址
* @param font:字体
* @param ch:待读取的字符
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t GB18030_48X48_get_address(uint16_t ch,struct _Font_Type *font_type,uint32_t *addr);

/*********************************************************************
*                           静态变量
**********************************************************************/

///**
//* @brief 字体结构
//*/

//static struct _Font_Type Font_Type[FONT_NUM + 1];

///*********************************************************************
//*                         静态函数
//**********************************************************************/

///**
//* @brief 初始化字体类型
//*/

//static void init_font_type(void);

/*********************************************************************
*                           函数
**********************************************************************/

/**
* @brief 得到字体高度
* @param font:字体
* @retval 高度
*/

uint8_t font_get_height(uint8_t font)
{
    switch (font)
    {
        case GB18030_17X17:
        {
            return GB18030_17X17_get_height();
        }
        case GB18030_20X20:
        {
            return GB18030_20X20_get_height();
        }
        case GB18030_24X24:
        {
            return GB18030_24X24_get_height();
        }
        case GB18030_30X30:
        {
            return GB18030_30X30_get_height();
        }
        case GB18030_37X37B:
        {
            return GB18030_37X37B_get_height();
        }
        case GB18030_40X40B:
        {
            return GB18030_40X40B_get_height();
        }
        case GB18030_48X48:
        {
            return GB18030_48X48_get_height();
        }
        default:
        {
            return 0;
        }
    }
}

/**
* @brief 读取字库
* @param font:字体
* @param c:待读取的字符
* @param font_type:返回的字体信息
* @param addr:数据存储地址
* @retval 读取结果.0:失败,1:成功
*/

uint8_t font_read(uint8_t font,uint16_t c,struct _Font_Type *font_type,uint32_t *addr)
{   
    switch (font)
    {
        case GB18030_17X17:
        {
            if (GB18030_17X17_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_20X20:
        {
            if (GB18030_20X20_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_24X24:
        {
            if (GB18030_24X24_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_30X30:
        {
            if (GB18030_30X30_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_37X37B:
        {
            if (GB18030_37X37B_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_40X40B:
        {
            if (GB18030_40X40B_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        case GB18030_48X48:
        {
            if (GB18030_48X48_get_address(c,font_type,addr))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        default:
        {
            return 0;
        }
    }
}

5.读取并显示可參考下面函数:

/**
* @brief 在指定位置显示单个字符
* @param x:x坐标
* @param y:y坐标
* @param font:字体
* @param c:字符
*/

void gui_disp_char_at(uint16_t x,uint16_t y,uint8_t font,uint16_t c)
{
    struct _Font_Type font_type;
    //uint8_t buf[100] = {0};
    uint8_t *buf_pt = 0;
    uint16_t i = 0;
    uint16_t j = 0;
    uint8_t k = 0;
    uint8_t bit = 0;
    uint8_t num_valid_bit = 0;
    uint8_t num_valid_byte = 0;

//  //读取字体信息
//  font_type = font_read_type(font);
    //读取字符信息
    font_read(font,c,&font_type,(uint32_t *)&buf_pt);
    //buf_pt = buf;

    //有效位数
    num_valid_bit = font_type.width % 8;
    //有效字节数
    num_valid_byte = font_type.width / 8;
    for (i = 0;i < font_type.height;i++)
    {
        for (j = 0;j < num_valid_byte;j++)
        {
            for (k = 0;k < 8;k++)
            {
                bit = (*buf_pt >> (7 - k)) & 0x1;
                gui_interface_draw_pixel(x + j * 8 + k,y + i,bit);
            }
            buf_pt++;
        }
        for (k = 0;k < num_valid_bit;k++)
        {
            bit = (*buf_pt >> (7 - k)) & 0x1;
            gui_interface_draw_pixel(x + j * 8 + k,y + i,bit);
        }
        if (num_valid_bit > 0)
        {
            buf_pt++;
        }
    }
}

例:

gui_disp_char_at(0,0,GB18030_20X20,”正”)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图