使用NWPU VHR-10数据集训练Faster R-CNN模型

  使用Faster R-CNN算法在NWPU VHR-10数据集上实现目标检测。
  使用Faster R-CNN算法在VOC2007数据集上实现目标检测的详细步骤→Windows10+Faster-RCNN-TensorFlow-Python3-master+VOC2007数据集

一、所需文件下载链接

  1. Faster R-CNN源码及操作步骤Github链接→Faster-RCNN-TensorFlow-Python3
  2. Faster-RCNN-TensorFlow-Python3-master压缩包百度云盘链接→提取码:76wq
  3. NWPU VHR-10数据集百度云盘链接→提取码:1iam

二、基础环境配置

  1. Windows10 + Anaconda3 + PyCharm 2019.3.3
  2. 安装CPU版本的TensorFlow
  3. 在PyCharm中配置好TensorFlow环境

三、训练及测试过程

  1. 下载并解压Faster-RCNN-TensorFlow-Python3-master.zip,将文件夹重命名为Faster-RCNN_for_NWPU VHR-10

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  2. 将NWPU VHR-10数据集的格式转换成VOC2007数据集的的格式,并将转换好的数据集文件夹复制到./Faster-RCNN_for_NWPU VHR-10/data路径下。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  3. 在配置好TensorFlow环境的PyCharm中打开Faster-RCNN_for_NWPU VHR-10项目。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  4. 安装源码运行需要的Python包。打开好的Faster-RCNN_for_NWPU VHR-10项目中有一个requirement.txt文件,其中记录了需要安装的包的名字。在PyCharm的终端Terminal中输入pip install -r requirements.txt后回车,安装需要的所有依赖包。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  5. 在PyCharm的终端Terminal中输入cd data\coco\PythonAPI切换路径到./data/coco/PythonAPI,然后输入python setup.py build_ext --inplace并回车。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型
    再输入python setup.py build_ext install并回车。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型

  6. 在PyCharm的终端Terminal中输入exit退出当前路径,然后输入cd lib\utils切换路径到./lib/utils,再输入python setup.py build_ext --inplace并回车。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型

  7. 下载预训练模型VGG16,在./data文件夹下新建文件夹imagenet_weights,将下载好的vgg_16_2016_08_28.tar.gz解压到./data/imagenet_weights路径下,并将vgg_16.ckpt重命名为vgg16.ckpt

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  8. 修改config.py文件。在./lib/config文件夹下的config.py文件,是专门的配置文件,其中定义了模型的诸多参数,大家可以根据自己的需要修改相关参数。在这里,为了减少训练时间,我将最大迭代次数max_iters参数由40000改为10000,同时将迭代多少次保存一次模型snap_iterations参数由5000改为2000,其他参数未作改变。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  9. 修改pascal_voc.py文件。在./lib/datasets文件夹下的pascal_voc.py文件,修改self._classes中指定的检测类别为NWPU VHR-10数据集的10个目标检测类别。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改的代码如下所示。

        self._classes = ('__background__',  # always index 0   # 训练类别标签,包含背景类
                         'airplane', 'ship', 'storage tank', 'baseball diamond',
                         'tennis court', 'basketball court', 'ground track field',
                         'harbor', 'bridge', 'vehicle')
  1. 在PyCharm中打开train.py文件 ,如果后面想要可视化loss的话,需要在如下图所示的两处添加代码,如果不需要的话,可忽略这一步。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型
    添加的代码如下所示。
        filename = './write_loss.txt'  # 添加的代码(保存loss的文件)
                # 添加的代码(可视化loss)
                fw = open(filename, 'a')
                fw.write(str(int(iter))+' '+str(float('%.4f' % total_loss))+"\n")
                fw.close()
                # 添加结束
  1. train.py文件界面,右击后点击Run 'train',开始训练。需要注意的是,每次训练前都要清空./data/cache./default/voc_2007_trainval/default文件夹里面的文件。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
  2. 在训练过程中可能会出现total loss=nan的情况,如下图所示。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    解决方案是将pascal_voc.py文件中的x1、x2、y1、y2变量的-1去掉。
    使用NWPU VHR-10数据集训练Faster R-CNN模型
    也可能出现image invalid, skipping的问题,如下图所示。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    解决方案是将config.py中的roi_bg_threshold_low的值改为0.0。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
  3. 清空./data/cache./default/voc_2007_trainval/default文件夹里面的文件。在train.py文件界面,右击后点击Run 'train',重新开始训练,直到迭代次数为10000。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
  4. 训练结束后,如果训练前在train.py文件中加了可视化loss的代码的话,可以在根路径下得到一个write_loss.txt文件,保存着每迭代10次对应的损失值。在根路径下新建一个Python文件visual_loss.py,代码如下,运行后可以在根目录下得到一张loss曲线图。
    write_loss.txt文件文件内容如下图所示。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    运行visual_loss.py文件,可得如下图所示的结果。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    visual_loss.py文件的代码如下所示。
import numpy as np
import matplotlib.pyplot as plt

y_ticks = [0, 0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0]  # 纵坐标的值,可以自己设置。
data_path = 'E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/write_loss.txt'  # log_loss的路径
result_path = 'E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/total_loss'  # 保存结果的路径

data1_loss = np.loadtxt(data_path)
x = data1_loss[:, 0]  # 冒号左边是行范围,冒号右边列范围,取第一列
y = data1_loss[:, 1]  # 取第2列

# 开始画图
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y, label='total_loss')
plt.yticks(y_ticks)  # 如果不想自己设置纵坐标,可以注释掉
ax.legend(loc='best')
ax.set_title('The loss curves')
ax.set_xlabel('batches')
fig.savefig(result_path)
plt.show()
  1. 训练时,模型保存的路径是./default/voc_2007_trainval/default,每次保存模型都是保存4个文件。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  2. 新建./output/vgg16/voc_2007_trainval/default文件夹,从./default/voc_2007_trainval/default路径下复制一组模型数据到新建的文件夹下,并将所有文件名改为vgg16.后缀

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  3. 准备开始测试已经训练好的模型。在PyCharm中打开demo.py文件,并对如下图所示的部分进行修改。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改的代码如下所示。

CLASSES = ('__background__',
           'airplane', 'ship', 'storage tank', 'baseball diamond',
           'tennis court', 'basketball court', 'ground track field',
           'harbor', 'bridge', 'vehicle')

NETS = {'vgg16': ('vgg16.ckpt',), 'res101': ('res101_faster_rcnn_iter_110000.ckpt',)}
    parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16 res101]',
                        choices=NETS.keys(), default='vgg16')
    parser.add_argument('--dataset', dest='dataset', help='Trained dataset [pascal_voc pascal_voc_0712]',
                        choices=DATASETS.keys(), default='pascal_voc')
  1. 为使得同一张图片上的所有目标检测框全部在一张图片上显示出来,需要修改demo.py文件如下所示的部分。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改的代码如下所示。
# def vis_detections(im, class_name, dets, thresh=0.5):  # 修改这行代码为下面这行
def vis_detections(im, class_name, dets, ax, thresh=0.5):  # 添加的代码(增加ax参数)(将同一张图片上的所有目标检测框全部在一张图片上显示)
    """Draw detected bounding boxes."""
    inds = np.where(dets[:, -1] >= thresh)[0]
    if len(inds) == 0:
        return

    # 注释的代码(将同一张图片上的所有目标检测框全部在一张图片上显示)
    # im = im[:, :, (2, 1, 0)]
    # fig, ax = plt.subplots(figsize=(12, 12))
    # ax.imshow(im, aspect='equal')
    # 注释结束

    for i in inds:
        bbox = dets[i, :4]
        score = dets[i, -1]

        ax.add_patch(
            plt.Rectangle((bbox[0], bbox[1]),
                          bbox[2] - bbox[0],
                          bbox[3] - bbox[1], fill=False,
                          edgecolor='red', linewidth=3.5)
        )
        ax.text(bbox[0], bbox[1] - 2,
                '{:s} {:.3f}'.format(class_name, score),
                bbox=dict(facecolor='blue', alpha=0.5),
                fontsize=14, color='white')

    ax.set_title(('{} detections with '
                  'p({} | box) >= {:.1f}').format(class_name, class_name,
                                                  thresh),
                 fontsize=14)
    # 注释的代码(将同一张图片上的所有目标检测框全部在一张图片上显示)
    # plt.axis('off')
    # plt.tight_layout()
    # plt.draw()
    # 注释结束

def demo(sess, net, image_name):
    """Detect object classes in an image using pre-computed object proposals."""

    # Load the demo image
    im_file = os.path.join(cfg.FLAGS2["data_dir"], 'demo', image_name)
    im = cv2.imread(im_file)

    # Detect all object classes and regress object bounds
    timer = Timer()
    timer.tic()
    scores, boxes = im_detect(sess, net, im)
    timer.toc()
    print('Detection took {:.3f}s for {:d} object proposals'.format(timer.total_time, boxes.shape[0]))

    # Visualize detections for each class
    CONF_THRESH = 0.4  # 进行适当修改
    NMS_THRESH = 0.2  # 进行适当修改
    # 添加的代码(复制前面注释掉的vis_detections函数中for循环之前的3行代码到此处)(将同一张图片上的所有目标检测框全部在一张图片上显示)
    im = im[:, :, (2, 1, 0)]
    fig, ax = plt.subplots(figsize=(12, 12))
    ax.imshow(im, aspect='equal')
    # 添加结束
    for cls_ind, cls in enumerate(CLASSES[1:]):
        cls_ind += 1  # because we skipped background
        cls_boxes = boxes[:, 4 * cls_ind:4 * (cls_ind + 1)]
        cls_scores = scores[:, cls_ind]
        dets = np.hstack((cls_boxes,
                          cls_scores[:, np.newaxis])).astype(np.float32)
        keep = nms(dets, NMS_THRESH)
        dets = dets[keep, :]
        # vis_detections(im, cls, dets, thresh=CONF_THRESH)  # 修改这行代码为下面这行
        vis_detections(im, cls, dets, ax,
                       thresh=CONF_THRESH)  # 添加的代码(将ax做为参数传入vis_detections)(将同一张图片上的所有目标检测框全部在一张图片上显示)
    # 添加的代码(复制前面注释掉的vis_detections函数中for循环之后的3行代码到此处)(将同一张图片上的所有目标检测框全部在一张图片上显示)
    plt.axis('off')
    plt.tight_layout()
    plt.draw()
    # 添加结束
  1. 为了在测试时可以批量读取文件夹JPEGImages中的图片,并将目标检测结果保存到一个新建文件夹中,需要修改demo.py文件如下所示的部分。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改的代码如下所示。
   # im_file = os.path.join(cfg.FLAGS2["data_dir"], 'demo', image_name)
    im_file = os.path.join('E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/data/VOCdevkit2007/VOC2007/JPEGImages', image_name)  # 修改测试图片路径
    # im_names = ['000456.jpg', '000457.jpg', '000542.jpg', '001150.jpg',
    #             '001763.jpg', '004545.jpg']
    im_names = os.listdir(
        'E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/data/VOCdevkit2007/VOC2007/JPEGImages')  # 需要进行测试的图片的路径
    for im_name in im_names:
        print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
        print('Demo for data/demo/{}'.format(im_name))
        demo(sess, net, im_name)

        # 保存测试图片目标检测结果,并设置输出格式
        plt.savefig('E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/test_result/' + im_name, format='png',
                    transparent=True, pad_inches=0, dpi=300, bbox_inches='tight')

    # plt.show()
  1. 在根路径下新建test_result文件夹,然后运行demo.py,可在test_result文件夹中得到NWPU VHR-10数据集的目标检测结果。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  2. 上述代码的运行结果,对于目标比较多且集中的图片来说,得到的目标检测可视化结果看起来不是很美观。如下图所示,甚至某一个目标的类别及精度标签会覆盖其他目标的预测框。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    所以为了得到更好的可视化结果,继续对demo.py文件进行如下修改。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改目标检测结果的保存路径。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    在根路径下新建test_result1文件夹,然后运行demo.py,可在test_result1文件夹中重新得到NWPU VHR-10数据集的目标检测结果。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    对于目标比较多且集中的图片来说,这次得到的目标检测可视化结果看起来就比较美观了,如下图所示。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  3. 如果想要在测试时输出PR曲线并计算AP值的话,首先需要在.\lib\datasets路径下打开pascal_voc.py文件,做如下修改。

    使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改和添加的代码如下所示。

# 添加的代码(输出PR曲线并计算AP值)
import matplotlib.pyplot as plt
import pylab as pl
# 添加结束
        # filename = self._get_comp_id() + '_det_' + self._image_set + '_{:s}.txt'  # 修改这行代码为下面这行
        filename = self._image_set + '_{:s}'  # 添加的代码(输出PR曲线并计算AP值)
    def _do_python_eval(self, output_dir='output'):
        annopath = self._devkit_path + '\\VOC' + self._year + '\\Annotations\\' + '{:s}.xml'
        imagesetfile = os.path.join(
            self._devkit_path,
            'VOC' + self._year,
            'ImageSets',
            'Main',
            self._image_set + '.txt')
        cachedir = os.path.join(self._devkit_path, 'annotations_cache')
        aps = []
        # The PASCAL VOC metric changed in 2010
        use_07_metric = True if int(self._year) < 2010 else False
        print('VOC07 metric? ' + ('Yes' if use_07_metric else 'No'))
        if not os.path.isdir(output_dir):
            os.mkdir(output_dir)
        for i, cls in enumerate(self._classes):
            if cls == '__background__':
                continue
            filename = self._get_voc_results_file_template().format(cls)
            rec, prec, ap = voc_eval(
                filename, annopath, imagesetfile, cls, cachedir, ovthresh=0.5,
                use_07_metric=use_07_metric)
            aps += [ap]
            # 添加的代码(输出PR曲线并计算AP值)
            recs = []
            precs = []
            recs += [rec[-1]]
            precs += [prec[-1]]
            print('recall for {} = {:.4f}'.format(cls, rec[-1]))
            print('precision for {} = {:.4f}'.format(cls, prec[-1]))
            pl.plot(rec, prec, lw=2,
                    label='Precision-recall curve of class {} (area = {:.4f})'
                          ''.format(cls, ap))
            # 添加结束
            print(('AP for {} = {:.4f}'.format(cls, ap)))
            with open(os.path.join(output_dir, cls + '_pr.pkl'), 'wb') as f:
                pickle.dump({'rec': rec, 'prec': prec, 'ap': ap}, f)
            # 添加的代码(输出PR曲线并计算AP值)
            pl.xlabel('Recall')
            pl.ylabel('Precision')
            plt.grid(True)
            pl.ylim([0.0, 1.05])
            pl.xlim([0.0, 1.05])
            pl.title('Precision-Recall')
            pl.legend(loc="upper right")
            plt.savefig('E:/Remote Sensing/Faster-RCNN_for_NWPU VHR-10/PR_result/' + cls + '_PR.jpg')
            plt.show()
            # 添加结束
        print(('Mean AP = {:.4f}'.format(np.mean(aps))))
        print('~~~~~~~~')
        print('Results:')
        for ap in aps:
            print(('{:.3f}'.format(ap)))
        print(('{:.3f}'.format(np.mean(aps))))
        print('~~~~~~~~')
        print('')
        print('--------------------------------------------------------------')
        print('Results computed with the **unofficial** Python eval code.')
        print('Results should be very close to the official MATLAB eval code.')
        print('Recompute with `./tools/reval.py --matlab ...` for your paper.')
        print('-- Thanks, The Management')
        print('--------------------------------------------------------------')
  1. 在根路径下新建PR_result文件夹,然后在.\lib\datasets路径下打开voc_eval.py文件,做如下修改。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    修改的代码如下所示。
    # tree = ET.parse(filename)  # 修改这行代码为下面这行
    tree = ET.parse('' + filename)  # 添加的代码(输入PR曲线并计算AP值)
  1. 最后在根路径下新建test_net.py文件,代码如下所示。
""""
Demo script showing detections in sample images.
See README.md for installation instructions before running.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import os

import tensorflow as tf
from lib.nets.vgg16 import vgg16
from lib.datasets.factory import get_imdb
from lib.utils.test import test_net

NETS = {'vgg16': ('vgg16.ckpt',)}  # 自己需要修改:训练输出模型
DATASETS = {'pascal_voc': ('voc_2007_trainval',), 'pascal_voc_0712': ('voc_2007_trainval+voc_2012_trainval',)}


def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Tensorflow Faster R-CNN test')
    parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16 res101]',
                        choices=NETS.keys(), default='vgg16')
    parser.add_argument('--dataset', dest='dataset', help='Trained dataset [pascal_voc pascal_voc_0712]',
                        choices=DATASETS.keys(), default='pascal_voc')
    args = parser.parse_args()
    return args


if __name__ == '__main__':
    args = parse_args()
    # model path
    demonet = args.demo_net
    dataset = args.dataset
    tfmodel = os.path.join('output', demonet, DATASETS[dataset][0], 'default', NETS[demonet][0])  # 模型路径
    # 获得模型文件名称
    filename = (os.path.splitext(tfmodel)[0]).split('\\')[-1]
    filename = 'default' + '/' + filename
    imdb = get_imdb("voc_2007_test")  # 得到
    imdb.competition_mode('competition mode')
    if not os.path.isfile(tfmodel + '.meta'):
        print(tfmodel)
        raise IOError(('{:s} not found.\nDid you download the proper networks from '
                       'our server and place them properly?').format(tfmodel + '.meta'))
    # set config
    tfconfig = tf.ConfigProto(allow_soft_placement=True)
    tfconfig.gpu_options.allow_growth = True
    # init session
    sess = tf.Session(config=tfconfig)
    # load network
    if demonet == 'vgg16':
        net = vgg16(batch_size=1)
    # elif demonet == 'res101':
    #     net = resnetv1(batch_size=1, num_layers=101)
    else:
        raise NotImplementedError
    net.create_architecture(sess, "TEST", 11,  # 自己需要修改:类别数量+1
                            tag='default', anchor_scales=[8, 16, 32])
    saver = tf.train.Saver()
    saver.restore(sess, tfmodel)
    print('Loaded network {:s}'.format(tfmodel))
    print(filename)
    test_net(sess, net, imdb, filename, max_per_image=100)
    sess.close()
  1. test_net.py文件界面,右击后点击Run 'test_net',开始测试。需要注意的是,每次测试前都要清空./data/VOCdevkit2007/annotation_cache文件夹里面的文件。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    运行结束后,可以得到每个类别各自的AP及mAP值。

    使用NWPU VHR-10数据集训练Faster R-CNN模型
    PR_result文件夹中,可以得到各个类别的PR曲线。

    使用NWPU VHR-10数据集训练Faster R-CNN模型

  至此,Faster R-CNN模型在NWPU VHR-10数据集上的整个训练与测试过程就结束了。

上一篇:Python中的设计模式


下一篇:2021-06-12