Skip to content

Commit

Permalink
Added working support for spacenet dataset. Spacenet processed data i…
Browse files Browse the repository at this point in the history
…s placed in . See INRIA persons with fast rcnn for example
  • Loading branch information
willxie committed Oct 21, 2016
1 parent 35cb624 commit 56991d9
Show file tree
Hide file tree
Showing 17 changed files with 2,321 additions and 16 deletions.
8 changes: 4 additions & 4 deletions lib/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def is_exe(fpath):

return None

if _which(MATLAB) is None:
msg = ("MATLAB command '{}' not found. "
"Please add '{}' to your PATH.").format(MATLAB, MATLAB)
raise EnvironmentError(msg)
# if _which(MATLAB) is None:
# msg = ("MATLAB command '{}' not found. "
# "Please add '{}' to your PATH.").format(MATLAB, MATLAB)
# raise EnvironmentError(msg)
9 changes: 9 additions & 0 deletions lib/datasets/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
__sets = {}

import datasets.pascal_voc
# import datasets.spacenet
from datasets import spacenet
import numpy as np

def _selective_search_IJCV_top_k(split, year, top_k):
Expand Down Expand Up @@ -37,6 +39,13 @@ def _selective_search_IJCV_top_k(split, year, top_k):
__sets[name] = (lambda split=split, year=year, top_k=top_k:
_selective_search_IJCV_top_k(split, year, top_k))

# Adding spacenet
spacenet_devkit_path = '/home/ubuntu/fast-rcnn/spacenet'
for split in ['train', 'val', 'test']:
name = '{}_{}'.format('spacenet', split)
__sets[name] = (lambda split=split: spacenet.spacenet(split, spacenet_devkit_path))


def get_imdb(name):
"""Get an imdb (image database) by name."""
if not __sets.has_key(name):
Expand Down
272 changes: 272 additions & 0 deletions lib/datasets/spacenet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# --------------------------------------------------------
# Fast R-CNN
# Copyright (c) 2015 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Written by Ross Girshick
# --------------------------------------------------------

import datasets
import datasets.spacenet
import os
import datasets.imdb
import xml.dom.minidom as minidom
import numpy as np
import scipy.sparse
import scipy.io as sio
import utils.cython_bbox
import cPickle
import subprocess

class spacenet(datasets.imdb):
def __init__(self, image_set, devkit_path):
datasets.imdb.__init__(self, image_set)
self._image_set = image_set
self._devkit_path = devkit_path
self._data_path = os.path.join(self._devkit_path, 'data')
self._classes = ('__background__', # always index 0
'building')
self._class_to_ind = dict(zip(self.classes, xrange(self.num_classes)))
self._image_ext = ['.png']
self._image_index = self._load_image_set_index()
# Default to roidb handler
self._roidb_handler = self.selective_search_roidb

# Specific config options
self.config = {'cleanup' : True,
'use_salt' : True,
'top_k' : 2000}

assert os.path.exists(self._devkit_path), \
'Devkit path does not exist: {}'.format(self._devkit_path)
assert os.path.exists(self._data_path), \
'Path does not exist: {}'.format(self._data_path)

def image_path_at(self, i):
"""
Return the absolute path to image i in the image sequence.
"""
return self.image_path_from_index(self._image_index[i])

def image_path_from_index(self, index):
"""
Construct an image path from the image's "index" identifier.
"""
for ext in self._image_ext:
image_path = os.path.join(self._data_path, 'Images',
index + ext)
if os.path.exists(image_path):
break
assert os.path.exists(image_path), \
'Path does not exist: {}'.format(image_path)
return image_path

def _load_image_set_index(self):
"""
Load the indexes listed in this dataset's image set file.
"""
# Example path to image set file:
# self._data_path + /ImageSets/val.txt
image_set_file = os.path.join(self._data_path, 'ImageSets',
self._image_set + '.txt')
assert os.path.exists(image_set_file), \
'Path does not exist: {}'.format(image_set_file)
with open(image_set_file) as f:
image_index = [x.strip() for x in f.readlines()]
return image_index

def gt_roidb(self):
"""
Return the database of ground-truth regions of interest.
This function loads/saves from/to a cache file to speed up future calls.
"""
cache_file = os.path.join(self.cache_path, self.name + '_gt_roidb.pkl')
if os.path.exists(cache_file):
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} gt roidb loaded from {}'.format(self.name, cache_file)
return roidb

gt_roidb = [self._load_spacenet_annotation(index)
for index in self.image_index]
with open(cache_file, 'wb') as fid:
cPickle.dump(gt_roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote gt roidb to {}'.format(cache_file)

return gt_roidb

def selective_search_roidb(self):
"""
Return the database of selective search regions of interest.
Ground-truth ROIs are also included.
This function loads/saves from/to a cache file to speed up future calls.
"""
cache_file = os.path.join(self.cache_path,
self.name + '_selective_search_roidb.pkl')

if os.path.exists(cache_file):
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} ss roidb loaded from {}'.format(self.name, cache_file)
return roidb

if self._image_set != 'test':
gt_roidb = self.gt_roidb()
ss_roidb = self._load_selective_search_roidb(gt_roidb)
roidb = datasets.imdb.merge_roidbs(gt_roidb, ss_roidb)
else:
roidb = self._load_selective_search_roidb(None)
print len(roidb)
with open(cache_file, 'wb') as fid:
cPickle.dump(roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote ss roidb to {}'.format(cache_file)

return roidb

def _load_selective_search_roidb(self, gt_roidb):
filename = os.path.abspath(os.path.join(self._devkit_path,
self.name + '.mat'))
assert os.path.exists(filename), \
'Selective search data not found at: {}'.format(filename)
raw_data = sio.loadmat(filename)['all_boxes'].ravel()

box_list = []
for i in xrange(raw_data.shape[0]):
box_list.append(raw_data[i][:, (1, 0, 3, 2)] - 1)

return self.create_roidb_from_box_list(box_list, gt_roidb)

def selective_search_IJCV_roidb(self):
"""
eturn the database of selective search regions of interest.
Ground-truth ROIs are also included.
This function loads/saves from/to a cache file to speed up future calls.
"""
cache_file = os.path.join(self.cache_path,
'{:s}_selective_search_IJCV_top_{:d}_roidb.pkl'.
format(self.name, self.config['top_k']))

if os.path.exists(cache_file):
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} ss roidb loaded from {}'.format(self.name, cache_file)
return roidb

gt_roidb = self.gt_roidb()
ss_roidb = self._load_selective_search_IJCV_roidb(gt_roidb)
roidb = datasets.imdb.merge_roidbs(gt_roidb, ss_roidb)
with open(cache_file, 'wb') as fid:
cPickle.dump(roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote ss roidb to {}'.format(cache_file)

return roidb

def _load_selective_search_IJCV_roidb(self, gt_roidb):
IJCV_path = os.path.abspath(os.path.join(self.cache_path, '..',
'selective_search_IJCV_data',
self.name))
assert os.path.exists(IJCV_path), \
'Selective search IJCV data not found at: {}'.format(IJCV_path)

top_k = self.config['top_k']
box_list = []
for i in xrange(self.num_images):
filename = os.path.join(IJCV_path, self.image_index[i] + '.mat')
raw_data = sio.loadmat(filename)
box_list.append((raw_data['boxes'][:top_k, :]-1).astype(np.uint16))

return self.create_roidb_from_box_list(box_list, gt_roidb)

def _load_spacenet_annotation(self, index):
"""
Load image and bounding boxes info from txt files of SPACENETPerson.
"""
filename = os.path.join(self._data_path, 'Annotations', index + '.txt')

coor_list = []
with open(filename) as f:
for i, line in enumerate(f):
coor_list.append(line.rstrip('\n').split(' '))

num_objs = i + 1

# Init
boxes = np.zeros((num_objs, 4), dtype=np.uint16)
gt_classes = np.zeros((num_objs), dtype=np.int32)
overlaps = np.zeros((num_objs, self.num_classes), dtype=np.float32)

for ix, coor in enumerate(coor_list):
x1 = float(coor[0])
y1 = float(coor[1])
x2 = float(coor[2])
y2 = float(coor[3])
cls = self._class_to_ind['building']
boxes[ix, :] = [x1, y1, x2, y2]
gt_classes[ix] = cls
overlaps[ix, cls] = 1.0

overlaps = scipy.sparse.csr_matrix(overlaps)

return {'boxes' : boxes,
'gt_classes': gt_classes,
'gt_overlaps' : overlaps,
'flipped' : False}

def _write_spacenet_results_file(self, all_boxes):
use_salt = self.config['use_salt']
comp_id = 'comp4'
if use_salt:
comp_id += '-{}'.format(os.getpid())

# VOCdevkit/results/comp4-44503_det_test_aeroplane.txt
path = os.path.join(self._devkit_path, 'results', self.name, comp_id + '_')
for cls_ind, cls in enumerate(self.classes):
if cls == '__background__':
continue
print 'Writing {} results file'.format(cls)
filename = path + 'det_' + self._image_set + '_' + cls + '.txt'
with open(filename, 'wt') as f:
for im_ind, index in enumerate(self.image_index):
dets = all_boxes[cls_ind][im_ind]
if dets == []:
continue
# the VOCdevkit expects 1-based indices
for k in xrange(dets.shape[0]):
f.write('{:s} {:.3f} {:.1f} {:.1f} {:.1f} {:.1f}\n'.
format(index, dets[k, -1],
dets[k, 0] + 1, dets[k, 1] + 1,
dets[k, 2] + 1, dets[k, 3] + 1))
return comp_id

def _do_matlab_eval(self, comp_id, output_dir='output'):
rm_results = self.config['cleanup']

path = os.path.join(os.path.dirname(__file__),
'VOCdevkit-matlab-wrapper')
cmd = 'cd {} && '.format(path)
cmd += '{:s} -nodisplay -nodesktop '.format(datasets.MATLAB)
cmd += '-r "dbstop if error; '
cmd += 'setenv(\'LC_ALL\',\'C\'); voc_eval(\'{:s}\',\'{:s}\',\'{:s}\',\'{:s}\',{:d}); quit;"' \
.format(self._devkit_path, comp_id,
self._image_set, output_dir, int(rm_results))
print('Running:\n{}'.format(cmd))
status = subprocess.call(cmd, shell=True)

def evaluate_detections(self, all_boxes, output_dir):
comp_id = self._write_spacenet_results_file(all_boxes)
self._do_matlab_eval(comp_id, output_dir)

def competition_mode(self, on):
if on:
self.config['use_salt'] = False
self.config['cleanup'] = False
else:
self.config['use_salt'] = True
self.config['cleanup'] = True

if __name__ == '__main__':
d = datasets.spacenet('train', '')
res = d.roidb
from IPython import embed; embed()
2 changes: 1 addition & 1 deletion lib/fast_rcnn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
__C.TRAIN.BBOX_THRESH = 0.5

# Iterations between snapshots
__C.TRAIN.SNAPSHOT_ITERS = 10000
__C.TRAIN.SNAPSHOT_ITERS = 5000

# solver.prototxt specifies the snapshot path prefix, this adds an optional
# infix to yield the path: <prefix>[_<infix>]_iters_XYZ.caffemodel
Expand Down
16 changes: 8 additions & 8 deletions lib/fast_rcnn/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ def snapshot(self):

if cfg.TRAIN.BBOX_REG:
# save original values
orig_0 = net.params['bbox_pred'][0].data.copy()
orig_1 = net.params['bbox_pred'][1].data.copy()
orig_0 = net.params['bbox_pred_new'][0].data.copy()
orig_1 = net.params['bbox_pred_new'][1].data.copy()

# scale and shift with bbox reg unnormalization; then save snapshot
net.params['bbox_pred'][0].data[...] = \
(net.params['bbox_pred'][0].data *
net.params['bbox_pred_new'][0].data[...] = \
(net.params['bbox_pred_new'][0].data *
self.bbox_stds[:, np.newaxis])
net.params['bbox_pred'][1].data[...] = \
(net.params['bbox_pred'][1].data *
net.params['bbox_pred_new'][1].data[...] = \
(net.params['bbox_pred_new'][1].data *
self.bbox_stds + self.bbox_means)

if not os.path.exists(self.output_dir):
Expand All @@ -78,8 +78,8 @@ def snapshot(self):

if cfg.TRAIN.BBOX_REG:
# restore net to original state
net.params['bbox_pred'][0].data[...] = orig_0
net.params['bbox_pred'][1].data[...] = orig_1
net.params['bbox_pred_new'][0].data[...] = orig_0
net.params['bbox_pred_new'][1].data[...] = orig_1

def train_model(self, max_iters):
"""Network training loop."""
Expand Down
Loading

0 comments on commit 56991d9

Please sign in to comment.