File size: 9,221 Bytes
8f92a2e
 
 
e318da8
8f92a2e
 
 
 
 
 
 
 
 
 
e318da8
8f92a2e
e318da8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f92a2e
 
 
 
 
 
 
 
 
 
 
 
e318da8
 
8f92a2e
 
e318da8
8f92a2e
 
 
 
 
 
e318da8
 
8f92a2e
e318da8
 
 
8f92a2e
e318da8
 
 
8f92a2e
e318da8
 
 
 
 
 
 
 
 
8f92a2e
e318da8
8f92a2e
e318da8
8f92a2e
e318da8
 
 
 
 
 
 
 
 
8f92a2e
e318da8
 
 
 
 
 
 
 
 
 
 
 
 
8f92a2e
e318da8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f92a2e
 
 
 
 
 
e318da8
8f92a2e
e318da8
8f92a2e
 
e318da8
8f92a2e
e318da8
 
8f92a2e
e318da8
 
 
 
8f92a2e
 
e318da8
8f92a2e
 
 
 
 
 
 
e318da8
8f92a2e
e318da8
 
 
 
 
 
 
8f92a2e
 
e318da8
 
 
 
 
 
 
 
 
8f92a2e
e318da8
 
 
 
 
8f92a2e
 
 
e318da8
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import cv2
import numpy as np
from pyzbar.pyzbar import decode
from PIL import Image, ImageDraw
import requests
from io import BytesIO
import os
import sys

# 设置默认编码为UTF-8
if sys.platform.startswith('win'):
    import locale
    locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')

def detect_and_extract_circular_qr(image):
    """

    检测并提取圆形二维码区域

    

    参数:

        image: numpy数组格式的图像

    

    返回:

        处理后的图像列表

    """
    processed_images = []
    
    # 转换为灰度图
    if len(image.shape) == 3:
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    else:
        gray = image.copy()
    
    # 使用霍夫圆检测
    circles = cv2.HoughCircles(
        gray,
        cv2.HOUGH_GRADIENT,
        dp=1,
        minDist=50,
        param1=50,
        param2=30,
        minRadius=50,
        maxRadius=500
    )
    
    if circles is not None:
        circles = np.uint16(np.around(circles))
        
        for circle in circles[0, :]:
            x, y, r = circle
            
            # 创建掩码
            mask = np.zeros(gray.shape, np.uint8)
            cv2.circle(mask, (x, y), r, 255, -1)
            
            # 提取圆形区域
            circular_region = cv2.bitwise_and(image, image, mask=mask)
            
            # 裁剪到包含圆形的最小矩形
            x_min = max(0, x - r)
            x_max = min(image.shape[1], x + r)
            y_min = max(0, y - r)
            y_max = min(image.shape[0], y + r)
            
            cropped = circular_region[y_min:y_max, x_min:x_max]
            processed_images.append(cropped)
            
            # 同时添加将圆形区域转换为方形的版本
            square_size = 2 * r
            square_region = np.zeros((square_size, square_size, 3), dtype=np.uint8)
            square_region[:] = 255  # 白色背景
            
            # 将圆形内容复制到方形图像中心
            if cropped.shape[0] > 0 and cropped.shape[1] > 0:
                y_offset = (square_size - cropped.shape[0]) // 2
                x_offset = (square_size - cropped.shape[1]) // 2
                
                if y_offset >= 0 and x_offset >= 0:
                    square_region[y_offset:y_offset+cropped.shape[0], 
                                x_offset:x_offset+cropped.shape[1]] = cropped
                    processed_images.append(square_region)
    
    return processed_images

def parse_qr_code_advanced(file_path):
    """

    高级二维码解析函数,支持各种形状的二维码

    

    参数:

        file_path: 二维码图片的本地路径

    

    返回:

        解析出的链接或文本

    """
    try:
        # 检查文件是否存在
        if not os.path.exists(file_path):
            print(f"错误:文件不存在 - {file_path}")
            return None
        
        # 使用PIL读取图片
        pil_image = Image.open(file_path)
        
        # 转换为RGB格式
        if pil_image.mode != 'RGB':
            pil_image = pil_image.convert('RGB')
        
        # 转换为numpy数组
        image_array = np.array(pil_image)
        
        # 准备多个版本的图像进行尝试
        images_to_try = [image_array]
        
        # 检测并提取圆形二维码
        circular_extracts = detect_and_extract_circular_qr(image_array)
        images_to_try.extend(circular_extracts)
        
        # 尝试各种预处理方法
        for idx, img in enumerate(images_to_try):
            print(f"尝试方法 {idx + 1}/{len(images_to_try)}...")
            
            # 直接尝试解码
            decoded_objects = decode(img)
            if decoded_objects:
                print(f"✓ 方法 {idx + 1} 成功解析!")
                return decoded_objects[0].data.decode('utf-8')
            
            # 转换为灰度图并尝试多种预处理
            if len(img.shape) == 3:
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            else:
                gray = img
            
            # 预处理方法列表
            preprocessing_methods = [
                # 简单二值化
                lambda g: cv2.threshold(g, 127, 255, cv2.THRESH_BINARY)[1],
                # OTSU阈值
                lambda g: cv2.threshold(g, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],
                # 自适应阈值
                lambda g: cv2.adaptiveThreshold(g, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                              cv2.THRESH_BINARY, 11, 2),
                # 反转颜色
                lambda g: cv2.bitwise_not(g),
                # 增强对比度
                lambda g: cv2.convertScaleAbs(g, alpha=2.0, beta=0),
                # 形态学操作 - 闭运算
                lambda g: cv2.morphologyEx(
                    cv2.threshold(g, 127, 255, cv2.THRESH_BINARY)[1],
                    cv2.MORPH_CLOSE,
                    cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
                ),
                # 高斯模糊后二值化
                lambda g: cv2.threshold(
                    cv2.GaussianBlur(g, (5, 5), 0),
                    127, 255, cv2.THRESH_BINARY
                )[1]
            ]
            
            for j, preprocess in enumerate(preprocessing_methods):
                try:
                    processed = preprocess(gray)
                    decoded_objects = decode(processed)
                    if decoded_objects:
                        print(f"✓ 方法 {idx + 1} 的预处理 {j + 1} 成功解析!")
                        return decoded_objects[0].data.decode('utf-8')
                except:
                    continue
        
        # 如果所有方法都失败,尝试更激进的方法
        print("尝试更多高级处理方法...")
        
        # 尝试旋转图像
        for angle in [90, 180, 270]:
            rotated = cv2.rotate(image_array, angle // 90 - 1)
            decoded_objects = decode(rotated)
            if decoded_objects:
                print(f"✓ 旋转 {angle}° 后成功解析!")
                return decoded_objects[0].data.decode('utf-8')
        
        # 尝试缩放图像
        for scale in [0.5, 0.75, 1.5, 2.0]:
            new_size = (int(image_array.shape[1] * scale), int(image_array.shape[0] * scale))
            resized = cv2.resize(image_array, new_size, interpolation=cv2.INTER_CUBIC)
            decoded_objects = decode(resized)
            if decoded_objects:
                print(f"✓ 缩放 {scale}x 后成功解析!")
                return decoded_objects[0].data.decode('utf-8')
        
        print("✗ 所有方法都无法解析此二维码")
        return None
        
    except Exception as e:
        print(f"解析二维码时出错: {e}")
        import traceback
        traceback.print_exc()
        return None

def batch_process_directory(directory_path=None):
    """

    批量处理目录下的所有图片文件

    

    参数:

        directory_path: 目录路径,默认为当前目录

    """
    if directory_path is None:
        directory_path = os.getcwd()
    
    image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp']
    
    print(f"扫描目录: {directory_path}")
    print("=" * 60)
    
    found_images = []
    for file in os.listdir(directory_path):
        if any(file.lower().endswith(ext) for ext in image_extensions):
            found_images.append(file)
    
    if not found_images:
        print("未找到图片文件")
        return
    
    print(f"找到 {len(found_images)} 个图片文件\n")
    
    success_count = 0
    for i, img_file in enumerate(found_images, 1):
        print(f"\n[{i}/{len(found_images)}] 正在处理: {img_file}")
        print("-" * 40)
        
        full_path = os.path.join(directory_path, img_file)
        result = parse_qr_code_advanced(full_path)
        
        if result:
            success_count += 1
            print(f"\n✅ 成功解析!")
            print(f"内容: {result}")
            
            # 如果是微信支付链接,特别标注
            if result.startswith('wxp://'):
                print("类型: 微信支付二维码")
            elif 'weixin://' in result or 'wx.tenpay.com' in result:
                print("类型: 微信相关链接")
        else:
            print(f"\n❌ 无法解析此文件")
        
        print("=" * 60)
    
    print(f"\n\n总结: 成功解析 {success_count}/{len(found_images)} 个文件")

# 使用示例
if __name__ == "__main__":
    print("=== 增强版二维码解析工具 ===")
    print("支持圆形、方形等各种形状的二维码\n")
    
    # 批量处理当前目录
    batch_process_directory()
    
    # 或者处理单个文件
    # result = parse_qr_code_advanced("your_circular_qr_code.jpg")
    # if result:
    #     print(f"解析结果: {result}")