<规则数字字符>识别—纯python代码实现

该博客记录了不使用机器学习和深度学习算法,用Python实现规则数字字符识别的过程。具体场景是识别图片上的速度,准确率达100%,处理速度为4 - 8 ms。步骤包括准确定位速度区域、字符分割和字符识别,并梳理了逻辑和给出功能函数。

前言

本篇博客主要记录在不使用机器学习和深度学习算法的情况下,自己实现规则数字字符的识别,具体场景如下:

  1. 先看图
    整幅图片1080*1920,图示为已经截取的部分
  2. 需求: 将对应图片上出现的速度识别出来,以用于数据的处理和结果的合并(比较多的应用场景基础)
  3. 效果: 准确率达到100%,在inter i7-cpu上,速度为:4-8 ms(完成对应图片的处理,输出速度)

具体步骤

1、准确定位速度区域:

  1. 裁剪: 将如上图区域切割出来;
  2. 模板匹配: 将速度区域的左上角右下角定位出来;
  3. 切片: 将定位出来的区域切割出来;
  4. 模板和最终截取图,如下: 因为速度位长度不一,所以需要两个模板;
    end
    start
    最终图片

2、字符分割:

  1. 图片二值化处理: 因为图片像素值并不均匀,也为后面判断提供便利,此处阈值设置为像素值小于20就置255否则就是0;
    在这里插入图片描述
  2. 投影法分割字符: 如下:
    在这里插入图片描述

3、字符识别:

  1. 可视化字符: 可视化字符,分析字符特性;
    在这里插入图片描述

  2. 特征分析: 将速度区域的左上角右下角定位出来;
    数字0: 字符上半部分有空心,下半部分有空心,且没有像素值全部是255的行;
    数字1: 没有像素值全部是255的行,字符的宽度最窄,在一列上像素值为255的最多和4一样;
    数字2: 在下半部分有像素值全部是255的行,且在一列上,为255的像素值不多;
    数字3: 上半部分没有空心,下半部分没有空心,且没有像素值全部是255的行;
    数字4: 在下半部分有像素值全部是255的行,且在一列上,为255的像素值最多和1差不多;
    数字5: 没有像素值全部是255的行,在下半部分像素值为<左无右有>;
    数字6: 没有像素值全部是255的行,在下半部分有空心,上半部分没有;
    数字7: 上半部分有像素值全部是255的行;
    数字8: 上半部分有空心,但是小一个单位,下半部分有空心,大一个单位;
    数字9: 没有像素值全部是255的行,在下半部分没有空心,上半部分有,与6相反;

  3. 逻辑梳理:
    1)、 依次读取一个文件夹下所有图片,并进行速度区域定位,切割该区域;
    2)、 将速度区域进行字符分割,依次对分割的图片进行识别,将识别内容保存并返回;
    3)、 判断识别出来的字符个数是否满足要求,根据要求输出速度;
    代码如下:

// 主函数
def main(image_path,star_tem_path,end_tem_path):

    num = 0
    name = "test_data"

    filename = os.listdir(image_path)
    start_tem_image = cv2.imread(star_tem_path)
    end_tem_image = cv2.imread(end_tem_path)
    sh, sw = start_tem_image.shape[0], start_tem_image.shape[1]
    eh, ew = end_tem_image.shape[0], end_tem_image.shape[1]

    for image_name in filename:
        alone_image_path = image_path + image_name
        image_data = cv2.imread(alone_image_path)
        image_speed = image_data[0:35, 0:1400]

        res = cv2.matchTemplate(image_speed, start_tem_image, method=cv2.TM_CCOEFF_NORMED)
        end_res = cv2.matchTemplate(image_speed, end_tem_image, method=cv2.TM_CCOEFF_NORMED)
        # 取匹配程度大于0.8的坐标
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        end_min_val, end_max_val, end_min_loc, end_max_loc = cv2.minMaxLoc(end_res)
        if max_val >= 0.8 and end_max_val >= 0.8:
            satrt_top_left = max_loc
            end_top_left = end_max_loc
            left = (satrt_top_left[0] + sw,satrt_top_left[1])
            right = (end_top_left[0],end_top_left[1] +  eh - 3)
            cv2.rectangle(image_speed, left, right, 255, 2)
            div_image = image_data[satrt_top_left[1]:(end_top_left[1] +  eh - 3),satrt_top_left[0]+ sw :end_top_left[0]]#调整剪切的高度和宽度
            name_all = name + "-%d"%num
            # print(div_image.shape)
            speed_result = chardivision(div_image,name_all)
            if speed_result != -1:
                print(speed_result)
                if len(speed_result) == 3:
                    print("速度是:{}.{}{}Km/h".format(speed_result[0],speed_result[1],speed_result[2]))
                elif len(speed_result) == 4:
                    print("速度是:{}{}.{}{}Km/h".format(speed_result[0],speed_result[1],speed_result[2],speed_result[3]))
            else:
                print("检测失败!!!")
            num += 1

        image_speed = cv2.cvtColor(image_speed, cv2.COLOR_BGR2RGB)
        result = np.asarray(image_speed)

        cv2.namedWindow("result", cv2.WINDOW_AUTOSIZE)
        cv2.imshow("result", result)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        sleep(1)

整体效果如下:
识别时间
准确率100%
3. 功能函数:
1)、 字符切割函数 chardivision(div_image,name_all):
代码如下:

def chardivision(image,name):

    image = image.astype(np.uint8)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    h,w= image.shape

    tran_tem_image = np.zeros((h,w))
    tran_tem_image[np.where(image[:,:]<=20)] = 255

    left_pix = 0
    num = 1
    speed_dete = []
    for w_pix in range(1,w-1):
        if  np.all(tran_tem_image[:,w_pix-1] == 0) and  np.any(tran_tem_image[:,w_pix] != 0):
            left_pix = w_pix
        elif np.any(tran_tem_image[:,w_pix] != 0) and np.all(tran_tem_image[:,w_pix+1] == 0):
            image_char = tran_tem_image[:,left_pix:w_pix+1]
            char_w = w_pix+1-left_pix
            if char_w > 3:

                speed_num = c0_6and9(image_char)
                if speed_num != -1:
                    speed_dete.append(speed_num)

                else:
                    path = "./a/other/" + name + "%d.jpg" % w_pix
                    cv2.imwrite(path, image_char)
                    cv2.imwrite("./a/other/" + name + "%d_%s.jpg" % (w_pix,"cuowu"), image)
                    continue
            left_pix = 0
            num += 1
    return speed_dete

2)、 字符识别函数 c0_6and9(image_char):

代码如下:

def c0_6and9(image):

    num1 = is1(image)
    if num1 == 1:
        return 1
    num2and4 = is2and4(image)
    if num2and4 == 2 or num2and4 == 4:
        return num2and4
    num7 = is7(image)
    if num7 == 7:
        return 7

    up_3, down_3,up_5, down_5,up,up_8,down = 0,0,0,0,0,0,0
    for i in range(image.shape[0]):
        if i <= int(image.shape[0] / 2):
            if len(image[np.where(image[i, 0] >= 200)]) == 1 and len(image[np.where(image[i, -1:] >= 200)]) == 1 and \
                    len(image[np.where(image[i, :] >= 200)]) <= 6:
                up += 1

            if len(image[np.where(image[i, 1] >= 200)]) == 1 and len(image[np.where(image[i, -2:-1] >= 200)]) == 1 and \
                    len(image[np.where(image[i, :] >= 200)]) <= 6:
                up_8 += 1

            if len(image[np.where(image[i, :2] >= 200)]) == 0 and len(image[np.where(image[i, -2:-1] >= 200)]) == 1:
                up_3 += 1

            if len(image[np.where(image[i, 0] >= 200)]) == 1 and len(image[np.where(image[i, -1:] >= 200)]) == 0:
                up_5 += 1

        elif i > int(image.shape[0] / 2):
            if len(image[np.where(image[i, 0] >= 200)])==0 and len(image[np.where(image[i, -1:] >= 200)])==1:
                down_5 += 1

            if len(image[np.where(image[i, 0] >= 200)]) == 1 and len(image[np.where(image[i, -1:] >= 200)]) == 1 and \
                    len(image[np.where(image[i, :] >= 200)]) <= 6:
                down += 1
            elif len(image[np.where(image[i, 0] >= 200)]) == 0 and len(image[np.where(image[i, -1:] >= 200)]) == 1:
                down_3 += 1


    if up >= 2 and down >= 2:
        return 0
    elif down == 0 and up >= 2:
        return 9
    elif up == 0 and up_8 == 0 and down >= 2 and num2and4 == -1:
        return 6
    elif up_8 >= 2 and down >= 2 and up == 0:
        return 8
    elif up_3 >= 2 and down_3 >= 2 and num2and4 == -1:
        return 3
    elif up_5 >= 2 and down_5 >= 2:
        return 5
    else:
        return -1

3)、 字符识别函数,判断是否是1,2,4,7字符函数:

代码如下:

def is1(image):

    is_1 = 0
    initial = np.array([i for i in range(image.shape[1])])#生成列索引
    for row_1 in range(image.shape[0]):
        col_len = []
        if len(np.where(image[row_1, :] >= 200)[0]) != 0:#开始判断是否有一行像素值全部大于阈值(200)
            frist_row_result = np.where(image[row_1, :] >= 200)[0]
            second_row_result = np.where(image[row_1 + 1, :] >= 200)[0]
            for val in initial:
                if val in frist_row_result:
                    col_len.append(val)
                elif val not in frist_row_result and val in second_row_result:
                    col_len.append(val)
        if len(col_len) == image.shape[1] and image.shape[1] > 6:#规定字符宽度达到阈值#有相等,但大于6个像素值宽度,则判断不是1
            is_1 += 1
            return -1
    for col_1 in range(image.shape[1]):
        if is_1 == 0 and len(np.where(image[:, col_1] >= 200)[0]) >= 12 and image.shape[1] < 7:#字符宽度小于7,才确定为1
            return 1

#判断是否是7
def is7(image):

    initial = np.array([i for i in range(image.shape[1])])#生成列索引
    for row in range(0, int(image.shape[0] / 2) + 1):#判断上半部分遍历是否有像素值大于阈值,且一行所有个数等于宽度
        col_len = []
        if len(np.where(image[row, :] >= 200)[0]) != 0:
            frist_row_result = np.where(image[row, :] >= 200)[0]
            second_row_result = np.where(image[row + 1, :] >= 200)[0]
            for val in initial:
                if val in frist_row_result:
                    col_len.append(val)
                elif val not in frist_row_result and val in second_row_result:
                    col_len.append(val)
        if len(col_len) == image.shape[1]:#上半部分如果有,则直接判断为7,为否没有,5的上半部分没有达到整个宽度
            return 7

    return -1

#判断是否是24
def is2and4(image):

    down_2, down_4 = 0,0
    initial = np.array([i for i in range(image.shape[1])])#生成列的index
    for row in range(int(image.shape[0] / 2), image.shape[0]):#遍历下半部分
        col_len = []
        frist_row_result = np.where(image[row, :] >= 200)[0]#该行所有列是否有像素值大于200
        if len(frist_row_result) != 0:
            second_row_result = np.where(image[row + 1, :] >= 200)[0]#该行的下一行所有列是否有像素值大于200
            for val in initial:
                if val in frist_row_result:
                    col_len.append(val)#将第一行大于200的值添加到新列表
                elif val not in frist_row_result and val in second_row_result:
                    col_len.append(val)#将第二列有且第一列没有的,大于200的像素值添加到新列表中
        if len(col_len) == image.shape[1]:#如果大于200的像素值的索引个数长度等于图片的宽度
            is5 = 0
            for col in range(image.shape[1]):

                if row - int(image.shape[0] / 2)  < 3:#在下半部分开始的三行内出现整行,判断为5
                    # print("这个字符是5")
                    is5 += 1
                    return -1
                # if np.sum(image[int(image.shape[0] / 2):row, col] > 200) >= 4 :
                if np.sum(image[:, col] > 200) >= 14:#整列大于14200以上的值则为4
                    down_4 += 1
                    return 4
            if is5 == 0 and down_4 == 0:#既不是5,也不是4的便是2
                down_2 += 1
                return 2
    return -1

4)、 最后:


if __name__ == "__main__":

    star_tem_path = './template/start_tem.jpg'#定位速度区域开始位置的模板
    end_tem_path = './template/end_tem.jpg'#定位速度区域结束位置的模板
    image_path = "./test_data/"#带检测的图片
    main(image_path,star_tem_path,end_tem_path)
    
请根据给定的文法设计并实现语法分析程序,能基于上次作业的词法分析程序所识别出的单词,识别出各类语法成分。输入输出及处理要求如下: (1)需按文法规则,用递归子程序法对文法中定义的所有种语法成分进行分析; (2)为了方便进行自动评测,输入的被编译源文件统一命名为testfile.txt;输出的结果文件统一命名为output.txt ;结果文件中包含如下两种信息: 1)按词法分析识别单词的顺序,按行输出每个单词的信息(要求同词法分析作业,对于预读的情况不能输出)。 2)在下列高亮显示的语法分析成分分析结束前,另起一行输出当前语法成分的名字,形如“<常量说明>”(注:未要求输出的语法成分仍需要分析) <加法运算符> ::= +|- <乘法运算符> ::= *|/ <关系运算符> ::= <|<=|>|>=|!=|== <字母> ::= _|a|...|z|A|...|Z <数字> ::= 0|<非零数字> <非零数字> ::= 1|...|9 <字符> ::= '<加法运算符>'|'<乘法运算符>'|'<字母>'|'<数字>' <字符串> ::= "{十进制编码为32,33,35-126的ASCII字符}" <程序> ::= [<常量说明>][<变量说明>]{<有返回值函数定义>|<无返回值函数定义>}<主函数> <常量说明> ::= const<常量定义>;{ const<常量定义>;} <常量定义> ::= int<标识符>=<整数>{,<标识符>=<整数>} | char<标识符>=<字符>{,<标识符>=<字符>} <无符号整数> ::= <非零数字>{<数字>}| 0 <整数> ::= [+|-]<无符号整数> <标识符> ::= <字母>{<字母>|<数字>} <声明头部> ::= int<标识符> |char<标识符> <变量说明> ::= <变量定义>;{<变量定义>;} <变量定义> ::= <类型标识符>(<标识符>|<标识符>'['<无符号整数>']'){,(<标识符>|<标识符>'['<无符号整数>']' )} //<无符号整数>表示数组元素的个数,其值需大于0 <类型标识符> ::= int | char <有返回值函数定义> ::= <声明头部>'('<参数表>')' '{'<复合语句>'}' <无返回值函数定义> ::= void<标识符>'('<参数表>')''{'<复合语句>'}' <复合语句> ::= [<常量说明>][<变量说明>]<语句列> <参数表> ::= <类型标识符><标识符>{,<类型标识符><标识符>}| <空> <主函数> ::= void main‘(’‘)’ ‘{’<复合语句>‘}’ <表达式> ::= [+|-]<项>{<加法运算符><项>} //[+|-]只作用于第一个<项> <项> ::= <因子>{<乘法运算符><因子>} <因子> ::= <标识符>|<标识符>'['<表达式>']'|'('<表达式>')'|<整数>|<字符>|<有返回值函数调用语句> <语句> ::= <条件语句>|<循环语句>| '{'<语句列>'}'| <有返回值函数调用语句>; |<无返回值函数调用语句>;|<赋值语句>;|<读语句>;|<写语句>;|<空>;|<返回语句>; <赋值语句> ::= <标识符>=<表达式>|<标识符>'['<表达式>']'=<表达式> <条件语句> ::= if '('<条件>')'<语句>[else<语句>] <条件> ::= <表达式><关系运算符><表达式> //整型表达式之间才能进行关系运算 |<表达式> //表达式为整型,其值为0条件为假,值不为0时条件为真 <循环语句> ::= while '('<条件>')'<语句>| do<语句>while '('<条件>')' |for'('<标识符>=<表达式>;<条件>;<标识符>=<标识符>(+|-)<步长>')'<语句> <步长>::= <无符号整数> <有返回值函数调用语句> ::= <标识符>'('<值参数表>')' <无返回值函数调用语句> ::= <标识符>'('<值参数表>')' <值参数表> ::= <表达式>{,<表达式>}|<空> <语句列> ::= {<语句>} <读语句> ::= scanf '('<标识符>{,<标识符>}')' <写语句> ::= printf '(' <字符串>,<表达式> ')'| printf '('<字符串> ')'| printf '('<表达式>')' <返回语句> ::= return['('<表达式>')'] 【输入形式】testfile.txt中的符合文法要求的测试程序。 【输出形式】按如上要求将语法分析结果输出至output.txt中,中文字符的编码格式要求是UTF-8。 【样例输入】 const int const1 = 1, const2 = -100; const char const3 = '_'; int change1; char change3; int gets1(int var1,int var2){ change1 = var1 + var2; return (change1); } void main(){ printf("Hello World"); printf(gets1(10, 20)); } 【样例输出】 CONSTTK const INTTK int IDENFR const1 ASSIGN = INTCON 1 <无符号整数> <整数> COMMA , IDENFR const2 ASSIGN = MINU - INTCON 100 <无符号整数> <整数> <常量定义> SEMICN ; CONSTTK const CHARTK char IDENFR const3 ASSIGN = CHARCON _ <常量定义> SEMICN ; <常量说明> INTTK int IDENFR change1 <变量定义> SEMICN ; CHARTK char IDENFR change3 <变量定义> SEMICN ; <变量说明> INTTK int IDENFR gets1 <声明头部> LPARENT ( INTTK int IDENFR var1 COMMA , INTTK int IDENFR var2 <参数表> RPARENT ) LBRACE { IDENFR change1 ASSIGN = IDENFR var1 <因子> <项> PLUS + IDENFR var2 <因子> <项> <表达式> <赋值语句> SEMICN ; <语句> RETURNTK return LPARENT ( IDENFR change1 <因子> <项> <表达式> RPARENT ) <返回语句> SEMICN ; <语句> <语句列> <复合语句> RBRACE } <有返回值函数定义> VOIDTK void MAINTK main LPARENT ( RPARENT ) LBRACE { PRINTFTK printf LPARENT ( STRCON Hello World <字符串> RPARENT ) <写语句> SEMICN ; <语句> PRINTFTK printf LPARENT ( IDENFR gets1 LPARENT ( INTCON 10 <无符号整数> <整数> <因子> <项> <表达式> COMMA , INTCON 20 <无符号整数> <整数> <因子> <项> <表达式> <值参数表> RPARENT ) <有返回值函数调用语句> <因子> <项> <表达式> RPARENT ) <写语句> SEMICN ; <语句> <语句列> <复合语句> RBRACE } <主函数> <程序>
最新发布
11-13
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值