From a4e3bf381e222fee92cc232255eee371d4f37172 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:21:51 +0800 Subject: [PATCH 01/30] Create image_object_detection --- src/image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/image_object_detection diff --git a/src/image_object_detection b/src/image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/image_object_detection @@ -0,0 +1 @@ + From 12a6503d4e26ea17678c8f9c0bea6ec4a5e834fc Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:24:47 +0800 Subject: [PATCH 02/30] Delete src/image_object_detection --- src/image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/image_object_detection diff --git a/src/image_object_detection b/src/image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From 0a15c3fcba88b16ab70b1adf3aa2636c1e61f772 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:25:29 +0800 Subject: [PATCH 03/30] Create image_object_detection --- src/image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/image_object_detection diff --git a/src/image_object_detection b/src/image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/image_object_detection @@ -0,0 +1 @@ + From 42d02ade9541f864960b9ac2482bcca3c3a09f3b Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:26:24 +0800 Subject: [PATCH 04/30] Delete src/image_object_detection --- src/image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/image_object_detection diff --git a/src/image_object_detection b/src/image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From acb8b4de9012476c5ab6f5de962a339c3285868b Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:27:06 +0800 Subject: [PATCH 05/30] Create Image_object_detection --- src/Image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/Image_object_detection @@ -0,0 +1 @@ + From 7e0dcb83b37450272fa7cd51a78dacd7b5a8a9cc Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:27:30 +0800 Subject: [PATCH 06/30] Delete src/Image_object_detection --- src/Image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/Image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From 7e26f7d825fdee07fc2f5afa070be48156f0259f Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:28:19 +0800 Subject: [PATCH 07/30] Create Image_object_detection --- src/Image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/Image_object_detection @@ -0,0 +1 @@ + From 17f5843806decb84cc2b2651887d2dc84027cf16 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:28:34 +0800 Subject: [PATCH 08/30] Delete src/Image_object_detection --- src/Image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/Image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From b7a579aa472f17d6ac477ec5419e33a9f7baa657 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:29:25 +0800 Subject: [PATCH 09/30] Create Image_object_detection --- src/Image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/Image_object_detection @@ -0,0 +1 @@ + From d093821e28c339cf92cbb1d3ab0f262ccbaf643c Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:30:10 +0800 Subject: [PATCH 10/30] Delete src/Image_object_detection --- src/Image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/Image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From e96466ea7e95238027fb804dd5f9c366299e60c6 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:31:18 +0800 Subject: [PATCH 11/30] Create Image_object_detection --- src/Image_object_detection | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/Image_object_detection @@ -0,0 +1 @@ + From 044ac8f205d8d5c6ddc2f1051a7d80205abebaf4 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 09:35:33 +0800 Subject: [PATCH 12/30] Delete src/Image_object_detection --- src/Image_object_detection | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/Image_object_detection diff --git a/src/Image_object_detection b/src/Image_object_detection deleted file mode 100644 index 8b13789179..0000000000 --- a/src/Image_object_detection +++ /dev/null @@ -1 +0,0 @@ - From 456f8b1faaa80dbff133e3dda18a3e6120a0e60e Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 8 Dec 2025 11:03:17 +0800 Subject: [PATCH 13/30] Add files via upload --- src/image_object_detection/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/image_object_detection/README.md diff --git a/src/image_object_detection/README.md b/src/image_object_detection/README.md new file mode 100644 index 0000000000..e0c4b707ee --- /dev/null +++ b/src/image_object_detection/README.md @@ -0,0 +1 @@ +这是一个使用 YOLO 训练模型来进行图像对象检测的系统 \ No newline at end of file From 989791c09d95db8a8dfec48f637c34ea2658befe Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 09:21:50 +0800 Subject: [PATCH 14/30] Add files via upload --- src/image_object_detection/main.py | 110 ++++++++++++++++++++ src/image_object_detection/requirements.txt | 15 +++ 2 files changed, 125 insertions(+) create mode 100644 src/image_object_detection/main.py create mode 100644 src/image_object_detection/requirements.txt diff --git a/src/image_object_detection/main.py b/src/image_object_detection/main.py new file mode 100644 index 0000000000..37419ff35f --- /dev/null +++ b/src/image_object_detection/main.py @@ -0,0 +1,110 @@ +# 导入核心库 +from ultralytics import YOLO +import cv2 +import matplotlib.pyplot as plt +import os # 新增:用于路径验证 + +# -------------------------- 1. 基础配置(重点:替换成你的图片路径!) -------------------------- +# 模型路径:YOLOv8n轻量级预训练模型(自动下载) +MODEL_PATH = "yolov8n.pt" + +# 🔥 关键修改:替换成你图片的绝对路径(右键图片→属性→复制完整路径,加r前缀避免转义) +# 示例:IMAGE_PATH = r"C:\Users\apple\OneDrive\桌面\my_test_image.jpg" +IMAGE_PATH = r"C:\Users\apple\OneDrive\桌面\test.jpg" + +# 检测结果保存路径(建议保存到桌面,方便查找) +SAVE_PATH = r"C:\Users\apple\OneDrive\桌面\detected_image.jpg" + +# -------------------------- 2. 加载YOLO模型 -------------------------- +# 加载预训练YOLOv8模型(首次运行自动下载权重,已下载则直接加载) +model = YOLO(MODEL_PATH) + +# -------------------------- 3. 图像检测核心函数(含路径验证) -------------------------- +def detect_image_with_pretrained_model(image_path, save_path): + """ + 用预训练YOLO模型检测图像,包含路径验证和友好报错 + :param image_path: 待检测图片路径 + :param save_path: 检测结果保存路径 + """ + # 第一步:验证图片路径是否存在(核心解决FileNotFoundError) + if not os.path.exists(image_path): + print(f"\n❌ 错误:找不到图片文件!") + print(f"当前设置的图片路径:{image_path}") + print(f"请检查:1. 图片是否存在 2. 路径是否正确 3. 路径无中文/空格/特殊符号\n") + return # 路径错误则终止函数 + + # 第二步:执行目标检测(conf=0.25:只显示置信度≥25%的目标) + print(f"\n✅ 开始检测图片:{image_path}") + results = model(image_path, conf=0.25) + + # 第三步:可视化检测结果(绘制边界框、类别、置信度) + annotated_image = results[0].plot() # 生成带标注的图片 + + # 转换颜色通道(OpenCV默认BGR,Matplotlib显示需要RGB) + annotated_image_rgb = cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB) + + # 第四步:显示检测结果图片 + plt.figure(figsize=(12, 8)) + plt.imshow(annotated_image_rgb) + plt.axis("off") # 隐藏坐标轴 + plt.title("YOLOv8 Object Detection Result", fontsize=16) + plt.show() + + # 第五步:保存检测结果到指定路径 + cv2.imwrite(save_path, annotated_image) + print(f"\n✅ 检测结果已保存:{save_path}") + + # 第六步:打印详细检测信息(类别、置信度、坐标) + print("\n📌 检测到的目标信息:") + for result in results: + boxes = result.boxes # 获取所有检测框 + if len(boxes) == 0: + print(" 未检测到任何目标(可降低conf阈值试试,比如conf=0.1)") + continue + for box in boxes: + cls_index = int(box.cls) # 类别索引 + cls_name = model.names[cls_index] # 类别名称(如person/car/cat) + confidence = box.conf.item() # 置信度 + coordinates = box.xyxy.tolist()[0] # 边界框坐标 [x1, y1, x2, y2] + print(f" 类别:{cls_name} | 置信度:{confidence:.2f} | 坐标:{[round(x, 2) for x in coordinates]}") + +# -------------------------- 4. 自定义数据集训练函数(可选) -------------------------- +def train_custom_yolo_model(data_yaml_path, epochs=10, imgsz=640): + """ + 训练自定义YOLO模型(需先准备数据集和.yaml配置文件) + :param data_yaml_path: 数据集配置文件路径(如dataset/data.yaml) + :param epochs: 训练轮数(入门建议10-30) + :param imgsz: 输入图像尺寸 + """ + if not os.path.exists(data_yaml_path): + print(f"\n❌ 错误:数据集配置文件不存在!路径:{data_yaml_path}") + return + + # 加载模型并开始训练 + train_model = YOLO(MODEL_PATH) + train_results = train_model.train( + data=data_yaml_path, + epochs=epochs, + imgsz=imgsz, + batch=-1, # 自动适配批次大小 + device="cpu", # 无GPU则用cpu,有GPU改0 + patience=50, + save=True, + project="runs/train", + name="custom_yolo", + exist_ok=True + ) + # 验证模型 + val_results = train_model.val() + print("\n✅ 自定义模型训练完成!验证集指标:", val_results.results_dict) + +# -------------------------- 主程序运行入口 -------------------------- +if __name__ == "__main__": + # 运行预训练模型检测(核心功能,必执行) + detect_image_with_pretrained_model(IMAGE_PATH, SAVE_PATH) + + # 如需训练自定义数据集,取消下面注释并配置data_yaml_path + # train_custom_yolo_model(data_yaml_path=r"C:\Users\apple\OneDrive\桌面\dataset\data.yaml", epochs=10) + + + diff --git a/src/image_object_detection/requirements.txt b/src/image_object_detection/requirements.txt new file mode 100644 index 0000000000..f1cc19c3c9 --- /dev/null +++ b/src/image_object_detection/requirements.txt @@ -0,0 +1,15 @@ +# YOLOv8核心库(必须) +ultralytics>=8.0.0 # 推荐8.2.0(稳定版) +# 图像处理(必须) +opencv-python>=4.5.0 # 推荐4.8.1.78 +# 可视化(必须) +matplotlib>=3.0.0 # 推荐3.7.1 +# PyTorch核心(ultralytics依赖,必须) +torch>=2.0.0 # 推荐2.0.1或2.1.0 +torchvision>=0.15.0 # 与torch版本匹配 +# 基础数值计算(间接依赖,建议指定) +numpy>=1.21.0 # 推荐1.24.3 +# 可选(补充依赖,避免隐性报错) +pillow>=8.0.0 # 推荐9.5.0 +psutil>=5.8.0 # ultralytics监控系统资源用 +pyyaml>=6.0 # 自定义训练时解析yaml配置文件 \ No newline at end of file From 09aeed328f3ed68906bbdbc6fe423ebeb741d4ec Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 10:40:27 +0800 Subject: [PATCH 15/30] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目源代码及其依赖版本 From 556825aea64df3468ae06e5c1e06727c407f0e70 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 10:47:21 +0800 Subject: [PATCH 16/30] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目源代码及依赖版本 From e5faaee653d68f8711293235e61c8f46fbd9041e Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 12:03:21 +0800 Subject: [PATCH 17/30] Delete src/image_object_detection/main.py --- src/image_object_detection/main.py | 110 ----------------------------- 1 file changed, 110 deletions(-) delete mode 100644 src/image_object_detection/main.py diff --git a/src/image_object_detection/main.py b/src/image_object_detection/main.py deleted file mode 100644 index 37419ff35f..0000000000 --- a/src/image_object_detection/main.py +++ /dev/null @@ -1,110 +0,0 @@ -# 导入核心库 -from ultralytics import YOLO -import cv2 -import matplotlib.pyplot as plt -import os # 新增:用于路径验证 - -# -------------------------- 1. 基础配置(重点:替换成你的图片路径!) -------------------------- -# 模型路径:YOLOv8n轻量级预训练模型(自动下载) -MODEL_PATH = "yolov8n.pt" - -# 🔥 关键修改:替换成你图片的绝对路径(右键图片→属性→复制完整路径,加r前缀避免转义) -# 示例:IMAGE_PATH = r"C:\Users\apple\OneDrive\桌面\my_test_image.jpg" -IMAGE_PATH = r"C:\Users\apple\OneDrive\桌面\test.jpg" - -# 检测结果保存路径(建议保存到桌面,方便查找) -SAVE_PATH = r"C:\Users\apple\OneDrive\桌面\detected_image.jpg" - -# -------------------------- 2. 加载YOLO模型 -------------------------- -# 加载预训练YOLOv8模型(首次运行自动下载权重,已下载则直接加载) -model = YOLO(MODEL_PATH) - -# -------------------------- 3. 图像检测核心函数(含路径验证) -------------------------- -def detect_image_with_pretrained_model(image_path, save_path): - """ - 用预训练YOLO模型检测图像,包含路径验证和友好报错 - :param image_path: 待检测图片路径 - :param save_path: 检测结果保存路径 - """ - # 第一步:验证图片路径是否存在(核心解决FileNotFoundError) - if not os.path.exists(image_path): - print(f"\n❌ 错误:找不到图片文件!") - print(f"当前设置的图片路径:{image_path}") - print(f"请检查:1. 图片是否存在 2. 路径是否正确 3. 路径无中文/空格/特殊符号\n") - return # 路径错误则终止函数 - - # 第二步:执行目标检测(conf=0.25:只显示置信度≥25%的目标) - print(f"\n✅ 开始检测图片:{image_path}") - results = model(image_path, conf=0.25) - - # 第三步:可视化检测结果(绘制边界框、类别、置信度) - annotated_image = results[0].plot() # 生成带标注的图片 - - # 转换颜色通道(OpenCV默认BGR,Matplotlib显示需要RGB) - annotated_image_rgb = cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB) - - # 第四步:显示检测结果图片 - plt.figure(figsize=(12, 8)) - plt.imshow(annotated_image_rgb) - plt.axis("off") # 隐藏坐标轴 - plt.title("YOLOv8 Object Detection Result", fontsize=16) - plt.show() - - # 第五步:保存检测结果到指定路径 - cv2.imwrite(save_path, annotated_image) - print(f"\n✅ 检测结果已保存:{save_path}") - - # 第六步:打印详细检测信息(类别、置信度、坐标) - print("\n📌 检测到的目标信息:") - for result in results: - boxes = result.boxes # 获取所有检测框 - if len(boxes) == 0: - print(" 未检测到任何目标(可降低conf阈值试试,比如conf=0.1)") - continue - for box in boxes: - cls_index = int(box.cls) # 类别索引 - cls_name = model.names[cls_index] # 类别名称(如person/car/cat) - confidence = box.conf.item() # 置信度 - coordinates = box.xyxy.tolist()[0] # 边界框坐标 [x1, y1, x2, y2] - print(f" 类别:{cls_name} | 置信度:{confidence:.2f} | 坐标:{[round(x, 2) for x in coordinates]}") - -# -------------------------- 4. 自定义数据集训练函数(可选) -------------------------- -def train_custom_yolo_model(data_yaml_path, epochs=10, imgsz=640): - """ - 训练自定义YOLO模型(需先准备数据集和.yaml配置文件) - :param data_yaml_path: 数据集配置文件路径(如dataset/data.yaml) - :param epochs: 训练轮数(入门建议10-30) - :param imgsz: 输入图像尺寸 - """ - if not os.path.exists(data_yaml_path): - print(f"\n❌ 错误:数据集配置文件不存在!路径:{data_yaml_path}") - return - - # 加载模型并开始训练 - train_model = YOLO(MODEL_PATH) - train_results = train_model.train( - data=data_yaml_path, - epochs=epochs, - imgsz=imgsz, - batch=-1, # 自动适配批次大小 - device="cpu", # 无GPU则用cpu,有GPU改0 - patience=50, - save=True, - project="runs/train", - name="custom_yolo", - exist_ok=True - ) - # 验证模型 - val_results = train_model.val() - print("\n✅ 自定义模型训练完成!验证集指标:", val_results.results_dict) - -# -------------------------- 主程序运行入口 -------------------------- -if __name__ == "__main__": - # 运行预训练模型检测(核心功能,必执行) - detect_image_with_pretrained_model(IMAGE_PATH, SAVE_PATH) - - # 如需训练自定义数据集,取消下面注释并配置data_yaml_path - # train_custom_yolo_model(data_yaml_path=r"C:\Users\apple\OneDrive\桌面\dataset\data.yaml", epochs=10) - - - From cbe3f1fa350e2090862e11d770ed7e9bb82a8d4f Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 12:03:33 +0800 Subject: [PATCH 18/30] Delete src/image_object_detection/requirements.txt --- src/image_object_detection/requirements.txt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/image_object_detection/requirements.txt diff --git a/src/image_object_detection/requirements.txt b/src/image_object_detection/requirements.txt deleted file mode 100644 index f1cc19c3c9..0000000000 --- a/src/image_object_detection/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# YOLOv8核心库(必须) -ultralytics>=8.0.0 # 推荐8.2.0(稳定版) -# 图像处理(必须) -opencv-python>=4.5.0 # 推荐4.8.1.78 -# 可视化(必须) -matplotlib>=3.0.0 # 推荐3.7.1 -# PyTorch核心(ultralytics依赖,必须) -torch>=2.0.0 # 推荐2.0.1或2.1.0 -torchvision>=0.15.0 # 与torch版本匹配 -# 基础数值计算(间接依赖,建议指定) -numpy>=1.21.0 # 推荐1.24.3 -# 可选(补充依赖,避免隐性报错) -pillow>=8.0.0 # 推荐9.5.0 -psutil>=5.8.0 # ultralytics监控系统资源用 -pyyaml>=6.0 # 自定义训练时解析yaml配置文件 \ No newline at end of file From de9a2d26ad9b83ffe76a31c699f545721d57ab39 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 17:55:40 +0800 Subject: [PATCH 19/30] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目需要的依赖及其版本 --- src/image_object_detection/requirements.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/image_object_detection/requirements.txt diff --git a/src/image_object_detection/requirements.txt b/src/image_object_detection/requirements.txt new file mode 100644 index 0000000000..f1cc19c3c9 --- /dev/null +++ b/src/image_object_detection/requirements.txt @@ -0,0 +1,15 @@ +# YOLOv8核心库(必须) +ultralytics>=8.0.0 # 推荐8.2.0(稳定版) +# 图像处理(必须) +opencv-python>=4.5.0 # 推荐4.8.1.78 +# 可视化(必须) +matplotlib>=3.0.0 # 推荐3.7.1 +# PyTorch核心(ultralytics依赖,必须) +torch>=2.0.0 # 推荐2.0.1或2.1.0 +torchvision>=0.15.0 # 与torch版本匹配 +# 基础数值计算(间接依赖,建议指定) +numpy>=1.21.0 # 推荐1.24.3 +# 可选(补充依赖,避免隐性报错) +pillow>=8.0.0 # 推荐9.5.0 +psutil>=5.8.0 # ultralytics监控系统资源用 +pyyaml>=6.0 # 自定义训练时解析yaml配置文件 \ No newline at end of file From 0c432cdd6d160f52ea6ed55c823d60177ffc43a3 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 17:56:39 +0800 Subject: [PATCH 20/30] Delete src/image_object_detection/requirements.txt --- src/image_object_detection/requirements.txt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/image_object_detection/requirements.txt diff --git a/src/image_object_detection/requirements.txt b/src/image_object_detection/requirements.txt deleted file mode 100644 index f1cc19c3c9..0000000000 --- a/src/image_object_detection/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# YOLOv8核心库(必须) -ultralytics>=8.0.0 # 推荐8.2.0(稳定版) -# 图像处理(必须) -opencv-python>=4.5.0 # 推荐4.8.1.78 -# 可视化(必须) -matplotlib>=3.0.0 # 推荐3.7.1 -# PyTorch核心(ultralytics依赖,必须) -torch>=2.0.0 # 推荐2.0.1或2.1.0 -torchvision>=0.15.0 # 与torch版本匹配 -# 基础数值计算(间接依赖,建议指定) -numpy>=1.21.0 # 推荐1.24.3 -# 可选(补充依赖,避免隐性报错) -pillow>=8.0.0 # 推荐9.5.0 -psutil>=5.8.0 # ultralytics监控系统资源用 -pyyaml>=6.0 # 自定义训练时解析yaml配置文件 \ No newline at end of file From d46dbef0c3f9c6f9256a62a5c03cb7ac75ce76a7 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 17:57:36 +0800 Subject: [PATCH 21/30] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目需要的依赖及其版本 --- src/image_object_detection/requirements.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/image_object_detection/requirements.txt diff --git a/src/image_object_detection/requirements.txt b/src/image_object_detection/requirements.txt new file mode 100644 index 0000000000..f1cc19c3c9 --- /dev/null +++ b/src/image_object_detection/requirements.txt @@ -0,0 +1,15 @@ +# YOLOv8核心库(必须) +ultralytics>=8.0.0 # 推荐8.2.0(稳定版) +# 图像处理(必须) +opencv-python>=4.5.0 # 推荐4.8.1.78 +# 可视化(必须) +matplotlib>=3.0.0 # 推荐3.7.1 +# PyTorch核心(ultralytics依赖,必须) +torch>=2.0.0 # 推荐2.0.1或2.1.0 +torchvision>=0.15.0 # 与torch版本匹配 +# 基础数值计算(间接依赖,建议指定) +numpy>=1.21.0 # 推荐1.24.3 +# 可选(补充依赖,避免隐性报错) +pillow>=8.0.0 # 推荐9.5.0 +psutil>=5.8.0 # ultralytics监控系统资源用 +pyyaml>=6.0 # 自定义训练时解析yaml配置文件 \ No newline at end of file From 7baa7199aff1ba38ffdf91e1c9b26d2d785ba9f5 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 18:11:58 +0800 Subject: [PATCH 22/30] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加YOLO对象检测系统的主程序(main),UI处理器(ui_handler),配置文件(config),检测引擎(detection_engint),图像检测器(image_detector) --- src/image_object_detection/config.py | 15 ++++ .../detection_engine.py | 57 ++++++++++++++ src/image_object_detection/image_detector.py | 63 ++++++++++++++++ src/image_object_detection/main.py | 11 +++ src/image_object_detection/ui_handler.py | 75 +++++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 src/image_object_detection/config.py create mode 100644 src/image_object_detection/detection_engine.py create mode 100644 src/image_object_detection/image_detector.py create mode 100644 src/image_object_detection/main.py create mode 100644 src/image_object_detection/ui_handler.py diff --git a/src/image_object_detection/config.py b/src/image_object_detection/config.py new file mode 100644 index 0000000000..abee289e3d --- /dev/null +++ b/src/image_object_detection/config.py @@ -0,0 +1,15 @@ +# config.py +class Config: + def __init__(self): + # 测试图像路径 + self.test_image_path = r"C:\Users\apple\OneDrive\桌面\test.jpg" + + # YOLO模型配置 + self.model_path = "yolov8n.pt" + self.confidence_threshold = 0.25 + + # 摄像头配置 + self.camera_index = 0 + + # 输出频率控制(秒) + self.output_interval = 1.0 \ No newline at end of file diff --git a/src/image_object_detection/detection_engine.py b/src/image_object_detection/detection_engine.py new file mode 100644 index 0000000000..6f2df4b08b --- /dev/null +++ b/src/image_object_detection/detection_engine.py @@ -0,0 +1,57 @@ +# detection_engine.py +from ultralytics import YOLO +import io +import sys + +class DetectionEngine: + def __init__(self, model_path="yolov8n.pt", conf_threshold=0.25): + self.model_path = model_path + self.conf_threshold = conf_threshold + self.model = self._load_model() + + def _load_model(self): + """加载YOLO模型并抑制输出""" + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = io.StringIO() + sys.stderr = io.StringIO() + + try: + model = YOLO(self.model_path) + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + + return model + + def detect(self, frame): + """对输入帧进行检测""" + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = io.StringIO() + sys.stderr = io.StringIO() + + try: + results = self.model(frame, conf=self.conf_threshold, verbose=False) + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + + annotated_frame = results[0].plot() + return annotated_frame, results + + def find_objects(self, frame): + """检测并分类图像中的对象""" + _, results = self.detect(frame) + objects = [] + for box in results[0].boxes: + cls_index = int(box.cls) + cls_name = self.model.names[cls_index] + coords = box.xyxy.tolist()[0] + confidence = box.conf.item() + objects.append({ + "type": cls_name, + "bbox": coords, + "confidence": confidence + }) + return objects \ No newline at end of file diff --git a/src/image_object_detection/image_detector.py b/src/image_object_detection/image_detector.py new file mode 100644 index 0000000000..ebfbc90bf4 --- /dev/null +++ b/src/image_object_detection/image_detector.py @@ -0,0 +1,63 @@ +# image_detector.py +import cv2 + +class ImageDetector: + def __init__(self, detection_engine): + self.engine = detection_engine + + def detect_static_image(self, image_path): + """检测静态图像""" + print(f"正在加载图像: {image_path}") + + if not self._check_image_exists(image_path): + return + + frame = self._load_image(image_path) + if frame is None: + print("错误: 无法读取图像文件") + return + + print("正在检测图像...") + results = self._perform_detection(frame) + + # 显示检测结果 + self._display_results(results) + + # 显示图像 + self._show_image(results[1][0].plot(), 'YOLO 检测结果 - 静态图像') + + def _check_image_exists(self, image_path): + """检查图像文件是否存在""" + if not cv2.os.path.exists(image_path): + print(f"错误: 图像文件不存在 - {image_path}") + return False + return True + + def _load_image(self, image_path): + """加载图像""" + return cv2.imread(image_path) + + def _perform_detection(self, frame): + """执行检测""" + annotated_frame, results = self.engine.detect(frame) + return results + + def _display_results(self, results): + """显示检测结果""" + detected_count = len(results[0].boxes) + print("检测完成,正在显示结果...") + print(f"检测到 {detected_count} 个对象:") + + for i, box in enumerate(results[0].boxes): + cls_index = int(box.cls) + cls_name = self.engine.model.names[cls_index] + confidence = box.conf.item() + print(f" {i+1}. {cls_name} (置信度: {confidence:.2f})") + + def _show_image(self, annotated_frame, window_name): + """显示图像""" + print("\n图像已显示。按任意键关闭窗口...") + cv2.imshow(window_name, annotated_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + print("窗口已关闭。") \ No newline at end of file diff --git a/src/image_object_detection/main.py b/src/image_object_detection/main.py new file mode 100644 index 0000000000..6c1bda8efd --- /dev/null +++ b/src/image_object_detection/main.py @@ -0,0 +1,11 @@ +# main.py +from ui_handler import UIHandler +from config import Config + +def main(): + config = Config() + ui_handler = UIHandler(config) + ui_handler.run() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/image_object_detection/ui_handler.py b/src/image_object_detection/ui_handler.py new file mode 100644 index 0000000000..53e4036e0d --- /dev/null +++ b/src/image_object_detection/ui_handler.py @@ -0,0 +1,75 @@ +# ui_handler.py +import cv2 +from detection_engine import DetectionEngine +from image_detector import ImageDetector +from camera_detector import CameraDetector +import sys +import os + +class UIHandler: + def __init__(self, config): + self.config = config + self.detection_engine = DetectionEngine() + self.image_detector = ImageDetector(self.detection_engine) + self.camera_detector = CameraDetector(self.detection_engine) + + def print_debug(self, message): + """调试输出函数""" + print(f"[DEBUG] {message}") + sys.stdout.flush() # 强制刷新输出缓冲区 + + def display_menu(self): + """显示主菜单""" + print("=" * 60) + print(" 欢迎使用 YOLO 对象检测系统") + print("=" * 60) + + print(f"\n测试图像路径: {self.config.test_image_path}") + print(f"图像文件存在: {os.path.exists(self.config.test_image_path)}") + + print("\n请选择检测模式:") + print("1. 检测静态图像") + print("2. 检测摄像头实时画面") + print("3. 退出程序") + + def handle_choice(self, choice): + """处理用户选择""" + if choice == '1': + print("\n您选择了: 检测静态图像") + self.image_detector.detect_static_image(self.config.test_image_path) + return True + elif choice == '2': + print("\n您选择了: 检测摄像头实时画面") + print("注意:要退出摄像头模式,请按 Ctrl+C !") + self.camera_detector.detect_camera() + return True + elif choice == '3': + print("\n程序已退出。") + return False + else: + print("\n无效选择,请输入 1、2 或 3") + return True + + def run(self): + """运行主程序""" + self.print_debug("程序开始执行") + self.display_menu() + + running = True + while running: + try: + choice = input("\n请输入您的选择 (1/2/3): ").strip() + self.print_debug(f"用户输入: {choice}") + running = self.handle_choice(choice) + + except KeyboardInterrupt: + print("\n\n程序被用户中断。") + break + except EOFError: + print("\n输入结束,程序退出。") + break + except Exception as e: + print(f"\n发生错误: {e}") + break + + self.print_debug("程序结束") \ No newline at end of file From e2c38edb5e86cab99bb8d14f57f3ec95cf6fde93 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 18:36:10 +0800 Subject: [PATCH 23/30] Update detection_engine.py --- .../detection_engine.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/image_object_detection/detection_engine.py b/src/image_object_detection/detection_engine.py index 6f2df4b08b..7e68491ece 100644 --- a/src/image_object_detection/detection_engine.py +++ b/src/image_object_detection/detection_engine.py @@ -25,7 +25,11 @@ def _load_model(self): return model def detect(self, frame): - """对输入帧进行检测""" + """ + 对输入帧进行检测。 + 返回: (annotated_frame: np.ndarray, results: List[Results]) + 即使无检测框,annotated_frame 也是原始图像(HWC格式)。 + """ old_stdout = sys.stdout old_stderr = sys.stderr sys.stdout = io.StringIO() @@ -37,21 +41,6 @@ def detect(self, frame): sys.stdout = old_stdout sys.stderr = old_stderr - annotated_frame = results[0].plot() + # YOLOv8 always returns at least one result + annotated_frame = results[0].plot() # numpy array (H, W, C) return annotated_frame, results - - def find_objects(self, frame): - """检测并分类图像中的对象""" - _, results = self.detect(frame) - objects = [] - for box in results[0].boxes: - cls_index = int(box.cls) - cls_name = self.model.names[cls_index] - coords = box.xyxy.tolist()[0] - confidence = box.conf.item() - objects.append({ - "type": cls_name, - "bbox": coords, - "confidence": confidence - }) - return objects \ No newline at end of file From 43271ab42e58c8da1b19fd449233c45af8ad45fc Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 18:37:00 +0800 Subject: [PATCH 24/30] Update image_detector.py --- src/image_object_detection/image_detector.py | 101 ++++++++++++++----- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/src/image_object_detection/image_detector.py b/src/image_object_detection/image_detector.py index ebfbc90bf4..b161b45597 100644 --- a/src/image_object_detection/image_detector.py +++ b/src/image_object_detection/image_detector.py @@ -1,5 +1,7 @@ # image_detector.py import cv2 +import numpy as np +import traceback class ImageDetector: def __init__(self, detection_engine): @@ -13,51 +15,98 @@ def detect_static_image(self, image_path): return frame = self._load_image(image_path) - if frame is None: - print("错误: 无法读取图像文件") + if frame is None or frame.size == 0: + print("错误: 无法读取图像文件或图像为空") return print("正在检测图像...") - results = self._perform_detection(frame) + results, annotated_frame = self._perform_detection(frame) - # 显示检测结果 + if annotated_frame is None: + print("错误: 检测未返回有效图像") + return + + # 显示检测结果文本 self._display_results(results) - # 显示图像 - self._show_image(results[1][0].plot(), 'YOLO 检测结果 - 静态图像') + # 显示图像窗口(带固定初始大小) + self._show_image(annotated_frame, 'YOLO 检测结果 - 静态图像') def _check_image_exists(self, image_path): - """检查图像文件是否存在""" - if not cv2.os.path.exists(image_path): + import os + if not os.path.exists(image_path): print(f"错误: 图像文件不存在 - {image_path}") return False return True def _load_image(self, image_path): - """加载图像""" - return cv2.imread(image_path) + try: + with open(image_path, "rb") as f: + bytes_data = bytearray(f.read()) + nparr = np.frombuffer(bytes_data, np.uint8) + frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) + return frame + except Exception as e: + print(f"加载图像时出错: {e}") + return None def _perform_detection(self, frame): - """执行检测""" - annotated_frame, results = self.engine.detect(frame) - return results + try: + annotated_frame, results = self.engine.detect(frame) + return results, annotated_frame + except Exception as e: + print(f"检测过程中发生错误: {e}") + traceback.print_exc() + return [], None def _display_results(self, results): - """显示检测结果""" - detected_count = len(results[0].boxes) + if not results: + print("未检测到任何对象(results 为空)") + return + + result = results[0] + boxes = result.boxes + if len(boxes) == 0: + print("未检测到任何对象") + return + print("检测完成,正在显示结果...") - print(f"检测到 {detected_count} 个对象:") + print(f"检测到 {len(boxes)} 个对象:") + + names_list = self.engine.model.names - for i, box in enumerate(results[0].boxes): - cls_index = int(box.cls) - cls_name = self.engine.model.names[cls_index] - confidence = box.conf.item() - print(f" {i+1}. {cls_name} (置信度: {confidence:.2f})") + for i, box in enumerate(boxes): + try: + cls_index = int(box.cls.item()) + confidence = box.conf.item() + + if 0 <= cls_index < len(names_list): + cls_name = names_list[cls_index] + else: + cls_name = f"unknown_class_{cls_index}" + print(f" ⚠️ 警告:类别索引 {cls_index} 超出范围(共 {len(names_list)} 类)") + + print(f" {i+1}. {cls_name} (置信度: {confidence:.2f})") + except Exception as e: + print(f" ⚠️ 解析第 {i+1} 个检测框时出错: {e}") def _show_image(self, annotated_frame, window_name): - """显示图像""" + """显示图像,并设置窗口为可调整的小尺寸""" + if annotated_frame is None or annotated_frame.size == 0: + print("错误: 标注帧无效,无法显示") + return + print("\n图像已显示。按任意键关闭窗口...") - cv2.imshow(window_name, annotated_frame) - cv2.waitKey(0) - cv2.destroyAllWindows() - print("窗口已关闭。") \ No newline at end of file + try: + # 创建可调整大小的窗口 + cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) + # 设置初始窗口大小为 800x600(你可以按需修改) + cv2.resizeWindow(window_name, 800, 600) + # 显示图像(OpenCV 会自动适应窗口) + cv2.imshow(window_name, annotated_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + print("窗口已关闭。") + except Exception as e: + print(f"显示图像时发生错误: {e}") + traceback.print_exc() From 13fa00c88373bb1b40f81efe13f3cd94fd54c391 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Mon, 15 Dec 2025 20:09:23 +0800 Subject: [PATCH 25/30] Create camera_detector.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加相机识别模块 --- src/image_object_detection/camera_detector.py | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/image_object_detection/camera_detector.py diff --git a/src/image_object_detection/camera_detector.py b/src/image_object_detection/camera_detector.py new file mode 100644 index 0000000000..3964fcc2c6 --- /dev/null +++ b/src/image_object_detection/camera_detector.py @@ -0,0 +1,98 @@ +# camera_detector.py +import cv2 +import time + +class CameraDetector: + def __init__(self, detection_engine): + self.engine = detection_engine + + def detect_camera(self): + """检测摄像头实时画面""" + try: + # 打开摄像头 + print("正在打开摄像头...") + cap = self._open_camera() + + if cap is None: + return + + print("摄像头已打开,开始实时检测。") + print("在显示窗口中按 'q' 键退出,或在终端中按 Ctrl+C 中断程序。\n") + + self._run_camera_loop(cap) + + except KeyboardInterrupt: + print("\n\n用户按下了 Ctrl+C,强制退出摄像头检测...") + print("程序已安全退出。") + except Exception as e: + print(f"摄像头检测过程中发生错误: {e}") + finally: + self._cleanup_resources(cap) + + def _open_camera(self): + """打开摄像头""" + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + print("错误: 无法打开摄像头") + return None + + return cap + + def _run_camera_loop(self, cap): + """摄像头检测主循环""" + # 记录上次输出时间,控制输出频率 + last_output_time = time.time() + output_interval = 1.0 # 每秒最多输出一次检测信息 + + while True: + ret, frame = cap.read() + if not ret: + print("无法读取摄像头画面,退出...") + break + + results = self._perform_detection(frame) + annotated_frame = results[0].plot() + + # 控制输出频率,避免终端被刷屏 + self._handle_output_frequency(results, last_output_time, output_interval) + last_output_time = time.time() + + cv2.imshow('YOLO 检测结果 - 摄像头', annotated_frame) + + # 按 'q' 键退出 + key = cv2.waitKey(1) & 0xFF + if key == ord('q'): + print("\n用户按下 'q' 键,退出摄像头检测...") + break + + def _perform_detection(self, frame): + """执行检测""" + annotated_frame, results = self.engine.detect(frame) + return results + + def _handle_output_frequency(self, results, last_output_time, output_interval): + """处理输出频率""" + current_time = time.time() + if current_time - last_output_time >= output_interval: + detected_count = len(results[0].boxes) + if detected_count > 0: + # 构建检测对象字符串 + detected_objects = [] + for box in results[0].boxes: + cls_index = int(box.cls) + cls_name = self.engine.model.names[cls_index] + confidence = box.conf.item() + detected_objects.append(f"{cls_name}({confidence:.2f})") + + print(f"检测到 {detected_count} 个对象: {', '.join(detected_objects)}") + + def _cleanup_resources(self, cap): + """清理资源""" + print("释放摄像头资源...") + try: + cap.release() + cv2.destroyAllWindows() + except: + pass + print("摄像头检测已停止。") From a85a0bd8dbcfcc27b0482c8b39c6e48b4f118fe8 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Tue, 16 Dec 2025 12:11:33 +0800 Subject: [PATCH 26/30] Update camera_detector.py --- src/image_object_detection/camera_detector.py | 172 ++++++++++-------- 1 file changed, 92 insertions(+), 80 deletions(-) diff --git a/src/image_object_detection/camera_detector.py b/src/image_object_detection/camera_detector.py index 3964fcc2c6..cb0ad65ca4 100644 --- a/src/image_object_detection/camera_detector.py +++ b/src/image_object_detection/camera_detector.py @@ -1,98 +1,110 @@ # camera_detector.py import cv2 import time +import os +import traceback class CameraDetector: - def __init__(self, detection_engine): + def __init__(self, detection_engine, camera_index=0): self.engine = detection_engine + self.camera_index = camera_index + self.window_name = "YOLO 实时检测 - 摄像头" def detect_camera(self): - """检测摄像头实时画面""" + """对外统一接口:启动摄像头实时检测""" + self.start_detection() + + def start_detection(self): + """实际执行摄像头检测的逻辑""" + print("正在初始化摄像头...") + cap = self._open_camera() + if cap is None: + return + + cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL) + cv2.resizeWindow(self.window_name, 800, 600) + + print("摄像头已启动。按 'q' 或 ESC 退出,按 's' 保存当前帧。") + + prev_time = time.time() + saved_frame_index = 0 + try: - # 打开摄像头 - print("正在打开摄像头...") - cap = self._open_camera() - - if cap is None: - return - - print("摄像头已打开,开始实时检测。") - print("在显示窗口中按 'q' 键退出,或在终端中按 Ctrl+C 中断程序。\n") - - self._run_camera_loop(cap) - - except KeyboardInterrupt: - print("\n\n用户按下了 Ctrl+C,强制退出摄像头检测...") - print("程序已安全退出。") + while True: + ret, frame = cap.read() + if not ret: + print("警告: 无法读取摄像头帧(可能设备被拔出或占用)") + break + + annotated_frame, results = self._perform_detection(frame) + if annotated_frame is None: + annotated_frame = frame + + curr_time = time.time() + fps = 1.0 / (curr_time - prev_time) if (curr_time - prev_time) > 0 else 0 + prev_time = curr_time + + num_objects = len(results[0].boxes) if results else 0 + info_text = f"FPS: {fps:.1f} | Objects: {num_objects}" + cv2.putText( + annotated_frame, + info_text, + (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, + (0, 255, 0), + 2 + ) + + cv2.imshow(self.window_name, annotated_frame) + + key = cv2.waitKey(1) & 0xFF + if key == ord('q') or key == 27: + print("用户请求退出。") + break + elif key == ord('s'): + saved_frame_index = self._save_frame(annotated_frame, saved_frame_index) + except Exception as e: - print(f"摄像头检测过程中发生错误: {e}") + print(f"检测过程中发生未预期错误: {e}") + traceback.print_exc() finally: - self._cleanup_resources(cap) - + cap.release() + cv2.destroyAllWindows() + print("摄像头已关闭,窗口已清理.") + def _open_camera(self): - """打开摄像头""" - cap = cv2.VideoCapture(0) - + cap = cv2.VideoCapture(self.camera_index) if not cap.isOpened(): - print("错误: 无法打开摄像头") + print(f"错误: 无法打开摄像头(索引 {self.camera_index})") + print("请检查摄像头是否连接、是否被其他程序占用。") return None + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) return cap - - def _run_camera_loop(self, cap): - """摄像头检测主循环""" - # 记录上次输出时间,控制输出频率 - last_output_time = time.time() - output_interval = 1.0 # 每秒最多输出一次检测信息 - - while True: - ret, frame = cap.read() - if not ret: - print("无法读取摄像头画面,退出...") - break - - results = self._perform_detection(frame) - annotated_frame = results[0].plot() - - # 控制输出频率,避免终端被刷屏 - self._handle_output_frequency(results, last_output_time, output_interval) - last_output_time = time.time() - - cv2.imshow('YOLO 检测结果 - 摄像头', annotated_frame) - - # 按 'q' 键退出 - key = cv2.waitKey(1) & 0xFF - if key == ord('q'): - print("\n用户按下 'q' 键,退出摄像头检测...") - break - + def _perform_detection(self, frame): - """执行检测""" - annotated_frame, results = self.engine.detect(frame) - return results - - def _handle_output_frequency(self, results, last_output_time, output_interval): - """处理输出频率""" - current_time = time.time() - if current_time - last_output_time >= output_interval: - detected_count = len(results[0].boxes) - if detected_count > 0: - # 构建检测对象字符串 - detected_objects = [] - for box in results[0].boxes: - cls_index = int(box.cls) - cls_name = self.engine.model.names[cls_index] - confidence = box.conf.item() - detected_objects.append(f"{cls_name}({confidence:.2f})") - - print(f"检测到 {detected_count} 个对象: {', '.join(detected_objects)}") - - def _cleanup_resources(self, cap): - """清理资源""" - print("释放摄像头资源...") try: - cap.release() - cv2.destroyAllWindows() - except: - pass - print("摄像头检测已停止。") + # 抑制 YOLO 内部输出 + import sys, os + old_stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + annotated_frame, results = self.engine.detect(frame) + sys.stdout = old_stdout + return annotated_frame, results + except Exception as e: + print(f"单帧检测失败: {e}") + return None, [] + + def _save_frame(self, frame, index): + save_dir = "saved_frames" + os.makedirs(save_dir, exist_ok=True) + filename = os.path.join(save_dir, f"frame_{index:04d}.jpg") + success = cv2.imwrite(filename, frame) + if success: + print(f"✅ 帧已保存: {filename}") + return index + 1 + else: + print("❌ 保存帧失败!") + return index From 4ec40e15c67ddd333f49eb43ae01f42dd2f85009 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Tue, 16 Dec 2025 17:28:03 +0800 Subject: [PATCH 27/30] Update main.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 禁用GUI --- src/image_object_detection/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/image_object_detection/main.py b/src/image_object_detection/main.py index 6c1bda8efd..dc6d559477 100644 --- a/src/image_object_detection/main.py +++ b/src/image_object_detection/main.py @@ -1,4 +1,7 @@ # main.py +import matplotlib +matplotlib.use('Agg') # 必须在最前,禁用 GUI 后端 + from ui_handler import UIHandler from config import Config @@ -8,4 +11,4 @@ def main(): ui_handler.run() if __name__ == "__main__": - main() \ No newline at end of file + main() From 93f4da1b708219ed7316098987c2535a7769f8ce Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Tue, 16 Dec 2025 17:29:31 +0800 Subject: [PATCH 28/30] Update image_detector.py --- src/image_object_detection/image_detector.py | 86 +++++--------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/src/image_object_detection/image_detector.py b/src/image_object_detection/image_detector.py index b161b45597..70f31256b2 100644 --- a/src/image_object_detection/image_detector.py +++ b/src/image_object_detection/image_detector.py @@ -2,111 +2,65 @@ import cv2 import numpy as np import traceback +import os class ImageDetector: def __init__(self, detection_engine): self.engine = detection_engine def detect_static_image(self, image_path): - """检测静态图像""" - print(f"正在加载图像: {image_path}") + print(f"Loading image: {image_path}") - if not self._check_image_exists(image_path): + if not os.path.exists(image_path): + print(f"Error: Image file not found - {image_path}") return - frame = self._load_image(image_path) + frame = cv2.imread(image_path) if frame is None or frame.size == 0: - print("错误: 无法读取图像文件或图像为空") + print("Error: Failed to load image") return - print("正在检测图像...") - results, annotated_frame = self._perform_detection(frame) + print("Running detection...") + annotated_frame, results = self.engine.detect(frame) - if annotated_frame is None: - print("错误: 检测未返回有效图像") + if annotated_frame is None or annotated_frame.size == 0: + print("Error: Invalid annotated frame") return - # 显示检测结果文本 self._display_results(results) - - # 显示图像窗口(带固定初始大小) - self._show_image(annotated_frame, 'YOLO 检测结果 - 静态图像') - - def _check_image_exists(self, image_path): - import os - if not os.path.exists(image_path): - print(f"错误: 图像文件不存在 - {image_path}") - return False - return True - - def _load_image(self, image_path): - try: - with open(image_path, "rb") as f: - bytes_data = bytearray(f.read()) - nparr = np.frombuffer(bytes_data, np.uint8) - frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) - return frame - except Exception as e: - print(f"加载图像时出错: {e}") - return None - - def _perform_detection(self, frame): - try: - annotated_frame, results = self.engine.detect(frame) - return results, annotated_frame - except Exception as e: - print(f"检测过程中发生错误: {e}") - traceback.print_exc() - return [], None + self._show_image(annotated_frame, "YOLO_Static_Detection") def _display_results(self, results): if not results: - print("未检测到任何对象(results 为空)") + print("No objects detected.") return result = results[0] boxes = result.boxes if len(boxes) == 0: - print("未检测到任何对象") + print("No objects detected.") return - print("检测完成,正在显示结果...") - print(f"检测到 {len(boxes)} 个对象:") - + print(f"Detected {len(boxes)} object(s):") names_list = self.engine.model.names for i, box in enumerate(boxes): try: cls_index = int(box.cls.item()) confidence = box.conf.item() - - if 0 <= cls_index < len(names_list): - cls_name = names_list[cls_index] - else: - cls_name = f"unknown_class_{cls_index}" - print(f" ⚠️ 警告:类别索引 {cls_index} 超出范围(共 {len(names_list)} 类)") - - print(f" {i+1}. {cls_name} (置信度: {confidence:.2f})") + cls_name = names_list[cls_index] if 0 <= cls_index < len(names_list) else f"unknown_{cls_index}" + print(f" {i+1}. {cls_name} (confidence: {confidence:.2f})") except Exception as e: - print(f" ⚠️ 解析第 {i+1} 个检测框时出错: {e}") + print(f" Warning: Failed to parse box {i+1}: {e}") - def _show_image(self, annotated_frame, window_name): - """显示图像,并设置窗口为可调整的小尺寸""" - if annotated_frame is None or annotated_frame.size == 0: - print("错误: 标注帧无效,无法显示") - return - - print("\n图像已显示。按任意键关闭窗口...") + def _show_image(self, annotated_frame, window_name="YOLO_Static_Detection"): try: - # 创建可调整大小的窗口 + cv2.destroyAllWindows() cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) - # 设置初始窗口大小为 800x600(你可以按需修改) cv2.resizeWindow(window_name, 800, 600) - # 显示图像(OpenCV 会自动适应窗口) cv2.imshow(window_name, annotated_frame) cv2.waitKey(0) cv2.destroyAllWindows() - print("窗口已关闭。") except Exception as e: - print(f"显示图像时发生错误: {e}") + print(f"Failed to display image: {e}") traceback.print_exc() From 2e4d4a8d39e3e8d58266db34a47c376cd93a5c92 Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Tue, 16 Dec 2025 17:30:16 +0800 Subject: [PATCH 29/30] Update camera_detector.py --- src/image_object_detection/camera_detector.py | 119 ++++++------------ 1 file changed, 41 insertions(+), 78 deletions(-) diff --git a/src/image_object_detection/camera_detector.py b/src/image_object_detection/camera_detector.py index cb0ad65ca4..466e923f15 100644 --- a/src/image_object_detection/camera_detector.py +++ b/src/image_object_detection/camera_detector.py @@ -1,110 +1,73 @@ # camera_detector.py import cv2 import time -import os import traceback class CameraDetector: - def __init__(self, detection_engine, camera_index=0): + def __init__(self, detection_engine, output_interval=1.0): self.engine = detection_engine - self.camera_index = camera_index - self.window_name = "YOLO 实时检测 - 摄像头" + self.output_interval = output_interval + self.last_output_time = 0 + self.frame_count = 0 + self.window_name = "YOLO_Live_Detection" # 👈 英文窗口名 - def detect_camera(self): - """对外统一接口:启动摄像头实时检测""" - self.start_detection() - - def start_detection(self): - """实际执行摄像头检测的逻辑""" - print("正在初始化摄像头...") - cap = self._open_camera() - if cap is None: + def start_detection(self, camera_index=0): + cap = cv2.VideoCapture(camera_index) + if not cap.isOpened(): + print(f"Error: Cannot open camera {camera_index}") return - cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL) - cv2.resizeWindow(self.window_name, 800, 600) - - print("摄像头已启动。按 'q' 或 ESC 退出,按 's' 保存当前帧。") - - prev_time = time.time() - saved_frame_index = 0 + print("Starting live detection. Press 'q' to quit, 's' to save frame.") + cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL) # 创建一次即可 try: while True: ret, frame = cap.read() if not ret: - print("警告: 无法读取摄像头帧(可能设备被拔出或占用)") + print("Warning: Failed to read frame from camera") break - annotated_frame, results = self._perform_detection(frame) - if annotated_frame is None: - annotated_frame = frame + current_time = time.time() + annotated_frame, results = self.engine.detect(frame) - curr_time = time.time() - fps = 1.0 / (curr_time - prev_time) if (curr_time - prev_time) > 0 else 0 - prev_time = curr_time - - num_objects = len(results[0].boxes) if results else 0 - info_text = f"FPS: {fps:.1f} | Objects: {num_objects}" - cv2.putText( - annotated_frame, - info_text, - (10, 30), - cv2.FONT_HERSHEY_SIMPLEX, - 0.7, - (0, 255, 0), - 2 - ) + if annotated_frame is None or annotated_frame.size == 0: + print("Warning: Invalid detection result") + continue cv2.imshow(self.window_name, annotated_frame) key = cv2.waitKey(1) & 0xFF - if key == ord('q') or key == 27: - print("用户请求退出。") + if key == ord('q'): break elif key == ord('s'): - saved_frame_index = self._save_frame(annotated_frame, saved_frame_index) - + self.save_frame(annotated_frame) + + self._print_fps_if_needed(current_time) + self.frame_count += 1 + + except KeyboardInterrupt: + print("\nDetection interrupted by user.") except Exception as e: - print(f"检测过程中发生未预期错误: {e}") + print(f"Unexpected error during detection: {e}") traceback.print_exc() finally: cap.release() cv2.destroyAllWindows() - print("摄像头已关闭,窗口已清理.") - - def _open_camera(self): - cap = cv2.VideoCapture(self.camera_index) - if not cap.isOpened(): - print(f"错误: 无法打开摄像头(索引 {self.camera_index})") - print("请检查摄像头是否连接、是否被其他程序占用。") - return None - - cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) - cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) - return cap - - def _perform_detection(self, frame): - try: - # 抑制 YOLO 内部输出 - import sys, os - old_stdout = sys.stdout - sys.stdout = open(os.devnull, 'w') - annotated_frame, results = self.engine.detect(frame) - sys.stdout = old_stdout - return annotated_frame, results - except Exception as e: - print(f"单帧检测失败: {e}") - return None, [] - - def _save_frame(self, frame, index): - save_dir = "saved_frames" - os.makedirs(save_dir, exist_ok=True) - filename = os.path.join(save_dir, f"frame_{index:04d}.jpg") + print("Camera released and windows closed.") + + def _print_fps_if_needed(self, current_time): + if current_time - self.last_output_time >= self.output_interval: + fps = self.frame_count / (current_time - self.last_output_time) if self.last_output_time > 0 else 0 + print(f"FPS: {fps:.2f}") + self.last_output_time = current_time + self.frame_count = 0 + + def save_frame(self, frame): + import os + timestamp = int(time.time()) + filename = f"saved_frame_{timestamp}.jpg" success = cv2.imwrite(filename, frame) if success: - print(f"✅ 帧已保存: {filename}") - return index + 1 + print(f"Frame saved as {filename}") else: - print("❌ 保存帧失败!") - return index + print("Failed to save frame") From 950cf9f25d2dca065bf4bc23c040b41da5cf978f Mon Sep 17 00:00:00 2001 From: is-leplus <3210209742@qq.com> Date: Tue, 16 Dec 2025 17:30:51 +0800 Subject: [PATCH 30/30] Update ui_handler.py --- src/image_object_detection/ui_handler.py | 94 +++++++++--------------- 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/src/image_object_detection/ui_handler.py b/src/image_object_detection/ui_handler.py index 53e4036e0d..1e3f1073c3 100644 --- a/src/image_object_detection/ui_handler.py +++ b/src/image_object_detection/ui_handler.py @@ -1,75 +1,49 @@ # ui_handler.py +import os import cv2 +import traceback from detection_engine import DetectionEngine from image_detector import ImageDetector from camera_detector import CameraDetector -import sys -import os class UIHandler: def __init__(self, config): self.config = config - self.detection_engine = DetectionEngine() - self.image_detector = ImageDetector(self.detection_engine) - self.camera_detector = CameraDetector(self.detection_engine) + self.engine = DetectionEngine( + model_path=config.model_path, + conf_threshold=config.confidence_threshold + ) + self.stop_flag = False - def print_debug(self, message): - """调试输出函数""" - print(f"[DEBUG] {message}") - sys.stdout.flush() # 强制刷新输出缓冲区 - - def display_menu(self): - """显示主菜单""" - print("=" * 60) - print(" 欢迎使用 YOLO 对象检测系统") - print("=" * 60) + def run(self): + print("=== YOLO Detection System ===") + print("1. Static Image Detection") + print("2. Live Camera Detection") + print("3. Exit") - print(f"\n测试图像路径: {self.config.test_image_path}") - print(f"图像文件存在: {os.path.exists(self.config.test_image_path)}") + choice = input("Please select an option (1-3): ").strip() - print("\n请选择检测模式:") - print("1. 检测静态图像") - print("2. 检测摄像头实时画面") - print("3. 退出程序") - - def handle_choice(self, choice): - """处理用户选择""" - if choice == '1': - print("\n您选择了: 检测静态图像") - self.image_detector.detect_static_image(self.config.test_image_path) - return True - elif choice == '2': - print("\n您选择了: 检测摄像头实时画面") - print("注意:要退出摄像头模式,请按 Ctrl+C !") - self.camera_detector.detect_camera() - return True - elif choice == '3': - print("\n程序已退出。") - return False + if choice == "1": + self._run_static_detection() + elif choice == "2": + self._run_camera_detection() + elif choice == "3": + print("Exiting program.") else: - print("\n无效选择,请输入 1、2 或 3") - return True + print("Invalid option. Please enter 1, 2, or 3.") - def run(self): - """运行主程序""" - self.print_debug("程序开始执行") - self.display_menu() + def _run_static_detection(self): + image_path = self.config.test_image_path + if not os.path.exists(image_path): + print(f"Image not found: {image_path}") + return - running = True - while running: - try: - choice = input("\n请输入您的选择 (1/2/3): ").strip() - self.print_debug(f"用户输入: {choice}") - running = self.handle_choice(choice) - - except KeyboardInterrupt: - print("\n\n程序被用户中断。") - break - except EOFError: - print("\n输入结束,程序退出。") - break - except Exception as e: - print(f"\n发生错误: {e}") - break - - self.print_debug("程序结束") \ No newline at end of file + detector = ImageDetector(self.engine) + detector.detect_static_image(image_path) + + def _run_camera_detection(self): + detector = CameraDetector( + detection_engine=self.engine, + output_interval=self.config.output_interval + ) + detector.start_detection(camera_index=self.config.camera_index)