yolo、voc格式数据集


我的理解是:
yolo格式的数据集:每个图片对应一个txt文件的标签
voc格式的数据集:每张图片对应一个xml文件的标签
都有相应的标注工具。

1、准备工作

1.1、200类的,分类存放的数据集,每个文件夹内是具体的类别
1.2、边框信息边框信息

1.3、类别信息在这里插入图片描述
1.4、图片信息在这里插入图片描述

2、需要这样的TXT

2.1、只含类别的TXT
一个脚本其实可以实现,通过手动也可以实现,手动实现的方法:
可以将1.3的TXT复制到Excel中,选中,按.分列,然后将类别那列复制出来放到TXT里,这样可能会遇到别的编码的问题,具体遇到再说
最后得到:命名为voc_classes.txt
在这里插入图片描述
2.2 图片的位置即图片上目标位置的TXT
将1.2的边框信息和1.4的图片信息拼接到一起,还是用2.1中的那种Excel的方法进行
最后得到:命名为2010_train.txt
在这里插入图片描述

3、txt转xml,即yolo格式转voc格式

import os
import sys
from xml.etree import ElementTree
from xml.etree.ElementTree import Element, SubElement
from lxml import etree # 我用lxml
import copy
import codecs
import random
import cv2
from tqdm import tqdm
from PIL import Image


XML_EXT = '.xml'
ENCODE_METHOD = 'utf-8'


class PascalVocWriter:

    def __init__(self, annotationDict):  # annotationDict
        self.annotationDict = annotationDict

    def save(self, path=None):  # 保存
        """
        return:
          生成一个VOC格式的xml,返回一个xml的根标签,以<annotation>开始
        """
        root = self.genXML()  # 生成xml-----------top = Element('annotation')
        if path is None:  # 写进文件夹
            out_file = codecs.open(self.annotationDict['filename'] + XML_EXT, 'w', encoding=ENCODE_METHOD)
            # codecs.open(filepath, method, encoding)
        else:
            out_file = codecs.open(path, 'w', encoding=ENCODE_METHOD)

        prettifyResult = self.prettify(root)  # 转化格式
        out_file.write(prettifyResult.decode('utf-8'))
        out_file.close()

    def genXML(self):
        # 生成xml
        top = Element('annotation')

        folder = SubElement(top, 'folder')
        folder.text = self.annotationDict['folder']

        filename = SubElement(top, 'filename')
        filename.text = self.annotationDict['filename']

        path = SubElement(top, 'path')
        path.text = self.annotationDict['path']

        source = SubElement(top, 'source')
        database = SubElement(source, 'database')
        database.text = self.annotationDict['source']['database']

        size_part = SubElement(top, 'size')
        width = SubElement(size_part, 'width')
        height = SubElement(size_part, 'height')
        depth = SubElement(size_part, 'depth')

        width.text = str(self.annotationDict['size']['width'])
        height.text = str(self.annotationDict['size']['height'])
        depth.text = str(self.annotationDict['size']['depth'])

        segmented = SubElement(top, 'segmented')
        segmented.text = str(self.annotationDict['segmented'])

        for obj in self.annotationDict['objects']:
            object_item = SubElement(top, 'object')

            name = SubElement(object_item, 'name')
            name.text = obj['name']

            pose = SubElement(object_item, 'pose')
            pose.text = obj['pose']

            truncated = SubElement(object_item, 'truncated')
            truncated.text = str(obj['truncated'])

            difficult = SubElement(object_item, 'difficult')
            difficult.text = str(obj['difficult'])

            bndbox = SubElement(object_item, 'bndbox')
            xmin = SubElement(bndbox, 'xmin')
            xmin.text = str(obj['bndbox']['xmin'])
            ymin = SubElement(bndbox, 'ymin')
            ymin.text = str(obj['bndbox']['ymin'])
            xmax = SubElement(bndbox, 'xmax')
            xmax.text = str(obj['bndbox']['xmax'])
            ymax = SubElement(bndbox, 'ymax')
            ymax.text = str(obj['bndbox']['ymax'])
        return top

    def prettify(self, elem):
        rough_string = ElementTree.tostring(elem, 'utf-8')# 改
        # rough_string = etree.tostring(elem, 'utf8')
        root = etree.fromstring(rough_string)
        return etree.tostring(root, pretty_print=True, encoding=ENCODE_METHOD).replace("  ".encode(), "\t".encode())
        # element, encoding=None, method=None, *,
        #              short_empty_elements=True

def loadTxt(txt_p):
    with open(txt_p, 'r',encoding="utf-8") as f: # encoding="utf-8"加在这里有效
        plist = [line[:-1] for line in f.readlines()]
    return plist


def getGtBoxInfoDict(boxInfoTxt):
    gtBoxInfoDict = {}
    lines = loadTxt(boxInfoTxt)

    for line in lines:
        imgFilename, xmin, ymin, xmax, ymax, name = line.split(',')
        boxInfoDict = {
            'name': name,
            'xmin': int(float(xmin)),
            'ymin': int(float(ymin)),
            'xmax': int(float(xmax)),
            'ymax': int(float(ymax))
        }
        if gtBoxInfoDict.get(imgFilename, None) is None:
            gtBoxInfoDict[imgFilename] = []
        gtBoxInfoDict[imgFilename].append(boxInfoDict)
    return gtBoxInfoDict


def createAnnotation(imgname, boxInfoList):
    path_dir = './VOC2007/JPEGImages/' + imgname
    img = Image.open(path_dir) # 1读图片
    annotation = {
        'folder': 'JPEGImages', 'filename': imgname,
        'path': path_dir,
        'source': {'database': 'Unknow'},
        'size': {'width':img.size[0], 'height':img.size[1], 'depth': 3},
        # 这里原代码有自己的赋值机制,对我的无效,我就改成了我的,用1读的图片进行取长宽操作,
        # 深度都是彩色图,直接赋值3,如果不确定是否都为彩色可以用img.size[2]代替
        'segmented': 0,
        'objects': []
    }
    for boxInfo in boxInfoList:
        obj = {
            'name': boxInfo['name'],
            'pose': 'Unspecified',
            'truncated': 0,
            'difficult': 0,
            'bndbox': {
                'xmin': boxInfo['xmin'],
                'ymin': boxInfo['ymin'],
                'xmax': boxInfo['xmax'],
                'ymax': boxInfo['ymax']
            }
        }
        annotation['objects'].append(obj)
        return annotation


def main():
    """添加的utf8"""
    # open("label/my.txt",encoding="gbk")
    gt_txt = r'2012_train.txt'  ##另一个.py生成的图片信息和位置信息放在一起的一个TXT##############################改了反斜杠#########
    annotation_dir = r'./VOC2007/Annotation'

    gtBoxInfoDict = getGtBoxInfoDict(gt_txt) #返回一个嵌套字典

    for imgname, boxInfoList in gtBoxInfoDict.items():
        # annotation = createAnnotation(imgname, boxInfoList)
        # PascalVocWriter(annotation).save(os.path.join(annotation_dir, os.path.splitext(imgname)[0]+ XML_EXT))
        annotation = createAnnotation(imgname, boxInfoList) #返回一个xml的东西
        #            createAnnotation(imgname, boxInfoList)
        # my_path=r"os.path.splitext(imgname)[0]"
        ####################################################
        data_list = imgname.split('/')  # 我的图片里名称带路径,我要切开,如果你有更好的办法,哈哈求推荐
        one = data_list[0]
        two = data_list[1]
        ####################################################
        PascalVocWriter(annotation).save(os.path.join(annotation_dir, one + os.path.splitext(two)[0] + XML_EXT))
        # 切开路径为这里做准备
        print(os.path.join(annotation_dir, one , os.path.splitext(two)[0] + XML_EXT))
#       top = Element('annotation')

if __name__ == '__main__':
    main()

生成的xml
在这里插入图片描述
JPEGImages和Annotations中对应的文件名是一致的

4、利用生成的xml生成4个txt,即拆分训练集和测试集

import os

import random

trainval_percent = 0.1

train_percent = 0.9

xmlfilepath = 'D:/py_project/yolo2/VOCdevkit/VOC2007/Annotations'

txtsavepath = 'D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets'

total_xml = os.listdir(xmlfilepath)

num = len(total_xml)

list = range(num)

tv = int(num * trainval_percent)

tr = int(tv * train_percent)

trainval = random.sample(list, tv)

train = random.sample(trainval, tr)

ftrainval = open('D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets/trainval.txt', 'w')

ftest = open('D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets/test.txt', 'w')

ftrain = open('D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets/train.txt', 'w')

fval = open('D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets/val.txt', 'w')

for i in list:

    name = total_xml[i][:-4] + '\n'

    if i in trainval:

        ftrainval.write(name)

        if i in train:

            ftest.write(name)

        else:

            fval.write(name)

    else:

        ftrain.write(name)

ftrainval.close()

ftrain.close()

fval.close()

ftest.close()

5、voc变为txt(yolo格式),生成每个类别对应的TXT,即数据集格式变为yolo格式

import xml.etree.ElementTree as ET
from read_class import csv_list ##生成类别列表1
import pickle

import os

from os import listdir, getcwd

from os.path import join

sets = ['train', 'test', 'val']
##########生成的类别列表
class_path = "C:\\Users\\sy\\Desktop\\online\\my71000_665_1044839747790380683\\class.csv"##生成类别列表2
classes = csv_list(class_path)  ##生成类别列表3


def convert(size, box):
    dw = 1. / size[0]

    dh = 1. / size[1]

    x = (box[0] + box[1]) / 2.0

    y = (box[2] + box[3]) / 2.0

    w = box[1] - box[0]

    h = box[3] - box[2]

    x = x * dw

    w = w * dw

    y = y * dh

    h = h * dh

    return (x, y, w, h)


def convert_annotation(image_id):
    in_file = open('D:/py_project/yolo2/VOCdevkit/VOC2007/Annotations/%s.xml' % (image_id))

    out_file = open('D:/py_project/yolo2/VOCdevkit/VOC2007/labels/%s.txt' % (image_id), 'w')

    tree = ET.parse(in_file)

    root = tree.getroot()

    size = root.find('size')

    w = int(size.find('width').text)

    h = int(size.find('height').text)

    for obj in root.iter('object'):

        difficult = obj.find('difficult').text

        cls = obj.find('name').text

        if cls not in classes or int(difficult) == 1:
            continue

        cls_id = classes.index(cls)

        xmlbox = obj.find('bndbox')

        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),

             float(xmlbox.find('ymax').text))

        bb = convert((w, h), b)

        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


wd = getcwd()

print(wd)

for image_set in sets:

    if not os.path.exists('D:/py_project/yolo2/VOCdevkit/VOC2007/labels/'):
        os.makedirs('D:/py_project/yolo2/VOCdevkit/VOC2007/labels/')

    image_ids = open('D:/py_project/yolo2/VOCdevkit/VOC2007/ImageSets/%s.txt' % (image_set)).read().strip().split()

    list_file = open('D:/py_project/yolo2/VOCdevkit/VOC2007/%s.txt' % (image_set), 'w')

    for image_id in image_ids:
        list_file.write('D:/py_project/yolo2/VOCdevkit/VOC2007/images/%s.jpg\n' % (image_id))

        convert_annotation(image_id)

    list_file.close()

<think>嗯,用户想了解Python中单下划线和双下划线的区别和意义。我得先回想一下相关知识,确保自己理解正确。首先,单下划线在Python中有几种用法,比如作为临时变量名,或者在类中表示受保护的成员。然后是双下划线,主要用于名称修饰,也就是在类属性或方法前加双下划线,Python会改写名称以避免命名冲突。 可能需要从命名约定、访问控制、名称修饰这几个方面来展开。用户可能有一定的Python基础,但可能对这些约定不太清楚,需要例子来帮助理解。另外,用户可能想知道实际应用中的区别,比如在继承时双下划线的行为,或者单下划线在模块导入时的作用。 要注意结构清晰,分点说明。先解释单下划线,再双下划线,然后比较它们的区别。还要提到特殊情况下,比如前后的双下划线(如__init__)是特殊方法,不属于名称修饰的情况。可能还需要提到单下划线作为变量名的习惯用法,比如忽略某个值的时候用_。 需要确保术语准确,比如名称修饰(name mangling)、受保护成员、私有成员(虽然Python没有真正的私有,只是约定)。可能还要提醒用户,这些只是约定,Python并不强制限制访问,开发者应该遵循这些约定来维护代码的可维护性。 例子部分要简单明了,比如一个类中使用单下划线和双下划线的属性,展示访问时的不同,以及在继承中的表现。这样用户能直观看到区别。同时,注意不要遗漏单下划线在模块导入中的使用,比如from module import * 时不会导入单下划线开头的变量或函数。 另外,需要检查是否有其他使用情况,比如在交互式环境中_保存最后一个结果,但用户的问题可能更关注变量和方法的命名,所以这部分可能可以简略带过或者不提,视情况而定。 总结时,要强调单下划线更多是约定,提示开发者“不要随意访问”,而双下划线通过名称修饰避免子类中的命名冲突。同时,前后双下划线的特殊方法不需要用户自己定义,除非重写操作符或内置函数等。</think>Python中单下划线(`_`)和双下划线(`__`)的用法和意义不同,主要涉及命名约定和访问控制。以下是逐步说明: --- ### 一、单下划线 `_` 的用途 #### 1. 临时变量或忽略值 - **示例**:`for _ in range(5)` 表示忽略循环变量。 - **意义**:用于表示变量是临时的或不重要的。 #### 2. 模块级私有化(约定) - **示例**:模块中定义 `_internal_var = 10`。 - **意义**:当使用 `from module import *` 时,`_` 开头的变量不会被导入(但依然可以通过显式引用访问)。 #### 3. 类中的“受保护”成员(约定) - **示例**:类属性 `_protected_var = 20`。 - **意义**:提示开发者“这是内部实现,不要直接访问”(但Python不强制限制)。 #### 4. 交互式环境中的特殊变量 - **示例**:在Python REPL中,`_` 表示上一次计算结果。 --- ### 二、双下划线 `__` 的用途 #### 1. 名称修饰(Name Mangling) - **示例**:类中定义 `__private_var = 30`。 - **意义**:Python会将其重命名为 `_ClassName__private_var`,避免子类意外覆盖。 - **代码验证**: ```python class MyClass: def __init__(self): self.__var = 10 obj = MyClass() print(obj.__dict__) # 输出 {'_MyClass__var': 10} ``` #### 2. 避免与魔术方法冲突 - **示例**:自定义方法避免命名为 `__init__` 或 `__str__`。 - **意义**:双下划线开头和结尾(如 `__xxx__`)是Python保留的魔术方法。 --- ### 三、关键区别总结 | 特性 | 单下划线 `_` | 双下划线 `__` | |--------------------|---------------------------|---------------------------| | 访问控制 | 约定上的“受保护”成员 | 通过名称修饰实现“伪私有” | | 模块导入 | `from module import *` 时忽略 | 无特殊影响 | | 名称改写 | 无 | 自动添加类名前缀(如 `_ClassName__var`) | | 典型用途 | 临时变量、模块/类内部成员 | 类中避免命名冲突的成员 | --- ### 四、实际代码示例 ```python class Example: def __init__(self): self.public = 1 self._protected = 2 # 约定上的“受保护” self.__private = 3 # 名称修饰为 _Example__private def _internal_method(self): print("这是一个内部方法") def __private_method(self): print("这是一个伪私有方法") # 测试访问 obj = Example() print(obj.public) # 正常访问 → 1 print(obj._protected) # 可以访问 → 2(但约定不推荐) print(obj.__private) # 报错:AttributeError(实际名称是 _Example__private) print(obj._Example__private) # 强制访问 → 3(不推荐) ``` --- ### 五、核心原则 1. **单下划线**:约定为主,提示开发者“谨慎使用”。 2. **双下划线**:通过语法规则避免命名冲突,但依然可绕过。 3. **前后双下划线**(如 `__init__`):保留给Python内置方法使用,不建议自定义。 遵循这些约定能提高代码可读性和维护性!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值