用自己的数据集训练SSD(一)(只有一个目标和一个背景)

制作voc数据集

1,我创建了一个项目文件夹叫做2007voc
将数据放在这个文件夹下,包括标记数据和训练数据
用自己的数据集训练SSD(一)(只有一个目标和一个背景)
2因为我的数据格式是下面这种,所以我集写了代码如下:主要是将多行的标记文件转在同一行。
用自己的数据集训练SSD(一)(只有一个目标和一个背景)
这种格式的数据,每一张图的框都是放在不同行的,只能重新处理

import pandas as pd
def read_csv(csv_path,pre_dir):
   
    data=pd.read_csv(csv_path,sep=',')   
    label_dict = {}
    
    for name,group in data.groupby(data['ID']):
        image_path = os.path.join(pre_dir, name)
        bbox=[]
        for g in group.values:
#             print(g[1])
            for i in g[1].split('\n'):
#             //j=list(i)
                if i is not None and len(i.strip()) > 0:
                    bbox.append(list(map(lambda x: round(float(x.strip())), i.split(' '))))
            label_dict.setdefault(image_path, bbox)
        print("结束了")
    return label_dict

写进去,

def write_csv(result_dict, out_path='out.csv'):
    '''
    :param result_dict: 只一个图片路径,对应存储相应bbox的list的字典
    :param out_path: 
    :return: 
    '''
    with open(out_path, 'w', newline='') as f:
        writer = csv.writer(f)
        # 写文件头
        writer.writerow(['name', 'coordinate'])

        for image in result_dict.keys():
            image_name = os.path.split(image)[-1]
            bbox = result_dict.get(image, [])
            bbox_rs = ';'.join(['_'.join(str(int(id)) for id in i) for i in bbox])
            writer.writerow([image_name, bbox_rs])

写入一个out.csv看下效果,将一张图的所有标签读写进来同一行

import csv
import os
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
    label_dict = read_csv(csv_path=r'./train_labels.csv',
                             pre_dir=r'train_dataset')
    write_csv(label_dict)

用自己的数据集训练SSD(一)(只有一个目标和一个背景)
2 正式做数据集

  • 第一步:改名
    为什么要改名,原因是
    (不重命名也可以).
    把你的图片放到JPEGSImages里面,在VOC2007里面,人家的图片文件名都是000001.jpg类似这样的,我们也统一格式,把我们的图片名字重命名成这样的.这里提醒下,若是使用软件或者其他代码重命名,一定注意jpg和xml文件一一对应,若有的程序是随机重命名,就要小心jpg和xml文件不对应.

# -*- coding: utf-8 -*-
# @Author  : matthew
# @File    : pack2voc.py
# @Software: PyCharm

import os

def rename_image(label_dict={}, out_file='rename_train_b.csv'):
    '''
    改文件名的同时,修改标签文件。并存储成新的CSV文件rename_train_b.csv
    :param label_dict:
    :param out_file:
    :return:
    '''
    new_label_dict = {}
    i = 1
    with open(out_file, 'w') as f:
        for key in label_dict.keys():
            if not os.path.isfile(key):
                continue

            image_name = os.path.split(key)[-1]
            new_image_name = '%09d' % i + '.jpg'
            i = i + 1
            # 改名
            new_key = key.replace(image_name, new_image_name)
            os.renames(key, new_key)

            new_label_dict.setdefault(new_key, label_dict.get(key, []))
            write_csv(new_label_dict, out_path=out_file)

    return out_file
if __name__ == '__main__':
#     label_dict = utils.read_csv(csv_path=r'./train_b.csv',
#                              pre_dir=r'/home/matthew/dataset')
    rename_image(label_dict) 
  • 第二步:建立VOC2007目录
def make_voc_dir():
    os.makedirs('VOC2007/Annotations')
    os.makedirs('VOC2007/ImageSets')
    os.makedirs('VOC2007/ImageSets/Main')
    os.makedirs('VOC2007/ImageSets/Layout')
    os.makedirs('VOC2007/ImageSets/Segmentation')
    os.makedirs('VOC2007/JPEGImages')
    os.makedirs('VOC2007/SegmentationClass')
    os.makedirs('VOC2007/SegmentationObject')
if __name__ == '__main__':
    make_voc_dir()
  • 开始制作标记文件
    代码如下,先将自己重名后的数据集放在’VOC2007/JPEGImages目录下面,接着运行下面的代码
# -*- coding: utf-8 -*-
# @Author  : matthew
# @File    : utils.py
# @Software: PyCharm

import csv
import os

def read_csv(csv_path, pre_dir):
    '''
    :param csv_path:csv文件路径 
    :param pre_dir: 图片数据所在的文件夹
    :return: 
    '''
    label_dict = {}
    with open(csv_path, "r") as f:
        reader = csv.reader(f)
        header = True
        for line in reader:
            # 除去文件头
            if header:
                header = False
                continue
            # 处理文件存储路径,当做标签
            image_path = os.path.join(pre_dir, line[0])
            # 处理后面的bbox
            bbox = []

            if line[1] is not None and len(line[1].strip()) > 0:
                for i in line[1].split(';'):
                    if i is not None and len(i.strip()) > 0:
                        bbox.append(list(map(lambda x: round(float(x.strip())), i.split('_'))))
            # 添加到label_dict
            label_dict.setdefault(image_path, bbox)
    return label_dict


def write_csv(result_dict, out_path='out.csv'):
    '''
    :param result_dict: 只一个图片路径,对应存储相应bbox的list的字典
    :param out_path: 
    :return: 
    '''
    with open(out_path, 'w', newline='') as f:
        writer = csv.writer(f)
        # 写文件头
        writer.writerow(['name', 'coordinate'])

        for image in result_dict.keys():
            image_name = os.path.split(image)[-1]
            bbox = result_dict.get(image, [])
            bbox_rs = ';'.join(['_'.join(str(int(id)) for id in i) for i in bbox])
            writer.writerow([image_name, bbox_rs])



  • 第三步:生成相应的Annotations目录下的XML文件

要修改图片的大小,不然后面就会导致标记框的不准确
我的图片的大小是:2660*2000
运行下面的代码

import lxml.html
def save_xml(image_name, bbox, save_dir='./VOC2007/Annotations', width=2660, height=2000, channel=3):
    '''
    将CSV中的一行
    000000001.jpg [[1,2,3,4],...]
    转化成
    000000001.xml

    :param image_name:图片名
    :param bbox:对应的bbox
    :param save_dir:
    :param width:这个是图片的宽度,博主使用的数据集是固定的大小的,所以设置默认
    :param height:这个是图片的高度,博主使用的数据集是固定的大小的,所以设置默认
    :param channel:这个是图片的通道,博主使用的数据集是固定的大小的,所以设置默认
    :return:
    '''
    from lxml.etree import Element, SubElement, tostring
    from xml.dom.minidom import parseString

    node_root = Element('annotation')

    node_folder = SubElement(node_root, 'folder')
    node_folder.text = 'JPEGImages'

    node_filename = SubElement(node_root, 'filename')
    node_filename.text = image_name

    node_size = SubElement(node_root, 'size')
    node_width = SubElement(node_size, 'width')
    node_width.text = '%s' % width

    node_height = SubElement(node_size, 'height')
    node_height.text = '%s' % height

    node_depth = SubElement(node_size, 'depth')
    node_depth.text = '%s' % channel

    for x, y, w, h in bbox:
        left, top, right, bottom = x, y, x + w, y + h
        node_object = SubElement(node_root, 'object')
        node_name = SubElement(node_object, 'name')
        node_name.text = 'car'
        node_difficult = SubElement(node_object, 'difficult')
        node_difficult.text = '0'
        node_bndbox = SubElement(node_object, 'bndbox')
        node_xmin = SubElement(node_bndbox, 'xmin')
        node_xmin.text = '%s' % left
        node_ymin = SubElement(node_bndbox, 'ymin')
        node_ymin.text = '%s' % top
        node_xmax = SubElement(node_bndbox, 'xmax')
        node_xmax.text = '%s' % right
        node_ymax = SubElement(node_bndbox, 'ymax')
        node_ymax.text = '%s' % bottom

    xml = tostring(node_root, pretty_print=True)  
    dom = parseString(xml)

    save_xml = os.path.join(save_dir, image_name.replace('jpg', 'xml'))
    with open(save_xml, 'wb') as f:
        f.write(xml)

    return


def change2xml(label_dict={}):
    for image in label_dict.keys():
        image_name = os.path.split(image)[-1]
        bbox = label_dict.get(image, [])
        save_xml(image_name, bbox)
    return


if __name__ == '__main__':
    # step 2
    # make_voc_dir()

    # step 3
    # label_dict = utils.read_csv(csv_path=r'./train_b.csv',
    #                             pre_dir=r'/home/matthew/dataset')
    # rename_image(label_dict)

    # step 3
    label_dict = read_csv1(csv_path=r'./rename_train_b.csv',
                                pre_dir=r'./VOC2007/JPEGImages')
    change2xml(label_dict)
  • 第四步:生成Main目录下的txt文件

这段代码放在建立make_train_val_test_set.py,放在VOC2007目录下,然后运行。

# -*- coding: utf-8 -*-
# @Author  : matthew
# @File    : make_train_val_test_set.py
# @Software: PyCharm

import os
import random


def _main():
    trainval_percent = 0.1
    train_percent = 0.9
    xmlfilepath = 'Annotations'
    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('ImageSets/Main/trainval.txt', 'w')
    ftest = open('ImageSets/Main/test.txt', 'w')
    ftrain = open('ImageSets/Main/train.txt', 'w')
    fval = open('ImageSets/Main/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()


if __name__ == '__main__':
    _main()
第五步:运行voc_annotation.py

运行的时候,注意修改这个脚本里面的一些路径和参数
import xml.etree.ElementTree as ET
from os import getcwd

# 注意这里的‘2007’,也许你的就需要修改
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]

# 注意类别
classes = ["car"]
def convert_annotation(year, image_id, list_file):
    # 注意路径
    in_file = open('F:/Jupyter/2007voc/VOC%s/Annotations/%s.xml'%(year, image_id))
    tree=ET.parse(in_file)
    root = tree.getroot()

    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 = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))
        list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))

wd = getcwd()
print(wd)
for year, image_set in sets:
    # 注意路径
#     image_ids1 = open('F:/Jupyter/voc2017/VOC%s/ImageSets/Main/%s.txt'%(year, image_set))
#     image_ids=image_ids1.read().strip().split()
    image_ids = open('F:/Jupyter/2007voc/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
    list_file = open('%s_%s.txt'%(year, image_set), 'a')
    print(list_file)
    for image_id in image_ids:
        # 注意路径
        list_file.write('%s/VOC%s/JPEGImages/%s.jpg'%(wd, year, image_id))
        convert_annotation(year, image_id, list_file)
        list_file.write('\n')
list_file.close()
    

后记

成功之后,会生成三个文件,像这样

用自己的数据集训练SSD(一)(只有一个目标和一个背景)
也会有下面的文件夹
用自己的数据集训练SSD(一)(只有一个目标和一个背景)
关于如何训练,下篇博文将会讲解,如果你的数据集一开始的标记文件就已经是一个图片的所有行都在同一行,那么既可以直接从改名开始做起。
就是省略下面的这一步
用自己的数据集训练SSD(一)(只有一个目标和一个背景)
参考文件:
一次将自己的数据集制作成PASCAL VOC格式的惨痛经历 - 叶罅 - 博客园 https://www.cnblogs.com/blog4ljy/p/9195752.html