You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需cv2.QRCodeDetector/pyzbar的OpenCV二维码检测方案求助

二维码检测实现方案(基于cv2.connectedComponentsWithStats)

核心问题说明

原有方案的核心缺陷是未利用二维码的固有特征做筛选:

  • 基于连通域的方案仅过滤了连通域的宽高比和面积,没有匹配二维码三个定位角点的嵌套方块特征,容易把与二维码连通的周边区域误识别
  • 基于轮廓的方案闭运算迭代次数过高,破坏了二维码的内部纹理结构,导致无法提取有效轮廓

实现思路

优先使用cv2.connectedComponentsWithStats实现,逻辑如下:

  1. 基础预处理:灰度化、高斯模糊去噪、OTSU二值化得到高对比度的二值图
  2. 遍历所有连通域,筛选符合二维码定位角点特征的连通域:定位角点为三层嵌套的黑白黑方块,宽高比接近1,面积在合理范围内
  3. 收集到≥3个符合要求的定位角点后,计算所有定位点的最小外接矩形,即为二维码的准确区域
  4. 自动适配不同尺寸的输入图,无需手动设置缩放比例

完整可运行代码

import cv2
import numpy as np

def detect_qr_code(image_path):
    img = cv2.imread(image_path)
    if img is None:
        return None, None
    og = img.copy()
    # 预处理流程
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5,5), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    # 提取所有连通域
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, 8, cv2.CV_32S)
    markers = []
    
    for i in range(1, num_labels):
        x = stats[i, cv2.CC_STAT_LEFT]
        y = stats[i, cv2.CC_STAT_TOP]
        w = stats[i, cv2.CC_STAT_WIDTH]
        h = stats[i, cv2.CC_STAT_HEIGHT]
        area = stats[i, cv2.CC_STAT_AREA]
        ratio = w / float(h)
        
        # 初筛:接近正方形,面积在合理区间
        if not (0.8 < ratio < 1.2 and 20 < area < 0.2 * img.shape[0] * img.shape[1]):
            continue
        
        # 验证定位角点的嵌套特征:二维码角点为7x7的黑-白-黑嵌套结构
        roi = thresh[y:y+h, x:x+w]
        small_roi = cv2.resize(roi, (7,7), interpolation=cv2.INTER_NEAREST)
        expected_pattern = np.array([
            [255,255,255,255,255,255,255],
            [255,0,0,0,0,0,255],
            [255,0,255,255,255,0,255],
            [255,0,255,255,255,0,255],
            [255,0,255,255,255,0,255],
            [255,0,0,0,0,0,255],
            [255,255,255,255,255,255,255]
        ], dtype=np.uint8)
        # 特征匹配度阈值设为0.7,兼容模糊、变形的二维码
        match_score = np.sum(small_roi == expected_pattern) / 49
        if match_score > 0.7:
            markers.append((x, y, w, h))
    
    # 二维码至少有3个定位角点
    if len(markers) >=3:
        # 提取所有定位点的四角坐标求最小外接矩形
        points = []
        for (x,y,w,h) in markers:
            points.append((x, y))
            points.append((x+w, y))
            points.append((x, y+h))
            points.append((x+w, y+h))
        points = np.array(points)
        x_min, y_min = np.min(points[:,0]), np.min(points[:,1])
        x_max, y_max = np.max(points[:,0]), np.max(points[:,1])
        # 加少量padding避免切到二维码边缘
        pad = 5
        x_min = max(0, x_min - pad)
        y_min = max(0, y_min - pad)
        x_max = min(img.shape[1], x_max + pad)
        y_max = min(img.shape[0], y_max + pad)
        # 绘制检测框、裁剪ROI
        cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0,255,0), 2)
        qr_roi = og[y_min:y_max, x_min:x_max]
        return img, qr_roi
    return img, None

if __name__ == "__main__":
    # 替换为你本地的图片路径即可测试
    res_img, qr_roi = detect_qr_code("你的测试图片路径.jpg")
    if qr_roi is not None:
        cv2.imshow("检测结果", res_img)
        cv2.imshow("二维码区域", qr_roi)
        cv2.imwrite("qr_roi.png", qr_roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

内容的提问来源于stack exchange,提问作者Filipe Almeida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 10:06:03