关于搞视觉识别的数据处理的一些注意事项及诸多代码

本文详细介绍了图像数据集处理的各个步骤,包括务必备份、图片拍摄的质量控制、图片处理如旋转和调整曝光,图片裁剪的注意事项,格式转化的必要性,以及图片分类、打标签和重命名的方法。强调了在构建数据集时平衡多样性和一致性的重要性,并提供了Python代码示例来自动化处理过程。
摘要由CSDN通过智能技术生成

目录

1.一定一定要备份

2.图片拍摄

3.图片处理

4.图片裁剪

5.格式转化

方案一

方案二

6.图片分类

7.打标签

8.重命名

9.随机挑选数据集


其实这部分内容并不多,主要就交代一下需要注意的地方

1.一定一定要备份

在拍摄图像数据集或者打完标签之类的时候,特别是进行大规模批量处理前,一定进行备份,这部分不需要代码,只需要简单的复制粘贴就可以,所以不过多陈述

哪怕搞得复杂一点也总比事后干着急强
​​​​​​

2.图片拍摄

数据集收集,不在于数量,而在于质量。这其实是一个比较复杂的,需要进行权衡的工作,我建议交给比较机灵的人去干。

比如,假如拍了1000张水瓶,但是这些水瓶的角度和姿态都大差不差,那这就是废品,这会导致在训练之后对水瓶的识别会变得十分苛刻。

又比如,假如拍了1000张水瓶,但是这些水瓶的角度全都不一样,这其实也是废品,这会导致在训练之后对水瓶的识别变得十分松懈或者更加苛刻。

所以需要在复杂和单一中进行权衡,对更容易出现的角度和姿态进行多拍,对不容易出现的角度少拍但是不能不拍。

这边建议先把垃圾盛放台和摄像头的大小间距之类的先决定好后直接开始搭建拍摄台,1:1复刻垃圾桶正式出炉的拍摄状态,这样会很利于后续的识别,也可以直接随心一点扔,数量多了就能看出哪种姿态更加常见,但是这对拍摄台的特征要求比较高。

哪怕只拍了两张也可以通过旋转或者调色调等增大数量还能提高随机性

3.图片处理

这部分我觉得没啥用,但是其实还是有点用的,比如最基础的旋转。

在旋转目标识别中有着非常大的作用。

或者比如曝光度或者对比度之类的,这使垃圾桶在面对不同光照下时的识别变得容易得多。

其他的比如改噪点啊,或者改色调啊,肯定还是有用,但是我感觉作用并不是那么明显了,我觉得可能会影响整体数据,也就不多阐述了,在本次数据集处理我是肯定不会做这些的,假如后人想试一试,那也可以搞一搞对这进行补充。

这是cv搞出来的实时图像处理,还是蛮酷的,可以用来装逼,代码用的是别人的,想要的话可以私信或者评论找我要

4.图片裁剪

图片裁剪一般都是进行大规模批量裁剪,所以像我最开始所说,一定要记得备份。

具体什么时候裁剪我就不多说了,肯定是会遇到的。

这里就放一下我用的批量裁剪代码吧

from PIL import Image
import os
import os.path

# 定义文件所在文件夹
image_dir = "文件夹路径"
for parent, dir_name, file_names in os.walk(image_dir):  # 遍历每一张图片
    for filename in file_names:
        pic_name = os.path.join(parent, filename)
        image = Image.open(pic_name)
        _width, _height = image.size
        print(_width, _height)

        # 定义裁剪范围(left, upper, right, lower)
        box = image.crop((440,0,1520,1080))
        filename = 'cut_' + filename
        box.save("文件夹路径" % filename)
    print('Done!')

需要你改的地方就三部分

第一部分image_dir,改你图片输入的文件夹

第二部分box,里面有四个数,分别是左上右下,你可以理解为两个一维坐标,分别是以左为零点和上为零点,根据你给出的四个数画出四条直线然后剪切

最后box.save就是保存路径。

注:一定注意切的尺寸,虽然可能切不到要进行识别的物体,但是可能在后续打标签之类的可能会碰见问题

5.格式转化

假如你用的是rolabelimg,那输出的应该是xml文件,然后要对xml文件进行转化,使其保留核心内容然后变成txt文件,当然根据你的需求不同,你转化后的文件格式也不一样,比如yolo形式或者dota形式等等。

这里我就放一下我的代码,可以进行dota和yolo的双重转化。

方案一

# 文件名称   :roxml_to_dota.py

# 功能描述   :把rolabelimg标注的xml文件转换成dota能识别的xml文件,

#             再转换成dota格式的txt文件

#            把旋转框 cx,cy,w,h,angle,转换成四点坐标x1,y1,x2,y2,x3,y3,x4,y4

import os

import xml.etree.ElementTree as ET

import math


def edit_xml(xml_file, dotaxml_file):

    """

    修改xml文件

    :param xml_file:xml文件的路径

    :return:

    """

    tree = ET.parse(xml_file)

    objs = tree.findall('object')

    for ix, obj in enumerate(objs):

        x0 = ET.Element("x0")  # 创建节点

        y0 = ET.Element("y0")

        x1 = ET.Element("x1")

        y1 = ET.Element("y1")

        x2 = ET.Element("x2")

        y2 = ET.Element("y2")

        x3 = ET.Element("x3")

        y3 = ET.Element("y3")

        # obj_type = obj.find('bndbox')

        # type = obj_type.text

        # print(xml_file)


        if (obj.find('robndbox') == None):

            obj_bnd = obj.find('bndbox')

            obj_xmin = obj_bnd.find('xmin')

            obj_ymin = obj_bnd.find('ymin')

            obj_xmax = obj_bnd.find('xmax')

            obj_ymax = obj_bnd.find('ymax')

            xmin = float(obj_xmin.text)

            ymin = float(obj_ymin.text)

            xmax = float(obj_xmax.text)

            ymax = float(obj_ymax.text)

            obj_bnd.remove(obj_xmin)  # 删除节点

            obj_bnd.remove(obj_ymin)

            obj_bnd.remove(obj_xmax)

            obj_bnd.remove(obj_ymax)

            x0.text = str(xmin)

            y0.text = str(ymax)

            x1.text = str(xmax)

            y1.text = str(ymax)

            x2.text = str(xmax)

            y2.text = str(ymin)

            x3.text = str(xmin)

            y3.text = str(ymin)

        else:

            obj_bnd = obj.find('robndbox')

            obj_bnd.tag = 'bndbox'  # 修改节点名

            obj_cx = obj_bnd.find('cx')

            obj_cy = obj_bnd.find('cy')

            obj_w = obj_bnd.find('w')

            obj_h = obj_bnd.find('h')

            obj_angle = obj_bnd.find('angle')

            cx = float(obj_cx.text)

            cy = float(obj_cy.text)

            w = float(obj_w.text)

            h = float(obj_h.text)

            angle = float(obj_angle.text)

            obj_bnd.remove(obj_cx)  # 删除节点

            obj_bnd.remove(obj_cy)

            obj_bnd.remove(obj_w)

            obj_bnd.remove(obj_h)

            obj_bnd.remove(obj_angle)


            x0.text, y0.text = rotatePoint(cx, cy, cx - w / 2, cy - h / 2, -angle)

            x1.text, y1.text = rotatePoint(cx, cy, cx + w / 2, cy - h / 2, -angle)

            x2.text, y2.text = rotatePoint(cx, cy, cx + w / 2, cy + h / 2, -angle)

            x3.text, y3.text = rotatePoint(cx, cy, cx - w / 2, cy + h / 2, -angle)


        # obj.remove(obj_type)  # 删除节点

        obj_bnd.append(x0)  # 新增节点

        obj_bnd.append(y0)

        obj_bnd.append(x1)

        obj_bnd.append(y1)

        obj_bnd.append(x2)

        obj_bnd.append(y2)

        obj_bnd.append(x3)

        obj_bnd.append(y3)


        tree.write(dotaxml_file, method='xml', encoding='utf-8')  # 更新xml文件


# 转换成四点坐标

def rotatePoint(xc, yc, xp, yp, theta):

    xoff = xp - xc;

    yoff = yp - yc;

    cosTheta = math.cos(theta)

    sinTheta = math.sin(theta)

    pResx = cosTheta * xoff + sinTheta * yoff

    pResy = - sinTheta * xoff + cosTheta * yoff

    return str(int(xc + pResx)), str(int(yc + pResy))


def totxt(xml_path, out_path):

    # 想要生成的txt文件保存的路径,这里可以自己修改


    files = os.listdir(xml_path)

    for file in files:


        tree = ET.parse(xml_path + os.sep + file)

        root = tree.getroot()

        name = file.strip('.xml')

        output = out_path + name + '.txt'

        file = open(output, 'w')

        objs = tree.findall('object')

        for obj in objs:

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

            box = obj.find('bndbox')

            x0 = int(float(box.find('x0').text))

            y0 = int(float(box.find('y0').text))

            x1 = int(float(box.find('x1').text))

            y1 = int(float(box.find('y1').text))

            x2 = int(float(box.find('x2').text))

            y2 = int(float(box.find('y2').text))

            x3 = int(float(box.find('x3').text))

            y3 = int(float(box.find('y3').text))

            file.write("{} {} {} {} {} {} {} {} {} 0\n".format(x0, y0, x1, y1, x2, y2, x3, y3, cls))

        file.close()

        print(output)

if __name__ == '__main__':

    # -----**** 第一步:把xml文件统一转换成旋转框的xml文件 ****-----

    roxml_path =  "你的路径" # 目录下保存的是需要转换的xml文件

    dotaxml_path = "你的路径"

    out_path = "你的路径"

    filelist = os.listdir(roxml_path)

    for file in filelist:

        edit_xml(os.path.join(roxml_path, file), os.path.join(dotaxml_path, file))



    # -----**** 第二步:把旋转框xml文件转换成txt格式 ****-----

    totxt(dotaxml_path, out_path)

常规需要改的仍然只有三部分roxml_path,dotaxml_path和out_path,分别是输入文件夹和输出dota文件夹和输出yolo文件夹。无需多做解释

假如项目不同使用的系统不同那对文件的内容可能要求也不同,那就需要对这段代码内部进行修改,那就需要你自己去研究和改了(其实也不难)

方案二

这一套是正在使用的数据集格式转化方案,具体方案要根据你的需求而定

# *_* coding : UTF-8 *_*

# 文件名称   :roxml_to_dota.py

# 功能描述   :把rolabelimg标注的xml文件转换成dota能识别的xml文件

#             就是把旋转框 cx,cy,w,h,angle,转换成四点坐标x1,y1,x2,y2,x3,y3,x4,y4

import os

import xml.etree.ElementTree as ET

import math

import cv2

import numpy as np


def edit_xml(xml_file):

    if ".xml" not in xml_file:

        return



    tree = ET.parse(xml_file)

    objs = tree.findall('object')



    txt = xml_file.replace(".xml", ".txt")



    jpg = xml_file.replace(".xml", ".png")  # 图片是jpg格式

    src = cv2.imread(jpg, 1)



    with open(txt, 'w') as wf:

        wf.write("imagesource:GoogleEarth\n")

        wf.write("gsd:0.115726939386\n")


        for ix, obj in enumerate(objs):

            x0text = ""

            y0text = ""

            x1text = ""

            y1text = ""

            x2text = ""

            y2text = ""

            x3text = ""

            y3text = ""

            difficulttext = ""

            className = ""

            obj_type = obj.find('type')

            type = obj_type.text

            obj_name = obj.find('name')

            className = obj_name.text

            obj_difficult = obj.find('difficult')

            difficulttext = obj_difficult.text

            if type == 'bndbox':  # 若用rolabelimg标注的是水平框

                obj_bnd = obj.find('bndbox')

                obj_xmin = obj_bnd.find('xmin')

                obj_ymin = obj_bnd.find('ymin')

                obj_xmax = obj_bnd.find('xmax')

                obj_ymax = obj_bnd.find('ymax')

                xmin = float(obj_xmin.text)

                ymin = float(obj_ymin.text)

                xmax = float(obj_xmax.text)

                ymax = float(obj_ymax.text)

                x0text = str(xmin)

                y0text = str(ymin)

                x1text = str(xmax)

                y1text = str(ymin)

                x2text = str(xmin)

                y2text = str(ymax)

                x3text = str(xmax)

                y3text = str(ymax)

                points = np.array([[int(float(x0text)), int(float(y0text))], [int(float(x1text)), int(float(y1text))],

                                   [int(float(x2text)), int(float(y2text))],

                                   [int(float(x3text)), int(float(y3text))]], np.int32)

                cv2.polylines(src, [points], True, (255, 0, 0))  # 画任意多边

            elif type == 'robndbox':  # 若用rolabelimg标注的是旋转框

                obj_bnd = obj.find('robndbox')

                obj_bnd.tag = 'bndbox'  # 修改节点名

                obj_cx = obj_bnd.find('cx')

                obj_cy = obj_bnd.find('cy')

                obj_w = obj_bnd.find('w')

                obj_h = obj_bnd.find('h')

                obj_angle = obj_bnd.find('angle')

                cx = float(obj_cx.text)

                cy = float(obj_cy.text)

                w = float(obj_w.text)

                h = float(obj_h.text)

                angle = float(obj_angle.text)

                x0text, y0text = rotatePoint(cx, cy, cx - w / 2, cy - h / 2, -angle)

                x1text, y1text = rotatePoint(cx, cy, cx + w / 2, cy - h / 2, -angle)

                x2text, y2text = rotatePoint(cx, cy, cx + w / 2, cy + h / 2, -angle)

                x3text, y3text = rotatePoint(cx, cy, cx - w / 2, cy + h / 2, -angle)

                points = np.array([[int(float(x0text)), int(float(y0text))], [int(float(x1text)), int(float(y1text))],

                                   [int(float(x2text)), int(float(y2text))],

                                   [int(float(x3text)), int(float(y3text))]], np.int32)

                cv2.polylines(src, [points], True, (255, 0, 0))  # 画任意多边形



            # print(x0text,y0text,x1text,y1text,x2text,y2text,x3text,y3text,className,difficulttext)

            wf.write(

                "{} {} {} {} {} {} {} {} {} {}\n".format(x0text, y0text, x1text, y1text, x2text, y2text, x3text, y3text,

                                                         className, difficulttext))

# 转换成四点坐标

def rotatePoint(xc, yc, xp, yp, theta):

    xoff = xp - xc;

    yoff = yp - yc;

    cosTheta = math.cos(theta)

    sinTheta = math.sin(theta)

    pResx = cosTheta * xoff + sinTheta * yoff

    pResy = - sinTheta * xoff + cosTheta * yoff

    return str(int(xc + pResx)), str(int(yc + pResy))

if __name__ == '__main__':

    dir = "你的路径"  # 此处的xml1130文件夹中必须既要放jpg图片集又要放图片对应的xml文件不然会报打不开文件的错误!!

    filelist = os.listdir(dir)

    for file in filelist:

        edit_xml(os.path.join(dir, file))

需要改的只有dir一个文件夹路径。这套方案直接把图片和xml文件放在一起了,随后的txt文件也会直接生成在这个文件夹中,所以你需要后续的分类。

6.图片分类

# 功能描述:从指定文件夹中根据文件名后缀提取相应文件以txt文件为例,并存储到新文件夹中

import os

import shutil

def filterfile(file_dir, save_dir, suffix):

    if os.path.exists(save_dir):

        shutil.rmtree(save_dir)  # 如果已经存在该文件夹,移除

    if not os.path.exists(save_dir):

        os.makedirs(save_dir)  # 如果不存在该文件夹,则创建,用于储存后续提取出来的文件

    filelist = []  # 存储要copy的文件全名

    for dirpath, dirnames, filenames in os.walk(file_dir):  # 根据路径执行树状的遍历,分别遍历根目录,根目录下的文件夹,文件夹下的文件

        for file in filenames:  # 遍历文件夹中的文件

            file_type = file.split('.')[-1]  # 对文件名根据.进行分隔,实现文件名,后缀名的分离

            if (file_type in suffix):  # 下面根据后缀名是否在列表中,提取文件

                file_fullname = os.path.join(dirpath, file)  # 文件全名

                filelist.append(file_fullname)  # 将符合要求的文件存放在列表中

    for file in filelist:

        shutil.copy(file, save_dir)  # 将列表中的文件复制到新的文件夹


if __name__ == "__main__":

    filterfile("mix", "label_txt", ['txt'])


  file_dir指读的文件目录;save_dir为保存文件的目录
  suffix用于存放打算提取的文件的后缀名,可以根据自己的需求进行修改

7.打标签

有的方案需要你进行打标签,比如建一个txt文件其中放置某一图片的分类,可能txt命名还要求和图片的名称一样,这时候手打就十分麻烦,可以用这套代码进行实现

import os

root_path="你的路径"

save_path="你的路径"#对应的label文件夹下也要建好相应的空子文件夹

names=os.listdir(root_path) #得到images文件夹下的夹的名称

for name in names:

    path=os.path.join(root_path,name)

    img_names=os.listdir(path)  #得到子文件夹下的图片的名称

    for img_name in img_names:

        save_name = img_name.split(".jpg")[0]+'.txt'    #得到相应的lable名称

        txt_path=os.path.join(save_path,name)           #得到label的子文件夹的路径

        with open(os.path.join(txt_path,save_name), "w") as f:  #结合子文件夹路径和相应子文件夹下图片的名称生成相应的子文件夹txt文件

            f.write(name)       #将label写入对应txt文件夹

            print(f.name)

root_path:输入文件夹

save_path:输出文件夹

save_name:括号里面的是要去掉的后缀名,后面是加上的后缀名,就能变成新的同名不同格文件

8.重命名

肯定也会伴随重命名,但是一个个改也很麻烦,就直接用python批量实现

import os

root_dir="你的路径"   #原文件夹路径

save_path="你的路径"  #新建文件夹路径

img_path=os.listdir(root_dir)

a=0

for i in img_path:

    a+=1

    i= os.path.join(os.path.abspath(root_dir), i)

    new_name=os.path.join(os.path.abspath(save_path), str(a) + '.txt')    #此处可以修改图片名称

    os.rename(i,new_name)    #特别注意:rename会删除原图

root_dir为输入文件夹

save_dir为输出文件夹

假如两个一样那就直接是覆盖了

new_name就是改的名字,这里改的是按数字顺序命名txt

9.随机挑选数据集

有的代码运行要求train和val分开,所以这个时候就需要随机挑选数据集了

import os

import random

import shutil

# 源文件夹路径

src_path = "你的路径"

# 目标文件夹路径

dst_path = "你的路径"

# 获取源文件夹下的所有文件名

file_list = os.listdir(src_path)

# 计算挑选文件个数

num_files = len(file_list)

num_selected = int(num_files*0.3)

# 随机挑选文件名

selected_files = random.sample(file_list, num_selected)

# 遍历选中的文件,将其剪切粘贴至目标文件夹

for file_name in selected_files:

    src_file = os.path.join(src_path, file_name)

    dst_file = os.path.join(dst_path, file_name)

    shutil.move(src_file, dst_file)

src_path输入文件夹

dst_path输出文件夹

num_selected就是用来改选多少的,通过改比例进行数量的更改

只写了这些不代表只有这些,后面再用到还会进行补充,有问题就评论私信,小问题

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值