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

Posted mousezhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了发票二维码扫描增强_05_构建目标二维码相关的知识,希望对你有一定的参考价值。

根据前面获得的行、列分割,我们可以得到 37 * 37个矩阵。根据每个矩阵内的数值信息,可以计算出当前矩阵所代表的数据点,是黑色格子还是白色格子。

从发票的二维码分析,存在以下几种图形特征:

1.图形整体界限分明,每个黑色格子内容饱满,白色格子留白清晰
2.图形大体界限分明,白色格子留白清晰,存在部分黑色格子打印质量较差,无法占满一行,只能占中心部分
3.图形整体糊化严重,黑色格子边界溢出严重,白色格子严重被侵蚀
4.图形大体界限分明,部分格子出现向右溢出的情况,部分格子无法清晰的分辨黑白

根据这些特征,我们编写多种策略,每个策略都生成对应的目标图形,按照概率从高到低一一输入给zbar进行扫描

为了匹配这些特征,我需要对每个格子计算以下信息值:

avg_center_mean:中心点的颜色平均值
avg_total_mean:整个矩阵颜色平均值
avg_round_mean:矩阵外围2px的包围图形的颜色平均值,用于计算周边格子的颜色侵入性
avg_margin_center_mean:矩阵向右偏移的中心点的颜色平均值,用于计算右侧便宜的图形的真实颜色
avg_margin_total_mean:矩阵向右便宜的整体图形的颜色平均值
avg_left_center_mean:矩阵左侧图形颜色均值
avg_right_center_mean:矩阵右侧图形颜色均值
avg_right_1_3_center_mean:矩阵右侧三分之一的颜色均值
avg_offset_3_center_mean:矩阵左侧3个px的图形中心的平均颜色,用于计算图片的向右偏移度

代码如下:


    # 根据原图和行列分割线生成新的二维码图片
    def generate_image(self, image, point_rows, point_columns, defect_flag):
        offset = 6 if defect_flag else 0
        margin_offset = 0
        round_offset = 2

        strategy_001 = np.zeros((407, 407), np.uint8)
        strategy_002 = np.zeros((407, 407), np.uint8)
        strategy_003 = np.zeros((407, 407), np.uint8)
        strategy_004 = np.zeros((407, 407), np.uint8)
        strategy_005 = np.zeros((407, 407), np.uint8)
        strategy_001[0:407, 0:407] = 255
        strategy_002[0:407, 0:407] = 255
        strategy_003[0:407, 0:407] = 255
        strategy_004[0:407, 0:407] = 255
        strategy_005[0:407, 0:407] = 255

        for i in range(-1, len(point_rows) - 1):
            margin_offset = 0
            for j in range(-1, len(point_columns) - 1):

                start_x = 0 if j == -1 else point_columns[j]
                start_y = 0 if i == -1 else point_rows[i]
                end_x = point_columns[j + 1]
                end_y = point_rows[i + 1]
                avg_center = image[start_y + round_offset:end_y - round_offset,
                             start_x + round_offset:end_x - round_offset]
                avg_total = image[start_y:end_y, start_x:end_x]
                avg_round = image[min(start_y - round_offset, 0):min(end_y + round_offset, 406),
                            min(start_x - round_offset, 0):max(end_x + round_offset, 406)]
                avg_y_round = image[min(start_y - round_offset, 0):min(end_y + round_offset, 406),
                              min(start_x, 0):max(end_x, 406)]
                avg_x_round = image[min(point_rows[i], 0):min(point_rows[i + 1], 406),
                              min(point_columns[j] - round_offset, 0):min(point_columns[j + 1] + round_offset, 406)]
                avg_offset_3_center = image[start_y + round_offset:end_y - round_offset, start_x:start_x + 3]
                avg_left_center = image[start_y + round_offset:end_y - round_offset,
                                  start_x:round((start_x + end_x) / 2)]
                avg_margin_center = image[start_y + round_offset:end_y - round_offset,
                                    start_x + round_offset + margin_offset:min(end_x - round_offset + margin_offset,
                                                                               406)]
                avg_margin_total = image[start_y:end_y,
                                   start_x + margin_offset:min(end_x + margin_offset, 406)]
                avg_right_center = image[start_y + round_offset:end_y - round_offset,
                                   round((start_x + end_x) / 2):end_x]
                avg_right_1_3_center = image[start_y + round_offset:end_y - round_offset,
                                       end_x - round((end_x - start_x) / 3):end_x]

                # 整体偏黑判定为黑色
                avg_center_mean = avg_center.mean()
                avg_total_mean = avg_total.mean()
                avg_round_mean = avg_round.mean()
                avg_margin_center_mean = avg_margin_center.mean()
                avg_margin_total_mean = avg_margin_total.mean()
                avg_left_center_mean = avg_left_center.mean()
                avg_right_center_mean = avg_right_center.mean()
                avg_right_1_3_center_mean = avg_right_1_3_center.mean()
                avg_offset_3_center_mean = avg_offset_3_center.mean()
                avg_inner_round_mean = (avg_total.sum() - avg_center.sum()) / (
                        avg_total.shape[0] * avg_total.shape[1] - avg_center.shape[0] * avg_center.shape[1])
                avg_out_round_mean = (avg_round.sum() - avg_total.sum()) / (
                        avg_round.shape[0] * avg_round.shape[1] - avg_total.shape[0] * avg_total.shape[1])
                avg_y_round_mean = (avg_y_round.sum() - avg_total.sum()) / (
                        avg_y_round.shape[0] * avg_y_round.shape[1] - avg_total.shape[0] * avg_total.shape[1])
                avg_x_round_mean = (avg_x_round.sum() - avg_total.sum()) / (
                        avg_x_round.shape[0] * avg_x_round.shape[1] - avg_total.shape[0] * avg_total.shape[1])

                # 如果整体判定为黑色,判断是否存在向右偏移的情况
                # 当前格子的偏移将影响下一个格子的偏移情况
                if avg_center_mean < 50:
                    current_offset = int(round(avg_offset_3_center_mean / 255.0 * 3))
                    margin_offset = current_offset if margin_offset < current_offset else margin_offset
                    if margin_offset == None:
                        margin_offset = 0

                # print("x轴:" + str(j + 1) + ",y轴:" + str(i + 1))
                # print("avg_center_mean:" + str(avg_center_mean))
                # print("avg_total_mean:" + str(avg_total_mean))
                # print("avg_round_mean:" + str(avg_round_mean))
                # print("avt_inner_round_mean:" + str(avg_inner_round_mean))
                # print("avt_out_round_mean:" + str(avg_out_round_mean))

                # 策略1:中心点策略,用于打印标准且溢出较小的情况
                # 中心点:低于80认为黑
                # 中心点:高于150认为白
                # 中心点高于内圈周边认为白
                # 其他认为黑
                if avg_center_mean < 50:
                    strategy_001[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0  # 表示为黑色
                elif avg_center_mean > 150:
                    # 中心点偏白判定为白色
                    strategy_001[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                elif avg_center_mean > avg_inner_round_mean:
                    strategy_001[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                else:
                    strategy_001[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0

                # 策略2:全局策略 + 向右偏移策略
                # 中心点:低于80认为黑
                # 中心点:高于150认为白
                # 右侧偏移中心点:低于80认为黑
                # 右侧偏移中心点:高于150认为白
                # 其他认为黑

                if margin_offset < 3:
                    if avg_center_mean < 120:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0  # 表示为黑色
                    elif avg_center_mean > 150:
                        # 中心点偏白判定为白色
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    elif avg_center_mean > avg_inner_round_mean:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    else:
                        strategy_002[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0
                else:
                    if avg_margin_center_mean < 120:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0  # 表示为黑色
                    elif avg_margin_center_mean > 150:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    elif avg_left_center_mean < 30 and avg_right_center_mean > 100 and avg_right_1_3_center_mean > 150:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    elif avg_right_center_mean < 80:
                        strategy_002[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0
                    elif avg_right_center_mean >= 150:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    elif avg_total_mean < avg_out_round_mean:
                        strategy_002[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0
                    elif avg_margin_center_mean > avg_inner_round_mean:
                        strategy_002[i * 11 + 11:i * 11 + 22,
                        (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色
                    else:
                        strategy_002[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0

                # 策略3:全局策略,黑色溢出严重
                # 中心点:低于50认为黑
                # 中心点:高于50认为白
                if avg_center_mean < 50:
                    strategy_003[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 0  # 表示为黑色
                elif avg_center_mean > 50:
                    # 中心点偏白判定为白色
                    strategy_003[i * 11 + 11:i * 11 + 22, (j + offset) * 11 + 11: (j + offset) * 11 + 22] = 255  # 表示为白色

        strategy_001 = self.fill_detect(strategy_001)
        strategy_002 = self.fill_detect(strategy_002)
        strategy_003 = self.fill_detect(strategy_003)
        strategy_004 = self.fill_detect(strategy_004)
        strategy_005 = self.fill_detect(strategy_005)

        strategy_001_path = self.tmp_image_path + "strategy001_" + self.image_name
        strategy_002_path = self.tmp_image_path + "strategy002_" + self.image_name
        strategy_003_path = self.tmp_image_path + "strategy003_" + self.image_name
        strategy_004_path = self.tmp_image_path + "strategy004_" + self.image_name
        strategy_005_path = self.tmp_image_path + "strategy005_" + self.image_name

        cv2.imwrite(strategy_001_path, strategy_001)
        cv2.imwrite(strategy_002_path, strategy_002)
        cv2.imwrite(strategy_003_path, strategy_003)
        cv2.imwrite(strategy_004_path, strategy_004)
        cv2.imwrite(strategy_005_path, strategy_005)

        return strategy_001_path, strategy_002_path, strategy_003_path, strategy_004_path, strategy_005_path


    # 补齐缺失图片
    def fill_detect(self, image):
        # 重构左侧坐标点
        image[0:77, 0:77] = 0
        image[11:66, 11:66] = 255
        image[22:55, 22:55] = 0

        image[330:407, 0:77] = 0
        image[341:396, 11:66] = 255
        image[352:385, 22:55] = 0

        # 重构右侧坐标点
        image[0:77, 330:407] = 0
        image[11:66, 341:396] = 255
        image[22:55, 352:385] = 0

        # 重构定位点
        image[66:77, 77:88] = 255
        image[66:77, 88:99] = 0
        image[66:77, 99:110] = 255
        image[66:77, 110:121] = 0
        image[66:77, 121:132] = 255
        image[66:77, 132:143] = 0
        image[66:77, 143:154] = 255
        image[66:77, 154:165] = 0
        image[66:77, 165:176] = 255
        image[66:77, 176:187] = 0
        image[66:77, 187:198] = 255
        image[66:77, 198:209] = 0
        image[66:77, 209:220] = 255
        image[66:77, 220:231] = 0
        image[66:77, 231:242] = 255
        image[66:77, 242:253] = 0
        image[66:77, 253:264] = 255
        image[66:77, 264:275] = 0
        image[66:77, 275:286] = 255
        image[66:77, 286:297] = 0
        image[66:77, 297:308] = 255
        image[66:77, 308:319] = 0
        image[66:77, 319:330] = 255

        image[77:88, 66:77] = 255
        image[88:99, 66:77] = 0
        image[99:110, 66:77] = 255
        image[110:121, 66:77] = 0
        image[121:132, 66:77] = 255
        image[132:143, 66:77] = 0
        image[143:154, 66:77] = 255
        image[154:165, 66:77] = 0
        image[165:176, 66:77] = 255
        image[176:187, 66:77] = 0
        image[187:198, 66:77] = 255
        image[198:209, 66:77] = 0
        image[209:220, 66:77] = 255
        image[220:231, 66:77] = 0
        image[231:242, 66:77] = 255
        image[242:253, 66:77] = 0
        image[253:264, 66:77] = 255
        image[264:275, 66:77] = 0
        image[275:286, 66:77] = 255
        image[286:297, 66:77] = 0
        image[297:308, 66:77] = 255
        image[308:319, 66:77] = 0
        image[319:330, 66:77] = 255

        # 重构右下角定位点
        # [29:34,29:34] 0
        # [30:33,30:33] 255
        # [31:32,31:32] 0
        image[308:363, 308:363] = 0
        image[319:352, 319:352] = 255
        image[330:341, 330:341] = 0
        return image

生成的图片,每个策略偏向于不同的图片场景,只需要确保所有策略生成的图片中存在可以扫描的一张即可:

策略1:

技术分享图片

策略2:
技术分享图片

策略3:
技术分享图片

以上是关于发票二维码扫描增强_05_构建目标二维码的主要内容,如果未能解决你的问题,请参考以下文章

发票二维码扫描增强_02_算法概述

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

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

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

如何检测 TIFF 图像中的多个条码/二维码并返回它们的值 + 位置?

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