首先,在以下路径中可以找到fast_rcnn_demo.m:
matconvnet/examples/fast_rcnn/fast_rcnn_demo.m
除此以外,还包含了两个重要文件fast_rcnn_train.m(用于训练模型)和fast_rcnn_demo.m(用于测试模型的准确率)。注意:这两个文件都需要数据库依赖,本文不涉及。
在官网下载预训练好的网络:http://pjreddie.com/projects/pascal-voc-dataset-mirror/。
把网络文件解压后放到 matconvnet/data/models 文件夹下 。
function fast_rcnn_demo(varargin) %FAST_RCNN_DEMO Demonstrates Fast-RCNN % % Copyright (C) 2016 Abhishek Dutta and Hakan Bilen. % All rights reserved. % % This file is part of the VLFeat library and is made available under % the terms of the BSD license (see the COPYING file). # 自动设置路径 run(fullfile(fileparts(mfilename('fullpath')), ... '..', '..', 'matlab', 'vl_setupnn.m')) ; # bounding box相关的functions的路径添加到matlab addpath(fullfile(vl_rootnn,'examples','fast_rcnn','bbox_functions')) ; # opts结构体?? opts.modelPath = '' ; opts.classes = {'car'} ; opts.gpu = [] ; opts.confThreshold = 0.5 ; opts.nmsThreshold = 0.3 ; opts = vl_argparse(opts, varargin) ; # 看这里的代码,其实可以发现,如果把下载的.mat文件放在paths所列路径中的任何一个就可以了 # paths是待选模型路径列表 % Load or download the Fast RCNN model paths = {opts.modelPath, ... './fast-rcnn-vgg16-dagnn.mat', ... fullfile(vl_rootnn, 'data', 'models', 'fast-rcnn-vgg16-pascal07-dagnn.mat'), ... fullfile(vl_rootnn, 'data', 'models-import', 'fast-rcnn-vgg16-pascal07-dagnn.mat')} ; # cellfun(@(x)exist(x,'file'), paths) # 以cell数组paths的members作为输入x,输入到exist(x,'file') # 得到输出数组,分别代表各个路径是否存在 # find()返回其index # 优先选取参数最小的:即paths中的排序靠前的为优先考虑的路径 ok = min(find(cellfun(@(x)exist(x,'file'), paths))) ; # 本地存在.mat则把相应路径设置opts.modelPath,否则自动下载 # http://www.vlfeat.org/matconvnet/models/fast-rcnn-vgg16-pascal07-dagnn.mat if isempty(ok) fprintf('Downloading the Fast RCNN model ... this may take a while\n') ; opts.modelPath = fullfile(vl_rootnn, 'data', 'models', 'fast-rcnn-vgg16-pascal07-dagnn.mat') ; mkdir(fileparts(opts.modelPath)) ; urlwrite('http://www.vlfeat.org/matconvnet/models/fast-rcnn-vgg16-pascal07-dagnn.mat', ... opts.modelPath) ; else opts.modelPath = paths{ok} ; end # 加载,设置‘测试’模式 % Load the network and put it in test mode. net = load(opts.modelPath) ; net = dagnn.DagNN.loadobj(net); net.mode = 'test' ; # 分类和边界框预测标为'precious',避免评价期间优化它们 # 即设置网络的输出层,避免其继续迭代(fast_rcnn会交互迭代bbox层和cls层) % Mark class and bounding box predictions as `precious` % so they are not optimized away during evaluation. net.vars(net.getVarIndex('cls_prob')).precious = 1 ; net.vars(net.getVarIndex('bbox_pred')).precious = 1 ; # 加载一幅测试图像和bounding box竞选者(一系列bounding box坐标) % Load a test image and candidate bounding boxes. im = single(imread('000004.jpg')) ; imo = im; % keep original image boxes = load('000004_boxes.mat') ; # 转置`,并保存原boxes boxes = single(boxes.boxes`) + 1 ; % keep original boxes boxeso = boxes - 1; # 把图像和boxes调整到适应网络的大小 % Resize images and boxes to a size compatible with the network. imageSize = size(im) ; # normalization.imagesize为224*224*3 # .cropSize为小数表示的裁剪倍数,原始尺寸fullImageSize经过crop后得到的imagesize,所以这里是以下公式 fullImageSize = net.meta.normalization.imageSize(1)/ net.meta.normalization.cropSize ; # 缩放倍数选择更大的,使缩放后的图像大小不小于标准的600*600 scale = max(fullImageSize ./ imageSize(1:2)) ; # interpolatoin选择插值方式(双三),缩小图像时不消除锯齿, im=imresize(im,scale,net.meta.normalization.interpolation,'antialiasing', false) ; # 变换boxes boxes = bsxfun(@times, boxes - 1, scale) + 1 ; % Remove the average color from the input image. imNorm = bsxfun(@minus, im, net.meta.normalization.averageImage) ; boxes是4*2888的向量,rois多一行(第一行)全是1 % Convert boxes into ROIs by prepending the image index. There is only % one image in this batch. rois = [ones(1,size(boxes,2)) ; boxes] ; % Evaluate network either on CPU or GPU. if numel(opts.gpu) > 0 gpuDevice(opts.gpu) ; imNorm = gpuArray(imNorm) ; rois = gpuArray(rois) ; net.move('gpu') ; end net.conserveMemory = false ; # 输入data和感兴趣区域rois,进行评价, # 结果在net.vars最后一层的value中 net.eval({'data', imNorm, 'rois', rois}); # 提取结果,包括分类结果'cls_prob' # 和对应的bounding_box位置'bbox_pred' % Extract class probabilities and bounding box refinements probs = squeeze(gather(net.vars(net.getVarIndex('cls_prob')).value)) ; deltas = squeeze(gather(net.vars(net.getVarIndex('bbox_pred')).value)) ; # 依次可视化 % Visualize results for one class at a time for i = 1:numel(opts.classes) # 获取opts.classes{i}所代表的类别在probs(k,:)中的index c = find(strcmp(opts.classes{i}, net.meta.classes.name)) ; # 获取'cars'的概率矩阵cprobs:1*2888 # 4*(c-1)+(1:4)为boxeso`(k,:)中'cars'的对应下标 # 列出每个'cars'类candidates的boxeso`(k,:)下标,cdeltas:1*4 # 合并为cboxes:2888*5 cprobs = probs(c,:) ; cdeltas = deltas(4*(c-1)+(1:4),:)` ; cboxes = bbox_transform_inv(boxeso`, cdeltas); cls_dets = [cboxes cprobs`] ; # 采用‘非最大值抑制’,保留cls_dets中prob大于阈值0.3的boxes keep = bbox_nms(cls_dets, opts.nmsThreshold) ; cls_dets = cls_dets(keep, :) ; # 再次筛选分类概率大于0.5的bbox选框 sel_boxes = find(cls_dets(:,end) >= opts.confThreshold) ; # 显示bbox并在标题上说明类别 imo = bbox_draw(imo/255,cls_dets(sel_boxes,:)); title(sprintf('Detections for class ''%s''', opts.classes{i})) ; # 在屏幕上打印分类结果 fprintf('Detections for category ''%s'':\n', opts.classes{i}); for j=1:size(sel_boxes,1) bbox_id = sel_boxes(j,1); fprintf('\t(%.1f,%.1f)\t(%.1f,%.1f)\tprobability=%.6f\n', ... cls_dets(bbox_id,1), cls_dets(bbox_id,2), ... cls_dets(bbox_id,3), cls_dets(bbox_id,4), ... cls_dets(bbox_id,end)); end endvl_rootnn是matconvnet/matlab/vl_rootnn.m,直接产生根路径。