pay / QR.py
samlax12's picture
Upload 4 files
e318da8 verified
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}")