简介:这是一套面向计算机相关专业在校学生与教师的Python水果识别系统源码,采用PyQt5搭建图形界面、OpenCV完成图像处理,适合作为毕业设计、课程设计或大作业的参考实现。项目围绕水果图像识别这一典型场景,涵盖界面交互、图像预处理、识别逻辑等模块,代码结构清晰且附带详细注释,便于理解与二次开发。压缩包共55个文件,包含16个py源码、20个png与2个jpg示例图片、2个ui界面文件及qrc资源文件,另有pyc缓存与md、txt说明文档,整体约4.63MB,解压后即可查看完整工程。目前已有439人学习下载,读者可从中获取可运行的识别流程、界面与算法分离的组织方式,以及图像去模糊、裁剪等处理思路,适合入门进阶或作为项目初期立项演示,也可在此基础上DIY其他识别功能。
1. 从一份能跑通的 PyQt5+OpenCV 水果识别源码说起
课程设计周最怕的不是不会写代码,而是 deadline 前三天还在纠结环境。这份基于 PyQt5+OpenCV+Python 的水果识别系统源码,恰好卡在一个很舒服的位置:它不依赖深度学习框架,不需要 GPU,装完 OpenCV 和 PyQt5 就能跑,识别逻辑用传统图像处理就能讲清楚。压缩包里包含完整的 GUI 界面文件、图像处理模块、百度 AI 调用脚本以及多张测试图片,注释密度足够让新手看懂每一步在做什么。适合计算机相关专业的课程大作业、毕业设计初期原型,或者想快速搭一个带界面的图像识别 demo 的人。我拿到包之后先跑了一遍主流程,又拆了各个模块的调用关系,下面把能复现的路径和容易翻车的地方一次讲透。
2. 环境搭建与依赖安装:把 PyQt5 和 OpenCV 装进同一个 Python 3.8
2.1 为什么锁定 Python 3.8 而不是追新版本
源码的__pycache__目录里躺着baidufruit.cpython-38.pyc、fruit_recognition.cpython-38.pyc这些文件,说明作者验证时用的是 Python 3.8。这不是随便选的版本——PyQt5 在 3.8 上的 wheel 包最齐全,OpenCV 的cv2模块在这个版本下和 Qt 的兼容性也最稳。如果你用 3.11 或 3.12,pip install pyqt5大概率能装上,但运行 GUI 时可能遇到qt.qpa.plugin: Could not load the Qt platform plugin "windows"这类玄学报错。我一般会直接建一个 3.8 的虚拟环境,把版本锁死,省得后面来回折腾。
# 创建 Python 3.8 虚拟环境(假设本机已装 3.8) python -m venv fruit_env # Windows 激活 fruit_env\Scripts\activate # macOS / Linux 激活 source fruit_env/bin/activate # 升级 pip 避免安装旧包时解析失败 python -m pip install --upgrade pip虚拟环境的好处是隔离:这个项目用的 OpenCV 版本可能和你其他项目的需求冲突,单独放一个环境里,删掉也不影响全局。激活之后命令行前面会出现(fruit_env)标识,后续所有操作都在这个环境里做。
2.2 安装 PyQt5、OpenCV 和配套依赖
源码里用到了cv2、PyQt5、numpy,可能还涉及Pillow做图像格式转换。安装命令不复杂,但顺序和版本有讲究。
# 先装 numpy,OpenCV 依赖它 pip install numpy==1.24.4 # 安装 OpenCV 主包(包含 contrib 模块) pip install opencv-python==4.8.1.78 # 安装 PyQt5 和 Qt Designer 工具 pip install PyQt5==5.15.9 PyQt5-tools==5.15.9.3.3 # 可选:如果代码里有 PIL 相关调用 pip install Pillow这里解释几个参数选择。numpy锁 1.24.4 是因为更高版本在 Python 3.8 上可能没有预编译 wheel,会触发源码编译,Windows 下大概率报错。opencv-python选 4.8.1 是兼顾稳定性和函数完整性,cv2.cvtColor、cv2.threshold、cv2.findContours这些本项目用到的函数都有。PyQt5-tools里带designer.exe,如果你想改.ui文件会用得上。
安装完成后验证一下:
# verify_env.py import cv2 import numpy as np from PyQt5.QtWidgets import QApplication import sys print("OpenCV version:", cv2.__version__) print("NumPy version:", np.__version__) print("PyQt5 import OK") # 快速测试 OpenCV 能否创建窗口(无显示环境下会报错,属正常) try: img = np.zeros((100, 100, 3), dtype=np.uint8) cv2.imshow("test", img) cv2.waitKey(1) cv2.destroyAllWindows() print("OpenCV GUI OK") except Exception as e: print("OpenCV GUI warning:", e)这段代码先打印版本号确认安装成功,再用一个全黑图像测试 OpenCV 的 GUI 模块。如果cv2.imshow报错但版本号正常,说明是显示环境问题,不影响后续识别逻辑。
提示:项目路径和文件名不要用中文,解压后建议重命名为
fruit_recognition这样的纯英文目录。OpenCV 的imread在 Windows 下对中文路径支持很差,读图返回None是高频翻车点。
3. 源码结构拆解:从 main_image_process 到 fruit_recognition 的调用链
3.1 核心模块分工与数据流向
解压后文件不少,但真正跑起来的主线只有几条。我按调用关系理了一遍:
| 文件 | 作用 | 关键函数/类 |
|---|---|---|
fruit_recognition.py | GUI 主入口,启动 Qt 应用 | MainWindow、main() |
main_image_process.py | 图像处理核心逻辑 | 预处理、分割、特征提取 |
baidufruit.py | 百度 AI 接口调用(可选) | 在线识别兜底 |
switch.py | 界面切换或模式选择 | 信号槽连接 |
deblur.py | 图像去模糊预处理 | 拉普拉斯算子 |
text.py/text2.py | 文本标签或结果展示 | 结果格式化 |
pics_ui_rc.py | Qt 资源文件编译产物 | 图标、图片资源 |
fruit_recognition.ui | Qt Designer 界面定义 | 可重新编辑 |
数据流向大致是:用户通过 GUI 选择图片 →main_image_process.py读取并预处理 → 分割出候选区域 → 提取颜色/形状特征 → 匹配水果类别 → 结果回传 GUI 显示。baidufruit.py是另一条路,把图片传到百度 AI 做在线识别,适合本地特征匹配失败时兜底。
3.2 本地识别流程的关键代码段
main_image_process.py是重点。我抽出了核心处理链,补上注释方便理解:
# main_image_process.py 核心逻辑还原 import cv2 import numpy as np def preprocess_image(image_path): """读取图片并做基础预处理""" # 用 imdecode 兼容中文路径的读法 img = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), cv2.IMREAD_COLOR) if img is None: raise ValueError(f"无法读取图片: {image_path}") # 统一缩放到 500x500 以内,减少计算量 h, w = img.shape[:2] scale = 500 / max(h, w) if scale < 1: img = cv2.resize(img, (int(w*scale), int(h*scale))) # 高斯模糊降噪,kernel 选 5x5 是经验值 blurred = cv2.GaussianBlur(img, (5, 5), 0) return img, blurred def segment_fruit(blurred): """基于 HSV 颜色空间分割水果区域""" hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV) # 这里以橙色水果为例,实际代码里会遍历多个颜色区间 lower_orange = np.array([5, 100, 100]) upper_orange = np.array([25, 255, 255]) mask = cv2.inRange(hsv, lower_orange, upper_orange) # 形态学操作去噪 kernel = np.ones((5, 5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) return mask def extract_features(mask, img): """提取轮廓和颜色特征""" contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) results = [] for cnt in contours: area = cv2.contourArea(cnt) if area < 500: # 过滤小噪点 continue # 计算外接矩形和圆形度 x, y, w, h = cv2.boundingRect(cnt) perimeter = cv2.arcLength(cnt, True) circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0 results.append({ "bbox": (x, y, w, h), "area": area, "circularity": circularity }) return resultspreprocess_image里用np.fromfile+cv2.imdecode替代直接cv2.imread,这是处理中文路径的标准做法。缩放阈值 500 是平衡速度和精度的经验值,太小会丢细节,太大拖慢 GUI 响应。segment_fruit里的 HSV 区间需要根据水果种类调整,源码里应该有多组阈值对应苹果、香蕉、橙子等。extract_features算圆形度是为了区分形状——苹果偏圆、香蕉细长,圆形度差异明显。
3.3 GUI 与处理逻辑的对接方式
fruit_recognition.py里用 PyQt5 的信号槽把按钮点击和图像处理函数连起来。典型写法:
# fruit_recognition.py 中的信号槽连接示意 from PyQt5.QtWidgets import QMainWindow, QFileDialog from PyQt5.QtGui import QPixmap from main_image_process import preprocess_image, segment_fruit, extract_features class MainWindow(QMainWindow): def __init__(self): super().__init__() # ... 加载 ui 文件 ... self.pushButton.clicked.connect(self.open_image) self.pushButton_2.clicked.connect(self.run_recognition) self.current_image_path = None def open_image(self): path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "Images (*.png *.jpg *.jpeg)") if path: self.current_image_path = path pixmap = QPixmap(path) self.label.setPixmap(pixmap.scaled(400, 400)) def run_recognition(self): if not self.current_image_path: return img, blurred = preprocess_image(self.current_image_path) mask = segment_fruit(blurred) features = extract_features(mask, img) # 根据 features 判断类别并更新 label self.label_result.setText(f"检测到 {len(features)} 个候选区域")这段代码展示了 GUI 和算法模块的解耦:界面只负责拿路径、显示图片,处理逻辑全在main_image_process里。改识别算法时不用动界面代码,改界面时也不影响算法。QFileDialog返回的路径直接传给处理函数,中间没有额外转换。
注意:如果你修改了
.ui文件,需要用pyuic5 fruit_recognition.ui -o fruit_recognition.py重新生成 Python 代码,或者用pics_ui_rc.py对应的pyrcc5编译.qrc资源文件。直接改.ui不重新编译,界面不会变。
4. 避坑与排查:跑不起来时先查这五个地方
4.1 现象:ModuleNotFoundError: No module named 'cv2'
原因通常是装错了包名。pip install cv2是无效的,正确的包名是opencv-python。另一个可能是虚拟环境没激活,装到了全局 Python 里。解决:先pip list | grep opencv确认,如果没有就pip install opencv-python。如果显示已安装但 import 仍报错,检查当前 Python 解释器路径是否和 pip 一致,用python -c "import sys; print(sys.executable)"和pip -V对比。
4.2 现象:GUI 窗口一闪而过或报 Qt platform plugin 错误
这是 PyQt5 在 Windows 上的经典问题。原因一般是 PyQt5 版本和 Qt 库不匹配,或者环境变量QT_QPA_PLATFORM_PLUGIN_PATH指向了错误目录。解决:先卸载重装pip uninstall PyQt5 PyQt5-tools再pip install PyQt5==5.15.9。如果还不行,在代码最前面加import os; os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = ''清空干扰。Linux 下如果没装libxcb-xinerama0也会报类似错误,sudo apt install libxcb-xinerama0即可。
4.3 现象:imread 返回 None,图片读不进来
九成是路径含中文或空格。OpenCV 的imread在 Windows 下对非 ASCII 路径支持极差。解决:用cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)替代。另外检查图片是否真的存在,os.path.exists(path)先判断一下。如果路径是相对路径,确认工作目录是否正确,GUI 里选图后拿到的是绝对路径,一般没问题。
4.4 现象:识别结果全是同一个类别或没有输出
先看segment_fruit里的 HSV 阈值是否匹配你的测试图片。源码自带的source_imagedata里有apple.png、banana.png等标准图,用这些图测试应该正常。如果你换成自己拍的照片,光照和背景差异会导致颜色分割失效。解决:用cv2.imshow把mask显示出来,看分割区域是否覆盖了水果。如果 mask 全黑,调低lower的 S/V 下限;如果 mask 全白,调高upper上限。圆形度阈值也需要根据实际轮廓调整。
4.5 现象:百度 AI 接口调用返回权限错误
baidufruit.py里通常需要填API_KEY和SECRET_KEY。如果你没有百度 AI 账号,这部分功能直接跳过,不影响本地识别主流程。如果要用,去百度 AI 开放平台申请图像识别应用,把 key 填进代码对应变量。注意免费额度有限,调试时别频繁调用。报{'error_code': 110, 'error_msg': 'Access token invalid'}说明 token 过期,重新获取即可。
5. 二次开发与效果验证:换水果类别和调参的实操路径
5.1 增加新水果类别的完整步骤
源码默认覆盖了苹果、香蕉、橙子、猕猴桃、山竹、西瓜这几类。如果你想加一个“草莓”,按下面几步走。
第一步,在source_imagedata目录放几张草莓的测试图,命名如strawberry.png。第二步,在main_image_process.py里找到颜色阈值定义的地方,增加草莓的 HSV 区间。草莓偏红,参考区间:
# 在颜色阈值字典里增加草莓条目 fruit_color_ranges = { "apple": [(np.array([0, 100, 100]), np.array([10, 255, 255]))], "banana": [(np.array([20, 100, 100]), np.array([35, 255, 255]))], "orange": [(np.array([5, 100, 100]), np.array([25, 255, 255]))], "strawberry": [ (np.array([0, 120, 70]), np.array([10, 255, 255])), (np.array([170, 120, 70]), np.array([180, 255, 255])) ], }草莓的红色在 HSV 里跨了 0 和 180 两个区间,所以要写两组阈值。第三步,在分类逻辑里根据circularity和area加判断:草莓通常比苹果小,圆形度接近 1。第四步,在 GUI 的结果标签里加上“草莓”的显示分支。改完用testimg.png和新增的草莓图各跑一遍,确认不会把苹果误判成草莓。
5.2 用混淆矩阵验证识别准确率
光靠肉眼看几张图不够,我一般会写个小脚本批量测试并统计。把source_imagedata里的图片按类别分好,跑一遍识别,记录预测结果和真实标签。
# evaluate.py 批量测试识别准确率 import os import cv2 import numpy as np from main_image_process import preprocess_image, segment_fruit, extract_features def predict(image_path): """返回预测类别,这里简化成根据颜色区间判断""" img, blurred = preprocess_image(image_path) mask = segment_fruit(blurred) features = extract_features(mask, img) if not features: return "unknown" # 实际代码里会有更复杂的分类逻辑 return "orange" # 占位 data_dir = "source_imagedata" true_labels = [] pred_labels = [] for fname in os.listdir(data_dir): if not fname.endswith(".png"): continue true_label = fname.split(".")[0].rstrip("0123456789") pred_label = predict(os.path.join(data_dir, fname)) true_labels.append(true_label) pred_labels.append(pred_label) print(f"{fname}: true={true_label}, pred={pred_label}") # 简单统计 correct = sum(1 for t, p in zip(true_labels, pred_labels) if t == p) print(f"准确率: {correct}/{len(true_labels)} = {correct/len(true_labels):.2%}")这个脚本把文件名去掉数字后缀当作真实标签,比如apple2.png的真实标签是apple。跑完能直观看到哪些类别容易混。如果橙子和苹果混淆严重,说明 HSV 区间重叠太多,需要收窄范围或者加入形状特征做二次判断。
5.3 调参时盯住的三个指标
调颜色阈值和形态学参数时,别凭感觉改。我习惯盯三个数:分割区域的像素面积占比、轮廓数量、最大轮廓的圆形度。面积占比突然从 30% 掉到 5%,说明阈值收太紧;轮廓数量从 1 变成 8,说明噪点没滤干净,要加大形态学 kernel 或提高面积过滤阈值;圆形度低于 0.6 还判成苹果,说明形状判断逻辑需要加权重。
从那以后我每次改完参数,都会先把mask和原图并排显示出来,肉眼确认分割区域贴合水果边缘,再跑批量测试看准确率变化。这个习惯帮我省了很多来回试的时间。希望这份拆解能帮你顺利跑通自己的水果识别 demo。
本文还有配套的精品资源,点击获取