根据前面获得的行、列分割,我们可以得到 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: