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

前言

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

  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)
    
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值