zoukankan      html  css  js  c++  java
  • 发票二维码扫描增强_06_持续优化

    整个功能完成后,对扫描速度进行了测试,在I7 6700的CPU机器上,一个图片扫描的时间大概在5-7秒之间:

    "E:python workspaceqrcode-complementvenvScriptspython.exe" "E:/python workspace/qrcode-complement/main.py"
    对二维码部分进行切片处理:0.25531697273254395
    对图片进行预处理:0.013962984085083008
    对图片基于极点进行拉伸转换:1.6815056800842285
    将图片基于自适应阈值进行二值化:0.007976531982421875
    left_top_flag:all
    left_bottom_flag:none
    right_top_flag:all
    right_bottom_flag:all
    将图片恢复竖直状态:0.2682826519012451
    寻找图片的分割线:1.8709981441497803
    生成目标图片:0.26429295539855957
    总共耗费时间:4.4122045040130615
    总共扫描出:0
    
    

    从时间耗费来看,拉伸转换和分割线耗费了大部分的时间。经过分析程序,应该是有两段for循环赋值的逻辑导致的速度过慢。

    按照我们预计的407 * 407 的图片大小,每次执行for循环都需要执行16.5万次,是一种比较低效的写法。

    比较高效的写法应该是采用numpy提供的一些矩阵运算的函数来进行计算,自己琢磨了一下,改进了一下算法

    矩阵运算

    自定义灰度算法

    
        # 加权平均值法灰度化
        def gray_scale(self, image):
            gray_image = image.copy()
    
            # 循环写法
            # for i in range(image.shape[0]):
            #     for j in range(image.shape[1]):
            #         if (image[i, j][0] < image[i, j][1] or image[i, j][0] < image[i, j][2]) and image[i, j][0] < 150:
            #             # 其他颜色值高于蓝色,且蓝色低于150,说明是其他颜色覆盖
            #             gray_image[i][j] = 255
            #         else:
            #             # gray_image[i, j] = min(0.8 * image[i, j][0] + 0.1 * image[i, j][1] + 0.1 * image[i, j][2],255)
            #             grayResult = max(min(-1.5 * image[i, j][0] + 1.5 * image[i, j][1] + 1.5 * image[i, j][2], 255), 0)
            #             gray_image[i][j] = grayResult
            # return gray_image
    
            # 矩阵算法
            ratio = 1.5
            b = np.ones((image.shape[0], image.shape[1]), np.int16)
            g = np.ones((image.shape[0], image.shape[1]), np.int16)
            r = np.ones((image.shape[0], image.shape[1]), np.int16)
            source_b, source_g, source_r = cv2.split(gray_image)
    
            b[:, :] = source_b[:, :]
            g[:, :] = source_g[:, :]
            r[:, :] = source_r[:, :]
    
            g_minus_b = g - b
            r_minus_b = r - b
    
            g_minus_b = np.where(g_minus_b > 0, 1000, 0)
            r_minus_b = np.where(r_minus_b > 0, 1000, 0)
    
            g_minus_b_150 = np.where((g_minus_b - b) >= 850, 255, 0)
            r_minus_b_150 = np.where((r_minus_b - b) >= 850, 255, 0)
            g_r_minus_b_150 = g_minus_b_150 + r_minus_b_150
            not_b_lt_150 = np.where(g_r_minus_b_150 >= 255, -9999, 0)
    
            b_plus_g_r = -1 * ratio * b + ratio * g + ratio * r
            b_plus_g_r = np.where(b_plus_g_r < 0, 0, b_plus_g_r)
            b_plus_g_r = np.where(b_plus_g_r > 255, 255, b_plus_g_r)
            b_result = not_b_lt_150 + b_plus_g_r
            b_result = np.where(b_result < -9000, 255, b_result)
            b_result = np.where(b_result < 0, 0, b_result)
            b_result = np.where(b_result > 255, 255, b_result)
    
            gray_image[:, :, 0] = b_result[:, :]
            gray_image[:, :, 1] = b_result[:, :]
            gray_image[:, :, 2] = b_result[:, :]
    
            return gray_image
    

    寻找分割线

    
    def Point_rows(mat, border):
        # 先找上下限
        cv2.imwrite("images/erode9.1.jpg", mat)
    
        # loop写法
        # lower_limit_j = []
        # for i in range(0, mat.shape[1]):
        #     for j2 in range(0, min(border, mat.shape[0]) - 1):
        #         if mat[0:j2 + 1, i].mean() == 255 and mat[j2 + 1, i] == 0 and j2 > 2:  # 如果这一列,超过5个全是白色,下一个是黑色  则认为也到了边界
        #             lower_limit_j.append(j2)
        #             break
        #         elif mat[j2, i] == 0 and mat[j2 + 1, i] == 255 and j2 > 2:  # 如果这个是黑色而且下一个是白色,并且黑色数量大于五个,则认为到达了下限边界
        #             lower_limit_j.append(j2)
        #             break
        #     if mat[:, i].mean() == 0:  # 如果这一列全部为黑色,则占满13
        #         # lower_limit_j.append(12.4)
        #         continue
        #     elif mat[:, i].mean() == 255:  # 如果这一列全部为白色色,则占11
        #         # lower_limit_j.append(11.3)
        #         continue
        # if len(lower_limit_j) == 0:
        #     return 6
        # return max(int(np.median(np.array(lower_limit_j))), int((np.array(lower_limit_j).mean())))
    
        # 矩阵算法
        gap = 3
        result_array = []
        white_tmp_matrix = np.zeros((1, mat.shape[1]), np.int16)
        black_last_matrix = np.zeros((1, mat.shape[1]), np.int16)
        # 从白色点变为黑色点,白色连续线段超过2,下一个点为黑点
        for i in range(0, min(border, mat.shape[0])):
            # 循环所有列
            if i == 0:
                white_tmp_matrix = np.where(mat[i, :] == 255, 1, 0)
            else:
                white_current_matrix = np.where(mat[i, :] == 255, 1, -1)
                white_cal_matrix = white_tmp_matrix * white_current_matrix
                white_count_matrix = np.where(white_cal_matrix == -1, 1, 0)
                white_change_count = white_count_matrix.sum()
    
                # 变更tmp,将已经变化的点修改为0
                white_tmp_matrix = white_tmp_matrix * np.where(white_cal_matrix == -1, 0, 1)
                # j > gap宽度则计入计算
                if i > gap and white_change_count > 0:
                    for k in range(0, white_change_count):
                        result_array.append(i - 1)
    
            # 循环所有列
            if i == 0:
                black_last_matrix = np.where(mat[i, :] == 0, 1, 0)
            else:
                black_current_matrix = np.where(mat[i, :] == 0, 5, 1)
                # 计算结果为:
                # 上次为白,本次为黑:5
                # 上次为黑,本次为白,2
                # 上次为白,本次为白,1
                # 上次为黑,本次为黑,6
                # 历史存在过黑, 1000 +,不参与计算
    
                black_cal_matrix = black_last_matrix + black_current_matrix
                # 统计上次为黑本次为白
                black_count_matrix = np.where(black_cal_matrix == 2, 1, 0)
                black_change_count = black_count_matrix.sum()
    
                # 变更tmp
                black_last_matrix = black_cal_matrix
                # 上次为白,本次为白,更新为0
                black_last_matrix = np.where(black_last_matrix == 1, 0, black_last_matrix)
                # 上次为黑,本次为白,更新为1000
                black_last_matrix = np.where(black_last_matrix == 2, 1000, black_last_matrix)
                # 上次为白,本次为黑,更新为1
                black_last_matrix = np.where(black_last_matrix == 5, 1, black_last_matrix)
                # 上次为黑,本次为黑,更新为1
                black_last_matrix = np.where(black_last_matrix == 6, 1, black_last_matrix)
    
                # j > gap宽度则计入计算
                if i > gap and black_change_count > 0:
                    for k in range(0, black_change_count):
                        result_array.append(i - 1)
    
            # 无需考虑_白点和黑点的变更都计入,不在参与下一行的处理
            # if i != 0:
            #     white_change_matrix = np.where(white_cal_matrix == -1, 1000, 0)
            #     black_change_matrix = np.where(black_current_matrix == 2, 0, 1)
            #
            #     black_last_matrix = black_last_matrix + white_change_matrix
            #     white_tmp_matrix = white_tmp_matrix * black_change_matrix
        result = max(int(np.median(np.array(result_array))), int((np.array(result_array).mean())))
        return result
    
    

    优化后的扫描速度:1.4秒

    对二维码部分进行切片处理:0.2293868064880371
    对图片进行预处理:0.011968851089477539
    对图片基于极点进行拉伸转换:0.0857694149017334
    将图片基于自适应阈值进行二值化:0.007978439331054688
    left_top_flag:all
    left_bottom_flag:none
    right_top_flag:all
    right_bottom_flag:all
    将图片恢复竖直状态:0.2982046604156494
    寻找图片的分割线:0.3809800148010254
    生成目标图片:0.37898731231689453
    总共耗费时间:1.4411475658416748
    

    从时间耗费来看,只要存在循环处理的部分,程序的速度还是偏慢,而调用opencv的api确实速度非常快。

    这种优化的主要思想是:

    借助数值分段,将原本需要进行循环读取,进行if判断的逻辑,通过存储的数值的分段进行分割。最终通过numpy.where方法,将分段区间内的数字转换成目标值。

    降低运算量

    还有一种速度优化的思路,就是降低目标图片的分辨率。这种方案可能对图片的识别效果产生一定的不良影响,但是可以极大的提升扫描速度。

    例如,当我们定义每一个单元格由5px组成是,那么整个图片的目标面积为 37 * 5 * 37 * 5 = 3.4万,比起目前的16.5万整体运算量直接降低到20%

    这个识别率和扫描速度最优的点需要调试后才能得出,作为后续持续优化的一个方向。

  • 相关阅读:
    mongo 查询某个字段的值不为空列表!
    pdftohtml的使用
    Activiti数据库表结构(表详细版)
    ElasticSearch在linux上安装部署
    构建Spring Web应用程序—关于spring中的validate注解后台校验的解析
    构建Spring Web应用程序—SpringMVC详解
    高级装配—运行时注入
    高级装配—bean的作用域
    高级装配—条件化的Bean
    高级装配—配置profile bean
  • 原文地址:https://www.cnblogs.com/mousezhou/p/9261165.html
Copyright © 2011-2022 走看看