
By using the "回" shape feature of the three positioning points of the QR code, and through template matching, the positioning area of the QR code is identified, thereby determining whether a QR code exists here.
The idea is as follows:


The main method is to locate the area by using the template image, and it is in the form of a square.
Because there are no other similar "loop"-shaped areas in my image that could interfere, we can assume that once the positioning area is found, it indicates that a QR code exists here.

Source code:
import cv2
import os
from pyzbar.pyzbar import decode
import numpy as np
# 读取目标图像和模板图像
def show_image(img):
cv2.namedWindow("1",cv2.WINDOW_AUTOSIZE )
cv2.imshow("1",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 1. 生成标准回字形模板(7x7)
def create_qrcode_template(size=7):
"""创建二维码定位点的回字形模板(黑=0,白=255)"""
template = np.ones((size, size), dtype=np.uint8) * 255 # 初始全白
# 外层黑框(第0行、第6行,第0列、第6列)
template[0, :] = 0
template[-1, :] = 0
template[:, 0] = 0
template[:, -1] = 0
# 内层黑方块(第2-4行,第2-4列)
template[2:5, 2:5] = 0
return template
def exist_rect(candidates,score_x,score_y,score_w):
for (x, y, w, h, score) in candidates:
if abs(score_x -x )< 10 and abs(score_y - y) < 10 and 0.8 <= score_w / w <= 1.2:
return True
return False
# 2. 多尺度模板匹配
def match_template_multi_scale(img_gray, template, min_scale=3, max_scale=7.0, step=0.1):
"""在多个尺度下进行模板匹配,返回高相似度区域"""
h, w = template.shape[:2]
candidates = []
# 遍历不同尺度
scale = min_scale
while scale <= max_scale:
# 缩放模板
scaled_h = int(h * scale)
scaled_w = int(w * scale)
if scaled_h < 7 or scaled_w < 7: # 最小不小于原始模板尺寸(避免信息丢失)
scale += step
continue
scaled_template = cv2.resize(template, (scaled_w, scaled_h))
#show_image(scaled_template)
# 模板匹配(TM_CCOEFF_NORMED 输出归一化相关系数,值越接近1越匹配)
result = cv2.matchTemplate(img_gray, scaled_template, cv2.TM_CCOEFF_NORMED)
# 筛选匹配得分高于阈值的区域(阈值可根据图像质量调整)
threshold = 0.7
locations = np.where(result >= threshold)
for pt in zip(*locations[::-1]): # (x, y) 坐标
# 记录匹配区域(左上角坐标、宽高、得分)
score = result[pt[1], pt[0]]
if not exist_rect(candidates,pt[0], pt[1], scaled_w):
candidates.append((pt[0], pt[1], scaled_w, scaled_h, score))
scale += step
# 按得分排序,保留前N个高得分区域(去重)
candidates.sort(key=lambda x: x[4], reverse=True)
return candidates[:10] # 取前10个候选
# 3. 验证候选区域是否为正方形(定位点应为正方形)
def is_square(candidate, ratio_threshold=0.1):
x, y, w, h, _ = candidate
aspect_ratio = w / h
return 1 - ratio_threshold <= aspect_ratio <= 1 + ratio_threshold
# 4. 主函数:定位二维码的三个定位点
def find_qrcode_position_points(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#增强图像对比度
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
gray_c = clahe.apply(img_gray)
#高斯滤波
gray_c = cv2.GaussianBlur(gray_c, (3, 3), 0) # 二值化(增强黑白对比)
_, img_bin = cv2.threshold(gray_c, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_CLOSE, kernel)
#show_image(img_bin)
# 创建模板
template = create_qrcode_template()
# 多尺度匹配
candidates = match_template_multi_scale(img_bin, template)
# 筛选正方形候选区域
valid_candidates = [c for c in candidates if is_square(c)]
# 绘制结果
result_img = img.copy()
for (x, y, w, h, score) in valid_candidates[:3]: # 取前3个最可能的定位点
cv2.rectangle(result_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(result_img, f"{score:.2f}", (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
# range_img = result_img[y: y +h, x : x+w]
# show_image(range_img)
return result_img, valid_candidates[:3]
# 测试
def main():
folder_path = './imgs9'
#模板定位点信息
#bar_range = (1377, 138, 1536, 293)
bar_range = (152, 192, 352, 350)
paper_num = 0
error_paper_num = 0
less_than_3 = 0
# 递归遍历文件夹
for root, dirs, files in os.walk(folder_path):
for filename in files:
if filename.lower().endswith('.jpg'):
paper_num += 1
file_path = os.path.join(root, filename)
ori_img = cv2.imread(file_path, cv2.IMREAD_ANYCOLOR) # 目标图像
if len(ori_img.shape) == 3 and ori_img.shape[2] == 3:
gray_img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
elif len(ori_img.shape) == 2:
gray_img = ori_img.copy()
else:
print('error: bad image')
return
ori_bar_area = ori_img[bar_range[1]:bar_range[3], bar_range[0]:bar_range[2]]
# height, width = ori_bar_area.shape[:2]
# if height > 300 or width > 300:
# # 缩小图像:用INTER_AREA
# resized = cv2.resize(ori_bar_area, (300, 300), interpolation=cv2.INTER_AREA)
# else:
# # 放大图像:用INTER_CUBIC(更清晰)
# resized = cv2.resize(ori_bar_area, (300, 300), interpolation=cv2.INTER_CUBIC)
result_img, points = find_qrcode_position_points(ori_bar_area)
if len(points) > 0:
print('识别到特征点个数:' , len(points))
if(len(points)) < 2:
less_than_3 += 1
show_image(result_img)
#show_image(result_img)
pass
else:
error_paper_num += 1
# show_image(result_img)
# show_image(ori_img)
print('识别总份数: ', paper_num)
print('特征点低于3的份数 ', less_than_3)
print('未识别到二维码份数: ', error_paper_num)
print('二维码识别率: ', str(1 - error_paper_num / paper_num))
print()
# cv2.imshow("QR Code Position Points", result_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
if __name__ == "__main__":
main()原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。