first commit

main
tongminqi 2026-02-28 15:23:39 +08:00
commit 0c6bb6c6fe
15439 changed files with 115298 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.xml filter=lfs diff=lfs merge=lfs -text

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

12
.idea/TomatoDetection.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

BIN
.idea/encodings.xml (Stored with Git LFS) Normal file

Binary file not shown.

BIN
.idea/inspectionProfiles/Project_Default.xml (Stored with Git LFS) Normal file

Binary file not shown.

BIN
.idea/inspectionProfiles/profiles_settings.xml (Stored with Git LFS) Normal file

Binary file not shown.

BIN
.idea/misc.xml (Stored with Git LFS) Normal file

Binary file not shown.

BIN
.idea/modules.xml (Stored with Git LFS) Normal file

Binary file not shown.

BIN
.idea/vcs.xml (Stored with Git LFS) Normal file

Binary file not shown.

20
CITATION.cff Normal file
View File

@ -0,0 +1,20 @@
cff-version: 1.2.0
preferred-citation:
type: software
message: If you use this software, please cite it as below.
authors:
- family-names: Jocher
given-names: Glenn
orcid: "https://orcid.org/0000-0001-5950-6979"
- family-names: Chaurasia
given-names: Ayush
orcid: "https://orcid.org/0000-0002-7603-6750"
- family-names: Qiu
given-names: Jing
orcid: "https://orcid.org/0000-0003-3783-7069"
title: "YOLO by Ultralytics"
version: 8.0.0
# doi: 10.5281/zenodo.3908559 # TODO
date-released: 2023-1-10
license: AGPL-3.0
url: "https://github.com/ultralytics/ultralytics"

48
CameraTest.py Normal file
View File

@ -0,0 +1,48 @@
#coding:utf-8
import cv2
from ultralytics import YOLO
# 所需加载的模型目录
path = 'models/best.pt'
# Load the YOLOv8 model
model = YOLO(path)
ID = 0
while(ID<10):
cap = cv2.VideoCapture(ID)
# get a frame
ret, frame = cap.read()
if ret == False:
ID += 1
else:
print('摄像头ID:',ID)
break
# Loop through the video frames
while cap.isOpened():
# Read a frame from the video
success, frame = cap.read()
if success:
# Run YOLOv8 inference on the frame
results = model(frame)
# Visualize the results on the frame
annotated_frame = results[0].plot()
# Display the annotated frame
cv2.imshow("YOLOv8 Inference", annotated_frame)
# Break the loop if 'q' is pressed
if cv2.waitKey(1) & 0xFF == ord("q"):
break
else:
# Break the loop if the end of the video is reached
break
# Release the video capture object and close the display window
cap.release()
cv2.destroyAllWindows()

60
Config.py Normal file
View File

@ -0,0 +1,60 @@
#coding:utf-8
# 图片及视频检测结果保存路径
save_path = 'save_data'
# 使用的模型路径
# model_path = 'models/best.pt'
# names = {0: 'Rescued', 1: 'Rescuing'}
# CH_names = ['已救援', '救援中']
# 猫和狗
# 训练好的模型路径
# model_path = 'runs/detect/train5/weights/best.pt'
# # 类别名称
# names = {0: 'cat', 1: 'dog'}
# # 中文类别名称
# CH_names = ['猫', '狗']
# # 马甲
# # 训练好的模型路径
# model_path = 'runs/detect/train5/weights/best.pt'
# # # 类别名称
# names = {0: 'vest', 1: 'novest'}
# # # 中文类别名称
# CH_names = ['有马甲', '无马甲']
# # 仪表盘
# # 训练好的模型路径
# model_path = 'runs/detect/train6/weights/best.pt'
# # # 类别名称
# names = {0: 'dashboard', 1: 'nodashboard'}
# # # 中文类别名称
# CH_names = ['仪表盘', '无仪表盘']
# # 搭电设备
# # 训练好的模型路径
# model_path = 'runs/detect/train9/weights/best.pt'
# # # 类别名称
# names = {0: 'JumperCable', 1: 'NoJumperCable'}
# # # 中文类别名称
# CH_names = ['有搭电设备', '无搭电设备']
# 搭电测试
# 训练好的模型路径
model_path = 'runs/segment/train11/weights/best.pt'
# # 类别名称
names = {0: 'da_dian_xian', 1: 'tuo_che_shang_you_che',2:'yi_biao_pan',3:'lun_tai',4:'ping_an_ma_jia',5:'jian_ce_yi'}
# # 中文类别名称
CH_names = ['搭电线','拖车上油车','仪表盘','轮台','平安马甲','检测仪']

BIN
Font/platech.ttf Normal file

Binary file not shown.

625
MainProgram.py Normal file
View File

@ -0,0 +1,625 @@
# -*- coding: utf-8 -*-
import time
from PyQt5.QtWidgets import QApplication , QMainWindow, QFileDialog, \
QMessageBox,QWidget,QHeaderView,QTableWidgetItem, QAbstractItemView
import sys
import os
from PIL import ImageFont
from ultralytics import YOLO
sys.path.append('UIProgram')
from UIProgram.UiMain import Ui_MainWindow
import sys
from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal,QCoreApplication
import detect_tools as tools
import cv2
import Config
from UIProgram.QssLoader import QSSLoader
from UIProgram.precess_bar import ProgressBar
import numpy as np
import torch
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.initMain()
self.signalconnect()
# 加载css渲染效果
style_file = 'UIProgram/style.css'
qssStyleSheet = QSSLoader.read_qss_file(style_file)
self.setStyleSheet(qssStyleSheet)
self.conf = 0.25
self.iou = 0.7
def signalconnect(self):
self.ui.PicBtn.clicked.connect(self.open_img)
self.ui.comboBox.activated.connect(self.combox_change)
self.ui.VideoBtn.clicked.connect(self.vedio_show)
self.ui.CapBtn.clicked.connect(self.camera_show)
self.ui.SaveBtn.clicked.connect(self.save_detect_video)
self.ui.ExitBtn.clicked.connect(QCoreApplication.quit)
self.ui.FilesBtn.clicked.connect(self.detact_batch_imgs)
def initMain(self):
self.show_width = 770
self.show_height = 480
self.org_path = None
self.is_camera_open = False
self.cap = None
self.device = 0 if torch.cuda.is_available() else 'cpu'
# 加载检测模型
self.model = YOLO(Config.model_path, task='detect')
self.model(np.zeros((48, 48, 3)), device=self.device) #预先加载推理模型
self.fontC = ImageFont.truetype("Font/platech.ttf", 25, 0)
# 用于绘制不同颜色矩形框
self.colors = tools.Colors()
# 更新视频图像
self.timer_camera = QTimer()
# 更新检测信息表格
# self.timer_info = QTimer()
# 保存视频
self.timer_save_video = QTimer()
# 表格
self.ui.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
self.ui.tableWidget.verticalHeader().setDefaultSectionSize(40)
self.ui.tableWidget.setColumnWidth(0, 80) # 设置列宽
self.ui.tableWidget.setColumnWidth(1, 200)
self.ui.tableWidget.setColumnWidth(2, 150)
self.ui.tableWidget.setColumnWidth(3, 90)
self.ui.tableWidget.setColumnWidth(4, 230)
# self.ui.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 表格铺满
# self.ui.tableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
# self.ui.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) # 设置表格不可编辑
self.ui.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置表格整行选中
self.ui.tableWidget.verticalHeader().setVisible(False) # 隐藏列标题
self.ui.tableWidget.setAlternatingRowColors(True) # 表格背景交替
# 设置主页背景图片border-image: url(:/icons/ui_imgs/icons/camera.png)
# self.setStyleSheet("#MainWindow{background-image:url(:/bgs/ui_imgs/bg3.jpg)}")
def open_img(self):
if self.cap:
# 打开图片前关闭摄像头
self.video_stop()
self.is_camera_open = False
self.ui.CaplineEdit.setText('摄像头未开启')
self.cap = None
# 弹出的窗口名称:'打开图片'
# 默认打开的目录:'./'
# 只能打开.jpg与.gif结尾的图片文件
# file_path, _ = QFileDialog.getOpenFileName(self.ui.centralwidget, '打开图片', './', "Image files (*.jpg *.gif)")
file_path, _ = QFileDialog.getOpenFileName(None, '打开图片', './', "Image files (*.jpg *.jpeg *.png *.bmp)")
if not file_path:
return
self.ui.comboBox.setDisabled(False)
self.org_path = file_path
self.org_img = tools.img_cvread(self.org_path)
# 目标检测
t1 = time.time()
self.results = self.model(self.org_path, conf=self.conf, iou=self.iou)[0]
t2 = time.time()
take_time_str = '{:.3f} s'.format(t2 - t1)
self.ui.time_lb.setText(take_time_str)
location_list = self.results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
cls_list = self.results.boxes.cls.tolist()
self.cls_list = [int(i) for i in cls_list]
self.conf_list = self.results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each*100) for each in self.conf_list]
# now_img = self.cv_img.copy()
# for loacation, type_id, conf in zip(self.location_list, self.cls_list, self.conf_list):
# type_id = int(type_id)
# color = self.colors(int(type_id), True)
# # cv2.rectangle(now_img, (int(x1), int(y1)), (int(x2), int(y2)), colors(int(type_id), True), 3)
# now_img = tools.drawRectBox(now_img, loacation, Config.CH_names[type_id], self.fontC, color)
now_img = self.results.plot()
self.draw_img = now_img
# 获取缩放后的图片尺寸
self.img_width, self.img_height = self.get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img,(self.img_width, self.img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
self.ui.label_show.setPixmap(pix_img)
self.ui.label_show.setAlignment(Qt.AlignCenter)
# 设置路径显示
self.ui.PiclineEdit.setText(self.org_path)
# 目标数目
target_nums = len(self.cls_list)
self.ui.label_nums.setText(str(target_nums))
# 设置目标选择下拉框
choose_list = ['全部']
target_names = [Config.names[id]+ '_'+ str(index) for index,id in enumerate(self.cls_list)]
# object_list = sorted(set(self.cls_list))
# for each in object_list:
# choose_list.append(Config.CH_names[each])
choose_list = choose_list + target_names
self.ui.comboBox.clear()
self.ui.comboBox.addItems(choose_list)
if target_nums >= 1:
self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
self.ui.label_conf.setText(str(self.conf_list[0]))
# 默认显示第一个目标框坐标
# 设置坐标位置值
self.ui.label_xmin.setText(str(self.location_list[0][0]))
self.ui.label_ymin.setText(str(self.location_list[0][1]))
self.ui.label_xmax.setText(str(self.location_list[0][2]))
self.ui.label_ymax.setText(str(self.location_list[0][3]))
else:
self.ui.type_lb.setText('')
self.ui.label_conf.setText('')
self.ui.label_xmin.setText('')
self.ui.label_ymin.setText('')
self.ui.label_xmax.setText('')
self.ui.label_ymax.setText('')
# # 删除表格所有行
self.ui.tableWidget.setRowCount(0)
self.ui.tableWidget.clearContents()
self.tabel_info_show(self.location_list, self.cls_list, self.conf_list,path=self.org_path)
def detact_batch_imgs(self):
if self.cap:
# 打开图片前关闭摄像头
self.video_stop()
self.is_camera_open = False
self.ui.CaplineEdit.setText('摄像头未开启')
self.cap = None
directory = QFileDialog.getExistingDirectory(self,
"选取文件夹",
"./") # 起始路径
if not directory:
return
self.org_path = directory
img_suffix = ['jpg','png','jpeg','bmp']
for file_name in os.listdir(directory):
full_path = os.path.join(directory,file_name)
if os.path.isfile(full_path) and file_name.split('.')[-1].lower() in img_suffix:
# self.ui.comboBox.setDisabled(False)
img_path = full_path
self.org_img = tools.img_cvread(img_path)
# 目标检测
t1 = time.time()
self.results = self.model(img_path,conf=self.conf, iou=self.iou)[0]
t2 = time.time()
take_time_str = '{:.3f} s'.format(t2 - t1)
self.ui.time_lb.setText(take_time_str)
location_list = self.results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
cls_list = self.results.boxes.cls.tolist()
self.cls_list = [int(i) for i in cls_list]
self.conf_list = self.results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
now_img = self.results.plot()
self.draw_img = now_img
# 获取缩放后的图片尺寸
self.img_width, self.img_height = self.get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
self.ui.label_show.setPixmap(pix_img)
self.ui.label_show.setAlignment(Qt.AlignCenter)
# 设置路径显示
self.ui.PiclineEdit.setText(img_path)
# 目标数目
target_nums = len(self.cls_list)
self.ui.label_nums.setText(str(target_nums))
# 设置目标选择下拉框
choose_list = ['全部']
target_names = [Config.names[id] + '_' + str(index) for index, id in enumerate(self.cls_list)]
choose_list = choose_list + target_names
self.ui.comboBox.clear()
self.ui.comboBox.addItems(choose_list)
if target_nums >= 1:
self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
self.ui.label_conf.setText(str(self.conf_list[0]))
# 默认显示第一个目标框坐标
# 设置坐标位置值
self.ui.label_xmin.setText(str(self.location_list[0][0]))
self.ui.label_ymin.setText(str(self.location_list[0][1]))
self.ui.label_xmax.setText(str(self.location_list[0][2]))
self.ui.label_ymax.setText(str(self.location_list[0][3]))
else:
self.ui.type_lb.setText('')
self.ui.label_conf.setText('')
self.ui.label_xmin.setText('')
self.ui.label_ymin.setText('')
self.ui.label_xmax.setText('')
self.ui.label_ymax.setText('')
# # 删除表格所有行
# self.ui.tableWidget.setRowCount(0)
# self.ui.tableWidget.clearContents()
self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=img_path)
self.ui.tableWidget.scrollToBottom()
QApplication.processEvents() #刷新页面
def draw_rect_and_tabel(self, results, img):
now_img = img.copy()
location_list = results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
cls_list = results.boxes.cls.tolist()
self.cls_list = [int(i) for i in cls_list]
self.conf_list = results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
for loacation, type_id, conf in zip(self.location_list, self.cls_list, self.conf_list):
type_id = int(type_id)
color = self.colors(int(type_id), True)
# cv2.rectangle(now_img, (int(x1), int(y1)), (int(x2), int(y2)), colors(int(type_id), True), 3)
now_img = tools.drawRectBox(now_img, loacation, Config.CH_names[type_id], self.fontC, color)
# 获取缩放后的图片尺寸
self.img_width, self.img_height = self.get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
self.ui.label_show.setPixmap(pix_img)
self.ui.label_show.setAlignment(Qt.AlignCenter)
# 设置路径显示
self.ui.PiclineEdit.setText(self.org_path)
# 目标数目
target_nums = len(self.cls_list)
self.ui.label_nums.setText(str(target_nums))
if target_nums >= 1:
self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
self.ui.label_conf.setText(str(self.conf_list[0]))
self.ui.label_xmin.setText(str(self.location_list[0][0]))
self.ui.label_ymin.setText(str(self.location_list[0][1]))
self.ui.label_xmax.setText(str(self.location_list[0][2]))
self.ui.label_ymax.setText(str(self.location_list[0][3]))
else:
self.ui.type_lb.setText('')
self.ui.label_conf.setText('')
self.ui.label_xmin.setText('')
self.ui.label_ymin.setText('')
self.ui.label_xmax.setText('')
self.ui.label_ymax.setText('')
# 删除表格所有行
self.ui.tableWidget.setRowCount(0)
self.ui.tableWidget.clearContents()
self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=self.org_path)
return now_img
def combox_change(self):
com_text = self.ui.comboBox.currentText()
if com_text == '全部':
cur_box = self.location_list
cur_img = self.results.plot()
self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
self.ui.label_conf.setText(str(self.conf_list[0]))
else:
index = int(com_text.split('_')[-1])
cur_box = [self.location_list[index]]
cur_img = self.results[index].plot()
self.ui.type_lb.setText(Config.CH_names[self.cls_list[index]])
self.ui.label_conf.setText(str(self.conf_list[index]))
# 设置坐标位置值
self.ui.label_xmin.setText(str(cur_box[0][0]))
self.ui.label_ymin.setText(str(cur_box[0][1]))
self.ui.label_xmax.setText(str(cur_box[0][2]))
self.ui.label_ymax.setText(str(cur_box[0][3]))
resize_cvimg = cv2.resize(cur_img, (self.img_width, self.img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
self.ui.label_show.clear()
self.ui.label_show.setPixmap(pix_img)
self.ui.label_show.setAlignment(Qt.AlignCenter)
def get_video_path(self):
file_path, _ = QFileDialog.getOpenFileName(None, '打开视频', './', "Image files (*.avi *.mp4 *.wmv *.mkv)")
if not file_path:
return None
self.org_path = file_path
self.ui.VideolineEdit.setText(file_path)
return file_path
def video_start(self):
# 删除表格所有行
self.ui.tableWidget.setRowCount(0)
self.ui.tableWidget.clearContents()
# 清空下拉框
self.ui.comboBox.clear()
# 定时器开启,每隔一段时间,读取一帧
self.timer_camera.start(1)
self.timer_camera.timeout.connect(self.open_frame)
def tabel_info_show(self, locations, clses, confs, path=None):
path = path
for location, cls, conf in zip(locations, clses, confs):
row_count = self.ui.tableWidget.rowCount() # 返回当前行数(尾部)
self.ui.tableWidget.insertRow(row_count) # 尾部插入一行
item_id = QTableWidgetItem(str(row_count+1)) # 序号
item_id.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # 设置文本居中
item_path = QTableWidgetItem(str(path)) # 路径
# item_path.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
item_cls = QTableWidgetItem(str(Config.CH_names[cls]))
item_cls.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # 设置文本居中
item_conf = QTableWidgetItem(str(conf))
item_conf.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # 设置文本居中
item_location = QTableWidgetItem(str(location)) # 目标框位置
# item_location.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # 设置文本居中
self.ui.tableWidget.setItem(row_count, 0, item_id)
self.ui.tableWidget.setItem(row_count, 1, item_path)
self.ui.tableWidget.setItem(row_count, 2, item_cls)
self.ui.tableWidget.setItem(row_count, 3, item_conf)
self.ui.tableWidget.setItem(row_count, 4, item_location)
self.ui.tableWidget.scrollToBottom()
def video_stop(self):
self.cap.release()
self.timer_camera.stop()
# self.timer_info.stop()
def open_frame(self):
ret, now_img = self.cap.read()
if ret:
# 目标检测
t1 = time.time()
results = self.model(now_img,conf=self.conf, iou=self.iou)[0]
t2 = time.time()
take_time_str = '{:.3f} s'.format(t2 - t1)
self.ui.time_lb.setText(take_time_str)
location_list = results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
cls_list = results.boxes.cls.tolist()
self.cls_list = [int(i) for i in cls_list]
self.conf_list = results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
now_img = results.plot()
# 获取缩放后的图片尺寸
self.img_width, self.img_height = self.get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
self.ui.label_show.setPixmap(pix_img)
self.ui.label_show.setAlignment(Qt.AlignCenter)
# 目标数目
target_nums = len(self.cls_list)
self.ui.label_nums.setText(str(target_nums))
# 设置目标选择下拉框
choose_list = ['全部']
target_names = [Config.names[id] + '_' + str(index) for index, id in enumerate(self.cls_list)]
# object_list = sorted(set(self.cls_list))
# for each in object_list:
# choose_list.append(Config.CH_names[each])
choose_list = choose_list + target_names
self.ui.comboBox.clear()
self.ui.comboBox.addItems(choose_list)
if target_nums >= 1:
self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
self.ui.label_conf.setText(str(self.conf_list[0]))
# 默认显示第一个目标框坐标
# 设置坐标位置值
self.ui.label_xmin.setText(str(self.location_list[0][0]))
self.ui.label_ymin.setText(str(self.location_list[0][1]))
self.ui.label_xmax.setText(str(self.location_list[0][2]))
self.ui.label_ymax.setText(str(self.location_list[0][3]))
else:
self.ui.type_lb.setText('')
self.ui.label_conf.setText('')
self.ui.label_xmin.setText('')
self.ui.label_ymin.setText('')
self.ui.label_xmax.setText('')
self.ui.label_ymax.setText('')
# 删除表格所有行
# self.ui.tableWidget.setRowCount(0)
# self.ui.tableWidget.clearContents()
self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=self.org_path)
else:
self.cap.release()
self.timer_camera.stop()
def vedio_show(self):
if self.is_camera_open:
self.is_camera_open = False
self.ui.CaplineEdit.setText('摄像头未开启')
video_path = self.get_video_path()
if not video_path:
return None
self.cap = cv2.VideoCapture(video_path)
self.video_start()
self.ui.comboBox.setDisabled(True)
def camera_show(self):
self.is_camera_open = not self.is_camera_open
if self.is_camera_open:
self.ui.CaplineEdit.setText('摄像头开启')
self.cap = cv2.VideoCapture(0)
self.video_start()
self.ui.comboBox.setDisabled(True)
else:
self.ui.CaplineEdit.setText('摄像头未开启')
self.ui.label_show.setText('')
if self.cap:
self.cap.release()
cv2.destroyAllWindows()
self.ui.label_show.clear()
def get_resize_size(self, img):
_img = img.copy()
img_height, img_width , depth= _img.shape
ratio = img_width / img_height
if ratio >= self.show_width / self.show_height:
self.img_width = self.show_width
self.img_height = int(self.img_width / ratio)
else:
self.img_height = self.show_height
self.img_width = int(self.img_height * ratio)
return self.img_width, self.img_height
def save_detect_video(self):
if self.cap is None and not self.org_path:
QMessageBox.about(self, '提示', '当前没有可保存信息,请先打开图片或视频!')
return
if self.is_camera_open:
QMessageBox.about(self, '提示', '摄像头视频无法保存!')
return
if self.cap:
res = QMessageBox.information(self, '提示', '保存视频检测结果可能需要较长时间,请确认是否继续保存?',QMessageBox.Yes | QMessageBox.No , QMessageBox.Yes)
if res == QMessageBox.Yes:
self.video_stop()
com_text = self.ui.comboBox.currentText()
self.btn2Thread_object = btn2Thread(self.org_path, self.model, com_text,self.conf,self.iou)
self.btn2Thread_object.start()
self.btn2Thread_object.update_ui_signal.connect(self.update_process_bar)
else:
return
else:
if os.path.isfile(self.org_path):
fileName = os.path.basename(self.org_path)
name , end_name= fileName.rsplit(".",1)
save_name = name + '_detect_result.' + end_name
save_img_path = os.path.join(Config.save_path, save_name)
# 保存图片
cv2.imwrite(save_img_path, self.draw_img)
QMessageBox.about(self, '提示', '图片保存成功!\n文件路径:{}'.format(save_img_path))
else:
img_suffix = ['jpg', 'png', 'jpeg', 'bmp']
for file_name in os.listdir(self.org_path):
full_path = os.path.join(self.org_path, file_name)
if os.path.isfile(full_path) and file_name.split('.')[-1].lower() in img_suffix:
name, end_name = file_name.rsplit(".",1)
save_name = name + '_detect_result.' + end_name
save_img_path = os.path.join(Config.save_path, save_name)
results = self.model(full_path,conf=self.conf, iou=self.iou)[0]
now_img = results.plot()
# 保存图片
cv2.imwrite(save_img_path, now_img)
QMessageBox.about(self, '提示', '图片保存成功!\n文件路径:{}'.format(Config.save_path))
def update_process_bar(self,cur_num, total):
if cur_num == 1:
self.progress_bar = ProgressBar(self)
self.progress_bar.show()
if cur_num >= total:
self.progress_bar.close()
QMessageBox.about(self, '提示', '视频保存成功!\n文件在{}目录下'.format(Config.save_path))
return
if self.progress_bar.isVisible() is False:
# 点击取消保存时,终止进程
self.btn2Thread_object.stop()
return
value = int(cur_num / total *100)
self.progress_bar.setValue(cur_num, total, value)
QApplication.processEvents()
class btn2Thread(QThread):
"""
进行检测后的视频保存
"""
# 声明一个信号
update_ui_signal = pyqtSignal(int,int)
def __init__(self, path, model, com_text,conf,iou):
super(btn2Thread, self).__init__()
self.org_path = path
self.model = model
self.com_text = com_text
self.conf = conf
self.iou = iou
# 用于绘制不同颜色矩形框
self.colors = tools.Colors()
self.is_running = True # 标志位,表示线程是否正在运行
def run(self):
# VideoCapture方法是cv2库提供的读取视频方法
cap = cv2.VideoCapture(self.org_path)
# 设置需要保存视频的格式“xvid”
# 该参数是MPEG-4编码类型文件名后缀为.avi
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# 设置视频帧频
fps = cap.get(cv2.CAP_PROP_FPS)
# 设置视频大小
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# VideoWriter方法是cv2库提供的保存视频方法
# 按照设置的格式来out输出
fileName = os.path.basename(self.org_path)
name, end_name = fileName.split('.')
save_name = name + '_detect_result.avi'
save_video_path = os.path.join(Config.save_path, save_name)
out = cv2.VideoWriter(save_video_path, fourcc, fps, size)
prop = cv2.CAP_PROP_FRAME_COUNT
total = int(cap.get(prop))
print("[INFO] 视频总帧数:{}".format(total))
cur_num = 0
# 确定视频打开并循环读取
while (cap.isOpened() and self.is_running):
cur_num += 1
print('当前第{}帧,总帧数{}'.format(cur_num, total))
# 逐帧读取ret返回布尔值
# 参数ret为True 或者False,代表有没有读取到图片
# frame表示截取到一帧的图片
ret, frame = cap.read()
if ret == True:
# 检测
results = self.model(frame,conf=self.conf,iou=self.iou)[0]
frame = results.plot()
out.write(frame)
self.update_ui_signal.emit(cur_num, total)
else:
break
# 释放资源
cap.release()
out.release()
def stop(self):
self.is_running = False
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

BIN
TestFiles/1.mp4 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
TestFiles/unriped tomato_46.png (Stored with Git LFS) Normal file

Binary file not shown.

8
UIProgram/QssLoader.py Normal file
View File

@ -0,0 +1,8 @@
class QSSLoader:
def __init__(self):
pass
@staticmethod
def read_qss_file(qss_file_name):
with open(qss_file_name, 'r', encoding='UTF-8') as file:
return file.read()

522
UIProgram/UiMain.py Normal file
View File

@ -0,0 +1,522 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'UiMain.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1250, 830)
MainWindow.setMinimumSize(QtCore.QSize(1250, 830))
MainWindow.setMaximumSize(QtCore.QSize(1250, 830))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/ui_imgs/icons/目标检测.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setGeometry(QtCore.QRect(10, 100, 791, 711))
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.frame_2 = QtWidgets.QFrame(self.frame)
self.frame_2.setGeometry(QtCore.QRect(10, 0, 771, 481))
self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
self.label_show = QtWidgets.QLabel(self.frame_2)
self.label_show.setGeometry(QtCore.QRect(0, 0, 770, 480))
self.label_show.setMinimumSize(QtCore.QSize(770, 480))
self.label_show.setMaximumSize(QtCore.QSize(770, 480))
self.label_show.setStyleSheet("border-image: url(:/icons/ui_imgs/11.png);")
self.label_show.setText("")
self.label_show.setObjectName("label_show")
self.frame_3 = QtWidgets.QFrame(self.frame)
self.frame_3.setGeometry(QtCore.QRect(10, 480, 771, 221))
self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_3.setObjectName("frame_3")
self.groupBox_3 = QtWidgets.QGroupBox(self.frame_3)
self.groupBox_3.setGeometry(QtCore.QRect(0, 10, 771, 221))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
self.groupBox_3.setFont(font)
self.groupBox_3.setObjectName("groupBox_3")
self.tableWidget = QtWidgets.QTableWidget(self.groupBox_3)
self.tableWidget.setGeometry(QtCore.QRect(10, 30, 751, 181))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(14)
self.tableWidget.setFont(font)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(5)
self.tableWidget.setRowCount(0)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(4, item)
self.frame_4 = QtWidgets.QFrame(self.centralwidget)
self.frame_4.setGeometry(QtCore.QRect(810, 100, 431, 711))
self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_4.setObjectName("frame_4")
self.groupBox = QtWidgets.QGroupBox(self.frame_4)
self.groupBox.setGeometry(QtCore.QRect(0, 0, 431, 171))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
self.groupBox.setFont(font)
self.groupBox.setObjectName("groupBox")
self.PiclineEdit = QtWidgets.QLineEdit(self.groupBox)
self.PiclineEdit.setGeometry(QtCore.QRect(70, 40, 311, 31))
self.PiclineEdit.setInputMask("")
self.PiclineEdit.setObjectName("PiclineEdit")
self.VideolineEdit = QtWidgets.QLineEdit(self.groupBox)
self.VideolineEdit.setGeometry(QtCore.QRect(70, 80, 311, 31))
self.VideolineEdit.setObjectName("VideolineEdit")
self.CapBtn = QtWidgets.QPushButton(self.groupBox)
self.CapBtn.setGeometry(QtCore.QRect(30, 120, 30, 30))
self.CapBtn.setStyleSheet("border-image: url(:/icons/ui_imgs/icons/camera.png);")
self.CapBtn.setText("")
self.CapBtn.setObjectName("CapBtn")
self.PicBtn = QtWidgets.QPushButton(self.groupBox)
self.PicBtn.setGeometry(QtCore.QRect(30, 40, 30, 30))
self.PicBtn.setStyleSheet("border-image: url(:/icons/ui_imgs/icons/img.png);")
self.PicBtn.setText("")
self.PicBtn.setObjectName("PicBtn")
self.VideoBtn = QtWidgets.QPushButton(self.groupBox)
self.VideoBtn.setGeometry(QtCore.QRect(30, 80, 30, 30))
self.VideoBtn.setStyleSheet("border-image: url(:/icons/ui_imgs/icons/video.png);")
self.VideoBtn.setText("")
self.VideoBtn.setObjectName("VideoBtn")
self.CaplineEdit = QtWidgets.QLineEdit(self.groupBox)
self.CaplineEdit.setGeometry(QtCore.QRect(70, 120, 311, 31))
self.CaplineEdit.setObjectName("CaplineEdit")
self.FilesBtn = QtWidgets.QPushButton(self.groupBox)
self.FilesBtn.setGeometry(QtCore.QRect(390, 40, 30, 30))
self.FilesBtn.setStyleSheet("border-image: url(:/icons/ui_imgs/icons/folder.png);")
self.FilesBtn.setText("")
self.FilesBtn.setObjectName("FilesBtn")
self.groupBox_2 = QtWidgets.QGroupBox(self.frame_4)
self.groupBox_2.setGeometry(QtCore.QRect(0, 180, 431, 371))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
self.groupBox_2.setFont(font)
self.groupBox_2.setObjectName("groupBox_2")
self.frame_6 = QtWidgets.QFrame(self.groupBox_2)
self.frame_6.setGeometry(QtCore.QRect(0, 190, 431, 171))
self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_6.setObjectName("frame_6")
self.label_4 = QtWidgets.QLabel(self.frame_6)
self.label_4.setGeometry(QtCore.QRect(10, 10, 131, 41))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
self.label_4.setFont(font)
self.label_4.setStyleSheet("")
self.label_4.setObjectName("label_4")
self.layoutWidget = QtWidgets.QWidget(self.frame_6)
self.layoutWidget.setGeometry(QtCore.QRect(20, 60, 161, 37))
self.layoutWidget.setObjectName("layoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_6 = QtWidgets.QLabel(self.layoutWidget)
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
font.setBold(False)
font.setWeight(50)
self.label_6.setFont(font)
self.label_6.setObjectName("label_6")
self.horizontalLayout.addWidget(self.label_6)
self.label_xmin = QtWidgets.QLabel(self.layoutWidget)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label_xmin.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_xmin.setFont(font)
self.label_xmin.setText("")
self.label_xmin.setObjectName("label_xmin")
self.horizontalLayout.addWidget(self.label_xmin)
self.layoutWidget1 = QtWidgets.QWidget(self.frame_6)
self.layoutWidget1.setGeometry(QtCore.QRect(210, 60, 161, 37))
self.layoutWidget1.setObjectName("layoutWidget1")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget1)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_8 = QtWidgets.QLabel(self.layoutWidget1)
self.label_8.setObjectName("label_8")
self.horizontalLayout_2.addWidget(self.label_8)
self.label_ymin = QtWidgets.QLabel(self.layoutWidget1)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label_ymin.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_ymin.setFont(font)
self.label_ymin.setText("")
self.label_ymin.setObjectName("label_ymin")
self.horizontalLayout_2.addWidget(self.label_ymin)
self.layoutWidget2 = QtWidgets.QWidget(self.frame_6)
self.layoutWidget2.setGeometry(QtCore.QRect(20, 120, 161, 37))
self.layoutWidget2.setObjectName("layoutWidget2")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.layoutWidget2)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_7 = QtWidgets.QLabel(self.layoutWidget2)
self.label_7.setObjectName("label_7")
self.horizontalLayout_3.addWidget(self.label_7)
self.label_xmax = QtWidgets.QLabel(self.layoutWidget2)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label_xmax.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_xmax.setFont(font)
self.label_xmax.setText("")
self.label_xmax.setObjectName("label_xmax")
self.horizontalLayout_3.addWidget(self.label_xmax)
self.layoutWidget3 = QtWidgets.QWidget(self.frame_6)
self.layoutWidget3.setGeometry(QtCore.QRect(210, 120, 161, 37))
self.layoutWidget3.setObjectName("layoutWidget3")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.layoutWidget3)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label_9 = QtWidgets.QLabel(self.layoutWidget3)
self.label_9.setObjectName("label_9")
self.horizontalLayout_4.addWidget(self.label_9)
self.label_ymax = QtWidgets.QLabel(self.layoutWidget3)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label_ymax.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_ymax.setFont(font)
self.label_ymax.setText("")
self.label_ymax.setObjectName("label_ymax")
self.horizontalLayout_4.addWidget(self.label_ymax)
self.layoutWidget4 = QtWidgets.QWidget(self.groupBox_2)
self.layoutWidget4.setGeometry(QtCore.QRect(208, 40, 211, 37))
self.layoutWidget4.setObjectName("layoutWidget4")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.layoutWidget4)
self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.label = QtWidgets.QLabel(self.layoutWidget4)
self.label.setObjectName("label")
self.horizontalLayout_5.addWidget(self.label)
self.label_nums = QtWidgets.QLabel(self.layoutWidget4)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
self.label_nums.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_nums.setFont(font)
self.label_nums.setText("")
self.label_nums.setObjectName("label_nums")
self.horizontalLayout_5.addWidget(self.label_nums)
self.layoutWidget5 = QtWidgets.QWidget(self.groupBox_2)
self.layoutWidget5.setGeometry(QtCore.QRect(10, 90, 291, 38))
self.layoutWidget5.setObjectName("layoutWidget5")
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.layoutWidget5)
self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.label_5 = QtWidgets.QLabel(self.layoutWidget5)
self.label_5.setObjectName("label_5")
self.horizontalLayout_6.addWidget(self.label_5)
self.comboBox = QtWidgets.QComboBox(self.layoutWidget5)
self.comboBox.setObjectName("comboBox")
self.horizontalLayout_6.addWidget(self.comboBox)
self.layoutWidget_2 = QtWidgets.QWidget(self.groupBox_2)
self.layoutWidget_2.setGeometry(QtCore.QRect(10, 40, 171, 37))
self.layoutWidget_2.setObjectName("layoutWidget_2")
self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.layoutWidget_2)
self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
self.label_10 = QtWidgets.QLabel(self.layoutWidget_2)
self.label_10.setObjectName("label_10")
self.horizontalLayout_7.addWidget(self.label_10)
self.time_lb = QtWidgets.QLabel(self.layoutWidget_2)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
self.time_lb.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.time_lb.setFont(font)
self.time_lb.setText("")
self.time_lb.setObjectName("time_lb")
self.horizontalLayout_7.addWidget(self.time_lb)
self.layoutWidget6 = QtWidgets.QWidget(self.groupBox_2)
self.layoutWidget6.setGeometry(QtCore.QRect(210, 140, 191, 41))
self.layoutWidget6.setObjectName("layoutWidget6")
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.layoutWidget6)
self.horizontalLayout_8.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.label_11 = QtWidgets.QLabel(self.layoutWidget6)
self.label_11.setObjectName("label_11")
self.horizontalLayout_8.addWidget(self.label_11)
self.label_conf = QtWidgets.QLabel(self.layoutWidget6)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label_conf.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_conf.setFont(font)
self.label_conf.setText("")
self.label_conf.setObjectName("label_conf")
self.horizontalLayout_8.addWidget(self.label_conf)
self.layoutWidget_3 = QtWidgets.QWidget(self.groupBox_2)
self.layoutWidget_3.setGeometry(QtCore.QRect(10, 140, 191, 41))
self.layoutWidget_3.setObjectName("layoutWidget_3")
self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.layoutWidget_3)
self.horizontalLayout_9.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_9.setObjectName("horizontalLayout_9")
self.label_13 = QtWidgets.QLabel(self.layoutWidget_3)
self.label_13.setMaximumSize(QtCore.QSize(60, 16777215))
self.label_13.setObjectName("label_13")
self.horizontalLayout_9.addWidget(self.label_13)
self.type_lb = QtWidgets.QLabel(self.layoutWidget_3)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.type_lb.setPalette(palette)
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.type_lb.setFont(font)
self.type_lb.setText("")
self.type_lb.setObjectName("type_lb")
self.horizontalLayout_9.addWidget(self.type_lb)
self.groupBox_4 = QtWidgets.QGroupBox(self.frame_4)
self.groupBox_4.setGeometry(QtCore.QRect(0, 560, 431, 141))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(16)
self.groupBox_4.setFont(font)
self.groupBox_4.setObjectName("groupBox_4")
self.SaveBtn = QtWidgets.QPushButton(self.groupBox_4)
self.SaveBtn.setGeometry(QtCore.QRect(30, 50, 151, 51))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/icons/ui_imgs/icons/保存.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.SaveBtn.setIcon(icon1)
self.SaveBtn.setIconSize(QtCore.QSize(30, 30))
self.SaveBtn.setObjectName("SaveBtn")
self.ExitBtn = QtWidgets.QPushButton(self.groupBox_4)
self.ExitBtn.setGeometry(QtCore.QRect(250, 50, 151, 51))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/icons/ui_imgs/icons/退出.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.ExitBtn.setIcon(icon2)
self.ExitBtn.setIconSize(QtCore.QSize(30, 30))
self.ExitBtn.setObjectName("ExitBtn")
self.frame_5 = QtWidgets.QFrame(self.centralwidget)
self.frame_5.setGeometry(QtCore.QRect(10, 10, 1231, 91))
self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_5.setObjectName("frame_5")
self.label_3 = QtWidgets.QLabel(self.frame_5)
self.label_3.setGeometry(QtCore.QRect(280, 0, 811, 51))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(30)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.label_2 = QtWidgets.QLabel(self.frame_5)
self.label_2.setGeometry(QtCore.QRect(20, 60, 311, 21))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(14)
font.setUnderline(True)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.label_12 = QtWidgets.QLabel(self.frame_5)
self.label_12.setGeometry(QtCore.QRect(1070, 60, 131, 21))
font = QtGui.QFont()
font.setFamily("华文楷体")
font.setPointSize(14)
font.setUnderline(True)
self.label_12.setFont(font)
self.label_12.setObjectName("label_12")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "基于深度学习的救援过程识别系统"))
self.groupBox_3.setTitle(_translate("MainWindow", "检测结果与位置信息"))
item = self.tableWidget.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "序号"))
item = self.tableWidget.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "文件路径"))
item = self.tableWidget.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "类别"))
item = self.tableWidget.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "置信度"))
item = self.tableWidget.horizontalHeaderItem(4)
item.setText(_translate("MainWindow", "坐标位置"))
self.groupBox.setTitle(_translate("MainWindow", "文件导入"))
self.PiclineEdit.setPlaceholderText(_translate("MainWindow", "请选择图片文件"))
self.VideolineEdit.setPlaceholderText(_translate("MainWindow", "请选择视频文件"))
self.CaplineEdit.setPlaceholderText(_translate("MainWindow", "摄像头未开启"))
self.groupBox_2.setTitle(_translate("MainWindow", "检测结果"))
self.label_4.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">目标位置:</span></p></body></html>"))
self.label_6.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">xmin:</span></p></body></html>"))
self.label_8.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">ymin</span></p></body></html>"))
self.label_7.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">xmax</span></p></body></html>"))
self.label_9.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">ymax</span></p></body></html>"))
self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">目标数目:</span></p></body></html>"))
self.label_5.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">目标选择:</span></p></body></html>"))
self.label_10.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">用时:</span></p></body></html>"))
self.label_11.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">置信度:</span></p></body></html>"))
self.label_13.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600;\">类型:</span></p></body></html>"))
self.groupBox_4.setTitle(_translate("MainWindow", "操作"))
self.SaveBtn.setText(_translate("MainWindow", "保存"))
self.ExitBtn.setText(_translate("MainWindow", "退出"))
self.label_3.setText(_translate("MainWindow", "基于深度学习的救援过程识别系统"))
self.label_2.setText(_translate("MainWindow", "公众号:救援识别技术"))
self.label_12.setText(_translate("MainWindow", "作者:王海滨"))
import ui_sources_rc

1226
UIProgram/UiMain.ui Normal file

File diff suppressed because it is too large Load Diff

0
UIProgram/__init__.py Normal file
View File

48
UIProgram/precess_bar.py Normal file
View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# 进度条
from PyQt5.QtWidgets import QDialog, QLabel, QProgressBar, QPushButton, QVBoxLayout, QHBoxLayout
class ProgressBar(QDialog):
def __init__(self, parent=None):
super(ProgressBar, self).__init__(parent)
self.resize(350, 100)
self.setWindowTitle(self.tr("视频保存进度信息"))
self.TipLabel = QLabel(self.tr("当前帧/总帧数:0/0"))
self.FeatLabel = QLabel(self.tr("保存进度:"))
self.FeatProgressBar = QProgressBar(self)
self.FeatProgressBar.setMinimum(0)
self.FeatProgressBar.setMaximum(100) # 总进程换算为100
self.FeatProgressBar.setValue(0) # 进度条初始值为0
TipLayout = QHBoxLayout()
TipLayout.addWidget(self.TipLabel)
FeatLayout = QHBoxLayout()
FeatLayout.addWidget(self.FeatLabel)
FeatLayout.addWidget(self.FeatProgressBar)
self.cancelButton = QPushButton('取消保存', self)
buttonlayout = QHBoxLayout()
buttonlayout.addStretch(1)
buttonlayout.addWidget(self.cancelButton)
layout = QVBoxLayout()
layout.addLayout(FeatLayout)
layout.addLayout(TipLayout)
layout.addLayout(buttonlayout)
self.setLayout(layout)
self.cancelButton.clicked.connect(self.onCancel)
# self.show()
def setValue(self, start, end, progress):
self.TipLabel.setText(self.tr("当前帧/总帧数:" + " " + str(start) + "/" + str(end)))
# 确保 progress 是整数类型,避免 TypeError
self.FeatProgressBar.setValue(int(progress))
def onCancel(self, event):
self.close()

16
UIProgram/style.css Normal file
View File

@ -0,0 +1,16 @@
QGroupBox {
border: 2px solid gray;
}
QPushButton#SaveBtn{color:black;
background-color:rgb(255, 215, 0);
border-radius:6px}
QPushButton#ExitBtn{color:black;
background-color:rgb(255, 215, 0);
border-radius:6px}
QPushButton:hover{color:red}
QPushButton:pressed{background-color:rgb(180,180,180);border: None;}
#MainWindow{border-image:url(UIProgram/ui_imgs/bg22.png)}

BIN
UIProgram/ui_imgs/11.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/bg22.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/camera.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/folder.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/img.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/video.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/保存.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/目标检测.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
UIProgram/ui_imgs/icons/退出.png (Stored with Git LFS) Normal file

Binary file not shown.

10
UIProgram/ui_sources.py Normal file
View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui_sources.qrc'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

13
UIProgram/ui_sources.qrc Normal file
View File

@ -0,0 +1,13 @@
<RCC>
<qresource prefix="icons">
<file>ui_imgs/icons/目标检测.png</file>
<file>ui_imgs/icons/保存.png</file>
<file>ui_imgs/icons/退出.png</file>
<file>ui_imgs/icons/camera.png</file>
<file>ui_imgs/icons/folder.png</file>
<file>ui_imgs/icons/img.png</file>
<file>ui_imgs/icons/video.png</file>
<file>ui_imgs/11.png</file>
</qresource>
<qresource prefix="bgs"/>
</RCC>

70133
UIProgram/ui_sources_rc.py Normal file

File diff suppressed because it is too large Load Diff

38
VideoTest.py Normal file
View File

@ -0,0 +1,38 @@
#coding:utf-8
import cv2
from ultralytics import YOLO
# 所需加载的模型目录
path = 'models/best.pt'
# 需要检测的图片地址
video_path = "TestFiles/1.mp4"
# Load the YOLOv8 model
model = YOLO(path)
cap = cv2.VideoCapture(video_path)
# Loop through the video frames
while cap.isOpened():
# Read a frame from the video
success, frame = cap.read()
if success:
# Run YOLOv8 inference on the frame
results = model(frame)
# Visualize the results on the frame
annotated_frame = results[0].plot()
# annotated_frame = cv2.resize(annotated_frame, dsize=None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
# Display the annotated frame
cv2.imshow("YOLOv8 Inference", annotated_frame)
# Break the loop if 'q' is pressed
if cv2.waitKey(1) & 0xFF == ord("q"):
break
else:
# Break the loop if the end of the video is reached
break
# Release the video capture object and close the display window
cap.release()
cv2.destroyAllWindows()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

188
check_and_clean_images.py Normal file
View File

@ -0,0 +1,188 @@
"""
检查并清理没有标注文件的图片
找出 train 目录下没有对应 label 文件的图片并删除它们
"""
import os
from pathlib import Path
def get_image_files(directory):
"""获取目录下所有图片文件"""
image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.JPG', '.JPEG', '.PNG'}
image_files = []
for file in Path(directory).iterdir():
if file.is_file() and file.suffix in image_extensions:
image_files.append(file)
return image_files
def get_label_files(directory):
"""获取目录下所有标注文件"""
label_files = []
for file in Path(directory).iterdir():
if file.is_file() and file.suffix == '.txt':
label_files.append(file)
return label_files
def find_images_without_labels(train_dir, label_dir, dry_run=True):
"""
找出没有对应标注文件的图片
Args:
train_dir: 图片目录
label_dir: 标注目录
dry_run: 如果为True只检查不删除
Returns:
list: 没有标注的图片文件列表
"""
train_path = Path(train_dir)
label_path = Path(label_dir)
if not train_path.exists():
print(f"错误: 图片目录不存在: {train_dir}")
return []
if not label_path.exists():
print(f"错误: 标注目录不存在: {label_dir}")
return []
# 获取所有图片和标注文件
image_files = get_image_files(train_path)
label_files = get_label_files(label_path)
print(f"找到 {len(image_files)} 个图片文件")
print(f"找到 {len(label_files)} 个标注文件")
print("-" * 60)
# 创建标注文件名的集合(不含扩展名)
label_names = {f.stem for f in label_files}
# 找出没有对应标注的图片
images_without_labels = []
for img_file in image_files:
img_name_without_ext = img_file.stem
if img_name_without_ext not in label_names:
images_without_labels.append(img_file)
# 打印结果
if images_without_labels:
print(f"发现 {len(images_without_labels)} 个没有标注的图片:")
print("-" * 60)
for img in images_without_labels[:20]: # 只显示前20个
print(f" {img.name}")
if len(images_without_labels) > 20:
print(f" ... 还有 {len(images_without_labels) - 20} 个文件")
print("-" * 60)
if not dry_run:
# 删除文件
deleted_count = 0
failed_count = 0
for img_file in images_without_labels:
try:
img_file.unlink()
deleted_count += 1
except Exception as e:
print(f"删除失败: {img_file.name} - {e}")
failed_count += 1
print(f"\n删除完成:")
print(f" 成功删除: {deleted_count} 个文件")
if failed_count > 0:
print(f" 删除失败: {failed_count} 个文件")
else:
print("\n[预览模式] 未实际删除文件")
print("要删除这些文件,请运行: python check_and_clean_images.py --delete")
else:
print("✓ 所有图片都有对应的标注文件!")
return images_without_labels
def main():
"""主函数"""
import sys
# 默认路径
train_dir = "datasets/handleImage/train"
label_dir = "datasets/handleImage/label"
# 检查命令行参数
dry_run = True
force = False
if len(sys.argv) > 1:
if '--delete' in sys.argv or '-d' in sys.argv:
dry_run = False
if '--force' in sys.argv or '-f' in sys.argv:
force = True
if '--help' in sys.argv or '-h' in sys.argv:
print("=" * 60)
print("图片标注检查工具 - 使用说明")
print("=" * 60)
print("\n功能:")
print(" 检查图片文件是否有对应的标注文件,并删除没有标注的图片")
print("\n使用方法:")
print(" python check_and_clean_images.py")
print(" # 预览模式:检查但不删除文件(推荐先运行)")
print("\n python check_and_clean_images.py --delete")
print(" # 删除模式:删除没有标注的图片(需要确认)")
print("\n python check_and_clean_images.py --delete --force")
print(" # 强制删除:直接删除,不确认(适合脚本自动化)")
print("\n参数说明:")
print(" --delete, -d 启用删除模式")
print(" --force, -f 跳过确认,直接删除")
print(" --help, -h 显示此帮助信息")
print("\n默认路径:")
print(f" 图片目录: {train_dir}")
print(f" 标注目录: {label_dir}")
print("\n支持的图片格式:")
print(" .jpg, .jpeg, .png, .bmp, .tiff, .tif (大小写不敏感)")
print("\n标注文件格式:")
print(" .txt 文件")
print("\n文件名匹配规则:")
print(" 通过文件名(不含扩展名)匹配,例如:")
print(" image001.jpg ↔ image001.txt ✓")
print(" image001.jpg ↔ image_001.txt ✗")
print("\n注意事项:")
print(" ⚠ 删除操作不可恢复!建议先备份数据")
print(" ⚠ 使用 --delete 前建议先运行预览模式检查")
print("\n详细教程:")
print(" 查看文件: check_and_clean_images_使用教程.md")
print("=" * 60)
return
print("=" * 60)
print("检查没有标注的图片")
print("=" * 60)
print(f"图片目录: {train_dir}")
print(f"标注目录: {label_dir}")
print()
if not dry_run and not force:
try:
response = input("⚠ 警告: 将删除没有标注的图片文件!确认继续?(yes/no): ")
if response.lower() != 'yes':
print("已取消")
return
except (EOFError, KeyboardInterrupt):
print("\n已取消(非交互式环境,请使用 --force 参数)")
return
images_to_delete = find_images_without_labels(train_dir, label_dir, dry_run=dry_run)
if images_to_delete and dry_run:
print("\n提示: 使用 --delete 参数来实际删除这些文件")
if __name__ == '__main__':
main()

View File

@ -0,0 +1,355 @@
# 📋 图片标注检查工具使用教程
## 工具简介
`check_and_clean_images.py` 是一个用于检查和清理数据集的工具,可以:
- ✅ 检查图片文件是否有对应的标注文件
- ✅ 找出没有标注的图片
- ✅ 删除没有标注的图片文件
- ✅ 确保数据集完整性
---
## 🚀 快速开始
### 基本用法
#### 1. 预览模式(推荐先运行)
检查哪些图片没有标注,但**不删除**文件:
```bash
python check_and_clean_images.py
```
**输出示例:**
```
============================================================
检查没有标注的图片
============================================================
图片目录: datasets/handleImage/train
标注目录: datasets/handleImage/label
找到 1330 个图片文件
找到 1223 个标注文件
------------------------------------------------------------
发现 107 个没有标注的图片:
------------------------------------------------------------
订单1806953_51_7111322.jpg
订单1805323_51_7096066.jpg
...
------------------------------------------------------------
[预览模式] 未实际删除文件
要删除这些文件,请运行: python check_and_clean_images.py --delete
```
#### 2. 删除模式(需要确认)
删除没有标注的图片,但会要求确认:
```bash
python check_and_clean_images.py --delete
```
**交互提示:**
```
⚠ 警告: 将删除没有标注的图片文件!确认继续?(yes/no):
```
输入 `yes` 确认删除,输入 `no` 取消操作。
#### 3. 直接删除模式(不确认)
直接删除,不需要确认(适合脚本自动化):
```bash
python check_and_clean_images.py --delete --force
```
---
## 📖 详细说明
### 命令参数
| 参数 | 简写 | 说明 | 示例 |
|------|------|------|------|
| `--delete` | `-d` | 启用删除模式 | `--delete` |
| `--force` | `-f` | 跳过确认,直接删除 | `--force` |
| `--help` | `-h` | 显示帮助信息 | `--help` |
### 默认路径
工具默认检查以下路径:
- **图片目录**: `datasets/handleImage/train`
- **标注目录**: `datasets/handleImage/label`
### 支持的图片格式
工具会自动识别以下图片格式:
- `.jpg` / `.JPG`
- `.jpeg` / `.JPEG`
- `.png` / `.PNG`
- `.bmp`
- `.tiff` / `.tif`
### 标注文件格式
工具查找 `.txt` 格式的标注文件。
---
## 📝 使用场景
### 场景 1: 检查数据集完整性
在训练前检查数据集是否完整:
```bash
# 1. 先预览检查
python check_and_clean_images.py
# 2. 如果有问题,查看具体文件
# 3. 确认后删除
python check_and_clean_images.py --delete
```
### 场景 2: 清理导入的数据
从CVAT或其他工具导入数据后清理不完整的文件
```bash
# 直接删除没有标注的图片
python check_and_clean_images.py --delete --force
```
### 场景 3: 批量处理多个数据集
修改脚本中的路径,处理不同数据集:
```python
# 修改 check_and_clean_images.py 中的路径
train_dir = "datasets/你的数据集/train"
label_dir = "datasets/你的数据集/labels"
```
---
## ⚠️ 注意事项
### 1. 备份数据
**重要:** 删除操作不可恢复!建议在删除前备份数据:
```bash
# 备份整个数据集
cp -r datasets/handleImage datasets/handleImage_backup
```
### 2. 文件名匹配规则
工具通过**文件名(不含扩展名)**匹配图片和标注:
- ✅ 匹配:`image001.jpg` ↔ `image001.txt`
- ✅ 匹配:`订单1804264_51_7086683.jpg` ↔ `订单1804264_51_7086683.txt`
- ❌ 不匹配:`image001.jpg` ↔ `image_001.txt`(下划线不同)
- ❌ 不匹配:`image001.jpg` ↔ `image002.txt`(数字不同)
### 3. 空标注文件
如果标注文件存在但为空,工具**不会删除**对应的图片。如果需要处理空标注文件,需要额外检查。
### 4. 目录结构要求
确保目录结构正确:
```
datasets/handleImage/
├── train/ # 图片目录
│ ├── image1.jpg
│ ├── image2.jpg
│ └── ...
└── label/ # 标注目录
├── image1.txt
├── image2.txt
└── ...
```
---
## 🔍 输出说明
### 预览模式输出
```
找到 1330 个图片文件 # 图片总数
找到 1223 个标注文件 # 标注总数
发现 107 个没有标注的图片 # 需要删除的数量
```
### 删除模式输出
```
删除完成:
成功删除: 107 个文件 # 成功删除的数量
删除失败: 0 个文件 # 删除失败的数量(如果有)
```
### 完成状态输出
```
✓ 所有图片都有对应的标注文件!
```
---
## 💡 使用技巧
### 技巧 1: 先预览再删除
**推荐流程:**
```bash
# 步骤1: 预览检查
python check_and_clean_images.py
# 步骤2: 查看结果,确认要删除的文件
# 步骤3: 确认后删除
python check_and_clean_images.py --delete
```
### 技巧 2: 批量处理多个数据集
创建一个批处理脚本:
```bash
#!/bin/bash
# process_datasets.sh
datasets=("handleImage" "仪表盘" "检测仪")
for dataset in "${datasets[@]}"; do
echo "处理数据集: $dataset"
# 修改脚本中的路径或使用参数
python check_and_clean_images.py
done
```
### 技巧 3: 检查反向问题
如果需要检查**有标注但没有图片**的情况,可以修改脚本或创建新工具。
---
## 🐛 常见问题
### Q1: 提示"目录不存在"
**错误信息:**
```
错误: 图片目录不存在: datasets/handleImage/train
```
**解决方法:**
1. 检查路径是否正确
2. 确认目录是否存在
3. 修改脚本中的默认路径
### Q2: 删除后想恢复文件
**解决方法:**
- 如果之前有备份,从备份恢复
- 如果没有备份,文件无法恢复(删除操作不可逆)
### Q3: 文件名不匹配
**问题:** 图片和标注文件名不完全一致
**解决方法:**
- 检查文件名格式
- 确保文件名(不含扩展名)完全一致
- 可以使用重命名工具统一文件名格式
### Q4: 在非交互式环境运行
**问题:** 使用 `--delete` 时提示需要输入
**解决方法:**
使用 `--force` 参数跳过确认:
```bash
python check_and_clean_images.py --delete --force
```
---
## 📊 实际案例
### 案例 1: 清理 handleImage 数据集
```bash
# 1. 检查数据集
$ python check_and_clean_images.py
找到 1330 个图片文件
找到 1223 个标注文件
发现 107 个没有标注的图片
# 2. 确认后删除
$ python check_and_clean_images.py --delete
⚠ 警告: 将删除没有标注的图片文件!确认继续?(yes/no): yes
删除完成:
成功删除: 107 个文件
# 3. 再次验证
$ python check_and_clean_images.py
找到 1223 个图片文件
找到 1223 个标注文件
✓ 所有图片都有对应的标注文件!
```
### 案例 2: 处理其他数据集
修改脚本中的路径:
```python
# 在 check_and_clean_images.py 的 main() 函数中修改
train_dir = "datasets/仪表盘/train/images"
label_dir = "datasets/仪表盘/train/labels"
```
然后运行:
```bash
python check_and_clean_images.py
```
---
## 🔗 相关工具
- `verify_seg_labels.py` - 验证分割标注格式
- `check_label_format.py` - 检查标注格式类型
- `convert_bbox_to_seg.py` - 转换标注格式
---
## 📞 需要帮助?
如果遇到问题:
1. 查看本文档的"常见问题"部分
2. 运行 `python check_and_clean_images.py --help` 查看帮助
3. 检查数据集目录结构是否正确
---
## 📝 更新日志
- **v1.0** (2024): 初始版本
- 支持检查图片和标注文件
- 支持预览和删除模式
- 支持强制删除模式
---
**提示:** 使用前建议先备份数据,删除操作不可恢复!

39
check_dataset_match.py Normal file
View File

@ -0,0 +1,39 @@
import os
def check_dataset_match(images_dir, labels_dir):
"""检查图片和标注文件数量是否匹配"""
# 获取图片文件列表
image_files = [f for f in os.listdir(images_dir) if f.lower().endswith('.jpg')]
# 获取标注文件列表排除classes.txt
label_files = [f for f in os.listdir(labels_dir)
if f.lower().endswith('.txt') and f != 'classes.txt']
# 提取文件名(不带扩展名)
image_names = {os.path.splitext(f)[0] for f in image_files}
label_names = {os.path.splitext(f)[0] for f in label_files}
# 找出不匹配的文件
missing_images = label_names - image_names
missing_labels = image_names - label_names
print(f"图片数量: {len(image_files)}")
print(f"标注数量: {len(label_files)}")
print(f"匹配状态: {'匹配' if len(image_files) == len(label_files) else '不匹配'}")
if missing_images:
print("\n缺少对应的图片文件:")
for name in sorted(missing_images):
print(f"- {name}.jpg")
if missing_labels:
print("\n缺少对应的标注文件:")
for name in sorted(missing_labels):
print(f"- {name}.txt")
if __name__ == "__main__":
images_dir = r'F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\猫狗\train\images'
labels_dir = r'F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\猫狗\train\labels'
print("正在检查数据集匹配情况...")
check_dataset_match(images_dir, labels_dir)
print("\n检查完成")

92
convert_voc_to_yolo.py Normal file
View File

@ -0,0 +1,92 @@
import os
import xml.etree.ElementTree as ET
def convert_voc_to_yolo(xml_path, output_dir):
"""将VOC格式XML转换为YOLO格式TXT"""
tree = ET.parse(xml_path)
root = tree.getroot()
# 获取图像尺寸
size = root.find('size')
if size is None:
return
width_elem = size.find('width')
height_elem = size.find('height')
if width_elem is None or height_elem is None or width_elem.text is None or height_elem.text is None:
return
try:
width = int(width_elem.text)
height = int(height_elem.text)
except (ValueError, TypeError):
return
# 类别映射
class_map = {'': 0, '': 1}
# 准备输出内容
yolo_lines = []
for obj in root.findall('object'):
name_elem = obj.find('name')
if name_elem is None or name_elem.text is None:
continue
class_name = name_elem.text
class_id = class_map.get(class_name, -1)
if class_id == -1:
continue
bndbox = obj.find('bndbox')
if bndbox is None:
continue
try:
xmin_elem = bndbox.find('xmin')
xmin = int(xmin_elem.text) if xmin_elem is not None and xmin_elem.text is not None else 0
ymin_elem = bndbox.find('ymin')
ymin = int(ymin_elem.text) if ymin_elem is not None and ymin_elem.text is not None else 0
xmax_elem = bndbox.find('xmax')
xmax = int(xmax_elem.text) if xmax_elem is not None and xmax_elem.text is not None else 0
ymax_elem = bndbox.find('ymax')
ymax = int(ymax_elem.text) if ymax_elem is not None and ymax_elem.text is not None else 0
except (ValueError, TypeError):
continue
# 转换为YOLO格式
x_center = (xmin + xmax) / 2 / width
y_center = (ymin + ymax) / 2 / height
box_width = (xmax - xmin) / width
box_height = (ymax - ymin) / height
yolo_lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}")
# 写入TXT文件
if yolo_lines:
txt_filename = os.path.splitext(os.path.basename(xml_path))[0] + '.txt'
txt_path = os.path.join(output_dir, txt_filename)
with open(txt_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(yolo_lines))
def batch_convert(input_dir, output_dir):
"""批量转换目录中的所有XML文件"""
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if filename.endswith('.xml'):
xml_path = os.path.join(input_dir, filename)
convert_voc_to_yolo(xml_path, output_dir)
if __name__ == "__main__":
# 设置输入输出目录
input_dir = r'F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\猫狗\train\labels'
output_dir = input_dir # 同一目录输出
print(f"开始转换: {input_dir}")
batch_convert(input_dir, output_dir)
print(f"转换完成,结果保存在: {output_dir}")

48
copy_cat_images.py Normal file
View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
import os
import shutil
import random
def copy_random_images(src_dir, dst_dir, num_images=20, prefix=''):
"""
从源目录随机复制指定数量的图像到目标目录
:param src_dir: 源目录路径
:param dst_dir: 目标目录路径
:param num_images: 要复制的图像数量
:param prefix: 文件名前缀用于区分不同来源
"""
# 确保目标目录存在
os.makedirs(dst_dir, exist_ok=True)
# 获取源目录中所有图像文件
image_files = [f for f in os.listdir(src_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))]
# 随机选择指定数量的图像
selected_files = random.sample(image_files, min(num_images, len(image_files)))
# 复制文件
for file in selected_files:
src_path = os.path.join(src_dir, file)
dst_filename = f"{prefix}{file}" if prefix else file
dst_path = os.path.join(dst_dir, dst_filename)
shutil.copy2(src_path, dst_path)
print(f"已复制: {file} -> {dst_filename}")
print(f"已完成复制 {len(selected_files)} 张图像到 {dst_dir}")
if __name__ == "__main__":
# 目标目录
destination_dir = r"F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\猫狗\train\images"
# 复制猫图片
cat_source = r"F:\myprojects\cats_and_dogs_filtered\train\cats"
print("正在复制猫图片...")
copy_random_images(cat_source, destination_dir, 20, prefix='cat_')
# 复制狗图片
dog_source = r"F:\myprojects\cats_and_dogs_filtered\train\dogs"
print("正在复制狗图片...")
copy_random_images(dog_source, destination_dir, 20, prefix='dog_')
print("所有图片复制完成!")

BIN
datasets/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,4 @@
train: F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\TomatoData\train
val: F:\myprojects\啾啾救援识别系统\JJCarDetection\datasets\TomatoData\val
nc: 2
names: ['Riped', 'UnRiped']

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

BIN
datasets/TomatoData/train/images/Riped tomato_18.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Some files were not shown because too many files have changed in this diff Show More