python --两个文件夹文件名比对(yolo 图和label标注比对检查)

python 复制代码
# coding: utf-8
import os
from typing import Set, List, Tuple


class DatasetChecker:
    """数据集与标注文件比对工具,用于检查图片和标注文件的匹配情况"""

    def __init__(self, img_dir: str, label_dir: str):
        """
        初始化检查器

        :param img_dir: 图片文件夹路径
        :param label_dir: 标注文件夹路径
        """
        self.img_dir = img_dir
        self.label_dir = label_dir
        self.img_files = self._get_files(img_dir)
        self.label_files = self._get_files(label_dir)
        self.img_basenames = self._get_basenames(self.img_files)
        self.label_basenames = self._get_basenames(self.label_files)

    def _get_files(self, dir_path: str) -> List[str]:
        """获取文件夹中的所有文件名(包含扩展名)"""
        if not os.path.exists(dir_path):
            raise FileNotFoundError(f"文件夹不存在: {dir_path}")
        return os.listdir(dir_path)

    def _get_basename(self, filename: str) -> str:
        """提取文件名(去除扩展名)"""
        return os.path.splitext(filename)[0]

    def _get_basenames(self, files: List[str]) -> Set[str]:
        """将文件名列表转换为去除扩展名的集合"""
        return {self._get_basename(f) for f in files}

    def get_extra_files(self) -> Tuple[Set[str], Set[str]]:
        """
        计算多余的文件

        :return: 元组 (图片文件夹中多余的文件, 标注文件夹中多余的文件)
        """
        extra_imgs = self.img_basenames - self.label_basenames
        extra_labels = self.label_basenames - self.img_basenames
        return extra_imgs, extra_labels

    def find_original_filenames(self, basenames: Set[str], source_files: List[str]) -> List[str]:
        """
        根据去除扩展名的文件名,查找原始带扩展名的文件名

        :param basenames: 去除扩展名的文件名集合
        :param source_files: 原始文件列表(带扩展名)
        :return: 原始文件名列表
        """
        original_files = []
        for basename in basenames:
            matches = [f for f in source_files if self._get_basename(f) == basename]
            if matches:
                original_files.append(matches[0])  # 取第一个匹配项
        return original_files

    def print_results(self):
        """打印比对结果"""
        extra_imgs, extra_labels = self.get_extra_files()

        # 处理图片文件夹多余文件
        img_original = self.find_original_filenames(extra_imgs, self.img_files)
        print(f"图片文件夹中多余的文件(无对应标注):{len(img_original)} 个")
        for file in img_original:
            print(f"  - {file}")

        # 处理标注文件夹多余文件
        label_original = self.find_original_filenames(extra_labels, self.label_files)
        print(f"\n标注文件夹中多余的文件(无对应图片):{len(label_original)} 个")
        for file in label_original:
            print(f"  - {file}")


# 使用示例
if __name__ == "__main__":
    img_directory = r'C:\Users\123\Desktop\fsdownload\images'
    label_directory = r'C:\Users\123\Desktop\fsdownload\labels'

    try:
        checker = DatasetChecker(img_directory, label_directory)
        checker.print_results()
    except Exception as e:
        print(f"错误: {e}")

带界面

python 复制代码
# coding: utf-8
import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from typing import Set, List, Tuple
from datetime import datetime

class DatasetCheckerGUI:
    """带 TK 界面的数据集与标注文件比对工具"""
    def __init__(self, root):
        self.root = root
        self.root.title("数据集与标注文件比对工具")
        self.root.geometry("600x280")
        self.root.resizable(False, False)

        # 存储路径
        self.img_dir = ""
        self.label_dir = ""

        # 初始化界面组件
        self._init_ui()

    def _init_ui(self):
        """构建界面"""
        # 样式配置
        style = ttk.Style()
        style.configure("TButton", font=("微软雅黑", 10))
        style.configure("TLabel", font=("微软雅黑", 10))
        style.configure("TEntry", font=("微软雅黑", 10))

        # 1. 图片文件夹选择区域
        img_frame = ttk.Frame(self.root, padding="10")
        img_frame.pack(fill=tk.X, padx=20, pady=15)

        ttk.Label(img_frame, text="图片文件夹:").pack(side=tk.LEFT, padx=5)
        self.img_entry = ttk.Entry(img_frame, width=40)
        self.img_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(img_frame, text="选择", command=self._select_img_dir).pack(side=tk.LEFT, padx=5)

        # 2. 标注文件夹选择区域
        label_frame = ttk.Frame(self.root, padding="10")
        label_frame.pack(fill=tk.X, padx=20, pady=5)

        ttk.Label(label_frame, text="标注文件夹:").pack(side=tk.LEFT, padx=5)
        self.label_entry = ttk.Entry(label_frame, width=40)
        self.label_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(label_frame, text="选择", command=self._select_label_dir).pack(side=tk.LEFT, padx=5)

        # 3. 执行按钮
        btn_frame = ttk.Frame(self.root, padding="10")
        btn_frame.pack(pady=20)
        self.run_btn = ttk.Button(
            btn_frame,
            text="开始比对并保存结果",
            command=self._run_check,
            state=tk.DISABLED,
            style="Accent.TButton"
        )
        self.run_btn.pack(padx=5, pady=5, ipady=3)

        # 4. 状态显示区域
        self.status_var = tk.StringVar(value="请选择图片和标注文件夹")
        status_label = ttk.Label(
            self.root,
            textvariable=self.status_var,
            foreground="#666",
            font=("微软雅黑", 10)
        )
        status_label.pack(pady=10)

    def _select_img_dir(self):
        """选择图片文件夹"""
        dir_path = filedialog.askdirectory(title="选择图片文件夹")
        if dir_path:
            self.img_dir = dir_path
            self.img_entry.delete(0, tk.END)
            self.img_entry.insert(0, dir_path)
            self._check_btn_status()

    def _select_label_dir(self):
        """选择标注文件夹"""
        dir_path = filedialog.askdirectory(title="选择标注文件夹")
        if dir_path:
            self.label_dir = dir_path
            self.label_entry.delete(0, tk.END)
            self.label_entry.insert(0, dir_path)
            self._check_btn_status()

    def _check_btn_status(self):
        """检查按钮是否可启用"""
        if self.img_dir and self.label_dir:
            self.run_btn.config(state=tk.NORMAL)
            self.status_var.set("已选择文件夹,点击开始比对")
        else:
            self.run_btn.config(state=tk.DISABLED)
            self.status_var.set("请选择图片和标注文件夹")

    def _get_files(self, dir_path: str) -> List[str]:
        """获取文件夹中的所有文件名(包含扩展名)"""
        if not os.path.exists(dir_path):
            raise FileNotFoundError(f"文件夹不存在: {dir_path}")
        return [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]

    def _get_basename(self, filename: str) -> str:
        """提取文件名(去除扩展名)"""
        return os.path.splitext(filename)[0]

    def _get_basenames(self, files: List[str]) -> Set[str]:
        """将文件名列表转换为去除扩展名的集合"""
        return {self._get_basename(f) for f in files}

    def _get_extra_files(self) -> Tuple[List[str], List[str]]:
        """计算多余的文件"""
        # 获取文件列表
        img_files = self._get_files(self.img_dir)
        label_files = self._get_files(self.label_dir)

        # 提取文件名(无扩展名)
        img_basenames = self._get_basenames(img_files)
        label_basenames = self._get_basenames(label_files)

        # 查找多余文件
        extra_img_basenames = img_basenames - label_basenames
        extra_label_basenames = label_basenames - img_basenames

        # 转换为原始文件名
        extra_imgs = [f for f in img_files if self._get_basename(f) in extra_img_basenames]
        extra_labels = [f for f in label_files if self._get_basename(f) in extra_label_basenames]

        return extra_imgs, extra_labels

    def _save_results_to_txt(self, extra_imgs: List[str], extra_labels: List[str]) -> bool:
        """将结果保存到 TXT 文件"""
        # 生成文件名(包含时间戳)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        save_path = os.path.join(os.getcwd(), f"数据集比对结果_{timestamp}.txt")

        try:
            with open(save_path, "w", encoding="utf-8") as f:
                f.write("=" * 50 + "\n")
                f.write(f"数据集比对结果 - 生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"图片文件夹路径:{self.img_dir}\n")
                f.write(f"标注文件夹路径:{self.label_dir}\n")
                f.write("=" * 50 + "\n\n")

                # 写入图片文件夹多余文件
                f.write(f"一、图片文件夹中多余的文件(无对应标注):共 {len(extra_imgs)} 个\n")
                if extra_imgs:
                    for i, file in enumerate(extra_imgs, 1):
                        f.write(f"{i:2d}. {file}\n")
                else:
                    f.write("无\n")
                f.write("\n" + "-" * 30 + "\n\n")

                # 写入标注文件夹多余文件
                f.write(f"二、标注文件夹中多余的文件(无对应图片):共 {len(extra_labels)} 个\n")
                if extra_labels:
                    for i, file in enumerate(extra_labels, 1):
                        f.write(f"{i:2d}. {file}\n")
                else:
                    f.write("无\n")
                f.write("\n" + "=" * 50 + "\n")

            return save_path
        except Exception as e:
            messagebox.showerror("保存失败", f"结果保存出错:{str(e)}")
            return False

    def _run_check(self):
        """执行比对并保存结果"""
        self.status_var.set("正在比对...")
        self.root.update()  # 刷新界面

        try:
            # 执行比对
            extra_imgs, extra_labels = self._get_extra_files()

            # 保存结果到 TXT
            save_path = self._save_results_to_txt(extra_imgs, extra_labels)
            if save_path:
                self.status_var.set(f"比对完成!结果已保存到当前目录")
                messagebox.showinfo(
                    "成功",
                    f"比对完成!\n\n统计结果:\n图片文件夹多余文件:{len(extra_imgs)} 个\n标注文件夹多余文件:{len(extra_labels)} 个\n\n结果文件路径:\n{save_path}"
                )
            else:
                self.status_var.set("比对完成,但保存失败")

        except Exception as e:
            error_msg = f"比对出错:{str(e)}"
            self.status_var.set(error_msg)
            messagebox.showerror("错误", error_msg)

if __name__ == "__main__":
    root = tk.Tk()
    app = DatasetCheckerGUI(root)
    root.mainloop()

比对同一个文件夹内jpg/png 和 xml的不同

python 复制代码
# coding: utf-8
import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from typing import Set, List, Tuple
from datetime import datetime

class DatasetCheckerGUI:
    """同一文件夹下 图片与XML标注文件名比对工具"""
    def __init__(self, root):
        self.root = root
        self.root.title("图片与XML标注文件名比对工具")
        self.root.geometry("600x250")
        self.root.resizable(False, False)

        # 存储文件夹路径
        self.target_dir = ""

        # 初始化界面组件
        self._init_ui()

    def _init_ui(self):
        """构建界面"""
        # 样式配置
        style = ttk.Style()
        style.configure("TButton", font=("微软雅黑", 10))
        style.configure("TLabel", font=("微软雅黑", 10))
        style.configure("TEntry", font=("微软雅黑", 10))

        # 1. 文件夹选择区域
        dir_frame = ttk.Frame(self.root, padding="10")
        dir_frame.pack(fill=tk.X, padx=20, pady=30)

        ttk.Label(dir_frame, text="目标文件夹:").pack(side=tk.LEFT, padx=5)
        self.dir_entry = ttk.Entry(dir_frame, width=45)
        self.dir_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(dir_frame, text="选择", command=self._select_dir).pack(side=tk.LEFT, padx=5)

        # 2. 执行按钮
        btn_frame = ttk.Frame(self.root, padding="10")
        btn_frame.pack(pady=10)
        self.run_btn = ttk.Button(
            btn_frame,
            text="开始比对并保存结果",
            command=self._run_check,
            state=tk.DISABLED,
            style="Accent.TButton"
        )
        self.run_btn.pack(padx=5, pady=5, ipady=3)

        # 3. 状态显示区域
        self.status_var = tk.StringVar(value="请选择包含图片和XML的文件夹")
        status_label = ttk.Label(
            self.root,
            textvariable=self.status_var,
            foreground="#666",
            font=("微软雅黑", 10)
        )
        status_label.pack(pady=10)

    def _select_dir(self):
        """选择目标文件夹(图片和XML在同一目录)"""
        dir_path = filedialog.askdirectory(title="选择包含图片和XML标注的文件夹")
        if dir_path:
            self.target_dir = dir_path
            self.dir_entry.delete(0, tk.END)
            self.dir_entry.insert(0, dir_path)
            self.run_btn.config(state=tk.NORMAL)
            self.status_var.set("已选择文件夹,点击开始比对(仅检查JPG/PNG与XML)")
        else:
            self.run_btn.config(state=tk.DISABLED)
            self.status_var.set("请选择包含图片和XML的文件夹")

    def _get_files_by_ext(self, dir_path: str) -> Tuple[List[str], List[str]]:
        """
        获取文件夹中所有图片文件(JPG/PNG)和XML标注文件
        :return: (图片文件列表, XML文件列表)
        """
        if not os.path.exists(dir_path):
            raise FileNotFoundError(f"文件夹不存在: {dir_path}")

        img_files = []
        xml_files = []

        for filename in os.listdir(dir_path):
            file_path = os.path.join(dir_path, filename)
            if not os.path.isfile(file_path):
                continue  # 跳过子目录

            ext = os.path.splitext(filename)[1].lower()
            # 收集图片文件(JPG/PNG)
            if ext in [".jpg", ".jpeg", ".png"]:
                img_files.append(filename)
            # 收集XML标注文件
            elif ext == ".xml":
                xml_files.append(filename)

        return img_files, xml_files

    def _check_matching(self, img_files: List[str], xml_files: List[str]) -> Tuple[List[str], List[str]]:
        """
        检查文件名一致性(忽略扩展名)
        :return: (无对应XML的图片文件, 无对应图片的XML文件)
        """
        # 提取文件名(去除扩展名)
        img_basenames = {os.path.splitext(f)[0] for f in img_files}
        xml_basenames = {os.path.splitext(f)[0] for f in xml_files}

        # 找出不匹配的文件
        img_without_xml = [f for f in img_files if os.path.splitext(f)[0] not in xml_basenames]
        xml_without_img = [f for f in xml_files if os.path.splitext(f)[0] not in img_basenames]

        return img_without_xml, xml_without_img

    def _save_results_to_txt(self, img_without_xml: List[str], xml_without_img: List[str]) -> bool:
        """将比对结果保存到TXT文件"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        save_path = os.path.join(self.target_dir, f"文件名比对结果_{timestamp}.txt")

        try:
            with open(save_path, "w", encoding="utf-8") as f:
                f.write("=" * 60 + "\n")
                f.write(f"图片与XML标注文件名比对结果\n")
                f.write(f"生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"目标文件夹:{self.target_dir}\n")
                f.write(f"图片格式:JPG/JPEG/PNG | 标注格式:XML\n")
                f.write("=" * 60 + "\n\n")

                # 无对应XML的图片
                f.write(f"一、无对应XML标注的图片文件:共 {len(img_without_xml)} 个\n")
                if img_without_xml:
                    for i, file in enumerate(img_without_xml, 1):
                        f.write(f"{i:2d}. {file}\n")
                else:
                    f.write("无\n")
                f.write("\n" + "-" * 40 + "\n\n")

                # 无对应图片的XML
                f.write(f"二、无对应图片的XML标注文件:共 {len(xml_without_img)} 个\n")
                if xml_without_img:
                    for i, file in enumerate(xml_without_img, 1):
                        f.write(f"{i:2d}. {file}\n")
                else:
                    f.write("无\n")
                f.write("\n" + "=" * 60 + "\n")

            return save_path
        except Exception as e:
            messagebox.showerror("保存失败", f"结果保存出错:{str(e)}")
            return False

    def _run_check(self):
        """执行比对逻辑"""
        self.status_var.set("正在比对...")
        self.root.update()  # 刷新界面

        try:
            # 获取目标文件
            img_files, xml_files = self._get_files_by_ext(self.target_dir)
            if not img_files and not xml_files:
                messagebox.showwarning("无文件", "文件夹中未找到JPG/PNG图片或XML标注文件")
                self.status_var.set("比对完成:未找到目标文件")
                return

            # 检查文件名匹配
            img_without_xml, xml_without_img = self._check_matching(img_files, xml_files)

            # 保存结果
            save_path = self._save_results_to_txt(img_without_xml, xml_without_img)
            if save_path:
                self.status_var.set("比对完成!结果已保存到目标文件夹")
                # 弹窗显示统计信息
                msg = (
                    f"比对完成!\n\n"
                    f"统计结果:\n"
                    f"文件夹中图片总数(JPG/PNG):{len(img_files)} 个\n"
                    f"文件夹中XML标注总数:{len(xml_files)} 个\n"
                    f"无对应XML的图片:{len(img_without_xml)} 个\n"
                    f"无对应图片的XML:{len(xml_without_img)} 个\n\n"
                    f"结果文件已保存到:\n{save_path}"
                )
                messagebox.showinfo("成功", msg)

        except Exception as e:
            error_msg = f"比对出错:{str(e)}"
            self.status_var.set(error_msg)
            messagebox.showerror("错误", error_msg)

if __name__ == "__main__":
    root = tk.Tk()
    # 优化界面样式(适配Windows系统)
    try:
        root.tk.call("source", "azure.tcl")
        root.tk.call("set_theme", "light")
    except:
        pass  # 无样式文件时不影响功能
    app = DatasetCheckerGUI(root)
    root.mainloop()
相关推荐
长和信泰光伏储能12 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂13 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study13 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
笨鸟先飞,勤能补拙14 小时前
AI 赋能网络安全领域深度剖析
网络·人工智能·windows·安全·web安全·网络安全·github
卷无止境14 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境14 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd12314 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy123931021615 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
我上去就是一套QWER然后等待复活15 小时前
Windows 清理 CC Switch 并恢复官方 Codex CLI 登录流程(完整记录)
windows
bamb0015 小时前
一个项目带你入门AI应用开发01
python