7.2 简单的图片标注为xml代码和图片增强代码(python)

       yolov5已经普及常用,最常规的步骤就是获取数据(图片)、标注图片(标注xml文件)、对标注的数据增强(椒盐模糊、高斯模糊、翻转等) 、训练数据。

        在这里,假如客户新增一个目标,需要客户自己标注数据、增强数据和训练数据不合理,我们可以希望实现这样一个场景:建立一个界面,打开界面后:1.客户对新的产品从不同的角度拍七八张照片;2.客户在界面上对七八张图片的目标用长方形画框;3.界面系统自动对这几张标注好的图片进行数据扩增;4.将扩增后的数据自动移入训练的文件夹;5.训练模型。

        其实,希望客户做以上的1、2、5三步就行,3和4由界面系统自动完成,这样就能很简单的完成新模型的标注和训练。下面分别是标注和数据扩增的代码。

1.图片标注

代码如下:

import cv2
import numpy as np

#1. 在图片中标注矩形框
def draw_circle(event, x, y, flags, param):
    global ix, iy, drawing, px, py

    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            # cv2.rectangle(img, (ix, iy), (px, py), (0, 0, 0), thickness=2)  # 将刚刚拖拽的矩形涂黑
            # cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 0)
            px, py = x, y
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 0)
        preds.append([classes, [iy, ix, y, x]])
        # px, py = -1, -1

# 2. 将矩形框的坐标和名称写入xml文件
# 2.写入xml文件
def img_xml(img_path, xml_path, img_name, pred):
    if len(pred) != 0:

        # 1.读取图片(xml需要写入图片的长宽高)
        img = cv2.imread(img_path)

        # 2.写入xml文件
        # (1)写入文件头部
        files_path = img_path.split("\\")[-2]
        print("..:", files_path)

        xml_file = open((xml_path + img_name + '.xml'), 'w')
        xml_file.write('<annotation>\n')
        xml_file.write('	<folder>' + files_path + '</folder>\n')
        xml_file.write('	<filename>' + img_name + '.jpg' + '</filename>\n')
        xml_file.write('	<path>' + img_path + '</path>\n')

        xml_file.write('	<source>\n')
        xml_file.write('		<database>Unknown</database>\n')
        xml_file.write('	</source>\n')

        # (2)写入图片的长宽高信息
        xml_file.write('	<size>\n')
        xml_file.write('		<width>' + str(img.shape[1]) + '</width>\n')
        xml_file.write('		<height>' + str(img.shape[0]) + '</height>\n')
        xml_file.write('		<depth>' + str(img.shape[2]) + '</depth>\n')
        xml_file.write('	</size>\n')

        xml_file.write('	<segmented>0</segmented>\n')

        # 3.写入字符串信息:[["water",[15,20,30,40]],["red",[12,13,14,15]]]
        for item in pred:
            xml_file.write('	<object>\n')
            xml_file.write('		<name>' + str(item[0]) + '</name>\n')
            xml_file.write('		<pose>Unspecified</pose>\n')
            xml_file.write('		<truncated>0</truncated>\n')
            xml_file.write('		<difficult>0</difficult>\n')
            xml_file.write('		<bndbox>\n')

            # 写入字符串信息
            # [top, left, bottom, right]
            xml_file.write('			<xmin>' + str(item[1][1]) + '</xmin>\n')
            xml_file.write('			<ymin>' + str(item[1][0]) + '</ymin>\n')
            xml_file.write('			<xmax>' + str(item[1][3]) + '</xmax>\n')
            xml_file.write('			<ymax>' + str(item[1][2]) + '</ymax>\n')

            xml_file.write('		</bndbox>\n')
            xml_file.write('	</object>\n')
        xml_file.write('</annotation>\n')



if __name__ == "__main__":

    # 1.读取图片
    img_path = "D:\AI\yq\datas\imgs\\4.jpg"
    img = cv2.imread(img_path)

    xml_path = "D:\AI\yq\datas\labels\\"
    img_name = "4"

    # pred = [["water", [15, 20, 30, 40]], ["red", [12, 13, 14, 15]]]  #[top,left,bottom,right]
    classes = "luoding"
    preds = []

    #2. 鼠标给图片画矩形,并将矩形的左上角和右下角的坐标记录下来
    drawing = False  # 鼠标按下为真
    mode = True  # 如果为真,画矩形,按m切换为曲线
    ix, iy = -1, -1
    px, py = -1, -1

    cv2.namedWindow('image')
    cv2.setMouseCallback('image', draw_circle)

    while (1):
        cv2.imshow('image', img)
        k = cv2.waitKey(1) & 0xFF
        if k == ord('q'):
            print(preds)
            img_xml(img_path, xml_path, img_name, preds)
            break
        elif k == 27:
            break
    cv2.destroyAllWindows()

      以上是标注图片,并生成xml文件的代码,结果如下:

    

        在以上代码中,有2个函数,函数draw_circle(event, x, y, flags, param)是在图片中用鼠标画矩形框,并将矩形框的左上角和右下角的坐标记录下来(preds用来记录每个目标的坐标和目标名称),preds的打印结果如下:

        这个数据加上图片的路径和名称就可以写出图片目标的xml文件了。

        函数img_xml(img_path, xml_path, img_name, pred)是将用鼠标标注的图片信息写入xml文件,函数中的参数为

img_path:标注图片的路径

xml_path:保存xml文件的路径

img_name:保存图片的名称(不带.后缀,如1.jpg的图片名称为1)

pred:图片中目标的信息,是函数draw_circle(event, x, y, flags, param)的返回值

输出结果如下:

2.数据扩增

        常见的数据扩增的方式有:原数据成倍增加、椒盐模糊、高斯模糊、水平翻转、垂直翻转、按照某个角度翻转、平移、缩放、裁剪、亮度、直方图均衡化、调整白平衡、mixup、cutmix、cutout等。这些数据增强的方式,有的只需要处理图片,而图片中的xml文件中的坐标值不变,有的则会改变目标在图片中的具体位置,这时需要修改xml文件中的坐标值。下面分别介绍不同的扩增:

2.1 图片的水平翻转

        图片水平翻转后,其目标的坐标会发生变换。其xml文件需要修改扩增后的图片路径、图片名称、图片中目标的名称、图片中目标的坐标,其他信息可以根据自己的需要对该代码稍微修改即可。代码如下:

import cv2
import os
import random
import xml.etree.ElementTree as ET
from shutil import copyfile

# 1. 读取xml文件
def xml_read(xml_path):
    # 2. xml 文件的读取
    tree = ET.parse(xml_path)
    root = tree.getroot()
    ss = []
    modifys = []
    width = 0

    for element in root.iter():
        if element.tag == "width":
            width = int(element.text)
            # print("width:", width)
        if element.tag == "name":
            name = element.text
            ss.append(name)
        if element.tag == "xmin":
            xmin = int(element.text)
            # print("xmin:", xmin)
            ss.append(xmin)
        if element.tag == "ymin":
            ymin = int(element.text)
            # print("ymin:", ymin)
            ss.append(ymin)
        if element.tag == "xmax":
            xmax = int(element.text)
            # print("xmax:", xmax)
            ss.append(xmax)
        if element.tag == "ymax":
            ymax = int(element.text)
            # print("ymax:", ymax)
            ss.append(ymax)
        if len(ss) == 5:
            x_min = ss[1]
            y_min = ss[2]
            x_max = ss[3]
            y_max = ss[4]

            modifys.append([ss[0], [y_min, x_min, y_max, x_max]])
            ss = []
    return modifys


# 2.写入xml文件
def img_xml(img_path, xml_path, img_name, pred):
    if len(pred) != 0:
        # 1.读取图片(xml需要写入图片的长宽高)
        img = cv2.imread(img_path)

        # 2.写入xml文件
        # (1)写入文件头部
        folder = img_path.split("\\")[-2]
        filename = img_path.split("\\")[-1]
        print(filename)

        xml_file = open((xml_path + img_name + '.xml'), 'w')
        xml_file.write('<annotation>\n')
        xml_file.write('	<folder>' + folder + '</folder>\n')
        xml_file.write('	<filename>' + filename + '</filename>\n')
        xml_file.write('	<path>' + img_path + '</path>\n')

        xml_file.write('	<source>\n')
        xml_file.write('		<database>Unknown</database>\n')
        xml_file.write('	</source>\n')

        # (2)写入图片的长宽高信息
        xml_file.write('	<size>\n')
        xml_file.write('		<width>' + str(img.shape[1]) + '</width>\n')
        xml_file.write('		<height>' + str(img.shape[0]) + '</height>\n')
        xml_file.write('		<depth>' + str(img.shape[2]) + '</depth>\n')
        xml_file.write('	</size>\n')

        xml_file.write('	<segmented>0</segmented>\n')

        # 3.写入字符串信息:[["water",[15,20,30,40]],["red",[12,13,14,15]]]
        for item in pred:
            xml_file.write('	<object>\n')
            xml_file.write('		<name>' + str(item[0]) + '</name>\n')
            xml_file.write('		<pose>Unspecified</pose>\n')
            xml_file.write('		<truncated>0</truncated>\n')
            xml_file.write('		<difficult>0</difficult>\n')
            xml_file.write('		<bndbox>\n')

            # 写入字符串信息
            # [top, left, bottom, right]
            xml_file.write('			<xmin>' + str(item[1][1]) + '</xmin>\n')
            xml_file.write('			<ymin>' + str(item[1][0]) + '</ymin>\n')
            xml_file.write('			<xmax>' + str(item[1][3]) + '</xmax>\n')
            xml_file.write('			<ymax>' + str(item[1][2]) + '</ymax>\n')

            xml_file.write('		</bndbox>\n')
            xml_file.write('	</object>\n')
        xml_file.write('</annotation>\n')


# 3.3 图片的水平翻转
    # (1. img_path:图片具体路径,如: "D:\AI\yq\datas\strange\imgs\\1.jpg"
    # (2. xml_path:图片对应的xml文件的具体路径,如:"D:\AI\yq\datas\strange\labels\\1.xml"
    # (3. img_saves:需要保存的增强的图片路径,如:"D:\AI\yq\datas\strange\imgs_strange\\"
    # (4. xml_saves:需要保存的增强图片的xml文件的路径,如:xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"
    # (5. img_name:增强的图片名称,如 5 (5.jpg)
def img_horizen_rotation(img_path, xml_path, img_saves, xml_saves, img_name):# img, xmls,img3_path, xmls_path
    print("...............   图片翻转   ..................")

    # 1. 图片翻转
    img = cv2.imread(img_path)
    imgs = cv2.flip(img, 1)

    # 2.将翻转后的图片保存在增强图片的文件夹
    img_save_path = img_saves + img_name + ".jpg"
    cv2.imwrite(img_save_path, imgs)

    # 3. xml 文件的读取
    pred = xml_read(xml_path)

    # 4. 图片翻转后,需要修改图片中目标的坐标,所以修改pred中的坐标
    preds = []
    width = imgs.shape[1]
    for items in pred:
        x_min = width - items[1][3]
        y_min = items[1][0]
        x_max = width - items[1][1]
        y_max = items[1][2]
        preds.append([items[0], [y_min, x_min, y_max, x_max]])

    # 5. 将翻转的图片信息写入xml文件
    img_xml(img_save_path, xml_saves, img_name, preds)

    # 6. 画图
    for item in preds:
        a = (item[1][1], item[1][0])
        b = (item[1][3], item[1][2])
        cv2.rectangle(imgs, a, b, (0, 255, 0), 2)

    cv2.imshow("s2", imgs)
    cv2.waitKey(0)


if __name__ == "__main__":

    # 要处理的图片和对应的xml文件路径
    img_path = "D:\AI\yq\datas\strange\imgs\\1.jpg"
    xml_path = "D:\AI\yq\datas\strange\labels\\1.xml"

    # 保存处理后的图片和xml文件路径
    img_saves = "D:\AI\yq\datas\strange\imgs_strange\\"
    xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"

    # 水平翻转后的图片路径
    img3_path = "D:\AI\yq\datas\strange\imgs_strange\\5.jpg"
    img_name = "5"

    # 3. 处理图片,增加xml文件
      # xml文件需要修改的信息: (1. <folder>imgs</folder>  imgs: 图片所在的文件夹名称
                   # (2. <filename>1.jpg</filename>  1.jpg:图片名称
                   # (3. <path>D:\AI\yq\datas\imgs\1.jpg</path>  D:\AI\yq\datas\imgs\1.jpg:图片路径
      # (4. ['luoding', [241, 1095, 505, 1307]]   'luoding':目标的名称
      # (5. [241, 1095, 505, 1307]]:目标在图片中的坐标


    # 3.3 图片水平旋转
    img_horizen_rotation(img_path, xml_path, img_saves, xml_saves, img_name)


        以上代码中进行了原图片中目标坐标的转换,并写入新的xml文件。结果如下:

      

        后面一张是前面一张翻转后的图片,画的框是前面图片标注的框翻转后画在后面图片的目标上,可以看到转换后的坐标框是和目标物体吻合的。增强后的xml文件如下:

2.2  椒盐算法

代码如下:

import cv2
import os
import random
import xml.etree.ElementTree as ET
from shutil import copyfile


# 1. 读取xml文件
def xml_read(xml_path):
    # 2. xml 文件的读取
    tree = ET.parse(xml_path)
    root = tree.getroot()
    ss = []
    modifys = []
    width = 0

    for element in root.iter():
        if element.tag == "width":
            width = int(element.text)
            # print("width:", width)
        if element.tag == "name":
            name = element.text
            ss.append(name)
        if element.tag == "xmin":
            xmin = int(element.text)
            # print("xmin:", xmin)
            ss.append(xmin)
        if element.tag == "ymin":
            ymin = int(element.text)
            # print("ymin:", ymin)
            ss.append(ymin)
        if element.tag == "xmax":
            xmax = int(element.text)
            # print("xmax:", xmax)
            ss.append(xmax)
        if element.tag == "ymax":
            ymax = int(element.text)
            # print("ymax:", ymax)
            ss.append(ymax)
        if len(ss) == 5:
            x_min = ss[1]
            y_min = ss[2]
            x_max = ss[3]
            y_max = ss[4]

            modifys.append([ss[0], [y_min, x_min, y_max, x_max]])
            ss = []
    return modifys


# 2.写入xml文件
def img_xml(img_path, xml_path, img_name, pred):
    if len(pred) != 0:
        # 1.读取图片(xml需要写入图片的长宽高)
        img = cv2.imread(img_path)

        # 2.写入xml文件
        # (1)写入文件头部
        folder = img_path.split("\\")[-2]
        filename = img_path.split("\\")[-1]
        print(filename)

        xml_file = open((xml_path + img_name + '.xml'), 'w')
        xml_file.write('<annotation>\n')
        xml_file.write('	<folder>' + folder + '</folder>\n')
        xml_file.write('	<filename>' + filename + '</filename>\n')
        xml_file.write('	<path>' + img_path + '</path>\n')

        xml_file.write('	<source>\n')
        xml_file.write('		<database>Unknown</database>\n')
        xml_file.write('	</source>\n')

        # (2)写入图片的长宽高信息
        xml_file.write('	<size>\n')
        xml_file.write('		<width>' + str(img.shape[1]) + '</width>\n')
        xml_file.write('		<height>' + str(img.shape[0]) + '</height>\n')
        xml_file.write('		<depth>' + str(img.shape[2]) + '</depth>\n')
        xml_file.write('	</size>\n')

        xml_file.write('	<segmented>0</segmented>\n')

        # 3.写入字符串信息:[["water",[15,20,30,40]],["red",[12,13,14,15]]]
        for item in pred:
            xml_file.write('	<object>\n')
            xml_file.write('		<name>' + str(item[0]) + '</name>\n')
            xml_file.write('		<pose>Unspecified</pose>\n')
            xml_file.write('		<truncated>0</truncated>\n')
            xml_file.write('		<difficult>0</difficult>\n')
            xml_file.write('		<bndbox>\n')

            # 写入字符串信息
            # [top, left, bottom, right]
            xml_file.write('			<xmin>' + str(item[1][1]) + '</xmin>\n')
            xml_file.write('			<ymin>' + str(item[1][0]) + '</ymin>\n')
            xml_file.write('			<xmax>' + str(item[1][3]) + '</xmax>\n')
            xml_file.write('			<ymax>' + str(item[1][2]) + '</ymax>\n')

            xml_file.write('		</bndbox>\n')
            xml_file.write('	</object>\n')
        xml_file.write('</annotation>\n')

# 3.2.椒盐算法增强
    # (1. img_path:图片具体路径,如: "D:\AI\yq\datas\strange\imgs\\1.jpg"
    # (2. xml_path:图片对应的xml文件的具体路径,如:"D:\AI\yq\datas\strange\labels\\1.xml"
    # (3. img_saves:需要保存的增强的图片路径,如:"D:\AI\yq\datas\strange\imgs_strange\\"
    # (4. xml_saves:需要保存的增强图片的xml文件的路径,如:xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"
    # (5. img_name:增强的图片名称,如 5 (5.jpg)
    # (6. percentage:椒盐算法加噪点的比率

def pepper_and_salt(img_path, xml_path, img_saves, xml_saves, img_name,percentage):

    # 1. 椒盐算法
    img = cv2.imread(img_path)
    num=int(percentage*img.shape[0]*img.shape[1])#  椒盐噪声点数量
    random.randint(0, img.shape[0])
    img2=img.copy()
    for i in range(num):
        X=random.randint(0,img2.shape[0]-1)#从0到图像长度之间的一个随机整数,因为是闭区间所以-1
        Y=random.randint(0,img2.shape[1]-1)
        if random.randint(0,1) ==0: #黑白色概率55开
            img2[X,Y] = (255,255,255) # 白色
        else:
            img2[X,Y] = (0, 0, 0)  # 黑色

    # 2. 保存图片,将椒盐处理后的图片保存到增强图片的文件夹
    img_save_path = img_saves + img_name + ".jpg"
    cv2.imwrite(img_save_path, img2)

    # 3. xml 文件的读取
    pred = xml_read(xml_path)

    # 4. 将椒盐的图片信息写入xml文件,由于成倍增加图片不影响图片中的目标信息,故只需要修改xml文件的前3项信息即可
    img_xml(img_save_path, xml_saves, img_name, pred)

    # 3. 画图
    for item in pred:
        a = (item[1][1], item[1][0])
        b = (item[1][3], item[1][2])
        cv2.rectangle(img, a, b, (0, 255, 0), 2)
        cv2.rectangle(img2, a, b, (0, 255, 0), 2)

    cv2.imshow("s1", img)
    cv2.imshow("s2", img2)
    cv2.waitKey(0)



if __name__ == "__main__":

    # 要处理的图片和对应的xml文件路径
    img_path = "D:\AI\yq\datas\strange\imgs\\1.jpg"
    xml_path = "D:\AI\yq\datas\strange\labels\\1.xml"

    # 保存处理后的图片和xml文件路径
    img_saves = "D:\AI\yq\datas\strange\imgs_strange\\"
    xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"

    # 水平翻转后的图片路径
    img3_path = "D:\AI\yq\datas\strange\imgs_strange\\5.jpg"
    img_name = "5"

    # 3.2 椒盐算法
    pepper_and_salt(img_path, xml_path, img_saves, xml_saves, img_name, percentage = 0.08)


        这里只写了算法的函数,由于椒盐算法没有改变目标物在图片中的位置,所以不需要修改xml文件或者只需要修改新的图片路径即可。输出效果如下:

   

        前面的图片是原图,后面的图片是对原图做了椒盐变换,并将标注的坐标画成了框,显然是吻合的。写入的xml文件如下:

2.3.图片数量增加1倍

代码如下:

import cv2
import os
import random
import xml.etree.ElementTree as ET
from shutil import copyfile


# 1. 读取xml文件
def xml_read(xml_path):
    # 2. xml 文件的读取
    tree = ET.parse(xml_path)
    root = tree.getroot()
    ss = []
    modifys = []
    width = 0

    for element in root.iter():
        if element.tag == "width":
            width = int(element.text)
            # print("width:", width)
        if element.tag == "name":
            name = element.text
            ss.append(name)
        if element.tag == "xmin":
            xmin = int(element.text)
            # print("xmin:", xmin)
            ss.append(xmin)
        if element.tag == "ymin":
            ymin = int(element.text)
            # print("ymin:", ymin)
            ss.append(ymin)
        if element.tag == "xmax":
            xmax = int(element.text)
            # print("xmax:", xmax)
            ss.append(xmax)
        if element.tag == "ymax":
            ymax = int(element.text)
            # print("ymax:", ymax)
            ss.append(ymax)
        if len(ss) == 5:
            x_min = ss[1]
            y_min = ss[2]
            x_max = ss[3]
            y_max = ss[4]

            modifys.append([ss[0], [y_min, x_min, y_max, x_max]])
            ss = []
    return modifys


# 2.写入xml文件
def img_xml(img_path, xml_path, img_name, pred):
    if len(pred) != 0:
        # 1.读取图片(xml需要写入图片的长宽高)
        img = cv2.imread(img_path)

        # 2.写入xml文件
        # (1)写入文件头部
        folder = img_path.split("\\")[-2]
        filename = img_path.split("\\")[-1]
        print(filename)

        xml_file = open((xml_path + img_name + '.xml'), 'w')
        xml_file.write('<annotation>\n')
        xml_file.write('	<folder>' + folder + '</folder>\n')
        xml_file.write('	<filename>' + filename + '</filename>\n')
        xml_file.write('	<path>' + img_path + '</path>\n')

        xml_file.write('	<source>\n')
        xml_file.write('		<database>Unknown</database>\n')
        xml_file.write('	</source>\n')

        # (2)写入图片的长宽高信息
        xml_file.write('	<size>\n')
        xml_file.write('		<width>' + str(img.shape[1]) + '</width>\n')
        xml_file.write('		<height>' + str(img.shape[0]) + '</height>\n')
        xml_file.write('		<depth>' + str(img.shape[2]) + '</depth>\n')
        xml_file.write('	</size>\n')

        xml_file.write('	<segmented>0</segmented>\n')

        # 3.写入字符串信息:[["water",[15,20,30,40]],["red",[12,13,14,15]]]
        for item in pred:
            xml_file.write('	<object>\n')
            xml_file.write('		<name>' + str(item[0]) + '</name>\n')
            xml_file.write('		<pose>Unspecified</pose>\n')
            xml_file.write('		<truncated>0</truncated>\n')
            xml_file.write('		<difficult>0</difficult>\n')
            xml_file.write('		<bndbox>\n')

            # 写入字符串信息
            # [top, left, bottom, right]
            xml_file.write('			<xmin>' + str(item[1][1]) + '</xmin>\n')
            xml_file.write('			<ymin>' + str(item[1][0]) + '</ymin>\n')
            xml_file.write('			<xmax>' + str(item[1][3]) + '</xmax>\n')
            xml_file.write('			<ymax>' + str(item[1][2]) + '</ymax>\n')

            xml_file.write('		</bndbox>\n')
            xml_file.write('	</object>\n')
        xml_file.write('</annotation>\n')
        

# 3.1 原始图片成倍增加
 # (1. img_path:图片具体路径,如: "D:\AI\yq\datas\strange\imgs\\1.jpg"
 # (2. xml_path:图片对应的xml文件的具体路径,如:"D:\AI\yq\datas\strange\labels\\1.xml"
 # (3. img_saves:需要保存的增强的图片路径,如:"D:\AI\yq\datas\strange\imgs_strange\\"
 # (4. xml_saves:需要保存的增强图片的xml文件的路径,如:xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"
 # (5. img_name: 增强的图片名称,如 5 (5.jpg)
def img_double(img_path, xml_path, img_saves, xml_saves, img_name):
    print("               .........................   图片倍增   ............................\n")

    # 1.读取图片
    img = cv2.imread(img_path)

    # 2.读取xml文件,写成pred
    pred = xml_read(xml_path)
    print(pred)

    # 3. 将图片写入要保存的图片路径
    #img_save_path = os.path.join(img_saves, img_name + ".jpg")
    img_save_path = img_saves + img_name + ".jpg"
    cv2.imwrite(img_save_path, img)

    # 4. 将增强的图片信息写入xml文件,由于成倍增加图片不影响图片中的目标信息,故只需要修改xml文件的前3项信息即可
    img_xml(img_save_path, xml_saves, img_name, pred)


    # 3. 画图
    for item in pred:
        a = (item[1][1], item[1][0])
        b = (item[1][3], item[1][2])
        cv2.rectangle(img, a, b, (0, 255, 0), 2)

    cv2.imshow("s1", img)
    cv2.imshow("s2", img)
    cv2.waitKey(0)





if __name__ == "__main__":

    # 要处理的图片和对应的xml文件路径
    img_path = "D:\AI\yq\datas\strange\imgs\\1.jpg"
    xml_path = "D:\AI\yq\datas\strange\labels\\1.xml"

    # 保存处理后的图片和xml文件路径
    img_saves = "D:\AI\yq\datas\strange\imgs_strange\\"
    xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"

    # 水平翻转后的图片路径
    img3_path = "D:\AI\yq\datas\strange\imgs_strange\\5.jpg"
    img_name = "5"

    # 3. 处理图片,增加xml文件
      # xml文件需要修改的信息: (1. <folder>imgs</folder>  imgs: 图片所在的文件夹名称
                   # (2. <filename>1.jpg</filename>  1.jpg:图片名称
                   # (3. <path>D:\AI\yq\datas\imgs\1.jpg</path>  D:\AI\yq\datas\imgs\1.jpg:图片路径
      # (4. ['luoding', [241, 1095, 505, 1307]]   'luoding':目标的名称
      # (5. [241, 1095, 505, 1307]]:目标在图片中的坐标


    # 3.1 图片倍增
    img_double(img_path, xml_path, img_saves, xml_saves,img_name)


以上代码中,对每个函数已经做了具体的解释:

1. xml_read(xml_path):输入:标注的原函数的xml文件路径

      处理:读取原图片对应的xml文件的目标的名称和坐标

返回结果: [['luoding', [241, 399, 505, 611]], ['luoding', [412, 678, 679, 1155]], ['luoding', [200, 1056, 450, 1156]], ['luoding', [765, 1334, 906, 1439]], ['luoding', [771, 641, 949, 845]]] ,表示图片中有5个'luoding',后面是对应了每个目标的坐标

2. img_xml(img_path, xml_path, img_name, pred):   

        输入:(1. img_path:保存的增加的图片的路径

     (2. xml_path:保存的增加的图片对应的xml文件的路径

     (3. img_name:保存的增加的图片的名称,如 5 (5.jpg)

     (4. pred:函数xml_read(xml_path)的返回值

       处理:根据函数的输入信息,将增加的图片的信息写入新的xml文件

3. img_double(img_path, xml_path, img_saves, xml_saves,img_name) 

该函数的输入在代码中写的很详细了,这里不用具体的介绍。

增加数量的图片的自动标注结果如下:

2.4 图片旋转任意角度

代码如下:

import cv2
import os
import numpy as np
import random
import xml.etree.ElementTree as ET
from shutil import copyfile

# 1. 读取xml文件
def xml_read(xml_path):
    # 2. xml 文件的读取
    tree = ET.parse(xml_path)
    root = tree.getroot()
    ss = []
    modifys = []
    width = 0

    for element in root.iter():
        if element.tag == "width":
            width = int(element.text)
            # print("width:", width)
        if element.tag == "name":
            name = element.text
            ss.append(name)
        if element.tag == "xmin":
            xmin = int(element.text)
            # print("xmin:", xmin)
            ss.append(xmin)
        if element.tag == "ymin":
            ymin = int(element.text)
            # print("ymin:", ymin)
            ss.append(ymin)
        if element.tag == "xmax":
            xmax = int(element.text)
            # print("xmax:", xmax)
            ss.append(xmax)
        if element.tag == "ymax":
            ymax = int(element.text)
            # print("ymax:", ymax)
            ss.append(ymax)
        if len(ss) == 5:
            x_min = ss[1]
            y_min = ss[2]
            x_max = ss[3]
            y_max = ss[4]

            modifys.append([ss[0], [y_min, x_min, y_max, x_max]])
            ss = []
    return modifys

# 2.写入xml文件
def img_xml(img_path, xml_path, img_name, pred):
    if len(pred) != 0:
        # 1.读取图片(xml需要写入图片的长宽高)
        img = cv2.imread(img_path)

        # 2.写入xml文件
        # (1)写入文件头部
        folder = img_path.split("\\")[-2]
        filename = img_path.split("\\")[-1]
        print(filename)

        xml_file = open((xml_path + img_name + '.xml'), 'w')
        xml_file.write('<annotation>\n')
        xml_file.write('	<folder>' + folder + '</folder>\n')
        xml_file.write('	<filename>' + filename + '</filename>\n')
        xml_file.write('	<path>' + img_path + '</path>\n')

        xml_file.write('	<source>\n')
        xml_file.write('		<database>Unknown</database>\n')
        xml_file.write('	</source>\n')

        # (2)写入图片的长宽高信息
        xml_file.write('	<size>\n')
        xml_file.write('		<width>' + str(img.shape[1]) + '</width>\n')
        xml_file.write('		<height>' + str(img.shape[0]) + '</height>\n')
        xml_file.write('		<depth>' + str(img.shape[2]) + '</depth>\n')
        xml_file.write('	</size>\n')

        xml_file.write('	<segmented>0</segmented>\n')

        # 3.写入字符串信息:[["water",[15,20,30,40]],["red",[12,13,14,15]]]
        for item in pred:
            xml_file.write('	<object>\n')
            xml_file.write('		<name>' + str(item[0]) + '</name>\n')
            xml_file.write('		<pose>Unspecified</pose>\n')
            xml_file.write('		<truncated>0</truncated>\n')
            xml_file.write('		<difficult>0</difficult>\n')
            xml_file.write('		<bndbox>\n')

            # 写入字符串信息
            # [top, left, bottom, right]
            xml_file.write('			<xmin>' + str(item[1][1]) + '</xmin>\n')
            xml_file.write('			<ymin>' + str(item[1][0]) + '</ymin>\n')
            xml_file.write('			<xmax>' + str(item[1][3]) + '</xmax>\n')
            xml_file.write('			<ymax>' + str(item[1][2]) + '</ymax>\n')

            xml_file.write('		</bndbox>\n')
            xml_file.write('	</object>\n')
        xml_file.write('</annotation>\n')

# 按照任意角度旋转图片
def rotate_img(img, angle):
    '''
    img   --image
    angle --rotation angle
    return--rotated img
    '''
    h, w = img.shape[:2]
    rotate_center = (w/2, h/2)
    #获取旋转矩阵
    # 参数1为旋转中心点;
    # 参数2为旋转角度,正值-逆时针旋转;负值-顺时针旋转
    # 参数3为各向同性的比例因子,1.0原图,2.0变成原来的2倍,0.5变成原来的0.5倍
    M = cv2.getRotationMatrix2D(rotate_center, angle, 1.0)
    #计算图像新边界
    new_w = int(h * np.abs(M[0, 1]) + w * np.abs(M[0, 0]))
    new_h = int(h * np.abs(M[0, 0]) + w * np.abs(M[0, 1]))
    #调整旋转矩阵以考虑平移
    M[0, 2] += (new_w - w) / 2
    M[1, 2] += (new_h - h) / 2

    rotated_img = cv2.warpAffine(img, M, (new_w, new_h))
    return rotated_img


# 3.4 图片的任意角度旋转
    # (1. img_path:图片具体路径,如: "D:\AI\yq\datas\strange\imgs\\1.jpg"
    # (2. xml_path:图片对应的xml文件的具体路径,如:"D:\AI\yq\datas\strange\labels\\1.xml"
    # (3. img_saves:需要保存的增强的图片路径,如:"D:\AI\yq\datas\strange\imgs_strange\\"
    # (4. xml_saves:需要保存的增强图片的xml文件的路径,如:xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"
    # (5. img_name:增强的图片名称,如 5 (5.jpg)
    # (6. angle:图片的旋转角度(角度制)
def img_rotation(img_path, xml_path, img_saves, xml_saves, img_name,angle):
    print("...............   图片翻转   ..................")

    # 1. 图片翻转
    img = cv2.imread(img_path)
    imgs = rotate_img(img, angle) # 旋转后的图片
    # print(imgs.shape,img.shape)
    # cv2.imshow("s1", imgs)
    # cv2.waitKey(0)

    # 2.将翻转后的图片保存在增强图片的文件夹
    img_save_path = img_saves + img_name + ".jpg"
    cv2.imwrite(img_save_path, imgs)

    # 3. xml 文件的读取
    pred = xml_read(xml_path)

    # 4. 图片翻转后,需要修改图片中目标的坐标,所以修改pred中的坐标,以图片的中心点旋转
    preds = []
    # 图片中心坐标为 (x_center,y_center)
    x_center = img.shape[1] / 2
    y_center = img.shape[0] / 2

    # 旋转后的图片的中心位置
    x_centers = imgs.shape[1] / 2
    y_centers = imgs.shape[0] / 2

    # 旋转后的图片的坐标平移
    x_var = x_centers - x_center
    y_var = y_centers - y_center

    # 将旋转角度转换为弧度制
    angles = angle * 3.1415926 / 180

    # 根据坐标旋转处理每个目标的坐标
    for items in pred:

        # 将目标坐标平移到图片的中心位置
        x_min = items[1][1] - x_center
        y_min = items[1][0] - y_center
        x_max = items[1][3] - x_center
        y_max = items[1][2] - y_center

        # 坐标变换,根据图片的中心位置旋转,目标坐标也以该中心位置为坐标原点进行旋转,从而获得新的坐标
        x_0 = x_min * np.cos(angles) + y_min * np.sin(angles) + x_center + x_var
        y_0 = y_min * np.cos(angles) - x_min * np.sin(angles) + y_center + y_var

        x_1 = x_min * np.cos(angles) + y_max * np.sin(angles) + x_center + x_var
        y_1 = y_max * np.cos(angles) - x_min * np.sin(angles) + y_center + y_var

        x_2 = x_max * np.cos(angles) + y_min * np.sin(angles) + x_center + x_var
        y_2 = y_min * np.cos(angles) - x_max * np.sin(angles) + y_center + y_var

        x_3 = x_max * np.cos(angles) + y_max * np.sin(angles) + x_center + x_var
        y_3 = y_max * np.cos(angles) - x_max * np.sin(angles) + y_center + y_var

        xmin = np.asarray([x_0, x_1, x_2, x_3]).min()
        xmax = np.asarray([ x_0, x_1, x_2, x_3]).max()

        ymin = np.asarray([y_0, y_1, y_2, y_3]).min()
        ymax = np.asarray([y_0, y_1, y_2, y_3]).max()

        preds.append([items[0],[int(ymin), int(xmin), int(ymax), int(xmax)]])
    
    # 5. 将翻转的图片信息写入xml文件
    img_xml(img_save_path, xml_saves, img_name, preds)

    # 6. 画图
    for item in preds:
        a = (item[1][1], item[1][0])
        b = (item[1][3], item[1][2])
        cv2.rectangle(imgs, a, b, (0, 255, 0), 2)

    cv2.imshow("s2", imgs)
    cv2.waitKey(0)


if __name__ == "__main__":

    # 要处理的图片和对应的xml文件路径
    img_path = "D:\AI\yq\datas\strange\imgs\\1.jpg"
    xml_path = "D:\AI\yq\datas\strange\labels\\1.xml"

    # 保存处理后的图片和xml文件路径
    img_saves = "D:\AI\yq\datas\strange\imgs_strange\\"
    xml_saves = "D:\AI\yq\datas\strange\labels_strange\\"

    # 水平翻转后的图片路径
    img3_path = "D:\AI\yq\datas\strange\imgs_strange\\5.jpg"
    img_name = "5"
    # 3.4 图片以某个角度旋转,如上下旋转(90度)
    img_rotation(img_path, xml_path, img_saves, xml_saves, img_name,angle=60)






下面是旋转后对坐标变换,然后在旋转后的图片上画的图片

      

                               

 

比如,60度时的xml文件如下:

通过以上的结果可以看出,标定每问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值