进过前几次的学习,我们将Faster R-CNN整个核心代码都过了一遍了,接下来我们要做的事,了解trainval_net,test_net,demo.py中各个参数是如何配置的,并且能够针对性的做出一些修改,来适应自己的数据集。

该笔记大部分参考来自:https://blog.csdn.net/weixin_43872578/article/details/86607742

1.导入相关的库

  1. # --------------------------------------------------------
  2. # Tensorflow Faster R-CNN
  3. # Licensed under The MIT License [see LICENSE for details]
  4. # Written by Jiasen Lu, Jianwei Yang, based on code from Ross Girshick
  5. # --------------------------------------------------------
  6. # Python提供了__future__模块,把下一个新版本的特性导入到当前版本
  7. from __future__ import absolute_import
  8. '''
  9. 导入python未来支持的语言特征division(精确除法),当我们没有在程序中导入该特征时,
  10. "/"操作符执行的是截断除法(Truncating Division)
  11. 当我们导入精确除法之后,"/"执行的是精确除法
  12. '''
  13. from __future__ import division
  14. # 即使在python2.X,使用print就得像python3.X那样加括号使用
  15. from __future__ import print_function
  16. import _init_paths
  17. import os #通过os模块调用系统命令
  18. import sys #sys 模块包括了一组非常实用的服务,内含很多函数方法和变量
  19. '''numpy用来处理图片数据(多维数组), 尤其是numpy的broadcasting特性,
  20. 使得不同维度的数组可以一起操作(加,减,乘, 除, 等).
  21. '''
  22. import numpy as np
  23. import argparse # 为py文件封装好可以选择的参数
  24. import pprint #提供了打印出任何python数据结构类和方法。
  25. import pdb #使用 Pdb调试 Python程序
  26. import time
  27. import cv2
  28. import torch
  29. from torch.autograd import Variable
  30. #自动微分 vairable是tensor的一个外包装
  31. import torch.nn as nn
  32. import torch.optim as optim
  33. '''
  34. 为了方便加载以上五种数据库的数据,pytorch团队帮我们写了一个torchvision包。
  35. 使用torchvision就可以轻松实现数据的加载和预处理。
  36. '''
  37. import torchvision.transforms as transforms
  38. import torchvision.datasets as dset
  39. '''
  40. scipy.misc 下的图像处理
  41. imread():返回的是 numpy.ndarray 也即 numpy 下的多维数组对象;
  42. '''
  43. from scipy.misc import imread
  44. '''
  45. 这些封装的库都是前面的笔记中所学习的
  46. '''
  47. from roi_data_layer.roidb import combined_roidb
  48. from roi_data_layer.roibatchLoader import roibatchLoader
  49. # demo.py运行过程中的配置基本上都在config.py了. 后续的代码流程中会用到这些配置值.
  50. from model.utils.config import cfg, cfg_from_file, cfg_from_list, get_output_dir
  51. from model.rpn.bbox_transform import clip_boxes
  52. # from model.nms.nms_wrapper import nms
  53. from model.roi_layers import nms
  54. from model.rpn.bbox_transform import bbox_transform_inv
  55. from model.utils.net_utils import save_net, load_net, vis_detections
  56. from model.utils.blob import im_list_to_blob
  57. from model.faster_rcnn.vgg16 import vgg16
  58. from model.faster_rcnn.resnet import resnet
  59. import pdb
  60. try:
  61. xrange # Python 2
  62. except NameError:
  63. xrange = range # Python 3

2.解析参数 parse_args()


  1. def parse_args():
  2. """
  3. Parse input arguments
  4. """
  5. parser = argparse.ArgumentParser(description='Train a Fast R-CNN network')
  6. parser.add_argument('--dataset', dest='dataset',
  7. help='training dataset',
  8. default='pascal_voc', type=str)
  9. # 指代你跑得数据集名称,例如默认为pascal-voc
  10. parser.add_argument('--cfg', dest='cfg_file',
  11. help='optional config file',
  12. default='cfgs/vgg16.yml', type=str)
  13.  #配置文件 在目录faster_rcnn.pytorch-pytorch-1.0/cfgs中
  14. parser.add_argument('--net', dest='net',
  15. help='vgg16, res50, res101, res152',
  16. default='res101', type=str)
  17.  #backbone网络类型
  18. parser.add_argument('--set', dest='set_cfgs',
  19. help='set config keys', default=None,
  20. nargs=argparse.REMAINDER)
  21.  #设置
  22. parser.add_argument('--load_dir', dest='load_dir',
  23. help='directory to load models',
  24. default="/srv/share/jyang375/models")
  25.  #模型目录
  26. parser.add_argument('--image_dir', dest='image_dir',
  27. help='directory to load images for demo',
  28. default="images")
  29.  #图片目录
  30. parser.add_argument('--cuda', dest='cuda',
  31. help='whether use CUDA',
  32. action='store_true')
  33.  #是否用GPU
  34. parser.add_argument('--mGPUs', dest='mGPUs',
  35. help='whether use multiple GPUs',
  36. action='store_true')
  37.  #是不是多GPU
  38. parser.add_argument('--cag', dest='class_agnostic',
  39. help='whether perform class_agnostic bbox regression',
  40. action='store_true')
  41. '''
  42. class-agnostic 方式只回归2类bounding box,即前景和背景
  43. 结合每个box在classification 网络中对应着所有类别的得分,
  44. 以及检测阈值条件,就可以得到图片中所有类别的检测结果
  45. '''
  46. parser.add_argument('--parallel_type', dest='parallel_type',
  47. help='which part of model to parallel, 0: all, 1: model before roi pooling',
  48. default=0, type=int)
  49. #模型的哪一部分并行
  50. '''
  51. 以下是关于模型保存check的一些相关参数
  52. 这里的三个check参数,是定义了训好的检测模型名称
  53. faster_rcnn_1_20_10021,代表了checksession = 1,
  54. checkepoch = 20, checkpoint = 10021,
  55. 这样才可以读到模型“faster_rcnn_1_20_10021”。
  56. '''
  57. parser.add_argument('--checksession', dest='checksession',
  58. help='checksession to load model',
  59. default=1, type=int)
  60. parser.add_argument('--checkepoch', dest='checkepoch',
  61. help='checkepoch to load network',
  62. default=1, type=int)
  63. parser.add_argument('--checkpoint', dest='checkpoint',
  64. help='checkpoint to load network',
  65. default=10021, type=int)
  66. parser.add_argument('--bs', dest='batch_size',
  67. help='batch_size',
  68. default=1, type=int)
  69. #批大小
  70. parser.add_argument('--vis', dest='vis',
  71. help='visualization mode',
  72. action='store_true')
  73. #可视化模型
  74. parser.add_argument('--webcam_num', dest='webcam_num',
  75. help='webcam ID number',
  76. default=-1, type=int)
  77. # 是否调用摄像头
  78.   #parse_args()是将之前add_argument()定义的参数进行赋值,并返回相关的namespace。
  79. args = parser.parse_args()
  80. return args
  81. lr = cfg.TRAIN.LEARNING_RATE #学习率
  82. momentum = cfg.TRAIN.MOMENTUM #动量
  83. weight_decay = cfg.TRAIN.WEIGHT_DECAY #权重衰减

3.函数 _get_image_blob(im)


  1. def _get_image_blob(im):
  2. #这个函数其实就是读取图片,然后做尺寸变换,然后存储成矩阵的形式
  3. '''
  4. Converts an image into a network input.
  5. Arguments:
  6. im (ndarray): a color image in BGR order
  7. Returns:
  8. blob (ndarray): a data blob holding an image pyramid
  9. im_scale_factors (list): list of image scales (relative to im) used
  10. in the image pyramid
  11. '''
  12. #Numpy中 astype:转换数组的数据类型。
  13. im_orig = im.astype(np.float32, copy=True)
  14. #而pixel mean的话,其实是把训练集里面所有图片的所有R通道像素,求了均值,G,B通道类似
  15. im_orig -= cfg.PIXEL_MEANS
  16. im_shape = im_orig.shape
  17. im_size_min = np.min(im_shape[0:2])
  18. im_size_max = np.max(im_shape[0:2])
  19. processed_ims = []
  20. im_scale_factors = []
  21. for target_size in cfg.TEST.SCALES: # target_size = 600
  22. #遍历cfg.TEST.SCALES这个元组或列表中的值
  23. im_scale = float(target_size) / float(im_size_min)
  24. #测试的尺度除以图像最小长度(宽高的最小值)
  25. # Prevent the biggest axis from being more than MAX_SIZE
  26. #防止最大值超过MAX_SIZE,round函数四舍五入
  27. if np.round(im_scale * im_size_max) > cfg.TEST.MAX_SIZE:
  28. im_scale = float(cfg.TEST.MAX_SIZE) / float(im_size_max)
  29. #调整im_orig大小
  30. im = cv2.resize(im_orig, None, None, fx=im_scale, fy=im_scale,
  31. interpolation=cv2.INTER_LINEAR)
  32. #保存尺度值
  33. im_scale_factors.append(im_scale)
  34. #保存调整后的图像
  35. processed_ims.append(im)
  36. # Create a blob to hold the input images
  37. '''
  38. 创建一个blob来保存输入图像
  39. 这个函数出自这里 from model.utils.blob import im_list_to_blob
  40. '''
  41. blob = im_list_to_blob(processed_ims)
  42. return blob, np.array(im_scale_factors)

4. if name == ‘main’:


  1. if __name__ == '__main__':
  2. #这就是上面定义的那个函数
  3. args = parse_args()
  4. print('Called with args:')
  5. print(args)
  6. if args.cfg_file is not None: #配置文件
  7. cfg_from_file(args.cfg_file)
  8. #model.utils.config 该文件中函数
  9. #Load a config file and merge it into the default options.
  10. if args.set_cfgs is not None:
  11. cfg_from_list(args.set_cfgs)
  12. #model.utils.config文件中
  13. # Set config keys via list (e.g., from command line).
  14. '''
  15. Use GPU implementation of non-maximum suppression
  16. 解析参数是不是用GPU
  17. '''
  18. cfg.USE_GPU_NMS = args.cuda
  19. print('Using config:')
  20. pprint.pprint(cfg)
  21. '''
  22. 设置随机数种子
  23. 每次运行代码时设置相同的seed,则每次生成的随机数也相同,
  24. 如果不设置seed,则每次生成的随机数都会不一样
  25. '''
  26. np.random.seed(cfg.RNG_SEED)
  27. # train set
  28. # -- Note: Use validation set and disable the flipped to enable faster loading.
  29. input_dir = args.load_dir + "/" + args.net + "/" + args.dataset
  30. if not os.path.exists(input_dir):
  31. #当程序出现错误,python会自动引发异常,
  32. #也可以通过raise显示地引发异常。一旦执行了raise语句,raise后面的语句将不能执行
  33. raise Exception('There is no input directory for loading network from ' + input_dir)
  34. load_name = os.path.join(input_dir,
  35. 'faster_rcnn_{}_{}_{}.pth'.format(args.checksession,
  36. args.checkepoch,
  37. args.checkpoint))
  38. #这里的三个check参数,是定义了训好的检测模型名称,例如训好的名称为faster_rcnn_1_20_10021,
  39. '''
  40. PASCAL类别 1类背景 + 20类Object
  41. array和asarray都可以将结构数据转化为ndarray,但是主要区别就是当数据源是ndarray时,
  42. array仍然会copy出一个副本,占用新的内存,但asarray不会。
  43. '''
  44. pascal_classes = np.asarray(['__background__',
  45. 'aeroplane', 'bicycle', 'bird', 'boat',
  46. 'bottle', 'bus', 'car', 'cat', 'chair',
  47. 'cow', 'diningtable', 'dog', 'horse',
  48. 'motorbike', 'person', 'pottedplant',
  49. 'sheep', 'sofa', 'train', 'tvmonitor'])
  50. # initilize the network here.
  51. '''
  52. class-agnostic 方式只回归2类bounding box,即前景和背景
  53. '''
  54. if args.net == 'vgg16':
  55. fasterRCNN = vgg16(pascal_classes, pretrained=False, class_agnostic=args.class_agnostic)
  56. elif args.net == 'res101':
  57. fasterRCNN = resnet(pascal_classes, 101, pretrained=False, class_agnostic=args.class_agnostic)
  58. elif args.net == 'res50':
  59. fasterRCNN = resnet(pascal_classes, 50, pretrained=False, class_agnostic=args.class_agnostic)
  60. elif args.net == 'res152':
  61. fasterRCNN = resnet(pascal_classes, 152, pretrained=False, class_agnostic=args.class_agnostic)
  62. else:
  63. print("network is not defined")
  64. #到了pdb.set_trace()那就会定下来,就可以看到调试的提示符(Pdb)了
  65. pdb.set_trace()
  66. fasterRCNN.create_architecture()
  67. #model.faster_rcnn.faster_rcnn.py 初始化模型 初始化权重
  68. print("load checkpoint %s" % (load_name))#模型路径
  69. if args.cuda > 0:
  70. checkpoint = torch.load(load_name)
  71. else:
  72. checkpoint = torch.load(load_name, map_location=(lambda storage, loc: storage))
  73. fasterRCNN.load_state_dict(checkpoint['model'])#恢复模型
  74. if 'pooling_mode' in checkpoint.keys():
  75. cfg.POOLING_MODE = checkpoint['pooling_mode'] #pooling方式
  76. print('load model successfully!')
  77. # pdb.set_trace()
  78. print("load checkpoint %s" % (load_name))
  79. # initilize the tensor holder here.
  80. #新建一些 一维Tensor
  81. im_data = torch.FloatTensor(1)
  82. im_info = torch.FloatTensor(1)
  83. num_boxes = torch.LongTensor(1)
  84. gt_boxes = torch.FloatTensor(1)
  85. # ship to cuda
  86. if args.cuda > 0:#如果用GPU,张量放到GPU
  87. im_data = im_data.cuda()
  88. im_info = im_info.cuda()
  89. num_boxes = num_boxes.cuda()
  90. gt_boxes = gt_boxes.cuda()
  91. # make variable
  92. #Variable的volatile属性默认为False,如果某一个variable的volatile属性被设为True,
  93. #volatile属性为True的节点不会求导,volatile的优先级比requires_grad高。
  94. im_data = Variable(im_data, volatile=True)
  95. im_info = Variable(im_info, volatile=True)
  96. num_boxes = Variable(num_boxes, volatile=True)
  97. gt_boxes = Variable(gt_boxes, volatile=True)
  98. if args.cuda > 0:
  99. cfg.CUDA = True
  100. if args.cuda > 0:
  101. fasterRCNN.cuda()
  102. '''
  103. model.eval(),让model变成测试模式,
  104. 对dropout和batch normalization的操作在训练和测试的时候是不一样的
  105. pytorch会自动把BN和DropOut固定住,不会取平均,而是用训练好的值
  106. '''
  107. fasterRCNN.eval()
  108. start = time.time() #通过time()函数可以获取当前的时间
  109. max_per_image = 100
  110. thresh = 0.05
  111. vis = True
  112. webcam_num = args.webcam_num
  113. # Set up webcam or get image directories
  114. if webcam_num >= 0 :#应该就是判断要不要自己用电脑录视频
  115. '''
  116. cap = cv2.VideoCapture(0) 打开笔记本的内置摄像头。
  117. cap = cv2.VideoCapture('D:\output.avi') 打开视频文件
  118. '''
  119. cap = cv2.VideoCapture(webcam_num)
  120. num_images = 0
  121. else:#如果不用电脑录视频,那么就读取image路径下的图片
  122. #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表
  123. #这个列表以字母顺序
  124. imglist = os.listdir(args.image_dir)
  125. #有多少张图片
  126. num_images = len(imglist)
  127. print('Loaded Photo: {} images.'.format(num_images))
  128. while (num_images >= 0):
  129. total_tic = time.time()#当前时间
  130. if webcam_num == -1:#如果不用摄像头
  131. num_images -= 1
  132. # Get image from the webcam
  133. #从电脑摄像头读取图片
  134. if webcam_num >= 0:
  135. if not cap.isOpened():#摄像头开启失败
  136. raise RuntimeError("Webcam could not open. Please check connection.")
  137. #ret 为True 或者False,代表有没有读取到图片
  138. #frame表示截取到一帧的图片
  139. ret, frame = cap.read()
  140. im_in = np.array(frame)
  141. # Load the demo image
  142. else:
  143. #图片路径
  144. im_file = os.path.join(args.image_dir, imglist[num_images])
  145. # im = cv2.imread(im_file)
  146. #读取的的图片 存储为numpy数组
  147. im_in = np.array(imread(im_file))
  148. if len(im_in.shape) == 2:
  149. #np.newaxis的作用就是在这一位置增加一个一维,
  150. #这一位置指的是np.newaxis所在的位置,比较抽象,需要配合例子理解。
  151. '''
  152. example:
  153. x1 = np.array([1, 2, 3, 4, 5])
  154. the shape of x1 is (5,)
  155. x1_new = x1[:, np.newaxis]
  156. now, the shape of x1_new is (5, 1)
  157. array([[1],
  158. [2],
  159. [3],
  160. [4],
  161. [5]])
  162. x1_new = x1[np.newaxis,:]
  163. now, the shape of x1_new is (1, 5)
  164. array([[1, 2, 3, 4, 5]])
  165. '''
  166. im_in = im_in[:,:,np.newaxis]
  167. im_in = np.concatenate((im_in,im_in,im_in), axis=2)
  168. '''
  169. 数组拼接
  170. 若axis=0,则要求除了a.shape[0]和b.shape[0]可以不等之外,其它维度必须相等
  171. 若axis=0,则要求除了a.shape[0]和b.shape[0]可以不等之外,其它维度必须相等
  172. axis>=2 的情况以此类推,axis的值必须小于数组的维度
  173. '''
  174. # 可以理解为批处理
  175. # rgb -> bgr
  176. im = im_in[:,:,::-1]
  177. blobs, im_scales = _get_image_blob(im)
  178. #图片变换 该文件上面定义的函数,返回处理后的值 和尺度
  179. assert len(im_scales) == 1, "Only single-image batch implemented"
  180. im_blob = blobs #处理后的值,图像信息,长、宽、尺度
  181. im_info_np = np.array([[im_blob.shape[1],
  182. im_blob.shape[2],
  183. im_scales[0]]], dtype=np.float32)
  184. #从numpy变为Tensor
  185. im_data_pt = torch.from_numpy(im_blob)
  186. #permute 将tensor的维度换位。
  187. im_data_pt = im_data_pt.permute(0, 3, 1, 2)
  188. #图像信息也变为tensor
  189. im_info_pt = torch.from_numpy(im_info_np)
  190. with torch.no_grad():
  191. '''
  192. 将上面定义的一些tensor大小调整为指定大小
  193. 如果元素个数比当前的内存大小大,就将底层存储大小调整为与新元素数目一致的大小。
  194. '''
  195. im_data.resize_(im_data_pt.size()).copy_(im_data_pt)
  196. im_info.resize_(im_info_pt.size()).copy_(im_info_pt)
  197. gt_boxes.resize_(1, 1, 5).zero_()
  198. num_boxes.resize_(1).zero_()
  199. # pdb.set_trace()
  200. det_tic = time.time()#当前时间
  201. rois, cls_prob, bbox_pred, \
  202. rpn_loss_cls, rpn_loss_box, \
  203. RCNN_loss_cls, RCNN_loss_bbox, \
  204. rois_label = fasterRCNN(im_data, im_info, gt_boxes, num_boxes)
  205. '''
  206. rois:rois blob: holds R regions of interest, each is a 5-tuple
  207. (n, x1, y1, x2, y2) specifying an image batch index n and a
  208. rectangle (x1, y1, x2, y2)
  209. top[0].reshape(1, 5)
  210. cls_prob: softmax得到的概率值
  211. bbox_pred: 偏移
  212. rpn_loss_cls:分类损失,计算softmax的损失,
  213. 输入labels和cls layer的18个输出(中间reshape了一下),输出损失函数的具体值
  214. rpn_loss_box 计算的框回归损失函数具体的值
  215. '''
  216. scores = cls_prob.data #分类概率值
  217. boxes = rois.data[:, :, 1:5] # 输出rois的坐标值
  218. if cfg.TEST.BBOX_REG: #Train bounding-box regressors TRUE or FALSE
  219. # Apply bounding-box regression deltas
  220. box_deltas = bbox_pred.data #偏移值
  221. if cfg.TRAIN.BBOX_NORMALIZE_TARGETS_PRECOMPUTED:
  222. # Optionally normalize targets by a precomputed mean and stdev
  223. if args.class_agnostic:
  224. if args.cuda > 0:
  225. #box_deltas.view改变维度
  226. box_deltas = box_deltas.view(-1, 4) * torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_STDS).cuda() \
  227. + torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_MEANS).cuda()
  228. else:
  229. box_deltas = box_deltas.view(-1, 4) * torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_STDS) \
  230. + torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_MEANS)
  231. box_deltas = box_deltas.view(1, -1, 4)
  232. else:
  233. if args.cuda > 0:
  234. box_deltas = box_deltas.view(-1, 4) * torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_STDS).cuda() \
  235. + torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_MEANS).cuda()
  236. else:
  237. box_deltas = box_deltas.view(-1, 4) * torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_STDS) \
  238. + torch.FloatTensor(cfg.TRAIN.BBOX_NORMALIZE_MEANS)
  239. box_deltas = box_deltas.view(1, -1, 4 * len(pascal_classes))
  240. '''
  241. model.rpn.bbox_transform 根据anchor和偏移量计算proposals
  242. 最后返回的是左上和右下顶点的坐标[x1,y1,x2,y2]。
  243. '''
  244. pred_boxes = bbox_transform_inv(boxes, box_deltas, 1)
  245. '''
  246. model.rpn.bbox_transform
  247. 将改变坐标信息后超过图像边界的框的边框裁剪一下,使之在图像边界之内
  248. '''
  249. pred_boxes = clip_boxes(pred_boxes, im_info.data, 1)
  250. else:
  251. # Simply repeat the boxes, once for each class
  252. # Numpy的 tile() 函数,就是将原矩阵横向、纵向地复制,这里是横向
  253. pred_boxes = np.tile(boxes, (1, scores.shape[1]))
  254. pred_boxes /= im_scales[0]
  255. #squeeze 函数:从数组的形状中删除单维度条目,即把shape中为1的维度去掉
  256. scores = scores.squeeze()
  257. pred_boxes = pred_boxes.squeeze()
  258. det_toc = time.time()
  259. detect_time = det_toc - det_tic #detect_time
  260. misc_tic = time.time()
  261. if vis:
  262. im2show = np.copy(im)
  263. for j in xrange(1, len(pascal_classes)): #所有类别
  264. '''
  265. torch.nonzero
  266. 返回一个包含输入input中非零元素索引的张量,输出张量中的每行包含输入中非零元素的索引
  267. 若输入input有n维,则输出的索引张量output形状为z * n, 这里z是输入张量input中所有非零元素的个数
  268. '''
  269. inds = torch.nonzero(scores[:,j]>thresh).view(-1)
  270. #参数中的-1就代表这个位置由其他位置的数字来推断
  271. # if there is det
  272. #torch.numel() 返回一个tensor变量内所有元素个数,可以理解为矩阵内元素的个数
  273. if inds.numel() > 0:
  274. cls_scores = scores[:,j][inds]
  275. #torch.sort(input, dim=None, descending=False, out=None)有true,
  276. #则表示降序,默认升序,沿第0列降序
  277. _, order = torch.sort(cls_scores, 0, True)
  278. if args.class_agnostic: #两类
  279. cls_boxes = pred_boxes[inds, :]
  280. else:
  281. cls_boxes = pred_boxes[inds][:, j * 4:(j + 1) * 4]
  282. #按行连接起来,torch.unsqueeze()这个函数主要是对数据维度进行扩充
  283. cls_dets = torch.cat((cls_boxes, cls_scores.unsqueeze(1)), 1)
  284. # cls_dets = torch.cat((cls_boxes, cls_scores), 1)
  285. cls_dets = cls_dets[order]
  286. # keep = nms(cls_dets, cfg.TEST.NMS, force_cpu=not cfg.USE_GPU_NMS)
  287. keep = nms(cls_boxes[order, :], cls_scores[order], cfg.TEST.NMS)
  288. cls_dets = cls_dets[keep.view(-1).long()]
  289. if vis:
  290. im2show = vis_detections(im2show, pascal_classes[j], cls_dets.cpu().numpy(), 0.5)
  291. misc_toc = time.time()
  292. nms_time = misc_toc - misc_tic
  293. if webcam_num == -1:
  294. sys.stdout.write('im_detect: {:d}/{:d} {:.3f}s {:.3f}s \r' \
  295. .format(num_images + 1, len(imglist), detect_time, nms_time))
  296. sys.stdout.flush()
  297. if vis and webcam_num == -1:
  298. # cv2.imshow('test', im2show)
  299. # cv2.waitKey(0)
  300. result_path = os.path.join(args.image_dir, imglist[num_images][:-4] + "_det.jpg")
  301. cv2.imwrite(result_path, im2show)
  302. else:
  303. im2showRGB = cv2.cvtColor(im2show, cv2.COLOR_BGR2RGB)
  304. cv2.imshow("frame", im2showRGB)
  305. total_toc = time.time()
  306. total_time = total_toc - total_tic
  307. frame_rate = 1 / total_time
  308. print('Frame rate:', frame_rate)
  309. if cv2.waitKey(1) & 0xFF == ord('q'):
  310. break
  311. if webcam_num >= 0:
  312. cap.release()
  313. cv2.destroyAllWindows()

5.运行demo.py,查看检测结果

这里有一个巨坑!!!!!!!!!!

不要直接运行demo.py,因为文件里没有!!!!!没有!!!! 模型!!!!!

此处说的模型不是指Vgg或Resnet在分类数据集ImageNet下训好的Transfer模型,而是指的是在目标检测集VOC或COCO下进行fine-tune的模型。对于VOC2007数据集,训练好后该模型大概1个G,所以代码作者没有上传到github,要不你就自己训练,要不你就跟训好的人要,不过建议你自己训练,跑trainval_net.py。