2.基于xml标注信息数据增强

问题背景

在目标检测的过程中,数据集是一个很重要的组成部分,可以让我们的识别事半功倍,当数据不够的时候,就需要用到数据增强。但是也希望带着标注信息,不想增强之后重新标注,那么就可以用本文的脚本。内容应该是在一个老哥的博客中找的。使用的是imgaug 技术。

相关概念

常用的数据增强方法有
1.平移(Shift)变换:对原始图片在图像平面内以某种方式(预先定义或者随机方式确定平移的步长、范围及其方向)进行平移。
2.翻转(Flip)变换:沿竖直或者水平方向对原始图片进行翻转。
3.随机裁剪(Random Crop):随机定义感兴趣区域以裁剪图像,相当于增加随机扰动。
4.噪声扰动(Noise):对图像随机添加高斯噪声或者椒盐噪声等。
5.对比度变换(Contrast):改变图像对比度,相当于在HSV空间中,保持色调分量H不变,而改变亮度分量V和饱和度S,用于模拟现实环境的光照变化。
6.缩放变换(Zoom):以设定的比例缩小或者放大图像。
7.尺度变换(Scale):与缩放变换有点类似,不过尺度变换的对象是图像内容而非图像本身(可以参考SIFT特征提取方法),构建图像金字塔以得到不同大小、模糊程度的图像。

参考资料

https://www.likecs.com/show-203663098.html python图像数据增强——imgaug (一)
https://blog.csdn.net/qq_40280673/article/details/125960440 深度学习之快速实现数据集增强的方法 (主要看的这篇)

解决方案

代码需要改动5个地方,分别是四个路径和一个增强倍数。

import xml.etree.ElementTree as ET
import pickle
import os
from os import getcwd
import numpy as np
from PIL import Image
import shutil
import matplotlib.pyplot as plt

import imgaug as ia
from imgaug import augmenters as iaa


ia.seed(1)


def read_xml_annotation(root, image_id):
    in_file = open(os.path.join(root, image_id))
    tree = ET.parse(in_file)
    root = tree.getroot()
    bndboxlist = []

    for object in root.findall('object'):  # 找到root节点下的所有country节点
        bndbox = object.find('bndbox')  # 子节点下节点rank的值

        xmin = int(bndbox.find('xmin').text)
        xmax = int(bndbox.find('xmax').text)
        ymin = int(bndbox.find('ymin').text)
        ymax = int(bndbox.find('ymax').text)
        # print(xmin,ymin,xmax,ymax)
        bndboxlist.append([xmin, ymin, xmax, ymax])
        # print(bndboxlist)

    bndbox = root.find('object').find('bndbox')
    return bndboxlist


# (506.0000, 330.0000, 528.0000, 348.0000) -> (520.4747, 381.5080, 540.5596, 398.6603)
def change_xml_annotation(root, image_id, new_target):
    new_xmin = new_target[0]
    new_ymin = new_target[1]
    new_xmax = new_target[2]
    new_ymax = new_target[3]

    in_file = open(os.path.join(root, str(image_id) + '.xml'))  # 这里root分别由两个意思
    tree = ET.parse(in_file)
    xmlroot = tree.getroot()
    object = xmlroot.find('object')
    bndbox = object.find('bndbox')
    xmin = bndbox.find('xmin')
    xmin.text = str(new_xmin)
    ymin = bndbox.find('ymin')
    ymin.text = str(new_ymin)
    xmax = bndbox.find('xmax')
    xmax.text = str(new_xmax)
    ymax = bndbox.find('ymax')
    ymax.text = str(new_ymax)
    tree.write(os.path.join(root, str("%06d" % (str(id) + '.xml'))))


def change_xml_list_annotation(root, image_id, new_target, saveroot, id,img_name):
    in_file = open(os.path.join(root, str(image_id) + '.xml'))  # 这里root分别由两个意思
    tree = ET.parse(in_file)
    elem = tree.find('filename')
    elem.text = (img_name + str("_%06d" % int(id)) + '.jpg')
    xmlroot = tree.getroot()
    index = 0

    for object in xmlroot.findall('object'):  # 找到root节点下的所有country节点
        bndbox = object.find('bndbox')  # 子节点下节点rank的值

        # xmin = int(bndbox.find('xmin').text)
        # xmax = int(bndbox.find('xmax').text)
        # ymin = int(bndbox.find('ymin').text)
        # ymax = int(bndbox.find('ymax').text)

        new_xmin = new_target[index][0]
        new_ymin = new_target[index][1]
        new_xmax = new_target[index][2]
        new_ymax = new_target[index][3]

        xmin = bndbox.find('xmin')
        xmin.text = str(new_xmin)
        ymin = bndbox.find('ymin')
        ymin.text = str(new_ymin)
        xmax = bndbox.find('xmax')
        xmax.text = str(new_xmax)
        ymax = bndbox.find('ymax')
        ymax.text = str(new_ymax)

        index = index + 1

    tree.write(os.path.join(saveroot, img_name + str("_%06d" % int(id)) + '.xml'))


def mkdir(path):
    # 去除首位空格
    path = path.strip()
    # 去除尾部 \ 符号
    path = path.rstrip("\\")
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(path)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 创建目录操作函数
        os.makedirs(path)
        print(path + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建,并提示目录已存在
        print(path + ' 目录已存在')
        return False


if __name__ == "__main__":

    IMG_DIR = "F:/bbld/ldk/img_3"                  ### 原始数据集图像的路径
    XML_DIR = "F:/bbld/ldk/xml_3"              ### 原始xml文件的路径

# =============================================================================
#     AUG_XML_DIR = "./Annotations"  # 存储增强后的XML文件夹路径
# =============================================================================
    AUG_XML_DIR = "F:/bbld/ldk/xml_3_ext"              ### 数据增强后的xml文件的保存路径
    try:
        shutil.rmtree(AUG_XML_DIR)
    except FileNotFoundError as e:
        a = 1
    mkdir(AUG_XML_DIR)

# =============================================================================
#     AUG_IMG_DIR = "./JPEGImages"  # 存储增强后的影像文件夹路径
# =============================================================================
    AUG_IMG_DIR = "F:/bbld/ldk/img_3_ext"  ### 数据增强后图片的保存路径
    try:
        shutil.rmtree(AUG_IMG_DIR)
    except FileNotFoundError as e:
        a = 1
    mkdir(AUG_IMG_DIR)

    AUGLOOP = 10  # 每张影像增强的数量

    boxes_img_aug_list = []
    new_bndbox = []
    new_bndbox_list = []

    # 影像增强
    seq = iaa.Sequential([
        iaa.Flipud(0.5),  # vertically flip 20% of all images
        iaa.Fliplr(0.5),  # 镜像
        iaa.Multiply((1.2, 1.5)),  # change brightness, doesn't affect BBs
        iaa.GaussianBlur(sigma=(0, 3.0)),  # iaa.GaussianBlur(0.5),
        iaa.Affine(
            translate_px={"x": 15, "y": 15},
            scale=(0.8, 0.95),
            rotate=(-30, 30)
        )  # translate by 40/60px on x/y axis, and scale to 50-70%, affects BBs
    ])

    for root, sub_folders, files in os.walk(XML_DIR):

        for name in files:
            print(name)
            bndbox = read_xml_annotation(XML_DIR, name)
            shutil.copy(os.path.join(XML_DIR, name), AUG_XML_DIR)
            shutil.copy(os.path.join(IMG_DIR, name[:-4] + '.jpg'), AUG_IMG_DIR)

            for epoch in range(AUGLOOP):
                seq_det = seq.to_deterministic()  # 保持坐标和图像同步改变,而不是随机
                # 读取图片
                img = Image.open(os.path.join(IMG_DIR, name[:-4] + '.jpg'))
                # sp = img.size
                img = np.asarray(img)
                # bndbox 坐标增强
                for i in range(len(bndbox)):
                    bbs = ia.BoundingBoxesOnImage([
                        ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]),
                    ], shape=img.shape)

                    bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]
                    boxes_img_aug_list.append(bbs_aug)

                    # new_bndbox_list:[[x1,y1,x2,y2],...[],[]]
                    n_x1 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x1)))
                    n_y1 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y1)))
                    n_x2 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x2)))
                    n_y2 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y2)))
                    if n_x1 == 1 and n_x1 == n_x2:
                        n_x2 += 1
                    if n_y1 == 1 and n_y2 == n_y1:
                        n_y2 += 1
                    if n_x1 >= n_x2 or n_y1 >= n_y2:
                        print('error', name)
                    new_bndbox_list.append([n_x1, n_y1, n_x2, n_y2])
                # 存储变化后的图片
                image_aug = seq_det.augment_images([img])[0]
                path = os.path.join(AUG_IMG_DIR,
                                    name[:-4] + str( "_%06d" % (epoch + 1)) + '.jpg')
                image_auged = bbs.draw_on_image(image_aug, thickness=0)
                Image.fromarray(image_auged).save(path)

                # 存储变化后的XML
                change_xml_list_annotation(XML_DIR, name[:-4], new_bndbox_list, AUG_XML_DIR,
                                           epoch + 1,name[:-4])
                print( name[:-4] + str( "_%06d" % (epoch + 1)) + '.jpg')
                new_bndbox_list = []


  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,可以将原始VOC数据集中的所有图像和标签进行水平镜像翻转处理,并保存到新的文件夹中。这个示例可以帮助你理解如何扩充VOC数据集并同时调整标签。 ```python import os from PIL import Image import xml.etree.ElementTree as ET # 原始VOC数据集的路径 data_dir = "VOCdevkit/VOC2007/" # 存储扩充后的数据集的路径 augmented_dir = "VOCdevkit/VOC2007_augmented/" # 创建目录 if not os.path.exists(augmented_dir): os.makedirs(augmented_dir) # 水平翻转函数 def horizontal_flip(image_path): with Image.open(image_path) as img: # 水平翻转图像 flipped_img = img.transpose(method=Image.FLIP_LEFT_RIGHT) # 获取新的文件名 basename = os.path.basename(image_path) flipped_basename = "flipped_" + basename # 保存新的图像 flipped_img_path = os.path.join(augmented_dir, flipped_basename) flipped_img.save(flipped_img_path) return flipped_img_path # 解析xml文件和扩充标签函数 def parse_xml_and_augment_labels(xml_path, flipped_img_path): tree = ET.parse(xml_path) root = tree.getroot() # 扩充标签 for obj in root.findall('object'): # 找到所有边界框信息 bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) xmax = int(bndbox.find('xmax').text) ymin = int(bndbox.find('ymin').text) ymax = int(bndbox.find('ymax').text) # 将横坐标取反,水平翻转坐标轴 xmin_new = flipped_img.size[0] - xmax xmax_new = flipped_img.size[0] - xmin # 更新xml文件中的边界框信息 bndbox.find('xmin').text = str(xmin_new) bndbox.find('xmax').text = str(xmax_new) # 获取新的xml文件名 basename = os.path.basename(xml_path) flipped_basename = "flipped_" + basename flipped_xml_path = os.path.join(augmented_dir, flipped_basename) # 保存新的xml文件 tree.write(flipped_xml_path) # 扩充数据集 for img_name in os.listdir(os.path.join(data_dir, "JPEGImages")): if img_name.endswith(".jpg"): # 图像路径和xml文件路径 img_path = os.path.join(data_dir, "JPEGImages", img_name) xml_path = os.path.join(data_dir, "Annotations", os.path.splitext(img_name)[0] + ".xml") # 水平翻转图像并保存 flipped_img_path = horizontal_flip(img_path) # 扩充xml文件中的标签 parse_xml_and_augment_labels(xml_path, flipped_img_path) ``` 在运行完这个程序之后,你可以查看`VOCdevkit/VOC2007_augmented/`目录,里面包含了所有原始数据集中的图像和标签的水平镜像翻转版本。注意,这个示例只是一个简单的例子,你可以根据自己的需要进行修改并扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值