美文网首页目标检测
目标检测第2步-数据准备

目标检测第2步-数据准备

作者: 潇洒坤 | 来源:发表于2018-12-02 17:23 被阅读147次

    致谢声明

    本文在学习《Tensorflow object detection API 搭建属于自己的物体识别模型(2)——训练并使用自己的模型》的基础上优化并总结,此博客链接:https://blog.csdn.net/dy_guox/article/details/79111949感谢此博客作者。

    0.前言

    在进行本文操作之前,需要先安装好tensorflow的gpu版本
    本文作者的环境:python3.6、Windows10、tensorflow_gpu1.10
    已经安装好的可以跳过,学习如何安装tensorflow的gpu版本的读者请阅读本文作者的另外一篇文章《深度学习环境搭建-CUDA9.0、cudnn7.3、tensorflow_gpu1.10的安装》,链接:https://www.jianshu.com/p/4ebaa78e0233
    本文是写给目标检测入门新手的指导文章,会用示意图将每一步的详细实现过程展示出来。
    本文作者接触深度学习2个月后,开始进行目标检测实践。
    本文作者的专题《目标检测》,链接:https://www.jianshu.com/c/fd1d6f784c1f
    此专题的宗旨是让基础较为薄弱的新手能够顺利实现目标检测,专题内容偏向于掌握技能,学会工具的使用。
    本文作者尚未具备清楚讲述目标检测原理的能力,学习原理请自行另找文章。

    1.下载图片

    本文作者给读者演示的图片数据是来自ImageNet中的鲤鱼分类。
    数据集在百度云盘,链接: https://pan.baidu.com/s/1NksESNqBX--YqMJ4zptGdw 提取码: 6p3u
    在桌面新建文件夹目标检测,把下载好的压缩文件n01440764.tar放到其中,如下图所示:

    image.png
    选择解压到n01440764,如下图所示:
    image.png
    解压完成后,桌面的目标检测文件夹中如下图所示:
    image.png

    2.选择图片

    在此数据集中,大部分图片都较为清晰,但是有极少数图片像素点少,不清晰。
    像素点少的图片不利于模型训练或模型测试,所以在本章节中实现用python代码选出部分图片文件。
    在桌面的目标检测文件夹中打开cmd,即在路径中输入cmd后按Enter键,如下图所示:

    image.png
    在cmd中输入命令并运行:jupyter notebook,如下图所示:
    image.png
    浏览器会自动打开1个标签页,选择新建ipynb代码文件,如下图所示:
    image.png
    为ipynb文件重命名,重命名按钮如下图红色箭头标记处所示:
    image.png
    修改文件名为get_some_qualified_images,如下图所示:
    image.png
    复制下面一段代码到代码文件get_some_qualified_images.ipynb的单元格中,复制后运行即可:
    import os
    import random
    from PIL import Image
    import shutil
    
    #获取1000张图片中随机选出数量为sample_number*2的一部分图片的路径
    def get_some_imagePath(dirPath, sample_number):
        fileName_list = os.listdir(dirPath)
        all_filePath_list = [os.path.join(dirPath, fileName) for fileName in fileName_list]
        all_imagePath_list = [filePath for filePath in all_filePath_list if '.jpg' in filePath]
        some_filePath_list = random.sample(all_filePath_list, k=sample_number*2)
        return some_filePath_list
    
    #获取一部分像素足够,即长,宽都大于300的图片
    def get_some_qualified_images(dirPath, sample_number, new_dirPath):
        some_imagePath_list = get_some_imagePath(dirPath, sample_number)
        if os.path.isdir(new_dirPath):
            shutil.rmtree(new_dirPath)
        os.mkdir(new_dirPath)
        i = 0
        for imagePath in some_imagePath_list:
            image = Image.open(imagePath)
            width, height = image.size
            if width > 300 and height > 300:
                i += 1
                new_imagePath = 'selected_images/%03d.jpg' %i
                shutil.copy(imagePath, new_imagePath)
            if i == sample_number:
                break
    
    #获取数量为100的合格样本存放到selected_images文件夹中
    get_some_qualified_images('n01440764', 100, 'selected_images')
    

    代码运行完成后,在桌面的目标检测文件夹中,会有一个selected_images文件夹,如下图所示:

    image.png

    3.缩小图片

    在第2章选择图片中,选出了100张像素足够的图片存放在selected_images文件夹中,即淘汰了像素过小的图片。
    在本章第3章中用代码实现将像素过大的图片做缩小。
    在jupyter notebook中新建代码文件get_small_images.ipynb,步骤与上一章中相同:
    打开cmd——>运行jupyter notebook——>新建代码文件——>代码文件重命名
    复制下面一段代码到代码文件get_small_images.ipynb的单元格中,复制后运行即可:

    import os
    from PIL import Image
    
    def get_smaller_images(dirPath, new_dirPath):
        fileName_list = os.listdir(dirPath)
        filePath_list = [os.path.join(dirPath, fileName) for fileName in fileName_list]
        imagePath_list = [filePath for filePath in filePath_list if '.jpg' in filePath]
        if os.path.isdir(new_dirPath):
            shutil.rmtree(new_dirPath)
        os.mkdir(new_dirPath)
        for imagePath in imagePath_list:
            image = Image.open(imagePath)
            width, height = image.size
            imageName = imagePath.split('\\')[-1]
            save_path = os.path.join(new_dirPath, imageName)
            if width >= 600 and height >= 600:
                minification = min(width, height) // 300 #此变量表示缩小倍数
                new_width = width // minification
                new_height = height // minification
                resized_image = image.resize((new_width, new_height), Image.ANTIALIAS)
                print('图片%s原来的宽%d,高%d, 图片缩小后宽%d,高%d' %(
                    imageName, width, height, new_width, new_height))
                resized_image.save(save_path)
            else:
                image.save(save_path)
    
    get_smaller_images('selected_images', 'smaller_images')
    

    在本文作者的实践中,图片经过PIL库打开再保存,保持图片质量的情况下,能够缩小图片文件大小3倍左右。本文作者猜测可能是使用的图片压缩算法不同,完成此步后,文件夹大小如下图所示。


    image.png

    4.给图片打标签

    使用打标签工具LabelImg,下载页面链接:https://tzutalin.github.io/labelImg/
    如果下载页面链接没法访问,也可以从百度云盘中下载。
    链接: https://pan.baidu.com/s/1jVmkLxqQMZNJIzyv75HilA 提取码: yysh
    下载页面如下图所示:

    image.png
    选择下载Windows_v1.8.0,如下图中红色箭头标记处所示:
    image.png
    把压缩文件windows_v1.8.0.zip放到D盘根目录中,选择解压到当前文件夹
    解压后D盘根目录下会有windows_v1.8.0文件夹,LabelImg软件在文件夹中。
    选择D盘根目录的原因:如果windows_v1.8.0文件夹路径中带有中文,打开LabelImg软件会闪退
    打开LabelImg软件,点击下图红色箭头标记处。
    image.png
    在打开的软件界面,点击Open Dir按钮,如下图红色箭头标注处所示。
    image.png
    首先打开桌面的目标检测文件夹,在选中文件夹smaller_images的情况下,点击下图红色箭头标记处所示的选择文件夹按钮。
    注意:只需要鼠标选中文件夹smaller_images,不需要进入文件夹smaller_images中。
    image.png
    在输入法为英文输入的情况下,按键盘上的w键则可以开始绘制方框,方框会框住图片中的物体。
    完成绘制方框后,还需要为方框标上类别,如下图所示。
    注意:每完成一张图的打标签,一定要记得保存!!!
    image.png
    在本文演示中,需要给图片中的鲤鱼人脸2个类别打标签。
    鲤鱼的标签名叫做fish,人脸的标签名叫human_face,打标签的结果如下图所示。
    注意:用方框框住物体时,尽量框住物体的所有部位,例如本文中的鱼,鱼鳍是一个重要特征。保证框住物体所有部位的情况下,也不要使方框四周留出过多空白。
    image.png
    遇到特征不明显的图片,可以放弃为此图片打标签,举例如下图所示:
    image.png
    为100张图片打标签,本文作者共花费44分钟。
    用LabelImg软件打标签会给每张图片产生对应的xml文件。
    检查是否给所有图片都打上标签,在文件夹smaller_images中共有99个xml文件,如下图所示。
    因为有1张图片难以辨认,所以没有给它打标签。
    image.png
    本文作者把已经打标签好的文件夹smaller_images做成压缩文件smaller_images.zip,并上传到百度网盘。
    下载链接: https://pan.baidu.com/s/1tkCV95pzLyRV5gSRF9sF8A 提取码: 7j88

    5.xml转csv

    xml转csv的意思是,将xml文件中的信息整合到csv文件中。
    在桌面的目标检测文件夹中新建代码文件xml_to_csv.ipynb,步骤与第2章中相同:
    打开cmd——>运行jupyter notebook——>新建代码文件——>代码文件重命名
    复制下面一段代码到代码文件xml_to_csv.ipynb的单元格中,复制后运行即可:

    import os
    import pandas as pd
    import xml.etree.ElementTree as ET
    from sklearn.model_selection import train_test_split
    
    def xmlPath_list_to_df(xmlPath_list):
        xmlContent_list = []
        for xmlPath in xmlPath_list:
            tree = ET.parse(xmlPath)
            root = tree.getroot()
            for member in root.findall('object'):
                value = (root.find('filename').text,
                         int(root.find('size')[0].text),
                         int(root.find('size')[1].text),
                         member[0].text,
                         int(member[4][0].text),
                         int(member[4][1].text),
                         int(member[4][2].text),
                         int(member[4][3].text)
                         )
                xmlContent_list.append(value)
        column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
        xmlContent_df = pd.DataFrame(xmlContent_list, columns=column_name)   
        return xmlContent_df
    
    def dirPath_to_csv(dirPath):
        fileName_list = os.listdir(dirPath)
        all_xmlPath_list = [os.path.join(dirPath, fileName) for fileName in fileName_list if '.xml' in fileName]
        train_xmlPath_list, test_xmlPath_list = train_test_split(all_xmlPath_list, test_size=0.1, random_state=1)
        train_df = xmlPath_list_to_df(train_xmlPath_list)
        train_df.to_csv('train.csv')
        print('成功产生文件train.csv,训练集共有%d张图片' %len(train_xmlPath_list))
        test_df = xmlPath_list_to_df(test_xmlPath_list)
        test_df.to_csv('test.csv')
        print('成功产生文件test.csv,测试集共有%d张图片' %len(test_xmlPath_list))
        
    dirPath_to_csv('smaller_images')
    

    为了使读者与本文作者的复现结果一致,本文作者将函数train_test_split的参数random_state的值设为1,这样每次划分的训练集和测试集总是相同。如果不设置此参数,则每次划分的训练集和测试集不同。
    上面一段代码的运行结果如下:

    成功产生文件train.csv,训练集共有89张图片
    成功产生文件test.csv,测试集共有10张图片

    6.csv转tfrecord

    csv转tfrecord的意思是,将csv文件中的信息图片数据整合到tfrecord文件中。
    在桌面的目标检测文件夹中新建代码文件csv_to_tfrecord.ipynb,步骤与第2章中相同:
    打开cmd——>运行jupyter notebook——>新建代码文件——>代码文件重命名
    复制下面一段代码到代码文件csv_to_tfrecord.ipynb的单元格中,复制后运行即可:

    import os
    import pandas as pd
    import tensorflow as tf
    from object_detection.utils import dataset_util
    import shutil
    
    def csv2tfrecord(csv_path, imageDir_path, tfrecord_path):
        objectInfo_df = pd.read_csv(csv_path)
        tfrecord_writer = tf.python_io.TFRecordWriter(tfrecord_path)
        for filename, group in objectInfo_df.groupby('filename'):
            height = group.iloc[0]['height']
            width = group.iloc[0]['width']
            filename_bytes = filename.encode('utf-8')
            image_path = os.path.join(imageDir_path, filename)
            with open(image_path, 'rb') as file:
                encoded_jpg = file.read()
            image_format = b'jpg'
            xmin_list = list(group['xmin'] / width)
            xmax_list = list(group['xmax'] / width)
            ymin_list = list(group['ymin'] / height)
            ymax_list = list(group['ymax'] / height)
            classText_list = [classText.encode('utf-8') for classText in group['class']]
            classLabel_list = [classText_to_classLabel(classText) for classText in group['class']]
            tf_example = tf.train.Example(
                features = tf.train.Features(
                    feature = {
                        'image/height': dataset_util.int64_feature(height),
                        'image/width': dataset_util.int64_feature(width),
                        'image/filename': dataset_util.bytes_feature(filename_bytes),
                        'image/source_id': dataset_util.bytes_feature(filename_bytes),
                        'image/encoded': dataset_util.bytes_feature(encoded_jpg),
                        'image/format': dataset_util.bytes_feature(image_format),
                        'image/object/bbox/xmin': dataset_util.float_list_feature(xmin_list),
                        'image/object/bbox/xmax': dataset_util.float_list_feature(xmax_list),
                        'image/object/bbox/ymin': dataset_util.float_list_feature(ymin_list),
                        'image/object/bbox/ymax': dataset_util.float_list_feature(ymax_list),
                        'image/object/class/text': dataset_util.bytes_list_feature(classText_list),
                        'image/object/class/label': dataset_util.int64_list_feature(classLabel_list),
                    }))
            tfrecord_writer.write(tf_example.SerializeToString())
        tfrecord_writer.close()
        print('成功产生tfrecord文件,保存在路径:%s' %tfrecord_path)
    
    #如果训练自己的模型,目标检测的类别不同,需要修改此处
    def classText_to_classLabel(row_label):
        if row_label == 'fish':
            return 1
        elif row_label == 'human_face':
            return 2
        else:
            return None
    
    dir_name = 'training'
    if os.path.isdir(dir_name):
        shutil.rmtree(dir_name)
    os.mkdir(dir_name)
    csv2tfrecord('train.csv', 'smaller_images', 'training/train.tfrecord')
    csv2tfrecord('test.csv', 'smaller_images', 'training/test.tfrecord')
    

    此段代码对于读者来说直接使用即可。
    本文作者花费了2个小时左右将原博客作者的代码精简成上面一段代码,调用库更少,调用方法更常用,变量名命名表达意思更准确,使读者更容易理解本段代码的作用。
    上面一段代码的运行结果如下:

    成功产生tfrecord文件,保存在路径:training/train.tfrecord
    成功产生tfrecord文件,保存在路径:training/test.tfrecord

    上面一段代码运行完成后,桌面的目标检测文件夹中会产生一个文件夹training,如下图红色箭头标注处所示。

    image.png
    在文件夹training中,有2个tfrecord文件,如下图所示:
    image.png

    7.编写pbtxt文件

    在桌面文件夹目标检测的文件夹training中,创建文本文件my_label_map.pbtxt
    复制下面一段内容到文本文件my_label_map.pbtxt中。

    item {
        name : "fish"
        id : 1
    }
    item {
        name : "human_face"
        id : 2
    }
    

    复制后保存即可,此时文件夹training中有3个文件,如下图所示:

    image.png
    有一个细节需要特别注意,因为此细节,本文作者花费了1天半时间才解决报错问题。
    文本文件fish_label.pbtxt在Windows系统下默认编码格式是ANSI格式,工程中需要的就是此格式。
    但是本文作者在认为python3对utf-8编码支持较好,所以把文本文件fish_label.pbtxt的编码改成了utf-8格式,导致工程报错。

    8.编写配置文件

    在桌面文件夹目标检测的文件夹training中,创建配置文件ssdlite_mobilenet_v2_coco.config
    本文作者给读者提供2种方式获得正确的配置文件。

    8.1 网盘下载

    本文作者将适用于本文的配置文件的各项参数都已经设置好,并且上传百度网盘。
    链接: https://pan.baidu.com/s/1ERp0zJ4cpI5VHHeHcyI_Rg 提取码: ud28

    8.2 修改原生配置文件

    如果读者已经阅读过《目标检测》系列的第一篇文章:《目标检测第1步-运行tensorflow官方示例》,链接:https://www.jianshu.com/p/c1d8f1c76de7
    可以在object_detection文件夹中的samples/config路径下,找到原生配置文件ssdlite_mobilenet_v2_coco.config,如下图红色箭头标记处所示。

    image.png
    没有object_detection文件夹则通过下面链接下载。
    压缩文件object_detection.zip的百度云盘下载链接:https://pan.baidu.com/s/1Q9SxtKlOqEty08tpFeUUHA 提取码: p2sm
    原生配置文件ssdlite_mobilenet_v2_coco.config先复制1份到桌面文件目标检测的文件夹training中。
    原生配置文件中的需要修改的部分:
    1. 第9行的num_classes,对于本文来说,此数设置为2
    2. 第143行的batch_size,对于本文来说,此数设置为5,读者根据自己的电脑配置,可以调高或者调低。
    3. 第177行input_path设置成"training/train.tfrecord"
    4. 第179行label_map_path设置成"training/my_label_map.pbtxt"
    5. 第191行input_path设置成"training/test.tfrecord"
    6. 第193行label_map_path设置成"training/my_label_map.pbtxt"
    7. 第158、159这2行需要删除。

    修改配置文件ssdlite_mobilenet_v2_coco.config并保存后,此时文件夹training中有4个文件,如下图所示:

    image.png

    9.总结

    1.本篇文章到此结束,详细地介绍了数据准备的过程。
    本篇文章的阶段性成果training文件夹已经上传百度云盘。
    链接: https://pan.baidu.com/s/1Kgp9geSkTFVa_4tfc7ZPew 提取码: 9sy3
    2.《目标检测》系列的下一篇文章《目标检测第3步-模型训练》,链接:https://www.jianshu.com/p/0e5f9df4686a

    相关文章

      网友评论

        本文标题:目标检测第2步-数据准备

        本文链接:https://www.haomeiwen.com/subject/dxoqqqtx.html