当前位置: 代码网 > it编程>前端脚本>Python > Python实现识别图像中的二维码并纠正倾斜姿态

Python实现识别图像中的二维码并纠正倾斜姿态

2026年03月03日 Python 我要评论
该程序实现二维码检测与倾斜纠正功能,主要包含以下功能:检测图像中的二维码区域;纠正倾斜的二维码图像;识别二维码内容程序提供两种检测方法:基于opencv的qrcodedetector和基于轮廓分析的备

该程序实现二维码检测与倾斜纠正功能,主要包含以下功能:

  • 检测图像中的二维码区域;
  • 纠正倾斜的二维码图像;
  • 识别二维码内容

程序提供两种检测方法:基于opencv的qrcodedetector和基于轮廓分析的备用方法。通过透 视变换纠正倾斜二维码,并提供多种图像增强方法提高识别率。支持直接识别、纠正后识别和增强后识别三种处理方式,可输出识别结果和保存纠正后的图像。

识别效果如下:

实现代码如下: 

"""
二维码识别与倾斜纠正程序
功能:
1. 检测图像中的二维码
2. 纠正倾斜姿态
3. 识别二维码内容
"""

import cv2
import numpy as np
import pyzbar.pyzbar as pyzbar
from pyzbar.pyzbar import zbarsymbol


class qrcodedetector:
    """二维码检测与纠正类"""
    
    def __init__(self):
        self.debug = false
    
    def detect_qr_corners(self, image):
        """
        检测二维码的四个角点
        
        args:
            image: 输入图像 (bgr格式)
            
        returns:
            corners_list: 检测到的角点列表,每个元素是4个角点的数组
        """
        # 转换为灰度图
        gray = cv2.cvtcolor(image, cv2.color_bgr2gray)
        
        # 使用qrcodedetector检测二维码
        qr_detector = cv2.qrcodedetector()
        
        # 检测并解码
        retval, decoded_info, points, straight_qrcode = qr_detector.detectanddecodemulti(gray)
        
        corners_list = []
        if retval and points is not none:
            for i, pts in enumerate(points):
                if pts is not none:
                    corners = pts.reshape(4, 2).astype(np.float32)
                    corners_list.append(corners)
        
        return corners_list
    
    def detect_qr_by_contours(self, image):
        """
        使用轮廓检测方法找到二维码区域
        
        args:
            image: 输入图像
            
        returns:
            corners_list: 检测到的角点列表
        """
        gray = cv2.cvtcolor(image, cv2.color_bgr2gray)
        
        # 自适应阈值处理
        thresh = cv2.adaptivethreshold(gray, 255, cv2.adaptive_thresh_gaussian_c, 
                                       cv2.thresh_binary, 51, 10)
        
        # 形态学操作
        kernel = np.ones(5, np.uint8)
        morph = cv2.morphologyex(thresh, cv2.morph_close, kernel)
        
        # 查找轮廓
        contours, _ = cv2.findcontours(morph, cv2.retr_tree, cv2.chain_approx_simple)
        
        corners_list = []
        
        for contour in contours:
            # 计算轮廓面积
            area = cv2.contourarea(contour)
            if area < 1000:  # 过滤太小的区域
                continue
            
            # 多边形逼近
            peri = cv2.arclength(contour, true)
            approx = cv2.approxpolydp(contour, 0.02 * peri, true)
            
            # 寻找四边形
            if len(approx) == 4:
                corners = approx.reshape(4, 2).astype(np.float32)
                
                # 检查是否是正方形/矩形(二维码形状)
                angles = self._calculate_angles(corners)
                if all(70 < angle < 110 for angle in angles):
                    corners_list.append(self._order_points(corners))
        
        return corners_list
    
    def _calculate_angles(self, corners):
        """计算四边形的内角"""
        angles = []
        n = len(corners)
        for i in range(n):
            p1 = corners[(i - 1) % n]
            p2 = corners[i]
            p3 = corners[(i + 1) % n]
            
            v1 = p1 - p2
            v2 = p3 - p2
            
            angle = np.arctan2(v2[1], v2[0]) - np.arctan2(v1[1], v1[0])
            angle = np.degrees(angle)
            if angle < 0:
                angle += 360
            if angle > 180:
                angle = 360 - angle
            angles.append(angle)
        return angles
    
    def _order_points(self, pts):
        """
        对四个角点进行排序:左上、右上、右下、左下
        """
        rect = np.zeros((4, 2), dtype=np.float32)
        
        # 计算每个点的坐标和
        s = pts.sum(axis=1)
        rect[0] = pts[np.argmin(s)]  # 左上
        rect[2] = pts[np.argmax(s)]  # 右下
        
        # 计算差值
        diff = np.diff(pts, axis=1)
        rect[1] = pts[np.argmin(diff)]  # 右上
        rect[3] = pts[np.argmax(diff)]  # 左下
        
        return rect
    
    def correct_perspective(self, image, corners, output_size=none):
        """
        纠正透 视变换,将倾斜的二维码转正
        
        args:
            image: 输入图像
            corners: 四个角点 [左上, 右上, 右下, 左下]
            output_size: 输出图像大小 (宽, 高),默认自动计算
            
        returns:
            corrected_image: 纠正后的图像
        """
        # 确保角点顺序正确
        ordered_corners = self._order_points(corners)
        
        # 计算输出尺寸
        if output_size is none:
            # 根据角点计算宽度和高度
            width_top = np.linalg.norm(ordered_corners[1] - ordered_corners[0])
            width_bottom = np.linalg.norm(ordered_corners[2] - ordered_corners[3])
            width = int(max(width_top, width_bottom))
            
            height_left = np.linalg.norm(ordered_corners[3] - ordered_corners[0])
            height_right = np.linalg.norm(ordered_corners[2] - ordered_corners[1])
            height = int(max(height_left, height_right))
            
            output_size = (width, height)
        
        # 定义目标点
        dst_points = np.array([
            [0, 0],
            [output_size[0] - 1, 0],
            [output_size[0] - 1, output_size[1] - 1],
            [0, output_size[1] - 1]
        ], dtype=np.float32)
        
        # 计算透视变换矩阵
        matrix = cv2.getperspectivetransform(ordered_corners, dst_points)
        
        # 应用透 视变换
        corrected = cv2.warpperspective(image, matrix, output_size)
        
        return corrected
    
    def decode_qr(self, image):
        """
        解码二维码
        
        args:
            image: 输入图像
            
        returns:
            results: 解码结果列表
        """
        # 转换为灰度图
        if len(image.shape) == 3:
            gray = cv2.cvtcolor(image, cv2.color_bgr2gray)
        else:
            gray = image
        
        # 解码二维码
        decoded_objects = pyzbar.decode(gray, symbols=[zbarsymbol.qrcode])
        
        results = []
        for obj in decoded_objects:
            result = {
                'data': obj.data.decode('utf-8'),
                'type': obj.type,
                'rect': obj.rect,
                'polygon': obj.polygon
            }
            results.append(result)
        
        return results
    
    def process_image(self, image_path, save_corrected=false, output_path=none):
        """
        处理图像:检测、纠正、识别二维码
        
        args:
            image_path: 输入图像路径
            save_corrected: 是否保存纠正后的图像
            output_path: 纠正后图像的保存路径
            
        returns:
            results: 识别结果列表
        """
        # 读取图像
        image = cv2.imread(image_path)
        if image is none:
            raise valueerror(f"无法读取图像: {image_path}")
        
        print(f"处理图像: {image_path}")
        print(f"图像尺寸: {image.shape[1]} x {image.shape[0]}")
        
        results = []
        
        # 首先尝试直接识别(不纠正)
        direct_results = self.decode_qr(image)
        if direct_results:
            print("✓ 直接识别成功!")
            for i, result in enumerate(direct_results):
                print(f"  [{i+1}] 内容: {result['data']}")
                results.append({
                    'method': 'direct',
                    'corrected': false,
                    **result
                })
            return results
        
        print("直接识别失败,尝试检测并纠正倾斜...")
        
        # 尝试检测二维码角点
        corners_list = self.detect_qr_corners(image)
        
        if not corners_list:
            print("使用备用方法检测...")
            corners_list = self.detect_qr_by_contours(image)
        
        if not corners_list:
            print("✗ 未检测到二维码")
            return results
        
        print(f"检测到 {len(corners_list)} 个可能的二维码区域")
        
        # 对每个检测到的区域进行纠正和识别
        for i, corners in enumerate(corners_list):
            print(f"\n处理区域 {i+1}...")
            
            # 纠正透 视
            corrected_image = self.correct_perspective(image, corners)
            
            # 保存纠正后的图像(如果需要)
            if save_corrected and output_path:
                corrected_path = output_path.replace('.png', f'_corrected_{i+1}.png')
                cv2.imwrite(corrected_path, corrected_image)
                print(f"  已保存纠正后的图像: {corrected_path}")
            
            # 尝试解码纠正后的图像
            decoded_results = self.decode_qr(corrected_image)
            
            if decoded_results:
                print(f"  ✓ 纠正后识别成功!")
                for j, result in enumerate(decoded_results):
                    print(f"    [{j+1}] 内容: {result['data']}")
                    results.append({
                        'method': 'corrected',
                        'corrected': true,
                        'region_index': i + 1,
                        **result
                    })
            else:
                print(f"  ✗ 纠正后仍无法识别")
                
                # 尝试对纠正后的图像进行增强
                enhanced = self._enhance_qr_image(corrected_image)
                decoded_results = self.decode_qr(enhanced)
                
                if decoded_results:
                    print(f"  ✓ 增强后识别成功!")
                    for j, result in enumerate(decoded_results):
                        print(f"    [{j+1}] 内容: {result['data']}")
                        results.append({
                            'method': 'enhanced',
                            'corrected': true,
                            'region_index': i + 1,
                            **result
                        })
        
        return results
    
    def _enhance_qr_image(self, image):
        """
        增强二维码图像以提高识别率
        """
        if len(image.shape) == 3:
            gray = cv2.cvtcolor(image, cv2.color_bgr2gray)
        else:
            gray = image.copy()
        
        # 尝试多种预处理方法
        enhanced_images = []
        
        # 1. 自适应阈值
        enhanced_images.append(cv2.adaptivethreshold(gray, 255, 
            cv2.adaptive_thresh_gaussian_c, cv2.thresh_binary, 21, 5))
        
        # 2. otsu阈值
        _, otsu = cv2.threshold(gray, 0, 255, cv2.thresh_binary + cv2.thresh_otsu)
        enhanced_images.append(otsu)
        
        # 3. 直方图均衡化
        enhanced_images.append(cv2.equalizehist(gray))
        
        # 4. 高斯模糊后锐化
        blurred = cv2.gaussianblur(gray, (5, 5), 0)
        enhanced_images.append(cv2.addweighted(gray, 1.5, blurred, -0.5, 0))
        
        # 尝试每种增强方法
        for enhanced in enhanced_images:
            results = pyzbar.decode(enhanced, symbols=[zbarsymbol.qrcode])
            if results:
                return enhanced
        
        return gray


def main():
    """主函数"""
    import argparse
    import os
    
    parser = argparse.argumentparser(description='二维码识别与倾斜纠正工具')
    parser.add_argument('image', help='输入图像路径')
    parser.add_argument('-s', '--save', action='store_true', help='保存纠正后的图像')
    parser.add_argument('-o', '--output', help='输出图像路径')
    parser.add_argument('-d', '--debug', action='store_true', help='启用调试模式')
    
    args = parser.parse_args()
    
    # 检查输入文件是否存在
    if not os.path.exists(args.image):
        print(f"错误: 文件不存在 - {args.image}")
        return
    
    # 创建检测器
    detector = qrcodedetector()
    detector.debug = args.debug
    
    # 处理图像
    try:
        results = detector.process_image(
            args.image, 
            save_corrected=args.save, 
            output_path=args.output
        )
        
        print("\n" + "="*50)
        print("识别结果汇总:")
        print("="*50)
        
        if results:
            for i, result in enumerate(results):
                print(f"\n[{i+1}] 识别方法: {result['method']}")
                print(f"    内容: {result['data']}")
                print(f"    类型: {result['type']}")
                if result.get('corrected'):
                    print(f"    已纠正: 是")
        else:
            print("未能识别任何二维码")
            
    except exception as e:
        print(f"处理过程中出错: {e}")
        import traceback
        traceback.print_exc()


if __name__ == '__main__':
    main()

到此这篇关于python实现识别图像中的二维码并纠正倾斜姿态的文章就介绍到这了,更多相关python识别图像中二维码内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com