不多说,直接贴源码,很简单,直接复制下来运行就行
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton,
QListWidget, QProgressBar, QHBoxLayout, QMainWindow, QFileDialog, QMenu,
QDesktopWidget, QSplashScreen, QTimeEdit, QApplication, QStatusBar, QTextEdit, QMessageBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QTime
from PyQt5.QtGui import QPixmap, QIcon
import sys
import os
import fnmatch
import subprocess
import os
import xml.etree.ElementTree as ET
def convert_voc_to_yolo(voc_path, output_path, class_names, progress_callback=None):
"""
Convert VOC format annotations to YOLO format annotations.
:param voc_path: Directory containing VOC format XML files.
:param output_path: Directory to save YOLO format TXT files.
:param class_names: List of class names in the same order as their indices.
:param progress_callback: Callback function to update progress bar.
"""
if not os.path.exists(output_path):
os.makedirs(output_path)
xml_files = [f for f in os.listdir(voc_path) if f.endswith('.xml')]
total_files = len(xml_files)
processed_files = 0
for xml_file in xml_files:
xml_path = os.path.join(voc_path, xml_file)
tree = ET.parse(xml_path)
root = tree.getroot()
filename_without_ext = xml_file.split(".xml")[0]
img_width = int(root.find('size/width').text)
img_height = int(root.find('size/height').text)
yolo_lines = []
for obj in root.findall('object'):
class_name = obj.find('name').text
if class_name not in class_names:
continue
class_id = class_names.index(class_name)
bbox = obj.find('bndbox')
xmin = float(bbox.find('xmin').text)
ymin = float(bbox.find('ymin').text)
xmax = float(bbox.find('xmax').text)
ymax = float(bbox.find('ymax').text)
x_center = (xmin + xmax) / 2 / img_width
y_center = (ymin + ymax) / 2 / img_height
width = (xmax - xmin) / img_width
height = (ymax - ymin) / img_height
yolo_lines.append(f"{class_id} {x_center} {y_center} {width} {height}")
yolo_file = os.path.join(output_path, filename_without_ext + '.txt')
with open(yolo_file, 'w') as f:
f.write("\n".join(yolo_lines))
processed_files += 1
if progress_callback:
progress_callback(processed_files * 100 // total_files)
def resource_path(relative_path):
""" 获取资源文件的绝对路径 """
try:
base_path = sys._MEIPASS
except AttributeError:
base_path = os.path.dirname(__file__)
return os.path.join(base_path, relative_path)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('YOLOv10数据集转换器')
self.setGeometry(100, 100, 800, 400)
self.setWindowIcon(QIcon(resource_path('source/image/logo.ico')))
main_layout = QVBoxLayout()
load_layout = QHBoxLayout()
self.load_button = QPushButton("加载标注文件夹")
self.load_button.clicked.connect(self.load_dir)
load_layout.addWidget(self.load_button)
self.line_edit = QLineEdit()
self.line_edit.setPlaceholderText("请输入文本...")
load_layout.addWidget(self.line_edit)
output_layout = QHBoxLayout()
self.outputdir_load_button = QPushButton("输出文件夹路径")
self.outputdir_load_button.clicked.connect(self.load_output_dir)
output_layout.addWidget(self.outputdir_load_button)
self.outputdir_edit = QLineEdit()
self.outputdir_edit.setPlaceholderText("待输出文本...")
output_layout.addWidget(self.outputdir_edit)
class_layout = QHBoxLayout()
self.write_class_message = QLabel("输入类别信息:")
class_layout.addWidget(self.write_class_message)
self.classwirte_edit = QLineEdit()
self.classwirte_edit.setPlaceholderText("请输入类别信息如:{};该信息默认".format(["ok", "YiWu", "CanJiao", "CanXi","ShaoJian",
"BianXing","PaoMianTiePian", "PaoMianLouTie"]))
class_layout.addWidget(self.classwirte_edit)
main_layout.addLayout(load_layout)
main_layout.addLayout(output_layout)
main_layout.addLayout(class_layout)
button_layout = QHBoxLayout()
self.start_button = QPushButton("开始转换")
button_layout.addWidget(self.start_button)
self.defor_button = QPushButton("判断格式")
self.defor_button.clicked.connect(self.modeDet)
button_layout.addWidget(self.defor_button)
main_layout.addLayout(button_layout)
progress_layout = QHBoxLayout()
self.progress_bar = QProgressBar()
progress_layout.addWidget(self.progress_bar)
main_layout.addLayout(progress_layout)
system_layout = QVBoxLayout()
self.text = QLabel('状态栏')
system_layout.addWidget(self.text)
self.textbox = QTextEdit(self)
self.textbox.setReadOnly(True)
system_layout.addWidget(self.textbox)
main_layout.addLayout(system_layout)
central_widget = QWidget()
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
self.line_edit.textChanged.connect(self.on_text_changed)
self.load_button.clicked.connect(self.on_load_button_clicked)
self.start_button.clicked.connect(self.on_start_button_clicked)
self.mode = "Unknow"
def on_text_changed(self, text):
print(f"输入的文本: {text}")
def on_load_button_clicked(self):
self.status_bar.showMessage("加载标注文件夹...")
def load_dir(self):
print("load_dir is running!")
self.dir_path = QFileDialog.getExistingDirectory(self, 'Select Directory')
if self.dir_path:
self.line_edit.setText(self.dir_path)
self.files = [f for f in os.listdir(self.dir_path) if f.lower().endswith(('.xml'))]
if not self.files:
self.mode = "Qita"
QMessageBox.information(self, 'No message Found', 'No xml files found in the selected directory.')
return
self.mode = "VOC"
for file_name in self.files:
self.textbox.append("文件名为:{}".format(file_name))
else:
QMessageBox.information(self, 'Selection Cancelled', '您已取消文件夹选择')
def load_output_dir(self):
self.output_path = QFileDialog.getExistingDirectory(self, 'Select Directory')
print(self.output_path)
if self.output_path:
self.outputdir_edit.setText(self.output_path)
else:
QMessageBox.information(self, 'Selection Cancelled', '您已取消文件夹选择')
def modeDet(self):
self.status_bar.showMessage("数据集格式为:{}".format(self.mode))
def on_start_button_clicked(self):
print("inter is running!")
self.status_bar.showMessage("开始转换...")
if not self.classwirte_edit.text():
class_message = ["ok", "YiWu", "CanJiao", "CanXi","ShaoJian", "BianXing","PaoMianTiePian", "PaoMianLouTie"]
else:
class_message = self.classwirte_edit.text().split(',')
try:
if self.dir_path and self.output_path and class_message:
self.progress_bar.setValue(0)
convert_voc_to_yolo(self.dir_path, self.output_path, class_message, progress_callback=self.update_progress)
QMessageBox.information(self, '转换完成!', '嘿嘿')
else:
QMessageBox.information(self, '缺少信息', '请重新检查以上步骤!')
except Exception as e:
QMessageBox.information(self, '报错!', '错误为:{}'.format(e))
def update_progress(self, value):
self.progress_bar.setValue(value)
if __name__ == '__main__':
app = QApplication(sys.argv)
splash_pix = QPixmap(resource_path('source/image/loading.png'))
splash = QSplashScreen(splash_pix)
splash.show()
window = MainWindow()
QTimer.singleShot(500, lambda: (splash.close(), window.show()))
screen = QDesktopWidget().screenGeometry()
size = window.geometry()
window.move((screen.width() - size.width()) // 2, (screen.height() - size.height()) // 2-120)
sys.exit(app.exec_())
mainUI.spec
block_cipher = None
a = Analysis(
['mainUI.py'],
pathex=['E:\\0_ALL_Cpde\\PyQt5_practice1\\YoloDatasetProcess'],
binaries=[],
datas=[('source/image/loading.png', 'source/image/'), ('source/image/logo.ico', 'source/image/')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='VOC2YOLO转换器',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='source/image/logo.ico'
)