发票二维码扫描增强_06_持续优化

Posted mousezhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了发票二维码扫描增强_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%

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

以上是关于发票二维码扫描增强_06_持续优化的主要内容,如果未能解决你的问题,请参考以下文章

发票二维码扫描增强_05_构建目标二维码

建筑业统一发票上面的二维码怎么看

web程序构建 - 发票扫描记录

leetcode_1292. Maximum Side Length of a Square with Sum Less than or Equal to Threshold_[二维前缀和](代码片段

遭遇史上最严发票令?富立叶无线开票扫描枪3秒开票!

发票二维码怎么识别?发票二维码识别的办法是?