OCR序列号CRC,检查算法

时间:2015-06-09 16:55:51

标签: character ocr checksum crc

我想阅读一个使用光学字符识别的可变长度和base-36编码的序列号。由于可能发生错误,可以为循环冗余校验或校验和附加一个字符。一个基于36编码的字符可以支持36个不同的数字作为校验和。

序列号示例:LNG TZ746B8 +一个检查字符(base-36)

目前我尝试过不同的算法,比如Luhn Mod N(36)算法。

CRC

CRC最常用于网络传输检测中的误码,其中最高概率是一位发生变化。在OCR序列号识别的主题中,更可能的识别错误是对Ascii“8”字符(二进制00111000)的错误识别,而不是“B”字符(二进制01000010)导致不仅仅是一位错误。通过CRC,汉明距离(HD)用作错误检测的参数。在前面描述的用于CRC分割的0x05多项式中,使用大约3-4的HD,如果更多位改变则导致未检测到的错误。 HD Source

哪种多项式最好或哪种校验和算法可以使用?目前,我使用boost CRC实现获得最佳结果,而0x05作为5位CRC的多项式除法(36个字符需要6位,但最后一位未完全使用。对于测试,我只使用完整的5位= 31不同的人物)。多项式列表Wiki Polynomials

关键字

我可以使用哪种关键字来查找有关光学字符识别系统中错误检测主题的互联网信息?我在哪里可以找到使用OCR最可能识别错误的统计数据? (如E / 3,B / 8,错误。)

使用CRC的问题

36个不同的数字需要7位,因此并非所有7位都被使用。 7位最多可容纳63个数字。因此,我必须以CRC为结果(%)或仅使用6位多项式。 基于此,当我使用模数时,我的结果精度会下降。原因是更多位可以容纳更多不同的校验和,因此发生的冲突更少。

此外我还有一个问题,即OCR特殊字符如N / M,3 / E,B78很容易被误识别。由于我的要求是将所有字符识别为100%正确,因此引入校验和或CRC算法以防止错误识别而无需检测。

现在进一步的问题是,不同的序列号,例如序列号“S95I”和“5951”导致相同的校验和“GP”。由于OCR容易出现5 / S错误识别错误,因此不应发生这种校验错误。

相同字符校验和的更多示例。

Nr.1 - Nr.2 - Checksum 1 and 2
BSHB - 85H8 - KA - KA
BSJ8 - 85JB - IC - IC
BSJ8 - 85JB - IC - IC
BSQ8 - 85QB - KC - KC
BSQ8 - 85QB - KC - KC
BSSB - 85S8 - IA - IA
BS1B - 8518 - ES - ES
BS38 - 853B - GQ - GQ
BB7I - 8871 - E0 - E0
BB7I - 8871 - E0 - E0
B930 - 89EO - BI - BI
9331 - 9EEI - EQ - EQ
9EEI - 9331 - EQ - EQ

在我的实现中,我使用了boost CRC算法,可在此处找到Boost CRC

string data = "S95I";
boost::crc_optimal<11, 0x571> crc;
crc.process_bytes(data.data(), data.size());
stringstream checksum;
checksum << (int)crc() % 1296;
string resultCheck = checksum.str();

我想知道是否有其他算法具有较低的冲突可能性,或者其他可能的校验和实现。

如果有任何问题,或者我没有解释自己不够好,请不要犹豫,回答。我会尽快回复。

非常感谢, 克里斯托夫

1 个答案:

答案 0 :(得分:0)

您的支票值中没有足够的字符。你可以做的最好的事情是减少碰撞,增加更多的检查字符。你应该至少有四个。

使用Fletcher派生的检查而不是CRC,我能够在四个字符的情况下减少20%的冲突次数,因此:

unsigned long check(char *seq, size_t len)
{
    unsigned ch;
    unsigned long sum = 1, val = 0;

    while (len) {
        ch = seq[--len];
        if (ch <= '9')
            ch -= '0';
        else
            ch -= 'A' - 10;
        sum += ch;
        val += sum;
    }
    return (sum % 36) + 36 * (val % 36);
}

通过将检查偏向于仅区分可能存在识别错误​​的字符,我进一步将碰撞次数减少了大约6倍,因此:

unsigned long check(char *seq, size_t len)
{
    unsigned ch;
    unsigned long sum = 1, val = 0;
    static char incl[256] = {
        ['0'] = 1, ['1'] = 2, ['3'] = 3, ['5'] = 4, ['8'] = 5, ['M'] = 6,
        ['B'] = 7, ['E'] = 8, ['I'] = 9, ['N'] = 10, ['O'] = 11, ['S'] = 12
    };

    while (len) {
        ch = incl[(int)(seq[--len])];
        sum += ch;
        val += sum;
    }
    return (sum % 36) + 36 * (val % 36);
}