源码地址:https://github.com/endernewton/tf-faster-rcnn
代码运行路径运行在没有中文的路径中,环境要求:win10+vs2015+python3.4+tensorflow
python所需第三方包:cython
, opencv-python
, easydict(版本最好为1.6)、scipy、pillow
所需环境(其中有关GPU环境所需的cuda和cudann可不用下载)、源码、第三方包下载链接:
https://pan.baidu.com/s/1pcqHL4iNC1kVG3go1DSjHQ
一、训练模型
首先在cpu下完成训练就要把程序中用gpu的部分注释掉,具体需要注释的部分如下:
(1)tf-faster-rcnn-master/lib/model/config.py路径下USE_GPU_NMS 改为false:
(2)lib/model/nms_wrapper.py路径下,注释掉以下部分:
(3)lib/setup.py整个文件重新写一份,其中相应的gpu部分已经注释:
# --------------------------------------------------------
# Fast R-CNN
# Copyright (c) 2015 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Written by Ross Girshick
# --------------------------------------------------------
import os
from os.path import join as pjoin
import numpy as np
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
def find_in_path(name, path):
"Find a file in a search path"
#adapted fom http://code.activestate.com/recipes/52224-find-a-file-given-a-search-path/
for dir in path.split(os.pathsep):
binpath = pjoin(dir, name)
if os.path.exists(binpath):
return os.path.abspath(binpath)
return None
def locate_cuda():
"""Locate the CUDA environment on the system
Returns a dict with keys 'home', 'nvcc', 'include', and 'lib64'
and values giving the absolute path to each directory.
Starts by looking for the CUDAHOME env variable. If not found, everything
is based on finding 'nvcc' in the PATH.
"""
# first check if the CUDAHOME env variable is in use
if 'CUDAHOME' in os.environ:
home = os.environ['CUDAHOME']
nvcc = pjoin(home, 'bin', 'nvcc')
else:
# otherwise, search the PATH for NVCC
nvcc = find_in_path('nvcc.exe', os.environ['PATH'])
if nvcc is None:
# raise EnvironmentError('The nvcc binary could not be '
# 'located in your $PATH. Either add it to your path, or set $CUDAHOME')
return None
home = os.path.dirname(os.path.dirname(nvcc))
cudaconfig = {'home':home, 'nvcc':nvcc,
'include': pjoin(home, 'include'),
'lib64': pjoin(home, 'lib', 'x64')}
for k, v in iter(cudaconfig.items()):
if not os.path.exists(v):
# raise EnvironmentError('The CUDA %s path could not be located in %s' % (k, v))
return None
return cudaconfig
CUDA = locate_cuda()
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = np.get_include()
except AttributeError:
numpy_include = np.get_numpy_include()
def customize_compiler_for_nvcc(self):
# _msvccompiler.py imports:
import os
import shutil
import stat
import subprocess
import winreg
from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
CompileError, LibError, LinkError
from distutils.ccompiler import CCompiler, gen_lib_options
from distutils import log
from distutils.util import get_platform
from itertools import count
super = self.compile
self.src_extensions.append('.cu')
# find python include
import sys
py_dir = sys.executable.replace('\\', '/').split('/')[:-1]
py_include = pjoin('/'.join(py_dir), 'include')
# override method in _msvccompiler.py, starts from line 340
def compile(sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None):
if not self.initialized:
self.initialize()
compile_info = self._setup_compile(output_dir, macros, include_dirs,
sources, depends, extra_postargs)
macros, objects, extra_postargs, pp_opts, build = compile_info
compile_opts = extra_preargs or []
compile_opts.append('/c')
if debug:
compile_opts.extend(self.compile_options_debug)
else:
compile_opts.extend(self.compile_options)
add_cpp_opts = False
for obj in objects:
try:
src, ext = build[obj]
except KeyError:
continue
if debug:
# pass the full pathname to MSVC in debug mode,
# this allows the debugger to find the source file
# without asking the user to browse for it
src = os.path.abspath(src)
if ext in self._c_extensions:
input_opt = "/Tc" + src
elif ext in self._cpp_extensions:
input_opt = "/Tp" + src
add_cpp_opts = True
elif ext in self._rc_extensions:
# compile .RC to .RES file
input_opt = src
output_opt = "/fo" + obj
try:
self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
except DistutilsExecError as msg:
raise CompileError(msg)
continue
elif ext in self._mc_extensions:
# Compile .MC to .RC file to .RES file.
# * '-h dir' specifies the directory for the
# generated include file
# * '-r dir' specifies the target directory of the
# generated RC file and the binary message resource
# it includes
#
# For now (since there are no options to change this),
# we use the source-directory for the include file and
# the build directory for the RC file and message
# resources. This works at least for win32all.
h_dir = os.path.dirname(src)
rc_dir = os.path.dirname(obj)
try:
# first compile .MC to .RC and .H file
self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
base, _ = os.path.splitext(os.path.basename(src))
rc_file = os.path.join(rc_dir, base + '.rc')
# then compile .RC to .RES file
self.spawn([self.rc, "/fo" + obj, rc_file])
except DistutilsExecError as msg:
raise CompileError(msg)
continue
elif ext == '.cu':
# a trigger for cu compile
try:
# use the cuda for .cu files
# self.set_executable('compiler_so', CUDA['nvcc'])
# use only a subset of the extra_postargs, which are 1-1 translated
# from the extra_compile_args in the Extension class
postargs = extra_postargs['nvcc']
arg = [CUDA['nvcc']] + sources + ['-odir', pjoin(output_dir, 'nms')]
for include_dir in include_dirs:
arg.append('-I')
arg.append(include_dir)
arg += ['-I', py_include]
# arg += ['-lib', CUDA['lib64']]
arg += ['-Xcompiler', '/EHsc,/W3,/nologo,/Ox,/MD']
arg += postargs
self.spawn(arg)
continue
except DistutilsExecError as msg:
# raise CompileError(msg)
continue
else:
# how to handle this file?
raise CompileError("Don't know how to compile {} to {}"
.format(src, obj))
args = [self.cc] + compile_opts + pp_opts
if add_cpp_opts:
args.append('/EHsc')
args.append(input_opt)
args.append("/Fo" + obj)
args.extend(extra_postargs)
try:
self.spawn(args)
except DistutilsExecError as msg:
raise CompileError(msg)
return objects
self.compile = compile
# run the customize_compiler
class custom_build_ext(build_ext):
def build_extensions(self):
customize_compiler_for_nvcc(self.compiler)
build_ext.build_extensions(self)
ext_modules = [
Extension(
"utils.cython_bbox",
["utils/bbox.pyx"],
extra_compile_args={'gcc': ["-Wno-cpp", "-Wno-unused-function"]},
include_dirs = [numpy_include]
),
Extension(
"nms.cpu_nms",
["nms/cpu_nms.pyx"],
include_dirs = [numpy_include]
),
# Extension('nms.gpu_nms',
# ['nms/nms_kernel.cu', 'nms/gpu_nms.pyx'],
# library_dirs=[CUDA['lib64']],
# libraries=['cudart'],
# language='c++',
# # this syntax is specific to this build system
# # we're only going to use certain compiler args with nvcc and not with gcc
# # the implementation of this trick is in customize_compiler() below
# extra_compile_args={'gcc': ["-Wno-unused-function"],
# 'nvcc': ['-arch=sm_52',
# '--ptxas-options=-v',
# '-c']},
# include_dirs = [numpy_include, CUDA['include']]
# )
]
setup(
name='tf_faster_rcnn',
ext_modules=ext_modules,
# inject our custom trigger
cmdclass={'build_ext': custom_build_ext},
)
(4)
(a)文件lib/nms/cpu_nms.pyx
:
改变第25行:cdef np.ndarray[np.int_t, ndim=1] order = scores.argsort()[::-1]
为:cdef np.ndarray[np.int64_t, ndim=1] order = scores.argsort()[::-1]
(b)文件lib/nms/gpu_nms.pyx
:
改变25行: cdef np.ndarray[np.int_t, ndim=1] \
为:cdef np.ndarray[np.int64_t, ndim=1] \
(c)文件lib/datasets/pascal_voc.py
:
改变226行:'{:s}.xml')
为: '{0:s}.xml')
(d)文件 lib/datasets/voc_eval.py:
改变121行:with open(cachefile, 'w') as f:
为:with open(cachefile, 'wb') as f:
(5)转到 lib
目录下, 终端运行 python setup.py build_ext
.
这一步如果遇到一些错误,建议删除所有的.c和.cpp文件,(lib/nms/cpu_nms.c
, lib/nms/gpu_nms.cpp
, lib/utils/bbox.c
)
注:如果用gpu训练的话,可能会出现错误:
这种情况下,就前往nms/gpu_nms.cpp文件的2152行下,将:
_nms((&(*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_int32_t *, __pyx_pybuffernd_keep.rcbuffer->......
中的__pyx_t_5numpy_int32_t *”转换成 int *,即:
_nms((&(*__Pyx_BufPtrStrided1d(int *, __pyx_pybuffernd_keep.rcbuffer->
修改之后,在运行一次 python setup.py build_ext
. 就OK了
(6)训练模型:
python setup.py build_ext命令执行完后会在lib/build目录下生成如下文件,将nms文件夹里面的两个文件移动到lib/nms文件夹中,utils文件夹中的一个文件移动到lib/utils文件夹下
至此,就可以正式开始训练模型。因为GitHub原作者是在ubuntu系统下进行训练的,所以用./experiments/scripts/train_faster_rcnn.sh 0 pascal_voc vgg16就可以进行,而.sh文件不能用在windows平台上(本人对ubuntu一概不知,所以也不会弄),但是训练的过程它是调用的trainval_net.py开始训练的,所以我就把trainval_net.py进行修改,代码如下:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import _init_paths
from model.train_val import get_training_roidb, train_net
from model.config import cfg, cfg_from_file, cfg_from_list, get_output_dir, get_output_tb_dir
from datasets.factory import get_imdb
import datasets.imdb
import argparse
import pprint
import numpy as np
import sys
import tensorflow as tf
from nets.vgg16 import vgg16
from nets.resnet_v1 import resnetv1
class args:
"""
Parse input arguments
"""
cfg_file = '../experiments/cfgs/vgg16.yml'
weight = '../data/imagenet_weights/vgg16.ckpt'
imdb_name = 'voc_2007_trainval'
imdbval_name = 'voc_2007_test'
max_iters = 100000
tag = None
net = 'vgg16'
set_cfgs = ['ANCHOR_SCALES', '[8,16,32]', 'ANCHOR_RATIOS', '[0.5,1,2]', 'TRAIN.STEPSIZE', '[50000]']
def combined_roidb(imdb_names):
"""
Combine multiple roidbs
"""
def get_roidb(imdb_name):
imdb = get_imdb(imdb_name)
print('Loaded dataset `{:s}` for training'.format(imdb.name))
imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
print('Set proposal method: {:s}'.format(cfg.TRAIN.PROPOSAL_METHOD))
roidb = get_training_roidb(imdb)
return roidb
roidbs = [get_roidb(s) for s in imdb_names.split('+')]
roidb = roidbs[0]
if len(roidbs) > 1:
for r in roidbs[1:]:
roidb.extend(r)
tmp = get_imdb(imdb_names.split('+')[1])
imdb = datasets.imdb.imdb(imdb_names, tmp.classes)
else:
imdb = get_imdb(imdb_names)
return imdb, roidb
if __name__ == '__main__':
# args = parse_args()
print('Called with args:')
if args.cfg_file is not None:
cfg_from_file(args.cfg_file)
if args.set_cfgs is not None:
cfg_from_list(args.set_cfgs)
print('Using config:')
pprint.pprint(cfg)
np.random.seed(cfg.RNG_SEED)
# train set
imdb, roidb = combined_roidb(args.imdb_name)
print('{:d} roidb entries'.format(len(roidb)))
# output directory where the models are saved
output_dir = get_output_dir(imdb, args.tag)
print('Output will be saved to `{:s}`'.format(output_dir))
# tensorboard directory where the summaries are saved during training
tb_dir = get_output_tb_dir(imdb, args.tag)
print('TensorFlow summaries will be saved to `{:s}`'.format(tb_dir))
# also add the validation set, but with no flipping images
orgflip = cfg.TRAIN.USE_FLIPPED
cfg.TRAIN.USE_FLIPPED = False
_, valroidb = combined_roidb(args.imdbval_name)
print('{:d} validation roidb entries'.format(len(valroidb)))
cfg.TRAIN.USE_FLIPPED = orgflip
# load network
if args.net == 'vgg16':
net = vgg16()
elif args.net == 'res50':
net = resnetv1(num_layers=50)
elif args.net == 'res101':
net = resnetv1(num_layers=101)
elif args.net == 'res152':
net = resnetv1(num_layers=152)
elif args.net == 'mobile':
net = mobilenetv1()
else:
raise NotImplementedError
train_net(net, imdb, roidb, valroidb, output_dir, tb_dir,
pretrained_model=args.weight,
max_iters=args.max_iters)
原来的trainval_net.py文件中的args是通过命令行来传参的,这里我设置了一个args类对所需的变量进行赋值。
之后运行trainval_net.py就可以训练网络模型~~~
二、测试模型
对./tools/test_net.py进行修改,类似于在trainval_net.py文件中的修改,将原本需要通过命令行传递的参数写一个args类对所需的变量进行赋值
class args:
"""
Parse input arguments
"""
cfg_file = '../experiments/cfgs/vgg16.yml
//模型存放的路径
model = 'E:\workspace\python\zj/tf-faster-rcnn-master(endernewton)__gpu_v6\output(64,128)/vgg16/vgg16_faster_rcnn_iter_15000.ckpt'
# net = 'res101'
net = 'vgg16'
imdb_name = 'voc_2007_test'
comp_mode =True
TRAIN_IMDB="voc_2007_trainval"
TEST_IMDB="voc_2007_test"
ITERS=7000
max_per_image = 100
tag = None
set_cfgs = ['ANCHOR_SCALES', '[8,16,32]', 'ANCHOR_RATIOS', '[0.5,1,2]']
修改完之后,直接运行test_net.py对训练好的模型进行测试
三、demo测试
同样写一个args类进行传参,
class args:
cfg_file = '../experiments/cfgs/vgg16.yml'
model = 'E:\\workspace\\python\zj/tf-faster-rcnn-master(endernewton)__gpu_v6\\tf-faster-rcnn-master\\output\\vgg16\\voc_2007_trainval\\default\\vgg16_faster_rcnn_iter_65000.ckpt'
imdb_name = 'voc_2007_test'
comp_mode =True
TRAIN_IMDB="voc_2007_trainval"
TEST_IMDB="voc_2007_test"
ITERS=7000
max_per_image = 100
tag = None
# net = 'res101'
net = 'vgg16'
set_cfgs = ['ANCHOR_SCALES', '[8,16,32]', 'ANCHOR_RATIOS', '[0.5,1,2]']
demo.py默认从data/demo路径下读取图片进行检测,并将检测的结果可视化出来