图片标注处理程序

1、A文件夹中有图片和xml文件,图片中的标准不止一个类别,记为aa、bb、cc

通过这个程序,把A文件夹中所有标注为aa目标的截图、bb目标的截图、cc目标的截图都分类放到B文件夹中


import xml.etree.ElementTree as ET
import glob
import os
from PIL import Image


PATH_TO_ANNOTATIONS = r'C:/Users/Administrator/Desktop/A'
PATH_TO_IMAGES = r'C:/Users/Administrator/Desktop/A'
PATH_TO_SAVE = r'C:/Users/Administrator/Desktop/B'


def main():
    xml_files = glob.glob(os.path.join(PATH_TO_ANNOTATIONS, "*.xml"))
    index = 0
    for xml_file in xml_files:
        print("Progress:%d/%d" %(index + 1, len(xml_files)))
        print(xml_file)
        index = index + 1
        # 解析xml文件
        tree = ET.parse(xml_file)
        annotation = tree.getroot()
        obj_num = 0
        img = None
        for obj in annotation.findall('object'):
            value = (annotation.find('filename').text+'.jpg',
                     int(annotation.find('size').find('width').text),
                     int(annotation.find('size').find('height').text),
                     obj.find('name').text,
                     int(obj.find('bndbox').find('xmin').text),
                     int(obj.find('bndbox').find('ymin').text),
                     int(obj.find('bndbox').find('xmax').text),
                     int(obj.find('bndbox').find('ymax').text)
                     )
            # 获取对应的图片文件
            image_file = xml_file.split('\\')[-1].split('.')[0]
            crop_file = os.path.join(PATH_TO_SAVE, value[3], image_file + '_%d.jpg' % obj_num)
            image_file = os.path.join(PATH_TO_IMAGES, image_file + '.' + value[0].split('.')[-1])

            # 提取图片
            if obj_num == 0:
                img = Image.open(image_file)
            crop_img = img.crop((min(value[4], value[6]), min(value[5], value[7]), max(value[4], value[6]), max(value[5], value[7])))
            if not os.path.exists(os.path.join(PATH_TO_SAVE, value[3])):
                os.makedirs(os.path.join(PATH_TO_SAVE, value[3]))
            crop_img.save(crop_file)
            obj_num = obj_num + 1

if __name__ == '__main__':
    main()

2、A文件夹中有图片和xml文件,有些图片中没有目标,就不用标注,因此就没有xml文件。把A文件夹中有xml的图片以及对应的xml文件提取出来,复制到B文件夹中

import os
from PIL import Image
import xml.dom.minidom

src_file_path = "C:/Users/Administrator/Desktop/1113/A"
dst_file_path = "C:/Users/Administrator/Desktop/1113/B"



dir_list=os.listdir(src_file_path)


for file in dir_list:
    if file.endswith('.xml'):
        str_=file.split(".xml")[0]
        print(str_)
        img_src_path = src_file_path + '/' + str_+'.jpg'
        im = Image.open(img_src_path)

        img_save_path =dst_file_path+'/'+str_+'.jpg'
        im.save(img_save_path)

        xml_src_path=src_file_path+'/'+file
        dom=xml.dom.minidom.parse(xml_src_path)
        xml_save_path=dst_file_path+'/'+file
        with open(xml_save_path, 'w') as fh:
            dom.writexml(fh)

3、标注的xml文件转化为txt文件

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
 
def convert(size, box):
    # size=(width, height)  b=(xmin, xmax, ymin, ymax)
    # x_center = (xmax+xmin)/2        y_center = (ymax+ymin)/2
    # x = x_center / width            y = y_center / height
    # w = (xmax-xmin) / width         h = (ymax-ymin) / height
    
    x_center = (box[0]+box[1])/2.0
    y_center = (box[2]+box[3])/2.0
    x = x_center / size[0]
    y = y_center / size[1]
 
    w = (box[1] - box[0]) / size[0]
    h = (box[3] - box[2]) / size[1]
 
    # print(x, y, w, h)
    return (x,y,w,h)
 
def convert_annotation(xml_files_path, save_txt_files_path, classes):  
    xml_files = os.listdir(xml_files_path)
    # print(xml_files)
    for xml_name in xml_files:
        # print(xml_name)
        xml_file = os.path.join(xml_files_path, xml_name)
        out_txt_path = os.path.join(save_txt_files_path, xml_name.split('.')[0] + '.txt')
        out_txt_f = open(out_txt_path, 'w')
        tree=ET.parse(xml_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))
            # b=(xmin, xmax, ymin, ymax)
            # print(w, h, b)
            bb = convert((w,h), b)
            out_txt_f.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
 
 
if __name__ == "__main__":
    # 把forklift_pallet的voc的xml标签文件转化为yolo的txt标签文件
    # 1、需要转化的类别
    classes = ['smoke']#注意:这里根据自己的类别名称及种类自行更改
    # 2、voc格式的xml标签文件路径
    xml_files1 = r'D:/File/Pytorch/YOLO/yolov7-20220720/data/smoking/labels/train'
    # 3、转化为yolo格式的txt标签文件存储路径
    save_txt_files1 = r'D:/File/Pytorch/YOLO/yolov7-20220720/data/smoking/labels/label'
 
    convert_annotation(xml_files1, save_txt_files1, classes)

4、txt文件转换为xml文件

import os
import xml.etree.ElementTree as ET
from xml.dom.minidom import Document
import cv2
 
'''
import xml
xml.dom.minidom.Document().writexml()
def writexml(self,
             writer: Any,
             indent: str = "",
             addindent: str = "",
             newl: str = "",
             encoding: Any = None) -> None
'''
 
class YOLO2VOCConvert:
    def __init__(self, txts_path, xmls_path, imgs_path):
        self.txts_path = txts_path   # 标注的yolo格式标签文件路径
        self.xmls_path = xmls_path   # 转化为voc格式标签之后保存路径
        self.imgs_path = imgs_path   # 读取读片的路径各图片名字,存储到xml标签文件中
        '''#注意:这里根据自己的类别名称及种类自行更改'''
        self.classes = ['smoke']#注意:这里根据自己的类别名称及种类自行更改
 
    # 从所有的txt文件中提取出所有的类别, yolo格式的标签格式类别为数字 0,1,...
    # writer为True时,把提取的类别保存到'./Annotations/classes.txt'文件中
    def search_all_classes(self, writer=False):
        # 读取每一个txt标签文件,取出每个目标的标注信息
        all_names = set()
        txts = os.listdir(self.txts_path)
        # 使用列表生成式过滤出只有后缀名为txt的标签文件
        txts = [txt for txt in txts if txt.split('.')[-1] == 'txt']
        print(len(txts), txts)
        # 11 ['0002030.txt', '0002031.txt', ... '0002039.txt', '0002040.txt']
        for txt in txts:
            txt_file = os.path.join(self.txts_path, txt)
            with open(txt_file, 'r') as f:
                objects = f.readlines()
                for object in objects:
                    object = object.strip().split(' ')
                    print(object)  # ['2', '0.506667', '0.553333', '0.490667', '0.658667']
                    all_names.add(int(object[0]))
            # print(objects)  # ['2 0.506667 0.553333 0.490667 0.658667\n', '0 0.496000 0.285333 0.133333 0.096000\n', '8 0.501333 0.412000 0.074667 0.237333\n']
 
        print("所有的类别标签:", all_names, "共标注数据集:%d张" % len(txts))
 
        return list(all_names)
 
    def yolo2voc(self):
        # 创建一个保存xml标签文件的文件夹
        if not os.path.exists(self.xmls_path):
            os.mkdir(self.xmls_path)
 
        # 把上面的两个循环改写成为一个循环:
        imgs = os.listdir(self.imgs_path)
        txts = os.listdir(self.txts_path)
        txts = [txt for txt in txts if not txt.split('.')[0] == "classes"]  # 过滤掉classes.txt文件
        print(txts)
        # 注意,这里保持图片的数量和标签txt文件数量相等,且要保证名字是一一对应的   (后面改进,通过判断txt文件名是否在imgs中即可)
        if len(imgs) == len(txts):   # 注意:./Annotation_txt 不要把classes.txt文件放进去
            map_imgs_txts = [(img, txt) for img, txt in zip(imgs, txts)]
            txts = [txt for txt in txts if txt.split('.')[-1] == 'txt']
            print(len(txts), txts)
            for img_name, txt_name in map_imgs_txts:
                # 读取图片的尺度信息
                print("读取图片:", img_name)
                img = cv2.imread(os.path.join(self.imgs_path, img_name))
                height_img, width_img, depth_img = img.shape
                print(height_img, width_img, depth_img)   # h 就是多少行(对应图片的高度), w就是多少列(对应图片的宽度)
 
                # 获取标注文件txt中的标注信息
                all_objects = []
                txt_file = os.path.join(self.txts_path, txt_name)
                with open(txt_file, 'r') as f:
                    objects = f.readlines()
                    for object in objects:
                        object = object.strip().split(' ')
                        all_objects.append(object)
                        print(object)  # ['2', '0.506667', '0.553333', '0.490667', '0.658667']
 
                # 创建xml标签文件中的标签
                xmlBuilder = Document()
                # 创建annotation标签,也是根标签
                annotation = xmlBuilder.createElement("annotation")
 
                # 给标签annotation添加一个子标签
                xmlBuilder.appendChild(annotation)
 
                # 创建子标签folder
                folder = xmlBuilder.createElement("folder")
                # 给子标签folder中存入内容,folder标签中的内容是存放图片的文件夹,例如:JPEGImages
                folderContent = xmlBuilder.createTextNode(self.imgs_path.split('/')[-1])  # 标签内存
                folder.appendChild(folderContent)  # 把内容存入标签
                annotation.appendChild(folder)   # 把存好内容的folder标签放到 annotation根标签下
 
                # 创建子标签filename
                filename = xmlBuilder.createElement("filename")
                # 给子标签filename中存入内容,filename标签中的内容是图片的名字,例如:000250.jpg
                filenameContent = xmlBuilder.createTextNode(txt_name.split('.')[0] + '.jpg')  # 标签内容
                filename.appendChild(filenameContent)
                annotation.appendChild(filename)
 
                # 把图片的shape存入xml标签中
                size = xmlBuilder.createElement("size")
                # 给size标签创建子标签width
                width = xmlBuilder.createElement("width")  # size子标签width
                widthContent = xmlBuilder.createTextNode(str(width_img))
                width.appendChild(widthContent)
                size.appendChild(width)   # 把width添加为size的子标签
                # 给size标签创建子标签height
                height = xmlBuilder.createElement("height")  # size子标签height
                heightContent = xmlBuilder.createTextNode(str(height_img))  # xml标签中存入的内容都是字符串
                height.appendChild(heightContent)
                size.appendChild(height)  # 把width添加为size的子标签
                # 给size标签创建子标签depth
                depth = xmlBuilder.createElement("depth")  # size子标签width
                depthContent = xmlBuilder.createTextNode(str(depth_img))
                depth.appendChild(depthContent)
                size.appendChild(depth)  # 把width添加为size的子标签
                annotation.appendChild(size)   # 把size添加为annotation的子标签
 
                # 每一个object中存储的都是['2', '0.506667', '0.553333', '0.490667', '0.658667']一个标注目标
                for object_info in all_objects:
                    # 开始创建标注目标的label信息的标签
                    object = xmlBuilder.createElement("object")  # 创建object标签
                    # 创建label类别标签
                    # 创建name标签
                    imgName = xmlBuilder.createElement("name")  # 创建name标签
                    imgNameContent = xmlBuilder.createTextNode(self.classes[int(object_info[0])])
                    imgName.appendChild(imgNameContent)
                    object.appendChild(imgName)  # 把name添加为object的子标签
 
                    # 创建pose标签
                    pose = xmlBuilder.createElement("pose")
                    poseContent = xmlBuilder.createTextNode("Unspecified")
                    pose.appendChild(poseContent)
                    object.appendChild(pose)  # 把pose添加为object的标签
 
                    # 创建truncated标签
                    truncated = xmlBuilder.createElement("truncated")
                    truncatedContent = xmlBuilder.createTextNode("0")
                    truncated.appendChild(truncatedContent)
                    object.appendChild(truncated)
 
                    # 创建difficult标签
                    difficult = xmlBuilder.createElement("difficult")
                    difficultContent = xmlBuilder.createTextNode("0")
                    difficult.appendChild(difficultContent)
                    object.appendChild(difficult)
 
                    # 先转换一下坐标
                    # (objx_center, objy_center, obj_width, obj_height)->(xmin,ymin, xmax,ymax)
                    x_center = float(object_info[1])*width_img + 1
                    y_center = float(object_info[2])*height_img + 1
                    xminVal = int(x_center - 0.5*float(object_info[3])*width_img)   # object_info列表中的元素都是字符串类型
                    yminVal = int(y_center - 0.5*float(object_info[4])*height_img)
                    xmaxVal = int(x_center + 0.5*float(object_info[3])*width_img)
                    ymaxVal = int(y_center + 0.5*float(object_info[4])*height_img)
 
                    # 创建bndbox标签(三级标签)
                    bndbox = xmlBuilder.createElement("bndbox")
                    # 在bndbox标签下再创建四个子标签(xmin,ymin, xmax,ymax) 即标注物体的坐标和宽高信息
                    # 在voc格式中,标注信息:左上角坐标(xmin, ymin) (xmax, ymax)右下角坐标
                    # 1、创建xmin标签
                    xmin = xmlBuilder.createElement("xmin")  # 创建xmin标签(四级标签)
                    xminContent = xmlBuilder.createTextNode(str(xminVal))
                    xmin.appendChild(xminContent)
                    bndbox.appendChild(xmin)
                    # 2、创建ymin标签
                    ymin = xmlBuilder.createElement("ymin")  # 创建ymin标签(四级标签)
                    yminContent = xmlBuilder.createTextNode(str(yminVal))
                    ymin.appendChild(yminContent)
                    bndbox.appendChild(ymin)
                    # 3、创建xmax标签
                    xmax = xmlBuilder.createElement("xmax")  # 创建xmax标签(四级标签)
                    xmaxContent = xmlBuilder.createTextNode(str(xmaxVal))
                    xmax.appendChild(xmaxContent)
                    bndbox.appendChild(xmax)
                    # 4、创建ymax标签
                    ymax = xmlBuilder.createElement("ymax")  # 创建ymax标签(四级标签)
                    ymaxContent = xmlBuilder.createTextNode(str(ymaxVal))
                    ymax.appendChild(ymaxContent)
                    bndbox.appendChild(ymax)
 
                    object.appendChild(bndbox)
                    annotation.appendChild(object)  # 把object添加为annotation的子标签
                f = open(os.path.join(self.xmls_path, txt_name.split('.')[0]+'.xml'), 'w')
                xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
                f.close()
 
if __name__ == '__main__':
    # 把yolo的txt标签文件转化为voc格式的xml标签文件
    # yolo格式txt标签文件相对路径
    txts_path1 = r'D:/File/Pytorch/YOLO/yolov7-20220720/data/smoking/labels/train'
    # 转化为voc格式xml标签文件存储的相对路径
    xmls_path1 = r'D:/File/Pytorch/YOLO/yolov7-20220720/data/smoking/labels/label'
    # 存放图片的相对路径
    imgs_path1 = 'D:/File/Pytorch/YOLO/yolov7-20220720/data/smoking/images/train'
 
    yolo2voc_obj1 = YOLO2VOCConvert(txts_path1, xmls_path1, imgs_path1)
    labels = yolo2voc_obj1.search_all_classes()
    print('labels: ', labels)
    yolo2voc_obj1.yolo2voc()

3、4参考链接:

VOC格式xml标签与YOLO格式txt标签相互转换_ID茉莉的博客-CSDN博客_voc标签格式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLOv5本身并没有自动标注图片的功能,但是可以使用一些辅助工具来实现自动标注。下面是一个简单的流程: 1. 安装YOLOv5和相关依赖库。 2. 使用YOLOv5进行目标检测,生成包含目标位置信息的标注文件(如.txt文件)。 3. 使用图像处理库(如PIL)读取图像和标注文件。 4. 根据标注文件中的目标位置信息,在图像上绘制矩形框并保存。 下面是一个简单的Python代码示例: ```python import os from PIL import Image, ImageDraw # 定义目标类别 classes = ['person', 'car', 'dog'] # 定义标注文件和图像路径 label_path = './labels' image_path = './images' # 遍历标注文件 for file in os.listdir(label_path): # 读取标注文件 with open(os.path.join(label_path, file), 'r') as f: lines = f.readlines() # 读取对应的图像 image_file = os.path.join(image_path, file.replace('.txt', '.jpg')) image = Image.open(image_file) draw = ImageDraw.Draw(image) # 绘制矩形框 for line in lines: line = line.strip().split() class_id = int(line[0]) x, y, w, h = map(float, line[1:]) left = int((x - w / 2) * image.width) top = int((y - h / 2) * image.height) right = int((x + w / 2) * image.width) bottom = int((y + h / 2) * image.height) draw.rectangle((left, top, right, bottom), outline='red', width=2) draw.text((left, top), classes[class_id], fill='red') # 保存图像 image.save(os.path.join(image_path, file.replace('.txt', '.jpg'))) ``` 这个示例假设标注文件和图像文件分别存放在./labels和./images文件夹中,标注文件的格式为每行一个目标位置信息,第一个数字为类别编号(从0开始),后面四个数字分别为目标中心点的x坐标、y坐标、宽度和高度(均为相对值,范围在0到1之间)。代码会自动读取标注文件,并在对应的图像上绘制矩形框和类别名称,最后保存图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值