一行文本的CRNN推理结果是一个列表,列表每个元素包含单个字符的字典索引和置信度,输出在json里形式如下:
{"char": [
{
"confidence": 0.9982025623321533,
"index": 1429,
"position": 1
},
{
"confidence": 0.6489381790161133,
"index": 1857,
"position": 5
},
{
"confidence": 0.9498909711837769,
"index": 3958,
"position": 9
},
{
"confidence": 0.11971486359834671,
"index": 2231,
"position": 14
},
{
"confidence": 0.9841734170913696,
"index": 1845,
"position": 18
},
{
"confidence": 0.1517036408185959,
"index": 2659,
"position": 22
},
{
"confidence": 0.7262901067733765,
"index": 5045,
"position": 35
},
{
"confidence": 0.956331729888916,
"index": 6476,
"position": 38
}
]}
本文将字典作为const string直接写在代码里:
std::string index2string_alphabet = "这是一个字符索引字典,全文大概20000字,包含中英文和各种外文符号"
然后通过index2string_alphabet 的下标依次索引上述index得到字符。
index2string_alphabet 是一个string,通过下表索引仅得到一个字符,但是index2string_alphabet 这个变量里不同的字其实占的字符位数不一样,如果是英文26个字母,占1位,如果是中文则占3位,所以在索引的时候需要知道当前字符是什么字符,占多少位,按规定位数取出来的字符,放到string里才可以正常显示,否则就是乱码。
由于代码文件是utf-8编码方式,对于UTF-8编码中的任意字节B:
如果B的第一位为0,则B为ASCII码,并且B独立的表示一个字符;
如果B的第一位为1,第二位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的一个字节,并且不为字符的第一个字节编码;
如果B的前两位为1,第三位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由两个字节表示;
如果B的前三位为1,第四位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示;
如果B的前四位为1,第五位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由四个字节表示;
通过二进制表示如下:
0xxxxxxx (一位的情况,为ASCII)
110xxxxx 10xxxxxx (110开头,代表两位)
1110xxxx 10xxxxxx 10xxxxxx (1110开头代表三位)
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (11110开头代表四位)
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (111110开头,代表五位)
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (1111110开头,代表六位)
所以解析逻辑就很清楚:
对于某一个带查询的index,初始化变量index0=0,逐位索引字典,根据上述规则跳过对应字符位数,但是index0只+1,直到index等于index0则找到对应字符。
代码如下:
int index0 = 0;
std::string result = "";
int index = 6555;
for(int i=0; i< index2string_alphabet.size();)
{
std::string char_res;
unsigned char q = index2string_alphabet[i];
if ( q < 0x80 )
{
// std::cout<<"ascii char, index is "<<index2string_alphabet[i] <<std::endl;
char_res += index2string_alphabet[i];
i+=1;
index0+=1;
}
else if ( q < 0xC0 )
{
//invalid char between 0x80 and 0xC0
// std::cout<<"invalid char between 0x80 and 0xC0"<<std::endl;
i+=1;
}
else if ( q < 0xE0 )
{
//two chars
char_res += index2string_alphabet[i];
char_res += index2string_alphabet[i+1];
i+=2;
index0+=1;
}
else if ( q < 0xF0 )
{
//three chars
char_res += index2string_alphabet[i];
char_res += index2string_alphabet[i+1];
char_res += index2string_alphabet[i+2];
i+=3;
index0+=1;
}
else if ( q < 0xF8 )
{
//four chars
char_res += index2string_alphabet[i];
char_res += index2string_alphabet[i+1];
char_res += index2string_alphabet[i+2];
char_res += index2string_alphabet[i+3];
i+=4;
index0+=1;
}
else if ( q < 0xFC )
{
//five chars
char_res += index2string_alphabet[i];
char_res += index2string_alphabet[i+1];
char_res += index2string_alphabet[i+2];
char_res += index2string_alphabet[i+3];
char_res += index2string_alphabet[i+4];
i+=5;
index0+=1;
}
else if ( q < 0xFE )
{
//6 chars
char_res += index2string_alphabet[i];
char_res += index2string_alphabet[i+1];
char_res += index2string_alphabet[i+2];
char_res += index2string_alphabet[i+3];
char_res += index2string_alphabet[i+4];
char_res += index2string_alphabet[i+5];
i+=6;
index0+=1;
}
else
{
//>=0xFE
std::cout<<"invalid char >=0xFE"<<std::endl;
i+=1;
}
if(index == index0)
{
result = char_res;
break;
}
}