ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Django+OpenCV+pyzbar构建二维码识别系统:毕设实战指南

Django+OpenCV+pyzbar构建二维码识别系统:毕设实战指南 简介面向本科毕业设计场景的二维码识别系统完整项目包基于PythonDjangoMySQL构建B/S架构适合计算机相关专业学生参考学习。项目除用户与个人资料管理外核心实现了二维码的生成与识别流程通过输入文字内容调用算法生成二维码并可通过扫描读取码内信息完整覆盖从业务逻辑到前端交互的典型实现。压缩包共732个文件容量约9.6MB内含25个Python源码、30个编译后pyc文件、36个JavaScript脚本、19个CSS样式及7个HTML页面同时配有SQL数据库脚本和中文说明文档数量众多的SVG与GIF多用于界面图标和操作演示。压缩包内另附演示视频与说明文档可直观查看系统运行效果并能辅助理解项目部署流程。目前已有266人学习下载适合需快速搭建Django项目、梳理二维码生成识别细节或准备毕业设计答辩的学生。1. 二维码识别系统从毕设选题到能跑的 Django 项目毕设季一到很多同学都在找“既不太难又能讲清楚”的题目二维码识别系统就是这类题目的典型代表。它不涉及深度学习核心就是图像处理加 Web 展示底层用 OpenCV 做预处理、pyzbar 做解码外面套一层 Django 把识别过程做成网页。这份资源里带了源码、说明文档和演示视频解压后照着文档跑起来两天内就能看到一个能上传图片、返回二维码内容的完整项目。适合想用 Python 完成毕业设计、又不想纯抄论文的人也适合刚学完 Django 基础、想找一个综合练手项目的开发者。下面我会按实际开发顺序把技术选型、核心代码、嵌入式 Web 的坑一条条拆开讲。2. 技术栈与架构为什么选 Django OpenCV pyzbar2.1 识别引擎选型pyzbar、OpenCV 与 ZXing 的取舍二维码识别在这个项目里不是难点真正的难点是选对库。Python 生态里常见的方案有三种pyzbar、OpenCV 自带的 QRCodeDetector、以及 Java 生态的 ZXing。我实际测试下来pyzbar 对标准 QR 码的识别成功率最高尤其在二维码有轻微畸变、光照不均匀时表现稳定OpenCV 的 QRCodeDetector 不需要额外装依赖但识别率偏低而且对图片尺寸敏感ZXing 虽然强大但 Python 调用要靠 subprocess 绕一圈毕业设计答辩时不好解释。所以资源里最终选了 pyzbar 做主力OpenCV 负责灰度化和图像增强。方案依赖复杂度识别率适合场景pyzbar需安装 zbar 库高标准二维码、批量识别OpenCV QRCodeDetector低中简单演示、无额外依赖ZXingJava高最高工业级扫描不适合毕设2.2 Django 在毕设里的优势后台管理免费送二维码识别系统如果只做一个脚本答辩时撑不满十分钟。包一层 Django 后项目立刻变成“B/S 架构 数据库存储 后台管理”的完整课题。Django 自带 admin 后台识别记录可以一键查询、删除这部分不用写一行代码。对毕设来说这既是工作量又是亮点。资源里的源码结构是按 Django 实战项目的习惯组织的qr_app放核心业务逻辑templates放前端页面static放样式和上传文件。2.3 环境准备从零搭出可运行环境拿到资源包后第一步是建虚拟环境这一步能省掉后面大量依赖冲突。资源里的说明文档推荐 Python 3.8 到 3.10我测试时用的是 3.10Django 4.1、opencv-python 4.8、pyzbar 0.1.9整套组合没有版本冲突。python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # Linux/Mac pip install django opencv-python pyzbar pillow qrcode这段命令的逻辑是先创建隔离环境再安装五个核心依赖。django提供 Web 框架opencv-python做图像预处理pyzbar负责真实解码pillow用来处理上传的图片格式qrcode用来生成测试用的二维码。如果你只是跑识别不需要生成功能qrcode可以去掉。python manage.py runserver 8000 python manage.py makemigrations qr_app python manage.py migrate运行前先执行迁移命令Django 才能建出数据库表。资源里的演示视频就是按这个顺序录的第一次看到页面出来后整个项目就跑通了。3. 二维码生成与识别核心模块实现与参数调优3.1 用 qrcode 库生成可控的测试二维码写识别系统前要先有测试素材。用qrcode库生成二维码比上网找图方便得多参数可以精确控制方便后续测识别边界。资源里utils/qr_maker.py就是干这个的。import qrcode from qrcode.constants import ERROR_CORRECT_H qr qrcode.QRCode( version5, # 1-40数值越大能存的信息越多 error_correctionERROR_CORRECT_H, # 容错率L 7%, M 15%, Q 25%, H 30% box_size10, # 每个小格子的像素数 border4, # 四周留白宽度单位是格子数 ) qr.add_data(https://example.com/graduation) qr.make(fitTrue) img qr.make_image(fill_colorblack, back_colorwhite) img.save(test_qr.png)这段代码的关键参数有三个。error_correction决定二维码被遮挡或破损后还能不能识别H 档允许 30% 的面积损坏但二维码会变得更密border至少设为 4这是标准要求留白不够很多扫码器根本不认version不手动设、用fitTrue让库自动算是最省心的做法。生成图片后建议在图像里加一些噪点再测试这样才能验证识别模块的鲁棒性。3.2 识别主流程灰度化、解码、结果过滤识别模块的核心逻辑不复杂但每一步预处理都影响最终成功率。资源里的utils/qr_decoder.py写了一个可直接调用的函数输入图片路径返回解码内容列表。import cv2 import numpy as np from pyzbar.pyzbar import decode def decode_qr_from_image(image_path: str) - list: # 直接读成灰度图避免彩色通道干扰 img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) results decode(gray) parsed [] for obj in results: data obj.data.decode(utf-8) # bytes 转字符串 rect obj.rect # 返回二维码位置 (left, top, width, height) parsed.append({ content: data, type: obj.type, # 一般是 QRCODE left: rect.left, top: rect.top, width: rect.width, height: rect.height, }) return parseddecode函数直接返回二维码内容、类型和位置。有几个容易忽略的细节obj.data是 bytes 类型必须解码成字符串obj.rect返回的是识别出的二维码在图片中的矩形区域这个信息在毕设答辩时可以画框展示说服力很强。if __name__ __main__: import json res decode_qr_from_image(test_qr.png) print(json.dumps(res, ensure_asciiFalse, indent2))测试时用ensure_asciiFalse避免中文内容被转义成\uXXXX。如果返回空列表先别怀疑代码多数是生成的二维码 border 不够或图片里二维码占的面积太小往下看避坑章节。4. 接入 Django Web上传、解码与记录入库的完整链路4.1 模型设计识别记录表识别功能跑通后下一步是把图片上传和识别结果存进数据库。资源里qr_app/models.py只设计了一张表字段覆盖了展示需要图片路径、识别出的文本、识别时间。from django.db import models class QRRecord(models.Model): image models.ImageField(upload_toqr_uploads/) # 上传的原始图片 content models.TextField(blankTrue) # 解码后的内容 created_at models.DateTimeField(auto_now_addTrue) # 识别时间 class Meta: ordering [-created_at] def __str__(self): return fQRRecord {self.id}: {self.content[:20]}upload_to指定上传文件保存到MEDIA_ROOT/qr_uploads/下auto_now_addTrue会在记录创建时自动写入当前时间省去手动赋值。这里故意把content设为TextField因为二维码内容可能很长URL 或一段 JSON 都可能超出CharField长度。4.2 视图逻辑把识别函数接进请求响应视图是整条链路的粘合剂。前端上传图片视图函数保存图片文件、调用识别函数、把结果写入数据库最后返回 JSON。资源里的views.py就是按这个顺序写的。import json from django.http import JsonResponse from django.views.decorators.http import require_POST from .models import QRRecord from utils.qr_decoder import decode_qr_from_image require_POST def upload_and_decode(request): upload_file request.FILES.get(qr_image) if not upload_file: return JsonResponse({code: 1, msg: 未收到图片文件}, status400) # 1. 保存上传的图片到数据库记录 record QRRecord(imageupload_file) record.save() # 2. 调用识别核心函数 image_path record.image.path results decode_qr_from_image(image_path) if not results: record.content record.save(update_fields[content]) return JsonResponse({code: 0, msg: 未识别到二维码, data: []}) # 3. 取第一个结果存入数据库返回所有结果 record.content results[0][content] record.save(update_fields[content]) return JsonResponse({code: 0, msg: ok, data: results})request.FILES拿到上传文件后QRRecord(imageupload_file)这一步会自动把图片落盘到MEDIA_ROOT不用手动读写文件。record.image.path返回文件在磁盘上的绝对路径直接传给识别函数。最后update_fields指定只更新content字段避免不必要的写库操作。4.3 模板与路由最小可用的上传页面后端写完后需要一个能提交图片的页面。资源里的templates/upload.html用一个表单搞定不做多余的修饰答辩演示时反而清爽。!DOCTYPE html html head meta charsetutf-8 title二维码识别系统/title /head body h1上传二维码图片/h1 form methodpost enctypemultipart/form-data iduploadForm {% csrf_token %} input typefile nameqr_image acceptimage/* required button typesubmit识别/button /form div idresult/div script const form document.getElementById(uploadForm); form.onsubmit async (e) { e.preventDefault(); const resp await fetch(window.location.href, { method: POST, body: new FormData(form), }); const data await resp.json(); const resultDiv document.getElementById(result); if (data.code 0) { const items data.data.map(item p内容${item.content}br位置(${item.left}, ${item.top})/p ).join(); resultDiv.innerHTML h2识别结果/h2${items}; } else { resultDiv.innerHTML p stylecolor:red${data.msg}/p; } }; /script /body /html这个页面用 fetch 异步提交不刷新整页体验接近真实应用。enctypemultipart/form-data是文件上传表单的必要属性漏掉的话后端收不到文件。路由配置要匹配upload和upload_and_decode两个 URL资源里默认用的是同一个路径POST和GET共用时分流处理就行。from django.urls import path from . import views urlpatterns [ path(upload, views.upload_page, nameupload_page), path(upload, views.upload_and_decode, nameupload_api), ]同一个路径绑定两个视图函数在 Django 里是允许的实际执行时upload_page处理GET请求返回页面upload_and_decode用require_POST拦截POST请求返回 JSON。这种写法比拆两个 URL 更简洁也是资源里的原始设计。5. 踩坑清单识别失败、中文路径与部署翻车记录5.1 现象pyzbar 导入直接报错ImportError: unable to find libzbarWindows 下装完pyzbar一import就报错原因是 pyzbar 依赖 zbar 动态链接库pip 只装了 Python 绑定没有装底层 DLL。解决方式是安装zbar的 Windows 运行库或者直接换成conda install pyzbarconda 会自动把底层库一并装好。我用的方案是去 zbar 官网下载预编译包把 DLL 路径加进系统环境变量重启终端就好了。资源里的说明文档特别标注了这一步但很多同学喜欢跳过环境配置然后卡在这里半小时。5.2 现象识别中文二维码内容全是乱码二维码内容本身是 UTF-8 编码的字节串直接用decode(utf-8)理论上没问题。翻车场景是某些扫码工具生成二维码时用了 GBK 编码或者内容里混合了 URL 编码字符。解决方法是先判断能否按 UTF-8 解码不行再退到 GBK。在识别函数里包一层异常处理就能兼容两类编码。def safe_decode(raw: bytes) - str: try: return raw.decode(utf-8) except UnicodeDecodeError: return raw.decode(gbk, errorsreplace)5.3 现象图片路径含中文或空格时识别失败cv2.imread在 Windows 上有个老毛病路径含中文时返回None图片都没读进来后面全白做。原因是 OpenCV 的 C 底层没有正确处理系统编码。解决方式是用numpy从文件字节流读取再转成 OpenCV 的数组格式。import numpy as np def imread_chinese(path: str): data np.fromfile(path, dtypenp.uint8) return cv2.imdecode(data, cv2.IMREAD_COLOR)用np.fromfile读文件内容到内存cv2.imdecode直接从内存解码图片绕开了文件路径编码问题。我在资源里把decode_qr_from_image内部改成了这个读取方式默认兼容中文路径。5.4 现象上传到 Django 后能识别但结果页图片不显示数据库记录里image.url路径是对的浏览器就是 404。多半是settings.py里MEDIA_URL和MEDIA_ROOT没有配对或者开发服务器没挂媒体路由。Django 默认不提供媒体文件服务需要在urls.py里手动加一行。from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这句只对开发模式有效线上部署时static()会被忽略需要交给 Nginx 处理媒体目录。毕设演示用开发服务器的话加上这句就能显示历史识别记录里的图片。5.5 现象识别结果为空但图片用手机扫码能识别同一张图片手机扫得出来、程序识别不出来问题几乎都出在图像预处理层面。pyzbar 对图像分辨率有要求二维码区域太小、线条太密都会导致解码失败。常见的提升手段是先放大图像再做二值化增强对比度。在调用decode前加两步预处理成功率能提升一个档次。gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray cv2.resize(gray, None, fx1.5, fy1.5, interpolationcv2.INTER_CUBIC) _, thresh cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) results decode(thresh)fx1.5把图像长宽各放大 1.5 倍INTER_CUBIC双三次插值适合放大后保持边缘清晰。二值化用 Otsu 自适应阈值免去手动调阈值的麻烦。放大倍数不是越大越好超过 3 倍后识别时间明显变长收益却很小。6. 从识别到批量把核心逻辑封装成命令行工具的进阶做法如果你不止想要网页识别比如要批量处理几百张二维码图片、或者在答辩时演示“同时识别多个二维码的能力”可以再写一个 CLI 工具覆盖这个场景。资源里附加的tools/batch_decode.py扫描指定目录下所有图片输出 CSV 结果。import os import sys import csv from utils.qr_decoder import decode_qr_from_image def batch_decode(input_dir: str, output_csv: str): rows [] for filename in os.listdir(input_dir): if not filename.lower().endswith((.png, .jpg, .jpeg, .bmp)): continue path os.path.join(input_dir, filename) results decode_qr_from_image(path) for r in results: rows.append({file: filename, content: r[content]}) if not results: rows.append({file: filename, content: }) with open(output_csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[file, content]) writer.writeheader() writer.writerows(rows) if __name__ __main__: input_dir sys.argv[1] if len(sys.argv) 1 else uploads output_csv sys.argv[2] if len(sys.argv) 2 else result.csv batch_decode(input_dir, output_csv)这里用utf-8-sig写 CSV 是故意的Excel 打开 UTF-8 文件默认会乱码加 BOM 头后 Excel 能直接显示中文。批量识别是单线程遍历文件数量多时可以改用concurrent.futures.ThreadPoolExecutor但 pyzbar 底层有锁多线程收益不如多进程大。想压榨性能的话用multiprocessing.Pool按文件数分片。答辩时演示批量识别比单张上传更有说服力。我还遇到过演示现场图片光线差、识别失败的尴尬后来学乖了提前用脚本批量生成二十张干扰程度不同的二维码图片放在 uploads 目录里演示时直接跑批量命令输出 20 行 CSV 数据过程稳定、结果直观。从那以后我每次做识别类项目都会强制走一遍“中文路径 批量目录 无边框模糊图”三件套测试确认识别函数不会在任何一张图上静默失败。这套习惯也建议你保留——能提前暴露 80% 的现场翻车问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表