├── .gitignore ├── Contents.m ├── README.md ├── boxesData.m ├── boxesEval.m ├── demo_edgeboxes.py ├── edgeBoxes.m ├── edgeBoxesDemo.m ├── edgeBoxesSweeps.m ├── edge_boxes.py ├── edge_boxes_wrapper.m ├── edgesChns.m ├── edgesDemo.m ├── edgesDemoRgbd.m ├── edgesDetect.m ├── edgesEval.m ├── edgesEvalDir.m ├── edgesEvalImg.m ├── edgesEvalPlot.m ├── edgesSweeps.m ├── edgesTrain.m ├── license.txt ├── models └── forest │ └── modelBsds.mat ├── private ├── correspondPixels.mexa64 ├── correspondPixels.mexmaci64 ├── correspondPixels.mexw64 ├── edgeBoxesMex.cpp ├── edgeBoxesMex.mexa64 ├── edgeBoxesMex.mexmaci64 ├── edgeBoxesMex.mexw64 ├── edgesDetectMex.cpp ├── edgesDetectMex.mexa64 ├── edgesDetectMex.mexmaci64 ├── edgesDetectMex.mexw64 ├── edgesNmsMex.cpp ├── edgesNmsMex.mexa64 ├── edgesNmsMex.mexmaci64 ├── edgesNmsMex.mexw64 ├── spDetectMex.cpp ├── spDetectMex.mexa64 ├── spDetectMex.mexmaci64 ├── spDetectMex.mexw64 ├── ucm_mean_pb.mexa64 ├── ucm_mean_pb.mexmaci64 └── ucm_mean_pb.mexw64 ├── sedt_readme.txt ├── spAffinities.m ├── spDemo.m └── spDetect.m /.gitignore: -------------------------------------------------------------------------------- 1 | /BSR/ 2 | /models/ 3 | /sweeps* 4 | /boxes/ 5 | -------------------------------------------------------------------------------- /Contents.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/Contents.m -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is mostly Piotr Dollar's code for Edge Boxes object proposals from [Edge Boxes: Locating Object Proposals from Edges](https://github.com/pdollar/edges), downloaded in July 2015. 2 | I needed a way to call this stuff from Python; `edge_boxes.py` and `edge_boxes_wrapper.m` are the only new files you need to use the code. They have been adapted from 3 | Sergey Karayev's [Selective Search IJCV with Python](https://github.com/sergeyk/selective_search_ijcv_with_python) code, which wraps an alternative object-proposal generator. 4 | 5 | Make sure that the edges directory is in your PYTHONPATH and just do: 6 | 7 | import edges 8 | 9 | windows = edge_boxes.get_windows(image_filenames) 10 | 11 | To make sure this works, simply `python edge_boxes.py`. 12 | 13 | Finally, I needed the code to be able to use the proposals together with Ross Girshick's Fast R-CNN: [Fast Region-based Convolutional Networks for object detection](https://github.com/rbgirshick/fast-rcnn) . 14 | A demo file for this is also included (`demo_edgeboxes.py`). To try it out build Fast R-CNN and drop the file in its 'tools' subdirectory. 15 | 16 | The license is the same as for Piotr Dollar's Structured Edge Detection Toolbox V3.0 (see `license.txt`) and his original readme is in `sedt_readme.txt`. 17 | 18 | Enjoy! 19 | 20 | Dubravko Culibrk 21 | 22 Jul 2015 22 | 23 | P.S. Please note that the code uses Piotr's MATLAB toolbox (https://pdollar.github.io/toolbox/), which needs to be installed for any of this to work. (Thanks to Thomas Lau for pointing out that this should be stated in this README). 24 | -------------------------------------------------------------------------------- /boxesData.m: -------------------------------------------------------------------------------- 1 | function data = boxesData( varargin ) 2 | % Get ground truth data for object proposal bounding box evaluation. 3 | % 4 | % Used to get dataset information for evaluation of bounding box object 5 | % proposals using boxesEval.m. This requires separate download of the 6 | % appropriate dataset first. Currently only set up for PASCAL VOC 2007 but 7 | % it would be fairly simple to extend to other datasets. 8 | % 9 | % As a first step, it is necessary to download the PASCAL VOC 2007 dataset. 10 | % Go to http://pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/ and get: 11 | % VOCdevkit_08-Jun-2007, VOCtrainval_06-Nov-2007, VOCtest_06-Nov-2007 12 | % After downloading these files extract them to dataDir='boxes/VOCdevkit'. 13 | % Once complete this function will process the data and return information 14 | % in a convenient format for boxesEval.m. 15 | % 16 | % USAGE 17 | % data = boxesData( opts ) 18 | % 19 | % INPUTS 20 | % opts - parameters (struct or name/value pairs) 21 | % .resDir - ['boxes/'] location for results and evaluation 22 | % .dataDir - ['boxes/VOCdevkit/'] dir containing PASCAL VOC 2007 23 | % .split - ['val'] data split for evaluation 24 | % 25 | % OUTPUTS 26 | % data - dataset split information 27 | % .split - data split for evaluation 28 | % .n - number of images 29 | % .ids - list of string ids 30 | % .imgs - list of image filenames 31 | % .gt - ground truth for each image 32 | % 33 | % EXAMPLE 34 | % 35 | % See also edgeBoxesDemo, edgeBoxes, boxesEval 36 | % 37 | % Structured Edge Detection Toolbox Version 3.01 38 | % Code written by Piotr Dollar and Larry Zitnick, 2014. 39 | % Licensed under the MSR-LA Full Rights License [see license.txt] 40 | 41 | % get default parameters (unimportant parameters are undocumented) 42 | dfs={ 'resDir','boxes/', 'dataDir','boxes/VOCdevkit/', 'split','val' }; 43 | o=getPrmDflt(varargin,dfs,1); 44 | 45 | % locations of PASCAL VOC dataset 46 | if(~exist(o.dataDir,'dir')), error('dataset not found, see help'); end 47 | imDir=[o.dataDir 'VOC2007/JPEGImages/']; 48 | gtDir=[o.dataDir 'VOC2007/Annotations/']; 49 | imList=[o.dataDir 'VOC2007/ImageSets/Main/' o.split '.txt']; 50 | addpath(genpath([o.dataDir 'VOCcode'])); 51 | 52 | % check if data already exists, if yes load and return 53 | dataNm=[o.resDir '/GroundTruth' '-' o.split '.mat']; 54 | if(exist(dataNm,'file')), data=load(dataNm); data=data.data; return; end 55 | 56 | % get list of image ids 57 | if(~exist(imList,'file')), error('ids file not found'); end 58 | f=fopen(imList); ids=textscan(f,'%s %*s'); ids=ids{1}; fclose(f); 59 | 60 | % generate list of image and gt filenames then load gt 61 | n=length(ids); imgs=cell(n,1); gt=cell(n,1); 62 | for i=1:n, imgs{i}=[imDir ids{i} '.jpg']; end 63 | for i=1:n, gt{i}=[gtDir ids{i} '.xml']; end 64 | if(~exist(imgs{1},'file')), error('images not found'); end 65 | if(~exist(gt{1},'file')), error('annotations not found'); end 66 | parfor i=1:n, [~,gt{i}]=bbGt('bbLoad',gt{i},'format',1); end 67 | 68 | % create output structure and cache to disk 69 | data=struct('split',o.split,'n',n,'ids',{ids},'imgs',{imgs},'gt',{gt}); 70 | if(~exist(o.resDir,'dir')), mkdir(resDir); end; save(dataNm,'data'); 71 | 72 | end 73 | -------------------------------------------------------------------------------- /boxesEval.m: -------------------------------------------------------------------------------- 1 | function recall = boxesEval( varargin ) 2 | % Perform object proposal bounding box evaluation and plot results. 3 | % 4 | % boxesEval evaluates a set bounding box object proposals on the dataset 5 | % specified by the 'data' parameter (which is generated by boxesData.m). 6 | % The methods are specified by the vector 'names'. For each method the 7 | % boxes must be stored in the file [resDir '/' name '-' data.split]. Each 8 | % file should contain a single cell array 'bbs' of length n, with one set 9 | % of bbs per image, where each matrix row has the format [x y w h score]. 10 | % Here score is the confidence of detection but if the boxes are sorted the 11 | % score may be the same for every box. edgeBoxes.m stores results in this 12 | % format by default, other methods can easily be converted to this format. 13 | % 14 | % For every method, evaluation is performed at every threshold in 'thrs' 15 | % and every proposal count in 'cnts'. Two plots may be generated. If 16 | % |cnts|>1 creates a plot with count on x-axis (and plots a separate set of 17 | % curves for each threshold if necessary). If |thrs|>1 creates a plot with 18 | % thresholds on x-axis (and plots a separate set of curves for each count). 19 | % 20 | % USAGE 21 | % recall = boxesEval( opts ) 22 | % 23 | % INPUTS 24 | % opts - parameters (struct or name/value pairs) 25 | % .data - ['REQ'] data on which to evaluate (see boxesData.m) 26 | % .names - ['REQ'] string cell array of object proposal methods 27 | % .resDir - ['boxes/'] location for results and evaluation 28 | % .thrs - [.7] IoU threshold(s) to use for evaluation 29 | % .cnts - [...] propsal count(s) to use for evaluation 30 | % .maxn - [inf] maximum number of images to use for evaluation 31 | % .show - [1] figure for plotting results 32 | % .fName - [''] optional filename for saving plots/recall to disk 33 | % .col - [...] color(s) for plotting each method's results 34 | % 35 | % OUTPUTS 36 | % recall - [MxTxK] recall for each count/threshold/method 37 | % 38 | % EXAMPLE 39 | % 40 | % See also edgeBoxesDemo, edgeBoxes, boxesData, bbGt 41 | % 42 | % Structured Edge Detection Toolbox Version 3.01 43 | % Code written by Piotr Dollar and Larry Zitnick, 2014. 44 | % Licensed under the MSR-LA Full Rights License [see license.txt] 45 | 46 | cnts=[1 2 5 10 20 50 100 200 500 1000 2000 5000]; col=cell(100,1); 47 | for i=1:100, col{i}=max(.3,mod([.3 .47 .16]*(i+1),1)); end 48 | dfs={ 'data','REQ', 'names','REQ', 'resDir','boxes/', 'thrs',.7, ... 49 | 'cnts',cnts, 'maxn',inf, 'show',1, 'fName','', 'col',col }; 50 | o=getPrmDflt(varargin,dfs,1); if(~iscell(o.names)), o.names={o.names}; end 51 | recall=boxesEvalAll(o); if(o.show), plotResult(recall,o); end 52 | 53 | end 54 | 55 | function recall = boxesEvalAll( o ) 56 | % compute and gather all results (caches individual results to disk) 57 | M=length(o.cnts); T=length(o.thrs); K=length(o.names); 58 | gt=o.data.gt; n=min(o.maxn,o.data.n); gt=gt(1:n); 59 | recall=zeros(M,T,K); [ms,ts,ks]=ndgrid(1:M,1:T,1:K); 60 | parfor i=1:M*T*K, m=ms(i); t=ts(i); k=ks(i); 61 | % if evaluation result exists simply load it 62 | rdir=[o.resDir '/eval/' o.names{k} '/' o.data.split '/']; 63 | rnm=[rdir 'N' int2str2(n,5) '-W' int2str2(o.cnts(m),5) ... 64 | '-T' int2str2(round(o.thrs(t)*100),2) '.txt']; %#ok<*PFBNS> 65 | if(exist(rnm,'file')), recall(i)=load(rnm,'-ascii'); continue; end 66 | % perform evaluation if result does not exist 67 | bbs=load([o.resDir '/' o.names{k} '-' o.data.split]); bbs=bbs.bbs; 68 | bbs1=bbs(1:n); for j=1:n, bbs1{j}=bbs1{j}(1:min(end,o.cnts(m)),:); end 69 | [gt1,bbs1]=bbGt('evalRes',gt,bbs1,o.thrs(t)); 70 | [~,r]=bbGt('compRoc',gt1,bbs1,1); r=max(r); recall(i)=r; 71 | if(~exist(rdir,'dir')), mkdir(rdir); end; dlmwrite(rnm,r); 72 | end 73 | % display summary statistics 74 | [ts,ks]=ndgrid(1:T,1:K); ms=log(o.cnts); rt=.75; 75 | for i=1:T*K, t=ts(i); k=ks(i); r=recall(:,t,k)'; if(M==1), continue; end 76 | a=find(rt<=r); if(isempty(a)), m=inf; else a=a(1); b=a-1; 77 | m=round(exp((rt-r(b))/(r(a)-r(b))*(ms(a)-ms(b))+ms(b))); end 78 | auc=sum(diff(ms/ms(end)).*(r(1:end-1)+r(2:end))/2); 79 | fprintf('%15s T=%.2f A=%.2f M=%4i R=%.2f\n',... 80 | o.names{k},o.thrs(t),auc,m,max(r)); 81 | end 82 | % optionally save results to text file 83 | if(isempty(o.fName)), return; end 84 | d=[o.resDir '/plots/']; if(~exist(d,'dir')), mkdir(d); end 85 | dlmwrite([d o.fName '-' o.data.split '.txt'],squeeze(recall)); 86 | end 87 | 88 | function plotResult( recall, o ) 89 | % plot results 90 | [M,T,K]=size(recall); fSiz={'FontSize',12}; f=o.show; 91 | for type=1:2 92 | if(type==1), xs=o.cnts; else xs=o.thrs; end; 93 | if(length(xs)==1), continue; end; s=[T,M]; M=s(type); 94 | R=recall; if(type==2), R=permute(R,[2 1 3]); end 95 | figure(f); f=f+1; clf; hold on; hs=zeros(M,K); 96 | for i=1:M, for k=1:K, hs(i,k)=plot(xs,R(:,i,k),... 97 | 'Color',o.col{k},'LineWidth',3); end; end 98 | s={'# of proposals','IoU'}; xlabel(s{type},fSiz{:}); 99 | s={'log','linear'}; set(gca,'XScale',s{type}); 100 | ylabel('Detection Rate',fSiz{:}); set(gca,'YTick',0:.2:1); 101 | hold off; axis([min(xs) max(xs) 0 1]); grid on; set(gca,fSiz{:}); 102 | set(gca,'XMinorGrid','off','XMinorTic','off'); 103 | set(gca,'YMinorGrid','off','YMinorTic','off'); 104 | s={'nw','ne'}; legend(hs(1,:),o.names,'Location',s{type}); 105 | if(isempty(o.fName)), continue; end; s={'Cnt','IoU'}; 106 | savefig([o.resDir '/plots/' s{type} '-' o.data.split '-' o.fName],'png'); 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /demo_edgeboxes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # -------------------------------------------------------- 4 | # Fast R-CNN 5 | # Copyright (c) 2015 Microsoft 6 | # Licensed under The MIT License [see LICENSE for details] 7 | # Written by Ross Girshick 8 | # -------------------------------------------------------- 9 | 10 | """ 11 | Demo script showing detections in sample images. 12 | 13 | See README.md for installation instructions before running. 14 | """ 15 | 16 | import _init_paths 17 | from fast_rcnn.config import cfg 18 | from fast_rcnn.test import im_detect 19 | from utils.cython_nms import nms 20 | from utils.timer import Timer 21 | import matplotlib.pyplot as plt 22 | import numpy as np 23 | import scipy.io as sio 24 | import caffe, os, sys, cv2 25 | import argparse 26 | from edge_boxes import get_windows 27 | 28 | CLASSES = ('__background__', 29 | 'aeroplane', 'bicycle', 'bird', 'boat', 30 | 'bottle', 'bus', 'car', 'cat', 'chair', 31 | 'cow', 'diningtable', 'dog', 'horse', 32 | 'motorbike', 'person', 'pottedplant', 33 | 'sheep', 'sofa', 'train', 'tvmonitor') 34 | 35 | NETS = {'vgg16': ('VGG16', 36 | 'vgg16_fast_rcnn_iter_40000.caffemodel'), 37 | 'vgg_cnn_m_1024': ('VGG_CNN_M_1024', 38 | 'vgg_cnn_m_1024_fast_rcnn_iter_40000.caffemodel'), 39 | 'caffenet': ('CaffeNet', 40 | 'caffenet_fast_rcnn_iter_40000.caffemodel')} 41 | 42 | 43 | def vis_detections(im, class_name, dets, thresh=0.5): 44 | """Draw detected bounding boxes.""" 45 | inds = np.where(dets[:, -1] >= thresh)[0] 46 | if len(inds) == 0: 47 | return 48 | 49 | im = im[:, :, (2, 1, 0)] 50 | fig, ax = plt.subplots(figsize=(12, 12)) 51 | ax.imshow(im, aspect='equal') 52 | for i in inds: 53 | bbox = dets[i, :4] 54 | score = dets[i, -1] 55 | 56 | ax.add_patch( 57 | plt.Rectangle((bbox[0], bbox[1]), 58 | bbox[2] - bbox[0], 59 | bbox[3] - bbox[1], fill=False, 60 | edgecolor='red', linewidth=3.5) 61 | ) 62 | ax.text(bbox[0], bbox[1] - 2, 63 | '{:s} {:.3f}'.format(class_name, score), 64 | bbox=dict(facecolor='blue', alpha=0.5), 65 | fontsize=14, color='white') 66 | 67 | ax.set_title(('{} detections with ' 68 | 'p({} | box) >= {:.1f}').format(class_name, class_name, 69 | thresh), 70 | fontsize=14) 71 | plt.axis('off') 72 | plt.tight_layout() 73 | plt.draw() 74 | 75 | def demo(net, image_name, classes): 76 | """Detect object classes in an image using pre-computed object proposals.""" 77 | 78 | # Load the demo image 79 | im_file = os.path.join(cfg.ROOT_DIR, 'data', 'demo', image_name + '.jpg') 80 | im = cv2.imread(im_file) 81 | 82 | # Compute EdgeBoxes proposals 83 | image_filenames = [ im_file ] 84 | initial_boxes = get_windows(image_filenames); 85 | 86 | # Get just the boxes, without the EdgeBoxes confidence scores 87 | obj_proposals = (initial_boxes[0])[:, [0,1,2,3]] 88 | 89 | # Detect all object classes and regress object bounds 90 | timer = Timer() 91 | timer.tic() 92 | scores, boxes = im_detect(net, im, obj_proposals) 93 | timer.toc() 94 | print ('Detection took {:.3f}s for ' 95 | '{:d} object proposals').format(timer.total_time, boxes.shape[0]) 96 | 97 | # Visualize detections for each class 98 | CONF_THRESH = 0.8 99 | NMS_THRESH = 0.3 100 | for cls in classes: 101 | cls_ind = CLASSES.index(cls) 102 | cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)] 103 | cls_scores = scores[:, cls_ind] 104 | dets = np.hstack((cls_boxes, 105 | cls_scores[:, np.newaxis])).astype(np.float32) 106 | keep = nms(dets, NMS_THRESH) 107 | dets = dets[keep, :] 108 | print 'All {} detections with p({} | box) >= {:.1f}'.format(cls, cls, 109 | CONF_THRESH) 110 | vis_detections(im, cls, dets, thresh=CONF_THRESH) 111 | 112 | def parse_args(): 113 | """Parse input arguments.""" 114 | parser = argparse.ArgumentParser(description='Train a Fast R-CNN network') 115 | parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]', 116 | default=0, type=int) 117 | parser.add_argument('--cpu', dest='cpu_mode', 118 | help='Use CPU mode (overrides --gpu)', 119 | action='store_true') 120 | parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16]', 121 | choices=NETS.keys(), default='vgg16') 122 | 123 | args = parser.parse_args() 124 | 125 | return args 126 | 127 | if __name__ == '__main__': 128 | args = parse_args() 129 | 130 | prototxt = os.path.join(cfg.ROOT_DIR, 'models', NETS[args.demo_net][0], 131 | 'test.prototxt') 132 | caffemodel = os.path.join(cfg.ROOT_DIR, 'data', 'fast_rcnn_models', 133 | NETS[args.demo_net][1]) 134 | 135 | if not os.path.isfile(caffemodel): 136 | raise IOError(('{:s} not found.\nDid you run ./data/script/' 137 | 'fetch_fast_rcnn_models.sh?').format(caffemodel)) 138 | 139 | if args.cpu_mode: 140 | caffe.set_mode_cpu() 141 | else: 142 | caffe.set_mode_gpu() 143 | caffe.set_device(args.gpu_id) 144 | net = caffe.Net(prototxt, caffemodel, caffe.TEST) 145 | 146 | print '\n\nLoaded network {:s}'.format(caffemodel) 147 | 148 | print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' 149 | print 'Demo for data/demo/000004.jpg' 150 | demo(net, '000004', ('car',)) 151 | 152 | print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' 153 | print 'Demo for data/demo/001551.jpg' 154 | demo(net, '001551', ('sofa', 'tvmonitor')) 155 | 156 | plt.show() 157 | -------------------------------------------------------------------------------- /edgeBoxes.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/edgeBoxes.m -------------------------------------------------------------------------------- /edgeBoxesDemo.m: -------------------------------------------------------------------------------- 1 | % Demo for Edge Boxes (please see readme.txt first). 2 | 3 | %% load pre-trained edge detection model and set opts (see edgesDemo.m) 4 | model=load('models/forest/modelBsds'); model=model.model; 5 | model.opts.multiscale=0; model.opts.sharpen=2; model.opts.nThreads=4; 6 | 7 | %% set up opts for edgeBoxes (see edgeBoxes.m) 8 | opts = edgeBoxes; 9 | opts.alpha = .65; % step size of sliding window search 10 | opts.beta = .75; % nms threshold for object proposals 11 | opts.minScore = .01; % min score of boxes to detect 12 | opts.maxBoxes = 1e4; % max number of boxes to detect 13 | 14 | %% detect Edge Box bounding box proposals (see edgeBoxes.m) 15 | I = imread('peppers.png'); 16 | tic, bbs=edgeBoxes(I,model,opts); toc 17 | 18 | %% show evaluation results (using pre-defined or interactive boxes) 19 | gt=[122 248 92 65; 193 82 71 53; 410 237 101 81; 204 160 114 95; ... 20 | 9 185 86 90; 389 93 120 117; 253 103 107 57; 81 140 91 63]; 21 | if(0), gt='Please select an object box.'; disp(gt); figure(1); imshow(I); 22 | title(gt); [~,gt]=imRectRot('rotate',0); gt=gt.getPos(); end 23 | gt(:,5)=0; [gtRes,dtRes]=bbGt('evalRes',gt,double(bbs),.7); 24 | figure(1); bbGt('showRes',I,gtRes,dtRes(dtRes(:,6)==1,:)); 25 | title('green=matched gt red=missed gt dashed-green=matched detect'); 26 | 27 | %% run and evaluate on entire dataset (see boxesData.m and boxesEval.m) 28 | if(~exist('boxes/VOCdevkit/','dir')), return; end 29 | split='val'; data=boxesData('split',split); 30 | nm='EdgeBoxes70'; opts.name=['boxes/' nm '-' split '.mat']; 31 | edgeBoxes(data.imgs,model,opts); opts.name=[]; 32 | boxesEval('data',data,'names',nm,'thrs',.7,'show',2); 33 | boxesEval('data',data,'names',nm,'thrs',.5:.05:1,'cnts',1000,'show',3); 34 | -------------------------------------------------------------------------------- /edgeBoxesSweeps.m: -------------------------------------------------------------------------------- 1 | function edgeBoxesSweeps() 2 | % Parameter sweeps for Edges Boxes object proposals. 3 | % 4 | % Running the parameter sweeps requires altering internal flags. 5 | % The sweeps are not well documented, use at your own discretion. 6 | % 7 | % Structured Edge Detection Toolbox Version 3.01 8 | % Code written by Piotr Dollar and Larry Zitnick, 2014. 9 | % Licensed under the MSR-LA Full Rights License [see license.txt] 10 | 11 | % define parameter sweeps 12 | rt = [fileparts(mfilename('fullpath')) filesep]; 13 | expNms = {'alpha','beta','eta','minScore','edgeMinMag','edgeMergeThr',... 14 | 'clusterMinMag','maxAspectRatio','minBoxArea','gamma','kappa'}; 15 | expNms=expNms(1:end); opts=createExp(rt,expNms); maxn=inf; 16 | 17 | % run training and testing jobs 18 | jobs = createJobs(rt,opts,maxn); 19 | tic, for i=1:length(jobs), edgeBoxes(jobs{i}{:}); end; toc 20 | 21 | % plot all results 22 | for e=1:length(expNms) 23 | eval={'data',boxesData('split','val'),'names',{opts{e}.name},... 24 | 'resDir',[rt 'boxes/sweeps/'],'maxn',maxn,'fName',expNms{e}}; 25 | boxesEval(eval{:},'thrs',.7); 26 | ar=boxesEval(eval{:},'thrs',.5:.05:1,'cnts',1000); 27 | ar=squeeze(mean(ar,2)); [~,i]=max(ar); disp(opts{e}(i)) 28 | end 29 | 30 | end 31 | 32 | function jobs = createJobs( rt, opts, maxn ) 33 | % create jobs 34 | M='models/forest/modelBsds'; M=load(M); M=M.model; M.opts.nThreads=1; 35 | data=boxesData('split','val'); fs=data.imgs(1:min(end,maxn)); 36 | opts=[opts{:}]; N=length(opts); jobs=cell(1,N); D=zeros(1,N); 37 | for e=1:N, opts(e).name=[rt 'boxes/sweeps/' opts(e).name '-val.mat']; end 38 | for e=1:N, D(e)=exist(opts(e).name,'file')==2; jobs{e}={fs,M,opts(e)}; end 39 | [~,K]=unique({opts.name},'stable'); D=D(K); jobs=jobs(K); jobs=jobs(~D); 40 | fprintf('nJobs = %i\n',length(jobs)); 41 | end 42 | 43 | function opts = createExp( rt, expNm ) 44 | 45 | % if expNm is a cell, call recursively and return 46 | if( iscell(expNm) ) 47 | N=length(expNm); opts=cell(1,N); 48 | for e=1:N, opts{e}=createExp(rt,expNm{e}); end; return; 49 | end 50 | 51 | % setup opts 52 | opts=edgeBoxes(); opts.minScore=0; 53 | N=100; optsDefault=opts; opts=opts(ones(1,N)); 54 | switch expNm 55 | case 'alpha' 56 | vs=45:5:75; N=length(vs); 57 | for e=1:N, opts(e).alpha=vs(e)/100; end 58 | case 'beta' 59 | vs=60:5:90; N=length(vs); 60 | for e=1:N, opts(e).beta=vs(e)/100; end 61 | case 'eta' 62 | vs=0:5; N=length(vs); 63 | for e=1:N, opts(e).eta=1-vs(e)/10000; end 64 | case 'minScore' 65 | vs=[0 5 10 25 50 100]; N=length(vs); 66 | for e=1:N, opts(e).minScore=vs(e)/1000; end 67 | case 'edgeMinMag' 68 | vs=[0 50 100 200 400]; N=length(vs); 69 | for e=1:N, opts(e).edgeMinMag=vs(e)/1000; end 70 | case 'edgeMergeThr' 71 | vs=[25 50 100 200 400]; N=length(vs); 72 | for e=1:N, opts(e).edgeMergeThr=vs(e)/100; end 73 | case 'clusterMinMag' 74 | vs=[0 50 100 200 400]; N=length(vs); 75 | for e=1:N, opts(e).clusterMinMag=vs(e)/100; end 76 | case 'maxAspectRatio' 77 | vs=1:5; N=length(vs); 78 | for e=1:N, opts(e).maxAspectRatio=vs(e); end 79 | case 'minBoxArea' 80 | vs=[100 250 500 1000 2500 5000]; N=length(vs); 81 | for e=1:N, opts(e).minBoxArea=vs(e); end 82 | case 'gamma' 83 | vs=[25 50 100 200 400 10000]; N=length(vs); 84 | for e=1:N, opts(e).gamma=vs(e)/100; end 85 | case 'kappa' 86 | vs=50:25:200; N=length(vs); 87 | for e=1:N, opts(e).kappa=vs(e)/100; end 88 | otherwise, error('invalid exp: %s',expNm); 89 | end 90 | 91 | % produce final set of opts and find default opts 92 | O=1:N; opts=opts(O); d=0; 93 | for e=1:N, if(isequal(optsDefault,opts(e))), d=e; break; end; end 94 | if(d==0), disp(expNm); assert(false); end; opts(d).name='Default'; 95 | for e=1:N, if(e~=d), opts(e).name=[expNm int2str2(vs(e),5)]; end; end 96 | 97 | end 98 | -------------------------------------------------------------------------------- /edge_boxes.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | import subprocess 3 | import shlex 4 | import os 5 | import numpy as np 6 | import scipy.io 7 | 8 | script_dirname = os.path.abspath(os.path.dirname(__file__)) 9 | 10 | np.set_printoptions(threshold='nan') 11 | 12 | def get_windows(image_fnames, cmd='edge_boxes_wrapper'): 13 | """ 14 | Run MATLAB EdgeBoxes code on the given image filenames to 15 | generate window proposals. 16 | 17 | Parameters 18 | ---------- 19 | image_filenames: strings 20 | Paths to images to run on. 21 | cmd: string 22 | edge boxes function to call: 23 | - 'edge_boxes_wrapper' for effective detection proposals paper configuration. 24 | """ 25 | # Form the MATLAB script command that processes images and write to 26 | # temporary results file. 27 | f, output_filename = tempfile.mkstemp(suffix='.mat') 28 | os.close(f) 29 | fnames_cell = '{' + ','.join("'{}'".format(x) for x in image_fnames) + '}' 30 | command = "{}({}, '{}')".format(cmd, fnames_cell, output_filename) 31 | print(command) 32 | 33 | # Execute command in MATLAB. 34 | mc = "matlab -nojvm -r \"try; {}; catch; exit; end; exit\"".format(command) 35 | pid = subprocess.Popen( 36 | shlex.split(mc), stdout=open('/dev/null', 'w'), cwd=script_dirname) 37 | retcode = pid.wait() 38 | if retcode != 0: 39 | raise Exception("Matlab script did not exit successfully!") 40 | 41 | # Read the results and undo Matlab's 1-based indexing. 42 | all_boxes = list(scipy.io.loadmat(output_filename)['all_boxes'][0]) 43 | subtractor = np.array((1, 1, 0, 0, 0))[np.newaxis, :] 44 | all_boxes = [boxes - subtractor for boxes in all_boxes] 45 | 46 | # Remove temporary file, and return. 47 | os.remove(output_filename) 48 | if len(all_boxes) != len(image_fnames): 49 | raise Exception("Something went wrong computing the windows!") 50 | 51 | #print(all_boxes[0]) 52 | return all_boxes 53 | 54 | if __name__ == '__main__': 55 | """ 56 | Run a demo. 57 | """ 58 | import time 59 | 60 | image_filenames = [ 61 | #'peppers.png', 62 | script_dirname + '/000004.jpg', 63 | script_dirname + '/001551.jpg' 64 | #script_dirname + '/cat.jpg' 65 | ] 66 | t = time.time() 67 | boxes = get_windows(image_filenames) 68 | #print(boxes) 69 | print("EdgeBoxes processed {} images in {:.3f} s".format( 70 | len(image_filenames), time.time() - t)) 71 | -------------------------------------------------------------------------------- /edge_boxes_wrapper.m: -------------------------------------------------------------------------------- 1 | function all_boxes = edge_boxes_wrapper(image_filenames, output_filename) 2 | 3 | %% load pre-trained edge detection model and set opts (see edgesDemo.m) 4 | model=load('models/forest/modelBsds'); model=model.model; 5 | model.opts.multiscale=0; model.opts.sharpen=2; model.opts.nThreads=4; 6 | 7 | 8 | %% set up opts for edgeBoxes (see edgeBoxes.m) 9 | opts = edgeBoxes; 10 | opts.alpha = 0.6;%.65; % step size of sliding window search 11 | opts.beta = 0.7;%.75; % nms threshold for object proposals 12 | opts.minScore = .01; % min score of boxes to detect 13 | opts.maxBoxes = 1e4; % max number of boxes to detect 14 | 15 | %% process all images and detect Edge Box bounding box proposals (see edgeBoxes.m) 16 | all_boxes = {}; 17 | for i=1:length(image_filenames) 18 | im = imread(image_filenames{i}); 19 | tic, all_boxes{i} = edgeBoxes(im,model,opts); toc 20 | end 21 | 22 | %% convert the bounding boxes to the caffe input format (SelectiveSearch): 23 | % ['ymin', 'xmin', 'ymax', 'xmax'] 24 | for i=1:length(image_filenames) 25 | all_boxes{i} = [all_boxes{i}(:,2) all_boxes{i}(:,1) all_boxes{i}(:,2)+all_boxes{i}(:,4) all_boxes{i}(:,1)+all_boxes{i}(:,3) all_boxes{i}(:,5)]; 26 | end 27 | 28 | if nargin > 1 29 | all_boxes 30 | save(output_filename, 'all_boxes', '-v7'); 31 | end 32 | -------------------------------------------------------------------------------- /edgesChns.m: -------------------------------------------------------------------------------- 1 | function [chnsReg,chnsSim] = edgesChns( I, opts ) 2 | % Compute features for structured edge detection. 3 | % 4 | % For an introductory tutorial please see edgesDemo.m. 5 | % 6 | % USAGE 7 | % [chnsReg,chnsSim] = edgesChns( I, opts ) 8 | % 9 | % INPUTS 10 | % I - [h x w x 3] color input image 11 | % opts - structured edge model options 12 | % 13 | % OUTPUTS 14 | % chnsReg - [h x w x nChannel] regular output channels 15 | % chnsSim - [h x w x nChannel] self-similarity output channels 16 | % 17 | % EXAMPLE 18 | % 19 | % See also edgesDemo, edgesTrain, edgesDetect, gradientMag 20 | % 21 | % Structured Edge Detection Toolbox Version 3.01 22 | % Code written by Piotr Dollar, 2014. 23 | % Licensed under the MSR-LA Full Rights License [see license.txt] 24 | 25 | shrink=opts.shrink; nTypes=1; chns=cell(1,opts.nChns); k=0; 26 | if(size(I,3)>3), nTypes=2; Is={I(:,:,1:3),I(:,:,4:end)}; end 27 | for t=1:nTypes 28 | if(nTypes>1), I=Is{t}; end 29 | if(size(I,3)==1), cs='gray'; else cs='luv'; end; I=rgbConvert(I,cs); 30 | Ishrink=imResample(I,1/shrink); k=k+1; chns{k}=Ishrink; 31 | for i = 1:2, s=2^(i-1); 32 | if(s==shrink), I1=Ishrink; else I1=imResample(I,1/s); end 33 | I1 = convTri( I1, opts.grdSmooth ); 34 | [M,O] = gradientMag( I1, 0, opts.normRad, .01 ); 35 | H = gradientHist( M, O, max(1,shrink/s), opts.nOrients, 0 ); 36 | k=k+1; chns{k}=imResample(M,s/shrink); 37 | k=k+1; chns{k}=imResample(H,max(1,s/shrink)); 38 | end 39 | end 40 | chns=cat(3,chns{1:k}); assert(size(chns,3)==opts.nChns); 41 | chnSm=opts.chnSmooth/shrink; if(chnSm>1), chnSm=round(chnSm); end 42 | simSm=opts.simSmooth/shrink; if(simSm>1), simSm=round(simSm); end 43 | chnsReg=convTri(chns,chnSm); chnsSim=convTri(chns,simSm); 44 | 45 | end 46 | -------------------------------------------------------------------------------- /edgesDemo.m: -------------------------------------------------------------------------------- 1 | % Demo for Structured Edge Detector (please see readme.txt first). 2 | 3 | %% set opts for training (see edgesTrain.m) 4 | opts=edgesTrain(); % default options (good settings) 5 | opts.modelDir='models/'; % model will be in models/forest 6 | opts.modelFnm='modelBsds'; % model name 7 | opts.nPos=5e5; opts.nNeg=5e5; % decrease to speedup training 8 | opts.useParfor=0; % parallelize if sufficient memory 9 | 10 | %% train edge detector (~20m/8Gb per tree, proportional to nPos/nNeg) 11 | tic, model=edgesTrain(opts); toc; % will load model if already trained 12 | 13 | %% set detection parameters (can set after training) 14 | model.opts.multiscale=0; % for top accuracy set multiscale=1 15 | model.opts.sharpen=2; % for top speed set sharpen=0 16 | model.opts.nTreesEval=4; % for top speed set nTreesEval=1 17 | model.opts.nThreads=4; % max number threads for evaluation 18 | model.opts.nms=0; % set to true to enable nms 19 | 20 | %% evaluate edge detector on BSDS500 (see edgesEval.m) 21 | if(0), edgesEval( model, 'show',1, 'name','' ); end 22 | 23 | %% detect edge and visualize results 24 | I = imread('peppers.png'); 25 | tic, E=edgesDetect(I,model); toc 26 | figure(1); im(I); figure(2); im(1-E); 27 | -------------------------------------------------------------------------------- /edgesDemoRgbd.m: -------------------------------------------------------------------------------- 1 | % Demo for RGBD Structured Edge Detector (please see readme.txt first). 2 | 3 | %% set opts for training (see edgesTrain.m) 4 | opts=edgesTrain(); % default options (good settings) 5 | opts.modelDir='models/'; % model will be in models/forest 6 | opts.modelFnm='modelNyuRgbd'; % model name 7 | opts.nPos=5e5; opts.nNeg=5e5; % decrease to speedup training 8 | opts.useParfor=0; % parallelize if sufficient memory 9 | opts.fracFtrs=1/8; % sample fewer ftrs since using depth 10 | opts.bsdsDir='BSR/NYUD/data/'; % specify use of NYU data 11 | opts.rgbd=2; % specify use of rgb+d images 12 | 13 | %% train edge detector (~50m/8Gb per tree, proportional to nPos/nNeg) 14 | tic, model=edgesTrain(opts); toc; % will load model if already trained 15 | 16 | %% set detection parameters (can set after training) 17 | model.opts.multiscale=0; % for top accuracy set multiscale=1 18 | model.opts.sharpen=2; % for top speed set sharpen=0 19 | model.opts.nTreesEval=4; % for top speed set nTreesEval=1 20 | model.opts.nThreads=4; % max number threads for evaluation 21 | model.opts.nms=0; % set to true to enable nms 22 | 23 | %% evaluate edge detector on NYUD (see edgesEval.m) 24 | if(0), edgesEval( model, 'show',1, 'name','', 'maxDist',.011 ); end 25 | 26 | %% detect edge and visualize results 27 | iDir=[opts.bsdsDir 'images/val/']; dDir=[opts.bsdsDir 'depth/val/']; 28 | id=dir(fullfile(iDir,'*.png')); id={id.name}; id=id{1}; 29 | I=single(imread(fullfile(iDir,id)))/255; 30 | D=single(imread(fullfile(dDir,id)))/1e4; 31 | tic, E=edgesDetect(cat(3,I,D),model); toc 32 | figure(1); im(I); figure(2); im(1-E); 33 | -------------------------------------------------------------------------------- /edgesDetect.m: -------------------------------------------------------------------------------- 1 | function [E,O,inds,segs] = edgesDetect( I, model ) 2 | % Detect edges in image. 3 | % 4 | % For an introductory tutorial please see edgesDemo.m. 5 | % 6 | % The following model params may be altered prior to detecting edges: 7 | % prm = stride, sharpen, multiscale, nTreesEval, nThreads, nms 8 | % Simply alter model.opts.prm. For example, set model.opts.nms=1 to enable 9 | % non-maximum suppression. See edgesTrain for parameter details. 10 | % 11 | % USAGE 12 | % [E,O,inds,segs] = edgesDetect( I, model ) 13 | % 14 | % INPUTS 15 | % I - [h x w x 3] color input image 16 | % model - structured edge model trained with edgesTrain 17 | % 18 | % OUTPUTS 19 | % E - [h x w] edge probability map 20 | % O - [h x w] coarse edge normal orientation (0=left, pi/2=up) 21 | % inds - [h/s x w/s x nTreesEval] leaf node indices 22 | % segs - [g x g x h/s x w/s x nTreesEval] local segmentations 23 | % 24 | % EXAMPLE 25 | % 26 | % See also edgesDemo, edgesTrain, edgesChns 27 | % 28 | % Structured Edge Detection Toolbox Version 3.01 29 | % Code written by Piotr Dollar, 2014. 30 | % Licensed under the MSR-LA Full Rights License [see license.txt] 31 | 32 | % get parameters 33 | opts=model.opts; opts.nTreesEval=min(opts.nTreesEval,opts.nTrees); 34 | if(~isfield(opts,'sharpen')), opts.sharpen=0; end 35 | if(~isfield(model,'segs')), model.segs=[]; model.nSegs=[]; end 36 | opts.stride=max(opts.stride,opts.shrink); model.opts=opts; 37 | 38 | if( opts.multiscale ) 39 | % if multiscale run edgesDetect multiple times 40 | ss=2.^(-1:1); k=length(ss); inds=cell(1,k); segs=inds; 41 | siz=size(I); model.opts.multiscale=0; model.opts.nms=0; E=0; 42 | for i=1:k, s=ss(i); I1=imResample(I,s); 43 | if(nargout<4), [E1,~,inds{i}]=edgesDetect(I1,model); 44 | else [E1,~,inds{i},segs{i}]=edgesDetect(I1,model); end 45 | E=E+imResample(E1,siz(1:2)); 46 | end; E=E/k; model.opts=opts; 47 | 48 | else 49 | % pad image, making divisible by 4 50 | siz=size(I); r=opts.imWidth/2; p=[r r r r]; 51 | p([2 4])=p([2 4])+mod(4-mod(siz(1:2)+2*r,4),4); 52 | I = imPad(I,p,'symmetric'); 53 | 54 | % compute features and apply forest to image 55 | [chnsReg,chnsSim] = edgesChns( I, opts ); 56 | s=opts.sharpen; if(s), I=convTri(rgbConvert(I,'rgb'),1); end 57 | if(nargout<4), [E,inds] = edgesDetectMex(model,I,chnsReg,chnsSim); 58 | else [E,inds,segs] = edgesDetectMex(model,I,chnsReg,chnsSim); end 59 | 60 | % normalize and finalize edge maps 61 | t=opts.stride^2/opts.gtWidth^2/opts.nTreesEval; r=opts.gtWidth/2; 62 | if(s==0), t=t*2; elseif(s==1), t=t*1.8; else t=t*1.66; end 63 | E=E(1+r:siz(1)+r,1+r:siz(2)+r,:)*t; E=convTri(E,1); 64 | end 65 | 66 | % compute approximate orientation O from edges E 67 | if( opts.nms==-1 ), O=[]; elseif( nargout>1 || opts.nms ) 68 | [Ox,Oy]=gradient2(convTri(E,4)); 69 | [Oxx,~]=gradient2(Ox); [Oxy,Oyy]=gradient2(Oy); 70 | O=mod(atan(Oyy.*sign(-Oxy)./(Oxx+1e-5)),pi); 71 | end 72 | 73 | % perform nms 74 | if( opts.nms>0 ), E=edgesNmsMex(E,O,1,5,1.01,opts.nThreads); end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /edgesEval.m: -------------------------------------------------------------------------------- 1 | function varargout = edgesEval( model, varargin ) 2 | % Run and evaluate structured edge detector on BSDS500. 3 | % 4 | % This function first runs the trained structured edge detector on every 5 | % test or validation image in BSDS then call edgesEvalDir.m to perform the 6 | % actual edge evaluation. edgesEval is specific to the structured edge 7 | % detector and BSDS, edgesEvalDir is general purpose edge evaluation code. 8 | % For example usage of edgesEval see edgesDemo. 9 | % 10 | % USAGE 11 | % varargout = edgesEval( model, prms ) 12 | % 13 | % INPUTS 14 | % model - structured edge model trained with edgesTrain 15 | % prms - parameters (struct or name/value pairs) 16 | % .dataType - ['test'] should be either 'test' or 'val' 17 | % .name - [''] name to append to evaluation 18 | % .opts - {} list of model opts to overwrite 19 | % .show - [0] if true plot results using edgesEvalPlot 20 | % .pDistr - [{'type','parfor'}] parameters for fevalDistr 21 | % .cleanup - [0] if true delete temporary files 22 | % .thrs - [99] number or vector of thresholds for evaluation 23 | % .maxDist - [.0075] maximum tolerance for edge match 24 | % 25 | % OUTPUTS 26 | % varargout - same outputs as edgesEvalDir 27 | % 28 | % EXAMPLE 29 | % 30 | % See also edgesDemo, edgesDetect, edgesEvalDir, edgesEvalPlot 31 | % 32 | % Structured Edge Detection Toolbox Version 3.01 33 | % Code written by Piotr Dollar, 2014. 34 | % Licensed under the MSR-LA Full Rights License [see license.txt] 35 | 36 | % get default parameters 37 | dfs={'dataType','test', 'name','', 'opts',{}, 'show',0, ... 38 | 'pDistr',{{'type','parfor'}}, 'cleanup',0, 'thrs',99, 'maxDist',.0075 }; 39 | p=getPrmDflt(varargin,dfs,1); 40 | 41 | % load model and update model.opts acoording to opts 42 | if( ischar(model) ), model=load(model); model=model.model; end 43 | for i=1:length(p.opts)/2, model.opts.(p.opts{i*2-1})=p.opts{i*2}; end 44 | rgbd=model.opts.rgbd; model.opts.nms=1; 45 | 46 | % get list of relevant directories (image, depth, gt, results) 47 | name = [model.opts.modelFnm,p.name]; 48 | imgDir = fullfile(model.opts.bsdsDir,'images',p.dataType); 49 | depDir = fullfile(model.opts.bsdsDir,'depth',p.dataType); 50 | gtDir = fullfile(model.opts.bsdsDir,'groundTruth',p.dataType); 51 | resDir = fullfile(model.opts.modelDir,p.dataType,name); 52 | assert(exist(imgDir,'dir')==7); assert(exist(gtDir,'dir')==7); 53 | 54 | % run edgesDetect() on every image in imgDir and store in resDir 55 | if( ~exist(fullfile([resDir '-eval'],'eval_bdry.txt'),'file') ) 56 | if(~exist(resDir,'dir')), mkdir(resDir); end 57 | ids=dir(imgDir); ids=ids([ids.bytes]>0); ids={ids.name}; n=length(ids); 58 | ext=ids{1}(end-2:end); for i=1:n, ids{i}=ids{i}(1:end-4); end 59 | res=cell(1,n); for i=1:n, res{i}=fullfile(resDir,[ids{i} '.png']); end 60 | do=false(1,n); for i=1:n, do(i)=~exist(res{i},'file'); end 61 | ids=ids(do); res=res(do); m=length(ids); 62 | parfor i=1:m, id=ids{i}; 63 | I = imread(fullfile(imgDir,[id '.' ext])); D=[]; 64 | if(rgbd), D=single(imread(fullfile(depDir,[id '.png'])))/1e4; end 65 | if(rgbd==1), I=D; elseif(rgbd==2), I=cat(3,single(I)/255,D); end 66 | E=edgesDetect(I,model); imwrite(uint8(E*255),res{i}); 67 | end 68 | end 69 | 70 | % perform actual evaluation using edgesEvalDir 71 | varargout=cell(1,max(1,nargout)); 72 | [varargout{:}] = edgesEvalDir('resDir',resDir,'gtDir',gtDir,... 73 | 'pDistr',p.pDistr,'cleanup',p.cleanup,'thrs',p.thrs,'maxDist',p.maxDist); 74 | if( p.show ), figure(p.show); edgesEvalPlot(resDir,name); end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /edgesEvalDir.m: -------------------------------------------------------------------------------- 1 | function varargout = edgesEvalDir( varargin ) 2 | % Calculate edge precision/recall results for directory of edge images. 3 | % 4 | % Enhanced replacement for boundaryBench() from BSDS500 code: 5 | % http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/ 6 | % Uses same format for results and is fully compatible with boundaryBench. 7 | % Given default prms results are *identical* to boundaryBench with the 8 | % additional 9th output of R50 (recall at 50% precision). 9 | % 10 | % The odsF/P/R/T are results at the ODS (optimal dataset scale). 11 | % The oisF/P/R are results at the OIS (optimal image scale). 12 | % Naming convention: P=precision, R=recall, F=2/(1/P+1/R), T=threshold. 13 | % 14 | % In addition to the outputs, edgesEvalDir() creates three files: 15 | % eval_bdry_img.txt - per image OIS results [imgId T R P F] 16 | % eval_bdry_thr.txt - per threshold ODS results [T R P F] 17 | % eval_bdry.txt - complete results (*re-ordered* copy of output) 18 | % These files are identical to the ones created by boundaryBench. 19 | % 20 | % USAGE 21 | % [odsF,odsP,odsR,odsT,oisF,oisP,oisR,AP,R50] = edgesEvalDir( prms ) 22 | % [ODS,~,~,~,OIS,~,~,AP,R50] = edgesEvalDir( prms ) 23 | % 24 | % INPUTS 25 | % prms - parameters (struct or name/value pairs) 26 | % .resDir - ['REQ'] dir containing edge detection results (.png) 27 | % .gtDir - ['REQ'] dir containing ground truth (.mat) 28 | % .pDistr - [{'type','parfor'}] parameters for fevalDistr 29 | % .cleanup - [0] if true delete temporary files 30 | % .thrs - [99] number or vector of thresholds for evaluation 31 | % .maxDist - [.0075] maximum tolerance for edge match 32 | % .thin - [1] if true thin boundary maps 33 | % 34 | % OUTPUTS 35 | % odsF/P/R/T - F-measure, precision, recall and threshold at ODS 36 | % oisF/P/R - F-measure, precision, and recall at OIS 37 | % AP - average precision 38 | % R50 - recall at 50% precision 39 | % 40 | % EXAMPLE 41 | % 42 | % See also edgesEvalImg, edgesEvalPlot 43 | % 44 | % Structured Edge Detection Toolbox Version 3.01 45 | % Code written by Piotr Dollar, 2014. 46 | % Licensed under the MSR-LA Full Rights License [see license.txt] 47 | 48 | % get additional parameters 49 | dfs={ 'resDir','REQ', 'gtDir','REQ', 'pDistr',{{'type','parfor'}}, ... 50 | 'cleanup',0, 'thrs',99, 'maxDist',.0075, 'thin',1 }; 51 | p=getPrmDflt(varargin,dfs,1); resDir=p.resDir; gtDir=p.gtDir; 52 | evalDir=[resDir '-eval/']; if(~exist(evalDir,'dir')), mkdir(evalDir); end 53 | 54 | % check if results already exist, if so load and return 55 | fNm = fullfile(evalDir,'eval_bdry.txt'); 56 | if(exist(fNm,'file')), R=dlmread(fNm); R=mat2cell2(R,[1 8]); 57 | varargout=R([4 3 2 1 7 6 5 8]); if(nargout<=8), return; end; 58 | R=dlmread(fullfile(evalDir,'eval_bdry_thr.txt')); P=R(:,3); R=R(:,2); 59 | [~,o]=unique(P); R50=interp1(P(o),R(o),max(P(o(1)),.5)); 60 | varargout=[varargout R50]; return; 61 | end 62 | 63 | % perform evaluation on each image (this part can be very slow) 64 | assert(exist(resDir,'dir')==7); assert(exist(gtDir,'dir')==7); 65 | ids=dir(fullfile(gtDir,'*.mat')); ids={ids.name}; n=length(ids); 66 | do=false(1,n); jobs=cell(1,n); res=cell(1,n); 67 | for i=1:n, id=ids{i}(1:end-4); 68 | res{i}=fullfile(evalDir,[id '_ev1.txt']); do(i)=~exist(res{i},'file'); 69 | im1=fullfile(resDir,[id '.png']); gt1=fullfile(gtDir,[id '.mat']); 70 | jobs{i}={im1,gt1,'out',res{i},'thrs',p.thrs,'maxDist',p.maxDist,... 71 | 'thin',p.thin}; if(0), edgesEvalImg(jobs{i}{:}); end 72 | end 73 | fevalDistr('edgesEvalImg',jobs(do),p.pDistr{:}); 74 | 75 | % collect evaluation results 76 | I=dlmread(res{1}); T=I(:,1); 77 | Z=zeros(numel(T),1); cntR=Z; sumR=Z; cntP=Z; sumP=Z; 78 | oisCntR=0; oisSumR=0; oisCntP=0; oisSumP=0; scores=zeros(n,5); 79 | for i=1:n 80 | % load image results and accumulate 81 | I = dlmread(res{i}); 82 | cntR1=I(:,2); cntR=cntR+cntR1; sumR1=I(:,3); sumR=sumR+sumR1; 83 | cntP1=I(:,4); cntP=cntP+cntP1; sumP1=I(:,5); sumP=sumP+sumP1; 84 | % compute OIS scores for image 85 | [R,P,F] = computeRPF(cntR1,sumR1,cntP1,sumP1); [~,k]=max(F); 86 | [oisR1,oisP1,oisF1,oisT1] = findBestRPF(T,R,P); 87 | scores(i,:) = [i oisT1 oisR1 oisP1 oisF1]; 88 | % oisCnt/Sum will be used to compute dataset OIS scores 89 | oisCntR=oisCntR+cntR1(k); oisSumR=oisSumR+sumR1(k); 90 | oisCntP=oisCntP+cntP1(k); oisSumP=oisSumP+sumP1(k); 91 | end 92 | 93 | % compute ODS R/P/F and OIS R/P/F 94 | [R,P,F] = computeRPF(cntR,sumR,cntP,sumP); 95 | [odsR,odsP,odsF,odsT] = findBestRPF(T,R,P); 96 | [oisR,oisP,oisF] = computeRPF(oisCntR,oisSumR,oisCntP,oisSumP); 97 | 98 | % compute AP/R50 (interpolating 100 values, has minor bug: should be /101) 99 | if(0), R=[0; R; 1]; P=[1; P; 0]; F=[0; F; 0]; T=[1; T; 0]; end 100 | [~,k]=unique(R); k=k(end:-1:1); R=R(k); P=P(k); T=T(k); F=F(k); AP=0; 101 | if(numel(R)>1), AP=interp1(R,P,0:.01:1); AP=sum(AP(~isnan(AP)))/100; end 102 | [~,o]=unique(P); R50=interp1(P(o),R(o),max(P(o(1)),.5)); 103 | 104 | % write results to disk 105 | varargout = {odsF,odsP,odsR,odsT,oisF,oisP,oisR,AP,R50}; 106 | writeRes(evalDir,'eval_bdry_img.txt',scores); 107 | writeRes(evalDir,'eval_bdry_thr.txt',[T R P F]); 108 | writeRes(evalDir,'eval_bdry.txt',[varargout{[4 3 2 1 7 6 5 8]}]); 109 | 110 | % optionally perform cleanup 111 | if( p.cleanup ), delete([evalDir '/*_ev1.txt']); 112 | delete([resDir '/*.png']); rmdir(resDir); end 113 | 114 | end 115 | 116 | function [R,P,F] = computeRPF(cntR,sumR,cntP,sumP) 117 | % compute precision, recall and F measure given cnts and sums 118 | R=cntR./max(eps,sumR); P=cntP./max(eps,sumP); F=2*P.*R./max(eps,P+R); 119 | end 120 | 121 | function [bstR,bstP,bstF,bstT] = findBestRPF(T,R,P) 122 | % linearly interpolate to find best thr for optimizing F 123 | if(numel(T)==1), bstT=T; bstR=R; bstP=P; 124 | bstF=2*P.*R./max(eps,P+R); return; end 125 | A=linspace(0,1,100); B=1-A; bstF=-1; 126 | for j = 2:numel(T) 127 | Rj=R(j).*A+R(j-1).*B; Pj=P(j).*A+P(j-1).*B; Tj=T(j).*A+T(j-1).*B; 128 | Fj=2.*Pj.*Rj./max(eps,Pj+Rj); [f,k]=max(Fj); 129 | if(f>bstF), bstT=Tj(k); bstR=Rj(k); bstP=Pj(k); bstF=f; end 130 | end 131 | end 132 | 133 | function writeRes( alg, fNm, vals ) 134 | % write results to disk 135 | k=size(vals,2); fNm=fullfile(alg,fNm); fid=fopen(fNm,'w'); 136 | if(fid==-1), error('Could not open file %s for writing.',fNm); end 137 | frmt=repmat('%10g ',[1 k]); frmt=[frmt(1:end-1) '\n']; 138 | fprintf(fid,frmt,vals'); fclose(fid); 139 | end 140 | -------------------------------------------------------------------------------- /edgesEvalImg.m: -------------------------------------------------------------------------------- 1 | function [thrs,cntR,sumR,cntP,sumP,V] = edgesEvalImg( E, G, varargin ) 2 | % Calculate edge precision/recall results for single edge image. 3 | % 4 | % Enhanced replacement for evaluation_bdry_image() from BSDS500 code: 5 | % http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/ 6 | % Uses same format and is fully compatible with evaluation_bdry_image. 7 | % Given default prms results are *identical* to evaluation_bdry_image. 8 | % 9 | % In addition to performing the evaluation, this function can optionally 10 | % create a visualization of the matches and errors for a given edge result. 11 | % The visualization of edge matches V has the following color coding: 12 | % green=true positive, blue=false positive, red=false negative 13 | % If multiple ground truth labels are given the false negatives have 14 | % varying strength (and true positives can match *any* ground truth). 15 | % 16 | % This function calls the mex file correspondPixels. Pre-compiled binaries 17 | % for some systems are provided in /private, source for correspondPixels is 18 | % available as part of the BSDS500 dataset (see link above). Note: 19 | % correspondPixels is computationally expensive and very slow in practice. 20 | % 21 | % USAGE 22 | % [thrs,cntR,sumR,cntP,sumP,V] = edgesEvalImg( E, G, [prms] ) 23 | % 24 | % INPUTS 25 | % E - [h x w] edge probability map (may be a filename) 26 | % G - file containing a cell of ground truth boundaries 27 | % prms - parameters (struct or name/value pairs) 28 | % .out - [''] optional output file for writing results 29 | % .thrs - [99] number or vector of thresholds for evaluation 30 | % .maxDist - [.0075] maximum tolerance for edge match 31 | % .thin - [1] if true thin boundary maps 32 | % 33 | % OUTPUTS 34 | % thrs - [Kx1] vector of threshold values 35 | % cntR,sumR - [Kx1] ratios give recall per threshold 36 | % cntP,sumP - [Kx1] ratios give precision per threshold 37 | % V - [hxwx3xK] visualization of edge matches 38 | % 39 | % EXAMPLE 40 | % 41 | % See also edgesEvalDir 42 | % 43 | % Structured Edge Detection Toolbox Version 3.01 44 | % Code written by Piotr Dollar, 2014. 45 | % Licensed under the MSR-LA Full Rights License [see license.txt] 46 | 47 | % get additional parameters 48 | dfs={ 'out','', 'thrs',99, 'maxDist',.0075, 'thin',1 }; 49 | [out,thrs,maxDist,thin] = getPrmDflt(varargin,dfs,1); 50 | if(any(mod(thrs,1)>0)), K=length(thrs); thrs=thrs(:); else 51 | K=thrs; thrs=linspace(1/(K+1),1-1/(K+1),K)'; end 52 | 53 | % load edges (E) and ground truth (G) 54 | if(all(ischar(E))), E=double(imread(E))/255; end 55 | G=load(G); G=G.groundTruth; n=length(G); 56 | for g=1:n, G{g}=double(G{g}.Boundaries); end 57 | 58 | % evaluate edge result at each threshold 59 | Z=zeros(K,1); cntR=Z; sumR=Z; cntP=Z; sumP=Z; 60 | if(nargout>=6), V=zeros([size(E) 3 K]); end 61 | for k = 1:K 62 | % threshhold and thin E 63 | E1 = double(E>=max(eps,thrs(k))); 64 | if(thin), E1=double(bwmorph(E1,'thin',inf)); end 65 | % compare to each ground truth in turn and accumualte 66 | Z=zeros(size(E)); matchE=Z; matchG=Z; allG=Z; 67 | for g = 1:n 68 | [matchE1,matchG1] = correspondPixels(E1,G{g},maxDist); 69 | matchE = matchE | matchE1>0; 70 | matchG = matchG + double(matchG1>0); 71 | allG = allG + G{g}; 72 | end 73 | % compute recall (summed over each gt image) 74 | cntR(k) = sum(matchG(:)); sumR(k) = sum(allG(:)); 75 | % compute precision (edges can match any gt image) 76 | cntP(k) = nnz(matchE); sumP(k) = nnz(E1); 77 | % optinally create visualization of matches 78 | if(nargout<6), continue; end; cs=[1 0 0; 0 .7 0; .7 .8 1]; cs=cs-1; 79 | FP=E1-matchE; TP=matchE; FN=(allG-matchG)/n; 80 | for g=1:3, V(:,:,g,k)=max(0,1+FN*cs(1,g)+TP*cs(2,g)+FP*cs(3,g)); end 81 | V(:,2:end,:,k) = min(V(:,2:end,:,k),V(:,1:end-1,:,k)); 82 | V(2:end,:,:,k) = min(V(2:end,:,:,k),V(1:end-1,:,:,k)); 83 | end 84 | 85 | % if output file specified write results to disk 86 | if(isempty(out)), return; end; fid=fopen(out,'w'); assert(fid~=1); 87 | fprintf(fid,'%10g %10g %10g %10g %10g\n',[thrs cntR sumR cntP sumP]'); 88 | fclose(fid); 89 | 90 | end 91 | -------------------------------------------------------------------------------- /edgesEvalPlot.m: -------------------------------------------------------------------------------- 1 | function edgesEvalPlot( algs, nms, cols ) 2 | % Plot edge precision/recall results for directory of edge images. 3 | % 4 | % Enhanced replacement for plot_eval() from BSDS500 code: 5 | % http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/ 6 | % Uses same format and is fully compatible with plot_eval. Use this 7 | % function to plot the edge results created using edgesEvalDir. 8 | % 9 | % USAGE 10 | % edgesEvalPlot( algs, [nms], [cols] ) 11 | % 12 | % INPUTS 13 | % algs - {nx1} algorithm result directories 14 | % nms - [{nx1}] algorithm names (for legend) 15 | % cols - [{nx1}] algorithm colors 16 | % 17 | % OUTPUTS 18 | % 19 | % EXAMPLE 20 | % 21 | % See also edgesEvalDir 22 | % 23 | % Structured Edge Detection Toolbox Version 3.01 24 | % Code written by Piotr Dollar, 2014. 25 | % Licensed under the MSR-LA Full Rights License [see license.txt] 26 | 27 | % parse inputs 28 | if(nargin<2||isempty(nms)), nms={}; end; if(~iscell(nms)), nms={nms}; end 29 | if(nargin<3||isempty(cols)), cols=repmat({'r','g','b','k','m'},1,100); end 30 | if(~iscell(algs)), algs={algs}; end; if(~iscell(cols)), cols={cols}; end 31 | 32 | % setup basic plot (isometric contour lines and human performance) 33 | clf; box on; grid on; hold on; 34 | line([0 1],[.5 .5],'LineWidth',2,'Color',.7*[1 1 1]); 35 | for f=0.1:0.1:0.9, r=f:0.01:1; p=f.*r./(2.*r-f); %f=2./(1./p+1./r) 36 | plot(r,p,'Color',[0 1 0]); plot(p,r,'Color',[0 1 0]); end 37 | if(1), h=plot(0.7235,0.9014,'o','MarkerSize',8,'Color',[0 .5 0],... 38 | 'MarkerFaceColor',[0 .5 0],'MarkerEdgeColor',[0 .5 0]); end 39 | set(gca,'XTick',0:0.1:1,'YTick',0:0.1:1); 40 | grid on; xlabel('Recall'); ylabel('Precision'); 41 | axis equal; axis([0 1 0 1]); 42 | 43 | % load results for every algorithm (pr=[T,R,P,F]) 44 | n=length(algs); hs=zeros(1,n); res=zeros(n,9); prs=cell(1,n); 45 | for i=1:n, a=[algs{i} '-eval']; 46 | pr=dlmread(fullfile(a,'eval_bdry_thr.txt')); pr=pr(pr(:,2)>=1e-3,:); 47 | [~,o]=unique(pr(:,3)); R50=interp1(pr(o,3),pr(o,2),max(pr(o(1),3),.5)); 48 | res(i,1:8)=dlmread(fullfile(a,'eval_bdry.txt')); res(i,9)=R50; prs{i}=pr; 49 | end; 50 | 51 | % sort algorithms by ODS score 52 | [~,o]=sort(res(:,4),'descend'); res=res(o,:); prs=prs(o); 53 | cols=cols(o); if(~isempty(nms)), nms=nms(o); end 54 | 55 | % plot results for every algorithm (plot best last) 56 | for i=n:-1:1 57 | hs(i)=plot(prs{i}(:,2),prs{i}(:,3),'-','LineWidth',3,'Color',cols{i}); 58 | fprintf('ODS=%.3f OIS=%.3f AP=%.3f R50=%.3f',res(i,[4 7:9])); 59 | if(~isempty(nms)), fprintf(' - %s',nms{i}); end; fprintf('\n'); 60 | end 61 | 62 | % show legend if nms provided (report best first) 63 | hold off; if(isempty(nms)), return; end 64 | for i=1:n, nms{i}=sprintf('[F=.%i] %s',round(res(i,4)*100),nms{i}); end 65 | if(1), hs=[h hs]; nms=['[F=.80] Human'; nms(:)]; end 66 | legend(hs,nms,'Location','sw'); 67 | 68 | end 69 | -------------------------------------------------------------------------------- /edgesSweeps.m: -------------------------------------------------------------------------------- 1 | function edgesSweeps() 2 | % Parameter sweeps for structured edge detector. 3 | % 4 | % Running the parameter sweeps requires altering internal flags. 5 | % The sweeps are not well documented, use at your own discretion. 6 | % 7 | % Structured Edge Detection Toolbox Version 3.01 8 | % Code written by Piotr Dollar, 2014. 9 | % Licensed under the MSR-LA Full Rights License [see license.txt] 10 | 11 | % select type and location of cluster (see fevalDistr.m) 12 | rtDir = 'D:\code\research\edges\'; 13 | pDistrTrn={'type','local'}; pDistrTst=pDistrTrn; 14 | 15 | % define parameter sweeps 16 | expNms= {'MD-imWidth','MD-gtWidth','TR-nData','TR-nPos','TR-nImgs',... 17 | 'TR-sharpen','TR-nTrees','TR-split','TR-minChild','TR-maxDepth',... 18 | 'TR-fracFtrs','TR-nSamples','TR-nClasses','TR-discretize',... 19 | 'FT-nCells','FT-nOrients','FT-normRad','FT-shrink',... 20 | 'FT-grdSmooth','FT-chnSmooth','FT-simSmooth','final'}; 21 | expNms=expNms(1:end); T=5; full=3; dataset='BSDS'; 22 | [opts,lgd,lbl]=createExp(rtDir,expNms,dataset,full); 23 | 24 | % run training and testing jobs 25 | [jobsTrn,jobsTst] = createJobs(rtDir,opts,dataset,T); N=length(expNms); 26 | fprintf('nTrain = %i; nTest = %i\n',length(jobsTrn),length(jobsTst)); 27 | tic, s=fevalDistr('edgesTrain',jobsTrn,pDistrTrn); assert(s==1); toc 28 | tic, s=fevalDistr('edgesEval',jobsTst,pDistrTst); assert(s==1); toc 29 | 30 | % plot results 31 | for e=1:N, plotExps(expNms{e},opts{e},lgd{e},lbl{e},T,1); end 32 | 33 | end 34 | 35 | function plotExps( expNm, opts, lgd, lbl, T, type ) 36 | % get all results and display error 37 | disp([expNm ' [' lbl ']']); N=length(lgd); 38 | res=zeros(N,T); mNms=cell(1,N); 39 | for e=1:N, mNms{e}=[opts(e).modelDir 'val/' opts(e).modelFnm]; end 40 | for e=1:N, for t=1:T, r=dlmread([mNms{e} 'T' int2str2(t,2) ... 41 | '-eval/eval_bdry.txt']); r=r([4 7 8]); res(e,t)=r(type); end; end 42 | stds=std(res,0,2)*100; R=mean(res,2)*100; msg=' %.2f +/- %.2f [%s]\n'; 43 | for e=1:N, fprintf(msg,R(e),stds(e),lgd{e}); end 44 | if(0), disp(res); disp(max(res,[],2)); end 45 | types={'ODS','OIS','AP'}; type=types{type}; 46 | % plot sweeps (two cases for format of x labels) 47 | figPrp = {'Units','Pixels','Position',[800 600 640 220]}; 48 | figure(1); clf; set(1,figPrp{:}); set(gca,'FontSize',24); clr=[0 .69 .94]; 49 | pPl1={'LineWidth',3,'MarkerSize',15,'Color',clr,'MarkerFaceColor',clr}; 50 | pPl2=pPl1; clr=[1 .75 0]; pPl2{6}=clr; pPl2{8}=clr; d=0; 51 | for e=1:N, if(lgd{e}(end)=='*'), d=e; end; end; if(d), lgd{d}(end)=[]; end 52 | plot(R,'-d',pPl1{:}); hold on; if(d),plot(d,R(d),'d',pPl2{:}); end; e=.001; 53 | ylabel([type ' \times 100']); axis([.5 N+.5 min([R; 66])-e max([R; 72])+e]) 54 | if(isempty(lbl)), imLabel(lgd,'bottom',30,{'FontSize',24}); lgd=[]; end 55 | if(0); xlabel(lbl); end; set(gca,'XTick',1:1:N,'XTickLabel',lgd(1:1:N)); 56 | % save plot 57 | plDir=[opts(1).modelDir 'plots' type '/']; fFig=[plDir expNm]; 58 | if(~exist(plDir,'dir')), mkdir(plDir); end %#ok<*CTCH> 59 | for t=1:25, try savefig(fFig,1,'png'); break; catch, pause(1), end; end 60 | end 61 | 62 | function [jobsTrn,jobsTst] = createJobs( rtDir, opts, dataset, T ) 63 | % Prepare all jobs (one train and one test job per set of opts). 64 | opts=[opts{:}]; N=length(opts); NT=N*T; 65 | opts=repmat(opts,1,T); nms=cell(1,NT); 66 | jobsTrn=cell(1,NT); doneTrn=zeros(1,NT); 67 | jobsTst=cell(1,NT); doneTst=zeros(1,NT); 68 | thrs = logspace(-2,log10(.99),25)'; thrs(1)=1e-5; 69 | if( strcmpi(dataset,'bsds') ) 70 | pTest={'dataType','val', 'thrs',thrs, 'cleanup',1,... 71 | 'opts',{'modelDir',[rtDir '/sweepsBSDS/'],... 72 | 'bsdsDir',[rtDir '/BSR/BSDS500/data/']} }; 73 | else 74 | pTest={'dataType','val', 'thrs',thrs, 'cleanup',1, 'maxDist',.011,... 75 | 'opts',{'modelDir',[rtDir '/sweepsNYUD/'],... 76 | 'bsdsDir',[rtDir '/BSR/NYUD/data/']} }; 77 | end 78 | for e=1:NT 79 | t=ceil(e/N); opts(e).seed=(t-1)*100000+1; 80 | nm=[opts(e).modelFnm 'T' int2str2(t,2)]; opts(e).modelFnm=nm; 81 | mFnm=[opts(e).modelDir 'forest/' nm '.mat']; nms{e}=nm; 82 | eFnm=[opts(e).modelDir 'val/' nm '-eval/eval_bdry.txt']; 83 | doneTrn(e)=exist(mFnm,'file')==2; jobsTrn{e}={opts(e)}; 84 | doneTst(e)=exist(eFnm,'file')==2; jobsTst{e}=[mFnm pTest]; 85 | end 86 | [~,kp]=unique(nms,'stable'); 87 | doneTrn=doneTrn(kp); jobsTrn=jobsTrn(kp); jobsTrn=jobsTrn(~doneTrn); 88 | doneTst=doneTst(kp); jobsTst=jobsTst(kp); jobsTst=jobsTst(~doneTst); 89 | end 90 | 91 | function [opts,lgd,lbl] = createExp( rtDir, expNm, dataset, full ) 92 | 93 | % if expNm is a cell, call recursively and return 94 | if( iscell(expNm) ) 95 | N=length(expNm); opts=cell(1,N); lgd=opts; lbl=opts; 96 | for e=1:N, [opts{e},lgd{e},lbl{e}]=... 97 | createExp(rtDir,expNm{e},dataset,full); end; return; 98 | end 99 | 100 | % default params for edgesTrain.m 101 | opts=edgesTrain(); opts.nThreads=1; nData=2e5; nPos=.5; 102 | opts.nPos=round(nData*nPos); opts.nNeg=round(nData*(1-nPos)); 103 | assert(any(strcmpi(dataset,{'bsds','nyud'}))); 104 | if( strcmpi(dataset,'bsds') ) 105 | opts.modelDir=[rtDir '/sweepsBSDS/']; opts.nImgs=200; 106 | opts.bsdsDir=[rtDir '/BSR/BSDS500/data/']; 107 | else 108 | opts.modelDir=[rtDir '/sweepsNYUD/']; opts.nImgs=381; 109 | opts.bsdsDir=[rtDir '/BSR/NYUD/data/']; 110 | opts.rgbd=2; opts.fracFtrs=1/8; 111 | end 112 | 113 | % setup opts 114 | optsDefault=opts; N=100; lgd=cell(1,N); ss=lgd; 115 | opts=opts(ones(1,N)); hasDefault=1; 116 | switch expNm 117 | case 'MD-imWidth' 118 | lbl='window size for x'; vs=[1:4 6 8]*8; N=length(vs); 119 | for e=1:N, opts(e).imWidth=vs(e); end 120 | for e=1:N, opts(e).gtWidth=min(vs(e),opts(e).gtWidth); end 121 | case 'MD-gtWidth' 122 | lbl='window size for y'; vs=[1:4 6 8]*4; N=length(vs); 123 | for e=1:N, opts(e).gtWidth=vs(e); end 124 | case 'TR-nData' 125 | lbl='# train patches x10^4'; vs=[1 2 5 10 20 50 100 200]; N=length(vs); 126 | for e=1:N, opts(e).nPos=round(vs(e)*1e4*nPos); end 127 | for e=1:N, opts(e).nNeg=round(vs(e)*1e4*(1-nPos)); end 128 | if(full<2), N=5; elseif(full<3), N=6; end 129 | case 'TR-nPos' 130 | lbl = 'fraction positive data'; vs=20:10:80; N=length(vs); 131 | for e=1:N, opts(e).nPos=round(nData*vs(e)/100); end 132 | for e=1:N, opts(e).nNeg=round(nData*(1-vs(e)/100)); end 133 | for e=1:N, lgd{e}=sprintf('.%i',vs(e)/10); end 134 | case 'TR-nImgs' 135 | lbl='# train images'; vs=[10 20 50 100 opts(1).nImgs]; N=length(vs); 136 | for e=1:N, opts(e).nImgs=vs(e); end 137 | case 'TR-sharpen' 138 | lbl='sharpening radius'; vs=0:4; N=length(vs); 139 | for e=1:N, opts(e).sharpen=vs(e); end 140 | case 'TR-nTrees' 141 | lbl='# decision trees'; vs=2.^(0:4); N=length(vs); 142 | for e=1:N, opts(e).nTrees=vs(e); end; 143 | for e=1:N, opts(e).nTreesEval=max(1,vs(e)/2); end 144 | if(full<2), N=4; end 145 | case 'TR-split' 146 | lbl='information gain'; 147 | ss={'gini','entropy','twoing'}; N=length(ss); lgd=ss; 148 | for e=1:N, opts(e).split=ss{e}; end 149 | case 'TR-minChild' 150 | lbl='min samples per node'; vs=2:2:16; N=length(vs); 151 | for e=1:N, opts(e).minChild=vs(e); end 152 | case 'TR-maxDepth' 153 | lbl='max tree depth'; vs=2.^(2:6); N=length(vs); 154 | for e=1:N, opts(e).maxDepth=vs(e); end 155 | case 'TR-fracFtrs' 156 | lbl='fraction features'; vs=2.^(2:6); N=length(vs); 157 | for e=1:N, opts(e).fracFtrs=1/vs(e); end 158 | for e=1:N, lgd{e}=sprintf('1/%i',vs(e)); end 159 | case 'TR-nSamples' 160 | lbl='m (size of Z)'; vs=2.^(0:2:8); N=length(vs); 161 | for e=1:N, opts(e).nSamples=vs(e); end 162 | case 'TR-nClasses' 163 | lbl='k (size of C)'; vs=2.^(1:5); N=length(vs); 164 | for e=1:N, opts(e).nClasses=vs(e); end 165 | case 'TR-discretize' 166 | lbl='discretization type'; 167 | ss={'pca','kmeans'}; N=length(ss); lgd=ss; 168 | for e=1:N, opts(e).discretize=ss{e}; end 169 | case 'FT-nCells' 170 | lbl='# grid cells'; vs=1:2:7; N=length(vs); 171 | for e=1:N, opts(e).nCells=vs(e); end 172 | if(full<2), N=3; end 173 | case 'FT-nOrients' 174 | lbl='# gradient orients'; vs=0:2:8; N=length(vs); 175 | for e=1:N, opts(e).nOrients=vs(e); end 176 | case 'FT-normRad' 177 | lbl='normalization radius'; vs=[0 2.^(0:3)]; N=length(vs); 178 | for e=1:N, opts(e).normRad=vs(e); end 179 | case 'FT-shrink' 180 | lbl='channel downsample'; vs=2.^(0:2); N=length(vs); 181 | for e=1:N, opts(e).shrink=vs(e); end 182 | case 'FT-grdSmooth' 183 | lbl = 'gradient blur'; vs=[0 2.^(0:4)]; N=length(vs); 184 | for e=1:N, opts(e).grdSmooth=vs(e); end 185 | case 'FT-chnSmooth' 186 | lbl='channel blur'; vs=[0 2.^(0:4)]; N=length(vs); 187 | for e=1:N, opts(e).chnSmooth=vs(e); end 188 | case 'FT-simSmooth' 189 | lbl='self-similarity blur'; vs=[0 2.^(0:4)]; N=length(vs); 190 | for e=1:N, opts(e).simSmooth=vs(e); end 191 | case 'final' 192 | if(strcmpi(dataset,'bsds')), vs=[0 2]; else 193 | vs=0:3; rgbd=[2 2 1 0]; for e=2:4, opts(e).rgbd=rgbd(e); end; end 194 | lbl='final'; N=length(vs); nData=2e6; 195 | for e=2:N, opts(e).nPos=round(nData*nPos); end 196 | for e=2:N, opts(e).nNeg=round(nData*(1-nPos)); end 197 | if(full<3), N=1; end 198 | otherwise, error('invalid exp: %s',expNm); 199 | end 200 | 201 | % produce final set of opts and find default opts 202 | for e=1:N, if(isempty(lgd{e})), lgd{e}=int2str(vs(e)); end; end 203 | for e=1:N, if(isempty(ss{e})), ss{e}=int2str2(vs(e),5); end; end 204 | O=1:N; opts=opts(O); lgd=lgd(O); ss=ss(O); d=0; 205 | for e=1:N, if(isequal(optsDefault,opts(e))), d=e; break; end; end 206 | if(hasDefault && d==0), disp(expNm); assert(false); end 207 | for e=1:N, opts(e).modelFnm=[expNm ss{e}]; end 208 | if(hasDefault), lgd{d}=[lgd{d} '*']; opts(d).modelFnm='Default'; end 209 | if(0), disp([ss' lgd']'); end 210 | 211 | end 212 | -------------------------------------------------------------------------------- /edgesTrain.m: -------------------------------------------------------------------------------- 1 | function model = edgesTrain( varargin ) 2 | % Train structured edge detector. 3 | % 4 | % For an introductory tutorial please see edgesDemo.m. 5 | % 6 | % USAGE 7 | % opts = edgesTrain() 8 | % model = edgesTrain( opts ) 9 | % 10 | % INPUTS 11 | % opts - parameters (struct or name/value pairs) 12 | % (1) model parameters: 13 | % .imWidth - [32] width of image patches 14 | % .gtWidth - [16] width of ground truth patches 15 | % (2) tree parameters: 16 | % .nPos - [5e5] number of positive patches per tree 17 | % .nNeg - [5e5] number of negative patches per tree 18 | % .nImgs - [inf] maximum number of images to use for training 19 | % .nTrees - [8] number of trees in forest to train 20 | % .fracFtrs - [1/4] fraction of features to use to train each tree 21 | % .minCount - [1] minimum number of data points to allow split 22 | % .minChild - [8] minimum number of data points allowed at child nodes 23 | % .maxDepth - [64] maximum depth of tree 24 | % .discretize - ['pca'] options include 'pca' and 'kmeans' 25 | % .nSamples - [256] number of samples for clustering structured labels 26 | % .nClasses - [2] number of classes (clusters) for binary splits 27 | % .split - ['gini'] options include 'gini', 'entropy' and 'twoing' 28 | % (3) feature parameters: 29 | % .nOrients - [4] number of orientations per gradient scale 30 | % .grdSmooth - [0] radius for image gradient smoothing (using convTri) 31 | % .chnSmooth - [2] radius for reg channel smoothing (using convTri) 32 | % .simSmooth - [8] radius for sim channel smoothing (using convTri) 33 | % .normRad - [4] gradient normalization radius (see gradientMag) 34 | % .shrink - [2] amount to shrink channels 35 | % .nCells - [5] number of self similarity cells 36 | % .rgbd - [0] 0:RGB, 1:depth, 2:RBG+depth (for NYU data only) 37 | % (4) detection parameters (can be altered after training): 38 | % .stride - [2] stride at which to compute edges 39 | % .multiscale - [0] if true run multiscale edge detector 40 | % .sharpen - [2] sharpening amount (can only decrease after training) 41 | % .nTreesEval - [4] number of trees to evaluate per location 42 | % .nThreads - [4] number of threads for evaluation of trees 43 | % .nms - [0] if true apply non-maximum suppression to edges 44 | % (5) other parameters: 45 | % .seed - [1] seed for random stream (for reproducibility) 46 | % .useParfor - [0] if true train trees in parallel (memory intensive) 47 | % .modelDir - ['models/'] target directory for storing models 48 | % .modelFnm - ['model'] model filename 49 | % .bsdsDir - ['BSR/BSDS500/data/'] location of BSDS dataset 50 | % 51 | % OUTPUTS 52 | % model - trained structured edge detector w the following fields 53 | % .opts - input parameters and constants 54 | % .thrs - [nNodes x nTrees] threshold corresponding to each fid 55 | % .fids - [nNodes x nTrees] feature ids for each node 56 | % .child - [nNodes x nTrees] index of child for each node 57 | % .count - [nNodes x nTrees] number of data points at each node 58 | % .depth - [nNodes x nTrees] depth of each node 59 | % .eBins - data structure for storing all node edge maps 60 | % .eBnds - data structure for storing all node edge maps 61 | % 62 | % EXAMPLE 63 | % 64 | % See also edgesDemo, edgesChns, edgesDetect, forestTrain 65 | % 66 | % Structured Edge Detection Toolbox Version 3.01 67 | % Code written by Piotr Dollar, 2014. 68 | % Licensed under the MSR-LA Full Rights License [see license.txt] 69 | 70 | % get default parameters 71 | dfs={'imWidth',32, 'gtWidth',16, 'nPos',5e5, 'nNeg',5e5, 'nImgs',inf, ... 72 | 'nTrees',8, 'fracFtrs',1/4, 'minCount',1, 'minChild',8, ... 73 | 'maxDepth',64, 'discretize','pca', 'nSamples',256, 'nClasses',2, ... 74 | 'split','gini', 'nOrients',4, 'grdSmooth',0, 'chnSmooth',2, ... 75 | 'simSmooth',8, 'normRad',4, 'shrink',2, 'nCells',5, 'rgbd',0, ... 76 | 'stride',2, 'multiscale',0, 'sharpen',2, 'nTreesEval',4, ... 77 | 'nThreads',4, 'nms',0, 'seed',1, 'useParfor',0, 'modelDir','models/', ... 78 | 'modelFnm','model', 'bsdsDir','BSR/BSDS500/data/'}; 79 | opts = getPrmDflt(varargin,dfs,1); 80 | if(nargin==0), model=opts; return; end 81 | 82 | % if forest exists load it and return 83 | cd(fileparts(mfilename('fullpath'))); 84 | forestDir = [opts.modelDir '/forest/']; 85 | forestFn = [forestDir opts.modelFnm]; 86 | if(exist([forestFn '.mat'], 'file')) 87 | load([forestFn '.mat']); return; end 88 | 89 | % compute constants and store in opts 90 | nTrees=opts.nTrees; nCells=opts.nCells; shrink=opts.shrink; 91 | opts.nPos=round(opts.nPos); opts.nNeg=round(opts.nNeg); 92 | opts.nTreesEval=min(opts.nTreesEval,nTrees); 93 | opts.stride=max(opts.stride,shrink); 94 | imWidth=opts.imWidth; gtWidth=opts.gtWidth; 95 | imWidth=round(max(gtWidth,imWidth)/shrink/2)*shrink*2; 96 | opts.imWidth=imWidth; opts.gtWidth=gtWidth; 97 | nChnsGrad=(opts.nOrients+1)*2; nChnsColor=3; 98 | if(opts.rgbd==1), nChnsColor=1; end 99 | if(opts.rgbd==2), nChnsGrad=nChnsGrad*2; nChnsColor=nChnsColor+1; end 100 | nChns = nChnsGrad+nChnsColor; opts.nChns = nChns; 101 | opts.nChnFtrs = imWidth*imWidth*nChns/shrink/shrink; 102 | opts.nSimFtrs = (nCells*nCells)*(nCells*nCells-1)/2*nChns; 103 | opts.nTotFtrs = opts.nChnFtrs + opts.nSimFtrs; disp(opts); 104 | 105 | % generate stream for reproducibility of model 106 | stream=RandStream('mrg32k3a','Seed',opts.seed); 107 | 108 | % train nTrees random trees (can be trained with parfor if enough memory) 109 | if(opts.useParfor), parfor i=1:nTrees, trainTree(opts,stream,i); end 110 | else for i=1:nTrees, trainTree(opts,stream,i); end; end 111 | 112 | % merge trees and save model 113 | model = mergeTrees( opts ); 114 | if(~exist(forestDir,'dir')), mkdir(forestDir); end 115 | save([forestFn '.mat'], 'model', '-v7.3'); 116 | 117 | end 118 | 119 | function model = mergeTrees( opts ) 120 | % accumulate trees and merge into final model 121 | nTrees=opts.nTrees; gtWidth=opts.gtWidth; 122 | treeFn = [opts.modelDir '/tree/' opts.modelFnm '_tree']; 123 | for i=1:nTrees 124 | t=load([treeFn int2str2(i,3) '.mat'],'tree'); t=t.tree; 125 | if(i==1), trees=t(ones(1,nTrees)); else trees(i)=t; end 126 | end 127 | nNodes=0; for i=1:nTrees, nNodes=max(nNodes,size(trees(i).fids,1)); end 128 | % merge all fields of all trees 129 | model.opts=opts; Z=zeros(nNodes,nTrees,'uint32'); 130 | model.thrs=zeros(nNodes,nTrees,'single'); 131 | model.fids=Z; model.child=Z; model.count=Z; model.depth=Z; 132 | model.segs=zeros(gtWidth,gtWidth,nNodes,nTrees,'uint8'); 133 | for i=1:nTrees, tree=trees(i); nNodes1=size(tree.fids,1); 134 | model.fids(1:nNodes1,i) = tree.fids; 135 | model.thrs(1:nNodes1,i) = tree.thrs; 136 | model.child(1:nNodes1,i) = tree.child; 137 | model.count(1:nNodes1,i) = tree.count; 138 | model.depth(1:nNodes1,i) = tree.depth; 139 | model.segs(:,:,1:nNodes1,i) = tree.hs-1; 140 | end 141 | % remove very small segments (<=5 pixels) 142 | segs=model.segs; nSegs=squeeze(max(max(segs)))+1; 143 | parfor i=1:nTrees*nNodes, m=nSegs(i); 144 | if(m==1), continue; end; S=segs(:,:,i); del=0; 145 | for j=1:m, Sj=(S==j-1); if(nnz(Sj)>5), continue; end 146 | S(Sj)=median(single(S(convTri(single(Sj),1)>0))); del=1; end 147 | if(del), [~,~,S]=unique(S); S=reshape(S-1,gtWidth,gtWidth); 148 | segs(:,:,i)=S; nSegs(i)=max(S(:))+1; end 149 | end 150 | model.segs=segs; model.nSegs=nSegs; 151 | % store compact representations of sparse binary edge patches 152 | nBnds=opts.sharpen+1; eBins=cell(nTrees*nNodes,nBnds); 153 | eBnds=zeros(nNodes*nTrees,nBnds); 154 | parfor i=1:nTrees*nNodes 155 | if(model.child(i) || model.nSegs(i)==1), continue; end %#ok 156 | E=gradientMag(single(model.segs(:,:,i)))>.01; E0=0; 157 | for j=1:nBnds, eBins{i,j}=uint16(find(E & ~E0)'-1); E0=E; 158 | eBnds(i,j)=length(eBins{i,j}); E=convTri(single(E),1)>.01; end 159 | end 160 | eBins=eBins'; model.eBins=[eBins{:}]'; 161 | eBnds=eBnds'; model.eBnds=uint32([0; cumsum(eBnds(:))]); 162 | end 163 | 164 | function trainTree( opts, stream, treeInd ) 165 | % Train a single tree in forest model. 166 | 167 | % location of ground truth 168 | trnImgDir = [opts.bsdsDir '/images/train/']; 169 | trnDepDir = [opts.bsdsDir '/depth/train/']; 170 | trnGtDir = [opts.bsdsDir '/groundTruth/train/']; 171 | imgIds=dir(trnImgDir); imgIds=imgIds([imgIds.bytes]>0); 172 | imgIds={imgIds.name}; ext=imgIds{1}(end-2:end); 173 | nImgs=length(imgIds); for i=1:nImgs, imgIds{i}=imgIds{i}(1:end-4); end 174 | 175 | % extract commonly used options 176 | imWidth=opts.imWidth; imRadius=imWidth/2; 177 | gtWidth=opts.gtWidth; gtRadius=gtWidth/2; 178 | nChns=opts.nChns; nTotFtrs=opts.nTotFtrs; rgbd=opts.rgbd; 179 | nPos=opts.nPos; nNeg=opts.nNeg; shrink=opts.shrink; 180 | 181 | % finalize setup 182 | treeDir = [opts.modelDir '/tree/']; 183 | treeFn = [treeDir opts.modelFnm '_tree']; 184 | if(exist([treeFn int2str2(treeInd,3) '.mat'],'file')) 185 | fprintf('Reusing tree %d of %d\n',treeInd,opts.nTrees); return; end 186 | fprintf('\n-------------------------------------------\n'); 187 | fprintf('Training tree %d of %d\n',treeInd,opts.nTrees); tStart=clock; 188 | 189 | % set global stream to stream with given substream (will undo at end) 190 | streamOrig = RandStream.getGlobalStream(); 191 | set(stream,'Substream',treeInd); 192 | RandStream.setGlobalStream( stream ); 193 | 194 | % collect positive and negative patches and compute features 195 | fids=sort(randperm(nTotFtrs,round(nTotFtrs*opts.fracFtrs))); 196 | k = nPos+nNeg; nImgs=min(nImgs,opts.nImgs); 197 | ftrs = zeros(k,length(fids),'single'); 198 | labels = zeros(gtWidth,gtWidth,k,'uint8'); k = 0; 199 | tid = ticStatus('Collecting data',30,1); 200 | for i = 1:nImgs 201 | % get image and compute channels 202 | gt=load([trnGtDir imgIds{i} '.mat']); gt=gt.groundTruth; 203 | I=imread([trnImgDir imgIds{i} '.' ext]); siz=size(I); 204 | if(rgbd), D=single(imread([trnDepDir imgIds{i} '.png']))/1e4; end 205 | if(rgbd==1), I=D; elseif(rgbd==2), I=cat(3,single(I)/255,D); end 206 | p=zeros(1,4); p([2 4])=mod(4-mod(siz(1:2),4),4); 207 | if(any(p)), I=imPad(I,p,'symmetric'); end 208 | [chnsReg,chnsSim] = edgesChns(I,opts); 209 | % sample positive and negative locations 210 | nGt=length(gt); xy=[]; k1=0; B=false(siz(1),siz(2)); 211 | B(shrink:shrink:end,shrink:shrink:end)=1; 212 | B([1:imRadius end-imRadius:end],:)=0; 213 | B(:,[1:imRadius end-imRadius:end])=0; 214 | for j=1:nGt 215 | M=gt{j}.Boundaries; M(bwdist(M) 219 | [y,x]=find(~M.*B); k2=min(length(y),ceil(nNeg/nImgs/nGt)); 220 | rp=randperm(length(y),k2); y=y(rp); x=x(rp); 221 | xy=[xy; x y ones(k2,1)*j]; k1=k1+k2; %#ok 222 | end 223 | if(k1>size(ftrs,1)-k), k1=size(ftrs,1)-k; xy=xy(1:k1,:); end 224 | % crop patches and ground truth labels 225 | psReg=zeros(imWidth/shrink,imWidth/shrink,nChns,k1,'single'); 226 | lbls=zeros(gtWidth,gtWidth,k1,'uint8'); 227 | psSim=psReg; ri=imRadius/shrink; rg=gtRadius; 228 | for j=1:k1, xy1=xy(j,:); xy2=xy1/shrink; 229 | psReg(:,:,:,j)=chnsReg(xy2(2)-ri+1:xy2(2)+ri,xy2(1)-ri+1:xy2(1)+ri,:); 230 | psSim(:,:,:,j)=chnsSim(xy2(2)-ri+1:xy2(2)+ri,xy2(1)-ri+1:xy2(1)+ri,:); 231 | t=gt{xy1(3)}.Segmentation(xy1(2)-rg+1:xy1(2)+rg,xy1(1)-rg+1:xy1(1)+rg); 232 | if(all(t(:)==t(1))), lbls(:,:,j)=1; else [~,~,t]=unique(t); 233 | lbls(:,:,j)=reshape(t,gtWidth,gtWidth); end 234 | end 235 | if(0), figure(1); montage2(squeeze(psReg(:,:,1,:))); drawnow; end 236 | if(0), figure(2); montage2(lbls(:,:,:)); drawnow; end 237 | % compute features and store 238 | ftrs1=[reshape(psReg,[],k1)' stComputeSimFtrs(psSim,opts)]; 239 | ftrs(k+1:k+k1,:)=ftrs1(:,fids); labels(:,:,k+1:k+k1)=lbls; 240 | k=k+k1; if(k==size(ftrs,1)), tocStatus(tid,1); break; end 241 | tocStatus(tid,i/nImgs); 242 | end 243 | if(k0) = fids(tree.fids(tree.child>0)+1)-1; 252 | if(~exist(treeDir,'dir')), mkdir(treeDir); end 253 | save([treeFn int2str2(treeInd,3) '.mat'],'tree'); e=etime(clock,tStart); 254 | fprintf('Training of tree %d complete (time=%.1fs).\n',treeInd,e); 255 | RandStream.setGlobalStream( streamOrig ); 256 | 257 | end 258 | 259 | function ftrs = stComputeSimFtrs( chns, opts ) 260 | % Compute self-similarity features (order must be compatible w mex file). 261 | w=opts.imWidth/opts.shrink; n=opts.nCells; if(n==0), ftrs=[]; return; end 262 | nSimFtrs=opts.nSimFtrs; nChns=opts.nChns; m=size(chns,4); 263 | inds=round(w/n/2); inds=round((1:n)*(w+2*inds-1)/(n+1)-inds+1); 264 | chns=reshape(chns(inds,inds,:,:),n*n,nChns,m); 265 | ftrs=zeros(nSimFtrs/nChns,nChns,m,'single'); 266 | k=0; for i=1:n*n-1, k1=n*n-i; i1=ones(1,k1)*i; 267 | ftrs(k+1:k+k1,:,:)=chns(i1,:,:)-chns((1:k1)+i,:,:); k=k+k1; end 268 | ftrs = reshape(ftrs,nSimFtrs,m)'; 269 | end 270 | 271 | function [hs,segs] = discretize( segs, nClasses, nSamples, type ) 272 | % Convert a set of segmentations into a set of labels in [1,nClasses]. 273 | persistent cache; w=size(segs{1},1); assert(size(segs{1},2)==w); 274 | if(~isempty(cache) && cache{1}==w), [~,is1,is2]=deal(cache{:}); else 275 | % compute all possible lookup inds for w x w patches 276 | is=1:w^4; is1=floor((is-1)/w/w); is2=is-is1*w*w; is1=is1+1; 277 | kp=is2>is1; is1=is1(kp); is2=is2(kp); cache={w,is1,is2}; 278 | end 279 | % compute n binary codes zs of length nSamples 280 | nSamples=min(nSamples,length(is1)); kp=randperm(length(is1),nSamples); 281 | n=length(segs); is1=is1(kp); is2=is2(kp); zs=false(n,nSamples); 282 | for i=1:n, zs(i,:)=segs{i}(is1)==segs{i}(is2); end 283 | zs=bsxfun(@minus,zs,sum(zs,1)/n); zs=zs(:,any(zs,1)); 284 | if(isempty(zs)), hs=ones(n,1,'uint32'); segs=segs{1}; return; end 285 | % find most representative segs (closest to mean) 286 | [~,ind]=min(sum(zs.*zs,2)); segs=segs{ind}; 287 | % apply PCA to reduce dimensionality of zs 288 | U=pca(zs'); d=min(5,size(U,2)); zs=zs*U(:,1:d); 289 | % discretize zs by clustering or discretizing pca dimensions 290 | d=min(d,floor(log2(nClasses))); hs=zeros(n,1); 291 | for i=1:d, hs=hs+(zs(:,i)<0)*2^(i-1); end 292 | [~,~,hs]=unique(hs); hs=uint32(hs); 293 | if(strcmpi(type,'kmeans')) 294 | nClasses1=max(hs); C=zs(1:nClasses1,:); 295 | for i=1:nClasses1, C(i,:)=mean(zs(hs==i,:),1); end 296 | hs=uint32(kmeans2(zs,nClasses,'C0',C,'nIter',1)); 297 | end 298 | % optionally display different types of hs 299 | for i=1:0, figure(i); montage2(cell2array(segs(hs==i))); end 300 | end 301 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MICROSOFT RESEARCH LICENSE TERMS 2 | These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received, if any. The terms also apply to any Microsoft: 3 | * updates, 4 | * supplements, 5 | * Internet-based services, and 6 | * support services 7 | for this software, unless other terms accompany those items. If so, those terms apply. 8 | By using the software you accept these terms. If you do not accept them, do not use the software. If you comply with these license terms, you have the rights below. 9 | SCOPE OF RIGHTS: 10 | * License Grant. You may use, copy, modify, creative derivative works, and distribute the Software for non-commercial purposes, subject to the restrictions in this MSR-LA. Examples of non-commercial uses are teaching, academic research, public demonstrations and personal experimentation. 11 | * Publication. You may publish (or present papers or articles) on your results from using the software, provided that no software source code or object code or documentation is included in any such publication or presentation. 12 | * Third Party Programs. The software may include third party programs that Microsoft, not the third party, licenses to you under this agreement. Notices, if any, for the third party program are included for your information only. 13 | In return, we simply require that you agree: 14 | * If you distribute the Software or any derivative works of the Software, you will distribute them under the same terms and conditions as in this license, and you will not grant other rights to the Software or derivative works that are different from those provided by this MSR-LA. 15 | * If you have created derivative works of the software, and distribute such derivative works, you will cause the modified files to carry prominent notices so that recipients know that they are not receiving the original software. Such notices must state: (i) that you have changed the software; and (ii) the date of any changes. 16 | * That Microsoft is granted back, without any restrictions or limitations, a non-exclusive, perpetual, irrevocable, royalty-free, assignable and sub-licensable license, to reproduce, publicly perform or display, install, use, modify, post, distribute, make and have made, sell and transfer your modifications to and/or derivative works of the Software source code or data, for any purpose. 17 | * Not to alter any copyright, trademark or patent notice in the software; 18 | * Not to use Microsoft's trademarks in your programs' names or in a way that suggests your derivative works or modifications come from or are endorsed by Microsoft; 19 | * Not to include the software in malicious, deceptive or unlawful programs; 20 | TERM; TERMINATION. The term of this Agreement will commence upon your acceptance of these license terms and will continue indefinitely unless terminated as provided herein. If you breach this agreement or if you sue anyone over patents that you think may apply to or read on the software or anyone's use of the software, this agreement (and your license and rights obtained herein) terminate automatically. If this agreement is terminated, you must cease using and distributing any derivative works or modifications of the Software. Any sections that are intended to survive termination of this agreement shall survive. 21 | FEEDBACK. That any feedback about the Software provided by you to us is voluntarily given, and Microsoft shall be free to use the feedback as it sees fit without obligation or restriction of any kind, even if the feedback is designated by you as confidential. 22 | SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. The patent rights, if any, granted to you in this MSR-LA only apply to the Software, not to any derivative works you make. In using the Software, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not: 23 | * work around any technical limitations in the software; 24 | * reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation; 25 | * make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation; 26 | * rent, lease or lend the software; 27 | * transfer the software or this agreement to any third party; or 28 | * use the software for commercial software hosting services. 29 | EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting. 30 | ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. 31 | Governing Law and Venue. This Agreement is governed by and construed in accordance with the laws of the state of Washington, without reference to its choice of law principles to the contrary. Each party hereby consents to the jurisdiction and venue of the state and federal courts located in King County, Washington, with regard to any suit or claim arising under or by reason of this Agreement. 32 | DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS." YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY HAVE ADDITIONAL CONSUMER RIGHTS OR STATUTORY GUARANTEES UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 33 | THAT NEITHER MICROSOFT NOR ANY CONTRIBUTOR TO THE SOFTWARE WILL BE LIABLE FOR ANY DAMAGES RELATED TO THE SOFTWARE OR THIS MSR-LA, INCLUDING DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL OR INCIDENTAL DAMAGES, TO THE MAXIMUM EXTENT THE LAW PERMITS, NO MATTER WHAT LEGAL THEORY IT IS BASED 34 | -------------------------------------------------------------------------------- /models/forest/modelBsds.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/models/forest/modelBsds.mat -------------------------------------------------------------------------------- /private/correspondPixels.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/correspondPixels.mexa64 -------------------------------------------------------------------------------- /private/correspondPixels.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/correspondPixels.mexmaci64 -------------------------------------------------------------------------------- /private/correspondPixels.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/correspondPixels.mexw64 -------------------------------------------------------------------------------- /private/edgeBoxesMex.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Structured Edge Detection Toolbox Version 3.01 3 | * Code written by Piotr Dollar and Larry Zitnick, 2014. 4 | * Licensed under the MSR-LA Full Rights License [see license.txt] 5 | *******************************************************************************/ 6 | #include "mex.h" 7 | #include "math.h" 8 | #include 9 | #include 10 | using namespace std; 11 | #define PI 3.14159265f 12 | int clamp( int v, int a, int b ) { return vb?b:v; } 13 | 14 | // trivial array class encapsulating pointer arrays 15 | template class Array 16 | { 17 | public: 18 | Array() { _h=_w=0; _x=0; _free=0; } 19 | virtual ~Array() { clear(); } 20 | void clear() { if(_free) delete [] _x; _h=_w=0; _x=0; _free=0; } 21 | void init(int h, int w) { clear(); _h=h; _w=w; _x=new T[h*w](); _free=1; } 22 | T& val(size_t c, size_t r) { return _x[c*_h+r]; } 23 | int _h, _w; T *_x; bool _free; 24 | }; 25 | 26 | // convenient typedefs 27 | typedef vector vectorf; 28 | typedef vector vectori; 29 | typedef Array arrayf; 30 | typedef Array arrayi; 31 | 32 | // bounding box data structures and routines 33 | typedef struct { int c, r, w, h; float s; } Box; 34 | typedef vector Boxes; 35 | bool boxesCompare( const Box &a, const Box &b ) { return a.s _segAff; // segment affinities 59 | vector _segAffIdx; // segment neighbors 60 | 61 | // data structures for efficiency (see prepDataStructs) 62 | arrayf _segIImg, _magIImg; arrayi _hIdxImg, _vIdxImg; 63 | vector _hIdxs, _vIdxs; vectorf _scaleNorm; 64 | float _scStep, _arStep, _rcStepRatio; 65 | 66 | // data structures for efficiency (see scoreBox) 67 | arrayf _sWts; arrayi _sDone, _sMap, _sIds; int _sId; 68 | 69 | // helper routines 70 | void clusterEdges( arrayf &E, arrayf &O, arrayf &V ); 71 | void prepDataStructs( arrayf &E ); 72 | void scoreAllBoxes( Boxes &boxes ); 73 | void scoreBox( Box &box ); 74 | void refineBox( Box &box ); 75 | void drawBox( Box &box, arrayf &E, arrayf &V ); 76 | }; 77 | 78 | //////////////////////////////////////////////////////////////////////////////// 79 | 80 | void EdgeBoxGenerator::generate( Boxes &boxes, arrayf &E, arrayf &O, arrayf &V ) 81 | { 82 | clusterEdges(E,O,V); prepDataStructs(E); scoreAllBoxes(boxes); 83 | } 84 | 85 | void EdgeBoxGenerator::clusterEdges( arrayf &E, arrayf &O, arrayf &V ) 86 | { 87 | int c, r, cd, rd, i, j; h=E._h; w=E._w; 88 | 89 | // greedily merge connected edge pixels into clusters (create _segIds) 90 | _segIds.init(h,w); _segCnt=1; 91 | for( c=0; c.5) v=1-v; 107 | vs.push_back(v); cs.push_back(c0+cd); rs.push_back(r0+rd); 108 | } 109 | float minv=1000; j=0; 110 | for( i=0; i0 ) _segMag[j]+=E.val(c,r); 122 | for( c=1; c0 && _segMag[j]<=_clusterMinMag) 124 | _segIds.val(c,r)=0; 125 | i=1; while(i>0) { 126 | i=0; for( c=1; c.5) v=1-v; 132 | if( v0) i++; 135 | } 136 | } 137 | 138 | // compactify representation 139 | _segMag.assign(_segCnt,0); vectori map(_segCnt,0); _segCnt=1; 140 | for( c=1; c0 ) _segMag[j]+=E.val(c,r); 142 | for( i=0; i<_segMag.size(); i++ ) if( _segMag[i]>0 ) map[i]=_segCnt++; 143 | for( c=1; c0 ) _segIds.val(c,r)=map[j]; 145 | 146 | // compute positional means and recompute _segMag 147 | _segMag.assign(_segCnt,0); vectorf meanX(_segCnt,0), meanY(_segCnt,0); 148 | vectorf meanOx(_segCnt,0), meanOy(_segCnt,0), meanO(_segCnt,0); 149 | for( c=1; c0 ) { 156 | float m=_segMag[i]; meanX[i]/=m; meanY[i]/=m; 157 | meanO[i]=atan2(meanOy[i]/m,meanOx[i]/m)/2; 158 | } 159 | 160 | // compute segment affinities 161 | _segAff.resize(_segCnt); _segAffIdx.resize(_segCnt); 162 | for(i=0; i<_segCnt; i++) _segAff[i].resize(0); 163 | for(i=0; i<_segCnt; i++) _segAffIdx[i].resize(0); 164 | const int rad = 2; 165 | for( c=rad; c0 ) { _segC[j]=c; _segR[j]=r; } 183 | 184 | // optionally create visualization (assume memory initialized is 3*w*h) 185 | if( V._x ) for( c=0; c0 ) { 210 | E1.val(_segC[i],_segR[i]) = _segMag[i]; 211 | } 212 | _segIImg.init(h+1,w+1); 213 | for( c=1; c _edgeMinMag ? E.val(c,r) : 0; 222 | _magIImg.val(c+1,r+1) = e + _magIImg.val(c,r+1) + 223 | _magIImg.val(c+1,r) - _magIImg.val(c,r); 224 | } 225 | 226 | // create remaining data structures 227 | _hIdxs.resize(h); _hIdxImg.init(h,w); 228 | for( r=0; r0 && sDone[j]!=sId ) { 274 | sIds[n]=j; sWts[n]=1; sDone[j]=sId; sMap[j]=n++; 275 | } 276 | cs=_hIdxImg.val(c0,r1); ce=_hIdxImg.val(c1,r1); // bottom 277 | for( i=cs; i<=ce; i++ ) if( (j=_hIdxs[r1][i])>0 && sDone[j]!=sId ) { 278 | sIds[n]=j; sWts[n]=1; sDone[j]=sId; sMap[j]=n++; 279 | } 280 | rs=_vIdxImg.val(c0,r0); re=_vIdxImg.val(c0,r1); // left 281 | for( i=rs; i<=re; i++ ) if( (j=_vIdxs[c0][i])>0 && sDone[j]!=sId ) { 282 | sIds[n]=j; sWts[n]=1; sDone[j]=sId; sMap[j]=n++; 283 | } 284 | rs=_vIdxImg.val(c1,r0); re=_vIdxImg.val(c1,r1); // right 285 | for( i=rs; i<=re; i++ ) if( (j=_vIdxs[c1][i])>0 && sDone[j]!=sId ) { 286 | sIds[n]=j; sWts[n]=1; sDone[j]=sId; sMap[j]=n++; 287 | } 288 | // follow connected paths and set weights accordingly (w=1 means remove) 289 | for( i=0; isWts[sMap[q]] ) { sWts[sMap[q]]=wq; i=min(i,sMap[q]-1); } 296 | } else if(_segC[q]>=c0 && _segC[q]<=c1 && _segR[q]>=r0 && _segR[q]<=r1) { 297 | sIds[n]=q; sWts[n]=wq; sDone[q]=sId; sMap[q]=n++; 298 | } 299 | } 300 | } 301 | // finally remove segments connected to boundaries 302 | for( i=0; i=c0 && _segC[k]<=c1 && _segR[k]>=r0 && _segR[k]<=r1 ) 305 | v -= sWts[i]*_segMag[k]; 306 | } 307 | v*=norm; if(v<_minScore) v=0; box.s=v; 308 | } 309 | 310 | void EdgeBoxGenerator::refineBox( Box &box ) 311 | { 312 | int rStep = int(box.h*_rcStepRatio); 313 | int cStep = int(box.w*_rcStepRatio); 314 | while( 1 ) { 315 | // prepare for iteration 316 | rStep/=2; cStep/=2; if( rStep<=2 && cStep<=2 ) break; 317 | rStep=max(1,rStep); cStep=max(1,cStep); Box B; 318 | // search over r start 319 | B=box; B.r=box.r-rStep; B.h=B.h+rStep; scoreBox(B); 320 | if(B.s<=box.s) { B=box; B.r=box.r+rStep; B.h=B.h-rStep; scoreBox(B); } 321 | if(B.s>box.s) box=B; 322 | // search over r end 323 | B=box; B.h=B.h+rStep; scoreBox(B); 324 | if(B.s<=box.s) { B=box; B.h=B.h-rStep; scoreBox(B); } 325 | if(B.s>box.s) box=B; 326 | // search over c start 327 | B=box; B.c=box.c-cStep; B.w=B.w+cStep; scoreBox(B); 328 | if(B.s<=box.s) { B=box; B.c=box.c+cStep; B.w=B.w-cStep; scoreBox(B); } 329 | if(B.s>box.s) box=B; 330 | // search over c end 331 | B=box; B.w=B.w+cStep; scoreBox(B); 332 | if(B.s<=box.s) { B=box; B.w=B.w-cStep; scoreBox(B); } 333 | if(B.s>box.s) box=B; 334 | } 335 | } 336 | 337 | void EdgeBoxGenerator::drawBox( Box &box, arrayf &E, arrayf &V ) 338 | { 339 | // score box and draw color coded edges (red=out, green=in) 340 | int i, c, r; float e, o; if( !V._x ) return; 341 | int sId=_sId; scoreBox(box); int c0, r0, c1, r1; 342 | r1=clamp(box.r+box.h,0,h-1); r0=box.r=clamp(box.r,0,h-1); 343 | c1=clamp(box.c+box.w,0,w-1); c0=box.c=clamp(box.c,0,w-1); 344 | for( c=0; c=c0 && _segC[i]<=c1 && _segR[i]>=r0 && _segR[i]<=r1 ) ? 0 : 1; 350 | V.val(c+w*0,r)=1-e+e*o; V.val(c+w*1,r)=1-e*o; V.val(c+w*2,r)=1-e; 351 | } 352 | // finally draw bounding box 353 | r=r0; for(c=c0; c<=c1; c++) V.val(c+w*0,r)=V.val(c+w*1,r)=V.val(c+w*2,r)=0; 354 | r=r1; for(c=c0; c<=c1; c++) V.val(c+w*0,r)=V.val(c+w*1,r)=V.val(c+w*2,r)=0; 355 | c=c0; for(r=r0; r<=r1; r++) V.val(c+w*0,r)=V.val(c+w*1,r)=V.val(c+w*2,r)=0; 356 | c=c1; for(r=r0; r<=r1; r++) V.val(c+w*0,r)=V.val(c+w*1,r)=V.val(c+w*2,r)=0; 357 | } 358 | 359 | void EdgeBoxGenerator::scoreAllBoxes( Boxes &boxes ) 360 | { 361 | // get list of all boxes roughly distributed in grid 362 | boxes.resize(0); int arRad, scNum; float minSize=sqrt(_minBoxArea); 363 | arRad = int(log(_maxAspectRatio)/log(_arStep*_arStep)); 364 | scNum = int(ceil(log(max(w,h)/minSize)/log(_scStep))); 365 | for( int s=0; s=r1i || a.c>=c1i ) return 0; 392 | r1j=b.r+b.h; c1j=b.c+b.w; if( a.r>=r1j || a.c>=c1j ) return 0; 393 | areai = (float) a.w*a.h; r0=max(a.r,b.r); r1=min(r1i,r1j); 394 | areaj = (float) b.w*b.h; c0=max(a.c,b.c); c1=min(c1i,c1j); 395 | areaij = (float) max(0,r1-r0)*max(0,c1-c0); 396 | return areaij / (areai + areaj - areaij); 397 | } 398 | 399 | void boxesNms( Boxes &boxes, float thr, float eta, int maxBoxes ) 400 | { 401 | sort(boxes.rbegin(),boxes.rend(),boxesCompare); 402 | if( thr>.99 ) return; const int nBin=10000; 403 | const float step=1/thr, lstep=log(step); 404 | vector kept; kept.resize(nBin+1); 405 | int i=0, j, k, n=(int) boxes.size(), m=0, b, d=1; 406 | while( i.5) { thr*=eta; d=ceil(log(1/thr)/lstep); } 414 | } 415 | boxes.resize(m); i=0; 416 | for( j=0; j 2) mexErrMsgTxt("At most two outputs expected."); 430 | if(mxGetClassID(pr[0])!=mxSINGLE_CLASS) mexErrMsgTxt("E must be a float*"); 431 | if(mxGetClassID(pr[1])!=mxSINGLE_CLASS) mexErrMsgTxt("O must be a float*"); 432 | arrayf E; E._x = (float*) mxGetData(pr[0]); 433 | arrayf O; O._x = (float*) mxGetData(pr[1]); 434 | int h = (int) mxGetM(pr[0]); O._h=E._h=h; 435 | int w = (int) mxGetN(pr[0]); O._w=E._w=w; 436 | 437 | // optionally create memory for visualization 438 | arrayf V; if( nl>1 ) { 439 | const int ds[3] = {h,w,3}; 440 | pl[1] = mxCreateNumericArray(3,ds,mxSINGLE_CLASS,mxREAL); 441 | V._x = (float*) mxGetData(pl[1]); V._h=h; V._w=w; 442 | } 443 | 444 | // setup and run EdgeBoxGenerator 445 | EdgeBoxGenerator edgeBoxGen; Boxes boxes; 446 | edgeBoxGen._alpha = float(mxGetScalar(pr[2])); 447 | edgeBoxGen._beta = float(mxGetScalar(pr[3])); 448 | edgeBoxGen._eta = float(mxGetScalar(pr[4])); 449 | edgeBoxGen._minScore = float(mxGetScalar(pr[5])); 450 | edgeBoxGen._maxBoxes = int(mxGetScalar(pr[6])); 451 | edgeBoxGen._edgeMinMag = float(mxGetScalar(pr[7])); 452 | edgeBoxGen._edgeMergeThr = float(mxGetScalar(pr[8])); 453 | edgeBoxGen._clusterMinMag = float(mxGetScalar(pr[9])); 454 | edgeBoxGen._maxAspectRatio = float(mxGetScalar(pr[10])); 455 | edgeBoxGen._minBoxArea = float(mxGetScalar(pr[11])); 456 | edgeBoxGen._gamma = float(mxGetScalar(pr[12])); 457 | edgeBoxGen._kappa = float(mxGetScalar(pr[13])); 458 | edgeBoxGen.generate( boxes, E, O, V ); 459 | 460 | // create output bbs and output to Matlab 461 | int n = (int) boxes.size(); 462 | pl[0] = mxCreateNumericMatrix(n,5,mxSINGLE_CLASS,mxREAL); 463 | float *bbs = (float*) mxGetData(pl[0]); 464 | for(int i=0; i 7 | #include 8 | #include 9 | #include 10 | #ifdef USEOMP 11 | #include 12 | #endif 13 | 14 | typedef unsigned int uint32; 15 | typedef unsigned short uint16; 16 | typedef unsigned char uint8; 17 | template inline T min( T x, T y ) { return xnBnds-1 ) { sharpen=nBnds-1; mexPrintf(msgSharpen,sharpen); } 72 | 73 | // get dimensions and constants 74 | const mwSize *imgSize = mxGetDimensions(pr[1]); 75 | const int h = (int) imgSize[0]; 76 | const int w = (int) imgSize[1]; 77 | const int Z = mxGetNumberOfDimensions(pr[1])<=2 ? 1 : imgSize[2]; 78 | const mwSize *fidsSize = mxGetDimensions(mxGetField(model,0,"fids")); 79 | const int nTreeNodes = (int) fidsSize[0]; 80 | const int nTrees = (int) fidsSize[1]; 81 | const int h1 = (int) ceil(double(h-imWidth)/stride); 82 | const int w1 = (int) ceil(double(w-imWidth)/stride); 83 | const int h2 = h1*stride+gtWidth; 84 | const int w2 = w1*stride+gtWidth; 85 | const int imgDims[3] = {h,w,Z}; 86 | const int chnDims[3] = {h/shrink,w/shrink,nChns}; 87 | const int indDims[3] = {h1,w1,nTreesEval}; 88 | const int outDims[3] = {h2,w2,1}; 89 | const int segDims[5] = {gtWidth,gtWidth,h1,w1,nTreesEval}; 90 | 91 | // construct lookup tables 92 | uint32 *iids, *eids, *cids, *cids1, *cids2; 93 | iids = buildLookup( (int*)imgDims, gtWidth ); 94 | eids = buildLookup( (int*)outDims, gtWidth ); 95 | cids = buildLookup( (int*)chnDims, imWidth/shrink ); 96 | buildLookupSs( cids1, cids2, (int*)chnDims, imWidth/shrink, nCells ); 97 | 98 | // create outputs 99 | pl[0] = mxCreateNumericArray(3,outDims,mxSINGLE_CLASS,mxREAL); 100 | float *E = (float*) mxGetData(pl[0]); 101 | pl[1] = mxCreateNumericArray(3,indDims,mxUINT32_CLASS,mxREAL); 102 | uint32 *ind = (uint32*) mxGetData(pl[1]); 103 | if(nl>2) pl[2] = mxCreateNumericArray(5,segDims,mxUINT8_CLASS,mxREAL); 104 | uint8 *segsOut; if(nl>2) segsOut = (uint8*) mxGetData(pl[2]); 105 | 106 | // apply forest to all patches and store leaf inds 107 | #ifdef USEOMP 108 | nThreads = min(nThreads,omp_get_max_threads()); 109 | #pragma omp parallel for num_threads(nThreads) 110 | #endif 111 | for( int c=0; c2) memcpy(segsOut+(r+c*h1+t*h1*w1)*gtWidth*gtWidth, 142 | segs+k*gtWidth*gtWidth,gtWidth*gtWidth*sizeof(uint8)); 143 | } 144 | } 145 | } 146 | 147 | // computed sharpened edge maps, snapping to local color values 148 | if( sharpen ) { 149 | // compute neighbors array 150 | const int g=gtWidth; uint16 N[4096*4]; 151 | for( int c=0; c0 ? i-g : i; N1[1] = c0 ? i-1 : i; N1[3] = r 7 | #include 8 | #ifdef USEOMP 9 | #include 10 | #endif 11 | 12 | // return I[x,y] via bilinear interpolation 13 | inline float interp( float *I, int h, int w, float x, float y ) { 14 | x = x<0 ? 0 : (x>w-1.001 ? w-1.001 : x); 15 | y = y<0 ? 0 : (y>h-1.001 ? h-1.001 : y); 16 | int x0=int(x), y0=int(y), x1=x0+1, y1=y0+1; 17 | float dx0=x-x0, dy0=y-y0, dx1=1-dx0, dy1=1-dy0; 18 | return I[x0*h+y0]*dx1*dy1 + I[x1*h+y0]*dx0*dy1 + 19 | I[x0*h+y1]*dx1*dy0 + I[x1*h+y1]*dx0*dy0; 20 | } 21 | 22 | // E = mexFunction(E,O,r,s,m,nThreads) 23 | void mexFunction( int nl, mxArray *pl[], int nr, const mxArray *pr[] ) 24 | { 25 | float *E0 = (float*) mxGetData(pr[0]); // original edge map 26 | float *O = (float*) mxGetData(pr[1]); // orientation map 27 | int r = (int) mxGetScalar(pr[2]); // radius for nms supr 28 | int s = (int) mxGetScalar(pr[3]); // radius for supr boundaries 29 | float m = (float) mxGetScalar(pr[4]); // multiplier for conservative supr 30 | int nThreads = (int) mxGetScalar(pr[5]); // number of threads for evaluation 31 | 32 | int h=(int) mxGetM(pr[0]), w=(int) mxGetN(pr[0]); 33 | pl[0] = mxCreateNumericMatrix(h,w,mxSINGLE_CLASS,mxREAL); 34 | float *E = (float*) mxGetData(pl[0]); 35 | 36 | // suppress edges where edge is stronger in orthogonal direction 37 | #ifdef USEOMP 38 | nThreads = nThreadsw/2?w/2:s; s=s>h/2? h/2:s; 52 | for( int x=0; x 7 | #include 8 | #include 9 | #ifdef USEOMP 10 | #include 11 | #endif 12 | 13 | typedef unsigned int uint; 14 | typedef unsigned char uint8; 15 | template inline T min( T x, T y ) { return x inline T max( T x, T y ) { return xm ? S[x] : m; m++; 27 | float *ns=new float[m](), *mus=new float[m*5](); 28 | for( uint x=0; x0 ? x-1 : x; x1 = x0 ? y-1 : y; y1 = y0) { if(E[a]>E[b]) S[a]=0; else S[b]=0; } 114 | if(y0) { if(E[a]>E[c]) S[a]=0; else S[c]=0; } 115 | } 116 | // add 8-connectivity boundary 117 | #ifdef USEOMP 118 | #pragma omp parallel for num_threads(nThreads) 119 | #endif 120 | for( int xi=0; xim ? S[x] : m; m++; 157 | float *es=new float[m]; for( x=0; xm ? S[x] : m; m++; 190 | float *clrs=new float[m]; uint *cnts=new uint[n]; 191 | for( i=0; im ? S[x] : m; m++; 210 | float *wts=new float[w*h], *Sn=new float[m*m](), *Sd=new float[m*m](); 211 | for(uint i=0; is1) s1=seg[i]; } 240 | s1++; if( s1==1 ) { nTreesConst++; continue; } 241 | // populate per-label wts1 (starting at column 1) 242 | for( i=m1; im ? S[x] : m; 271 | for( x=0; x0; 307 | uint nThreads = (uint) mxGetScalar(pr[3]); 308 | pl[0] = mxCreateNumericMatrix(h,w,mxUINT32_CLASS,mxREAL); 309 | uint* T = (uint*) mxGetData(pl[0]); memcpy(T,S,h*w*sizeof(uint)); 310 | boundaries(T,h,w,E,add,nThreads); 311 | 312 | } else if(!strcmp(action,"merge")) { 313 | // S = merge( S, E, thr ); 314 | float *E = (float*) mxGetData(pr[1]); 315 | float thr = (float) mxGetScalar(pr[2]); 316 | pl[0] = mxCreateNumericMatrix(h,w,mxUINT32_CLASS,mxREAL); 317 | uint* T = (uint*) mxGetData(pl[0]); memcpy(T,S,h*w*sizeof(uint)); 318 | merge(T,h,w,E,thr); 319 | 320 | } else if(!strcmp(action,"visualize")) { 321 | // V = visualize( S, I, hasBnds ) 322 | float *I = (float*) mxGetData(pr[1]); 323 | bool hasBnds = mxGetScalar(pr[2])>0; 324 | const int dims[3] = {h,w,3}; 325 | pl[0] = mxCreateNumericArray(3,dims,mxSINGLE_CLASS,mxREAL); 326 | float* V = (float*) mxGetData(pl[0]); 327 | visualize(V,S,h,w,I,hasBnds); 328 | 329 | } else if(!strcmp(action,"affinities")) { 330 | // A = affinities( S, E, segs, nThreads ) 331 | float *E = (float*) mxGetData(pr[1]); 332 | uint8 *segs = (uint8*) mxGetData(pr[2]); 333 | uint nThreads = (uint) mxGetScalar(pr[3]); 334 | if( mxGetNumberOfDimensions(pr[2])!=5 ) mexErrMsgTxt("invalid input"); 335 | uint *dims = (uint*) mxGetDimensions(pr[2]); 336 | uint m=0; for( uint x=0; xm ? S[x] : m; 337 | pl[0] = mxCreateNumericMatrix(m,m,mxSINGLE_CLASS,mxREAL); 338 | float *A = (float*) mxGetData(pl[0]); 339 | affinities(A,S,h,w,E,segs,dims,nThreads); 340 | 341 | } else if(!strcmp(action,"edges")) { 342 | // E = edges(S,A); 343 | float* A = (float*) mxGetData(pr[1]); 344 | pl[0] = mxCreateNumericMatrix(h,w,mxSINGLE_CLASS,mxREAL); 345 | float *E = (float*) mxGetData(pl[0]); 346 | edges(E,S,h,w,A); 347 | 348 | } else mexErrMsgTxt("Invalid action."); 349 | } 350 | -------------------------------------------------------------------------------- /private/spDetectMex.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/spDetectMex.mexa64 -------------------------------------------------------------------------------- /private/spDetectMex.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/spDetectMex.mexmaci64 -------------------------------------------------------------------------------- /private/spDetectMex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/spDetectMex.mexw64 -------------------------------------------------------------------------------- /private/ucm_mean_pb.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/ucm_mean_pb.mexa64 -------------------------------------------------------------------------------- /private/ucm_mean_pb.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/ucm_mean_pb.mexmaci64 -------------------------------------------------------------------------------- /private/ucm_mean_pb.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dculibrk/edge_boxes_with_python/43d116acc547fd96bd12f31327f98f34bd70c39f/private/ucm_mean_pb.mexw64 -------------------------------------------------------------------------------- /sedt_readme.txt: -------------------------------------------------------------------------------- 1 | ################################################################### 2 | # # 3 | # Structured Edge Detection Toolbox V3.0 # 4 | # Piotr Dollar (pdollar-at-gmail.com) # 5 | # # 6 | ################################################################### 7 | 8 | 1. Introduction. 9 | 10 | Very fast edge detector (up to 60 fps depending on parameter settings) that achieves excellent accuracy. Can serve as input to any vision algorithm requiring high quality edge maps. Toolbox also includes the Edge Boxes object proposal generation method and fast superpixel code. 11 | 12 | If you use the Structured Edge Detection Toolbox, we appreciate it if you cite an appropriate subset of the following papers: 13 | 14 | @inproceedings{DollarICCV13edges, 15 | author = {Piotr Doll\'ar and C. Lawrence Zitnick}, 16 | title = {Structured Forests for Fast Edge Detection}, 17 | booktitle = {ICCV}, 18 | year = {2013}, 19 | } 20 | 21 | @article{DollarARXIV14edges, 22 | author = {Piotr Doll\'ar and C. Lawrence Zitnick}, 23 | title = {Fast Edge Detection Using Structured Forests}, 24 | journal = {ArXiv}, 25 | year = {2014}, 26 | } 27 | 28 | @inproceedings{ZitnickECCV14edgeBoxes, 29 | author = {C. Lawrence Zitnick and Piotr Doll\'ar}, 30 | title = {Edge Boxes: Locating Object Proposals from Edges}, 31 | booktitle = {ECCV}, 32 | year = {2014}, 33 | } 34 | 35 | ################################################################### 36 | 37 | 2. License. 38 | 39 | This code is published under the MSR-LA Full Rights License. 40 | Please read license.txt for more info. 41 | 42 | ################################################################### 43 | 44 | 3. Installation. 45 | 46 | a) This code is written for the Matlab interpreter (tested with versions R2013a-2013b) and requires the Matlab Image Processing Toolbox. 47 | 48 | b) Additionally, Piotr's Matlab Toolbox (version 3.26 or later) is also required. It can be downloaded at: 49 | http://vision.ucsd.edu/~pdollar/toolbox/doc/index.html. 50 | 51 | c) Next, please compile mex code from within Matlab (note: win64/linux64 binaries included): 52 | mex private/edgesDetectMex.cpp -outdir private [OMPPARAMS] 53 | mex private/edgesNmsMex.cpp -outdir private [OMPPARAMS] 54 | mex private/spDetectMex.cpp -outdir private [OMPPARAMS] 55 | mex private/edgeBoxesMex.cpp -outdir private 56 | Here [OMPPARAMS] are parameters for OpenMP and are OS and compiler dependent. 57 | Windows: [OMPPARAMS] = '-DUSEOMP' 'OPTIMFLAGS="$OPTIMFLAGS' '/openmp"' 58 | Linux V1: [OMPPARAMS] = '-DUSEOMP' CFLAGS="\$CFLAGS -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp" 59 | Linux V2: [OMPPARAMS] = '-DUSEOMP' CXXFLAGS="\$CXXFLAGS -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp" 60 | To compile without OpenMP simply omit [OMPPARAMS]; note that code will be single threaded in this case. 61 | 62 | d) Add edge detection code to Matlab path (change to current directory first): 63 | >> addpath(pwd); savepath; 64 | 65 | e) Finally, optionally download the BSDS500 dataset (necessary for training/evaluation): 66 | http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/ 67 | After downloading BSR/ should contain BSDS500, bench, and documentation. 68 | 69 | f) A fully trained edge model for RGB images is available as part of this release. Additional models are available online, including RGBD/D/RGB models trained on the NYU depth dataset and a larger more accurate BSDS model. 70 | 71 | ################################################################### 72 | 73 | 4. Getting Started. 74 | 75 | - Make sure to carefully follow the installation instructions above. 76 | - Please see "edgesDemo.m", "edgeBoxesDemo" and "spDemo.m" to run demos and get basic usage information. 77 | - For a detailed list of functionality see "Contents.m". 78 | 79 | ################################################################### 80 | 81 | 5. History. 82 | 83 | Version NEW 84 | - now hosting on github (https://github.com/pdollar/edges) 85 | - suppress Mac warnings, added Mac binaries 86 | - edgeBoxes: added adaptive nms variant described in arXiv15 paper 87 | 88 | Version 3.01 (09/08/2014) 89 | - spAffinities: minor fix (memory initialization) 90 | - edgesDetect: minor fix (multiscale / multiple output case) 91 | 92 | Version 3.0 (07/23/2014) 93 | - added Edge Boxes code corresponding to ECCV paper 94 | - added Sticky Superpixels code 95 | - edge detection code unchanged 96 | 97 | Version 2.0 (06/20/2014) 98 | - second version corresponding to arXiv paper 99 | - added sharpening option 100 | - added evaluation and visualization code 101 | - added NYUD demo and sweep support 102 | - various tweaks/improvements/optimizations 103 | 104 | Version 1.0 (11/12/2013) 105 | - initial version corresponding to ICCV paper 106 | 107 | ################################################################### 108 | -------------------------------------------------------------------------------- /spAffinities.m: -------------------------------------------------------------------------------- 1 | function [A,E,U] = spAffinities( S, E, segs, nThreads ) 2 | % Compute superpixel affinities and optionally corresponding edge map. 3 | % 4 | % Computes an m x m affinity matrix A where A(i,j) is the affinity between 5 | % superpixels i and j. A has values in [0,1]. Only affinities between 6 | % spatially nearby superpixels are computed; the rest are set to 0. 7 | % 8 | % The affinity between superpixels is computed using the output of the 9 | % structured edge detector. In edgesDetect, by default local predicted 10 | % segmentation masks are converted to edge maps and the overlapping local 11 | % edge maps are subsequently averaged to produce a soft edge map. Instead, 12 | % the local segmentation masks can be directly used to measure affinity 13 | % between superpixels (or any segments). Details are omitted, but the 14 | % resulting affinity reasonably captures superpixels similarity. There is 15 | % no corresponding publication for this code at this time but please cite 16 | % our edge detection work if you use this code. 17 | % 18 | % Given affinities, a corresponding edge map can be computed by setting the 19 | % edge strength between adjacent superpixels to be one minus the affinity 20 | % between them. The advantage of the resulting superpixel edge map over the 21 | % original edge map is that edges are connected and non-maximum suppression 22 | % is unnecessary. Given reasonable superpixels, the superpixel edges have 23 | % high benchmark scores (ODS/OIS/AP) similar to the edges from edgesDetect. 24 | % 25 | % Finally, given the superpixel edges, the ultrametric contour map (UCM) 26 | % can be computed. The UCM is an edge map with the remarkable property that 27 | % when thresholded at any value it produces a set of closed curves (the 28 | % same is not true of the original edge map). In other words the UCM is a 29 | % soft representation of a segmentation. For more details see "From 30 | % Contours to Regions: An Empirical Evaluation" by Pablo Arbelaez et al. in 31 | % CVPR 2009. Computing the UCM requires the mex file ucm_mean_pb. 32 | % Pre-compiled binaries for some systems are provided in /private, source 33 | % for ucm_mean_pb is available as part of the BSDS500 dataset (see readme). 34 | % 35 | % USAGE 36 | % [A,E,U] = spAffinities( S, E, segs, [nThreads] ) 37 | % 38 | % INPUTS 39 | % S - [h x w] superpixel label map (S==0 are boundaries) 40 | % E - [h x w] edge probability map (output of edgesDetect) 41 | % segs - local segmentations (output of edgesDetect) 42 | % nThreads - [4] number of computation threads 43 | % 44 | % OUTPUTS 45 | % A - [m x m] superpixel affinity matrix 46 | % E - [h x w] superpixel edge probability map 47 | % U - [h x w] ultrametric contour map (segmenation) 48 | % 49 | % EXAMPLE 50 | % 51 | % See also spDemo, spDetect, edgesDetect 52 | % 53 | % Structured Edge Detection Toolbox Version 3.01 54 | % Code written by Piotr Dollar, 2014. 55 | % Licensed under the MSR-LA Full Rights License [see license.txt] 56 | 57 | if(nargin<4 || isempty(nThreads)), nThreads=4; end 58 | A = spDetectMex('affinities',S,E,segs,nThreads); 59 | if(nargout>1), E = spDetectMex('edges',S,A); end 60 | if(nargout>2), U = computeUcm( E ); end 61 | 62 | end 63 | 64 | function U = computeUcm( E ) 65 | % creates ultrametric contour map from SP contours 66 | E = upsampleEdges(E); 67 | S=bwlabel(E==0,8); S=S(2:2:end,2:2:end)-1; 68 | S(end,:)=S(end-1,:); S(:,end)=S(:,end-1); 69 | E(end+1,:)=E(end,:); E(:,end+1)=E(:,end); 70 | U=ucm_mean_pb(E,S); U=U(1:2:end-2,1:2:end-2); 71 | end 72 | 73 | function E = upsampleEdges( E0 ) 74 | % upsample E by factor of two while mostly keeping edges thin 75 | [h,w]=size(E0); h=h*2; w=w*2; E=zeros(h,w); E(1:2:h-1,1:2:w-1)=E0; 76 | E(1:2:h-1,2:2:w-2)=min(E0(:,1:end-1),E0(:,2:end)); E(h,:)=E(h-1,:); 77 | E(2:2:h-2,1:2:w-1)=min(E0(1:end-1,:),E0(2:end,:)); E(:,w)=E(:,w-1); 78 | % remove single pixel segments created by thick edges in E0 (2x2 blocks) 79 | A=single(ones(2))/4; A=conv2(single(E0>0),A)==1; [xs,ys]=find(A); 80 | for i = 1:length(xs) 81 | x=(xs(i)-1)*2; y=(ys(i)-1)*2; es=ones(2,4)+1; 82 | if(x>2 && y>2 ), es(:,1)=[E(x-2,y-1) E(x-1,y-2)]; end 83 | if(x2 ), es(:,2)=[E(x+2,y-1) E(x+1,y-2)]; end 84 | if(x2 && y big sp) 12 | opts.alpha = .5; % relative importance of regularity versus data terms 13 | opts.beta = .9; % relative importance of edge versus color terms 14 | opts.merge = 0; % set to small value to merge nearby superpixels at end 15 | 16 | %% detect and display superpixels (see spDetect.m) 17 | I = imread('peppers.png'); 18 | [E,~,~,segs]=edgesDetect(I,model); 19 | tic, [S,V] = spDetect(I,E,opts); toc 20 | figure(1); im(I); figure(2); im(V); 21 | 22 | %% compute ultrametric contour map from superpixels (see spAffinities.m) 23 | tic, [~,~,U]=spAffinities(S,E,segs,opts.nThreads); toc 24 | figure(3); im(1-U); return; 25 | 26 | %% compute video superpixels reusing initialization from previous frame 27 | Is=seqIo(which('peds30.seq'),'toImgs'); Vs=single(Is); opts.bounds=0; tic 28 | for i=1:size(Is,4), I=Is(:,:,:,i); E=edgesDetect(I,model); 29 | [opts.seed,Vs(:,:,:,i)]=spDetect(I,E,opts); end; opts.seed=[]; toc 30 | Vs=uint8(Vs*255); playMovie([Is Vs],15,-10,struct('hasChn',1)) 31 | -------------------------------------------------------------------------------- /spDetect.m: -------------------------------------------------------------------------------- 1 | function [S,V] = spDetect( I, E, varargin ) 2 | % Detect Sticky Superpixels in image. 3 | % 4 | % Detect "Sticky Edge Adhesive Superpixels" in image. High quality, fast 5 | % superpixels that "stick" to edges. Without edge term the code computes 6 | % superpixels using an iterative approach motivated by both SLIC (Achanta 7 | % et al., PAMI12) and SEEDS (Bergh et al., ECCV12) superpixels. With edge 8 | % term added, the superpixels snap to edges, resulting in higher quality 9 | % boundaries. There is no corresponding publication for this code at this 10 | % time but please cite our edge detection work if you use this code. 11 | % 12 | % The most important parameter is k which controls superpixel scale. 13 | % Note that the edge image E is optional (that is E=[] may be used). 14 | % 15 | % USAGE 16 | % opts = spDetect() 17 | % [S,V] = spDetect( I, [E], [opts] ) 18 | % 19 | % INPUTS 20 | % I - [h x w x 3] color input image (in [0,255]) 21 | % E - [h x w] type single edge image (in [0,1]), or [] array 22 | % opts - parameters (struct or name/value pairs) 23 | % .type - ['sticky'] options are 'sticky' or 'watershed' 24 | % .nIter - [4] number of iterations 25 | % .nThreads - [4] number of computation threads 26 | % .k - [512] controls scale of superpixels (big k -> big sp) 27 | % .alpha - [.5] relative importance of regularity versus data terms 28 | % .beta - [.9] relative importance of edge versus color terms 29 | % .merge - [0] set to small value to merge nearby superpixels at end 30 | % .bounds - [1] if true add boundaries to superpixels 31 | % .seed - [] optional initial seed superpixels 32 | % 33 | % OUTPUTS 34 | % S - [h x w] superpixel label map (S==0 are boundaries) 35 | % V - [h x w] superpixel visualization 36 | % 37 | % EXAMPLE 38 | % 39 | % See also spDemo, spAffinities, watershed 40 | % 41 | % Structured Edge Detection Toolbox Version 3.01 42 | % Code written by Piotr Dollar, 2014. 43 | % Licensed under the MSR-LA Full Rights License [see license.txt] 44 | 45 | % get default parameters 46 | dfs = { 'type','sticky', 'nIter',4, 'nThreads',4, 'k',512, ... 47 | 'alpha',.5, 'beta',.9, 'merge',0, 'bounds',1, 'seed',[] }; 48 | o = getPrmDflt(varargin,dfs,1); if(nargin==0), S=o; return; end 49 | type=lower(o.type(1)); assert( type=='w' || type=='s' ); 50 | sigs = [ o.k*o.alpha/1e4 o.alpha/1e4 ... 51 | (1-o.alpha)*o.beta (1-o.alpha)*(1-o.beta) ]; 52 | 53 | % check dimensions and type of image and edge map 54 | [h,w,~]=size(I); assert(isa(I,'uint8') && size(I,3)==3); 55 | if(nargin<2 || isempty(E)), E=zeros(h,w,'single'); end 56 | assert(isa(E,'single') && size(E,1)==h && size(E,2)==w); 57 | I=rgbConvert(I,'rgb'); 58 | 59 | if( type=='w' ) 60 | % run watershed algorithm 61 | S = uint32(watershed(convTri(E,1))); b=1; 62 | 63 | else 64 | if( ~isempty(o.seed) ) 65 | % utilize seed segmentation removing boundaries if necessary 66 | S = o.seed; assert(isa(S,'uint32') && size(S,1)==h && size(S,2)==w); 67 | if(o.bounds), S = spDetectMex('boundaries',S,E,0,o.nThreads); end 68 | 69 | else 70 | % initialize superpixels at half resolution 71 | s=1/2; h1 = h-mod(h,1/s); w1 = w-mod(w,1/s); 72 | I0 = imResample(I(1:h1,1:w1,:),s); 73 | E0 = imResample(E(1:h1,1:w1),s); 74 | S = uint32(reshape(0:h1*w1*s*s-1,h1*s,w1*s)); 75 | 76 | % refine superpixels at half resolution 77 | p = [o.nIter*2 o.nThreads sigs(1)*s*s sigs(2)/s/s sigs(3:4)]; 78 | S = spDetectMex('sticky',S,convTri(I0,1),E0,p); 79 | S = imResample(S,1/s,'nearest'); 80 | S = uint32(imPad(single(S),[0 h-h1 0 w-w1],'replicate')); 81 | end 82 | 83 | % refine superpixels at full resolution 84 | p = [o.nIter o.nThreads sigs]; b=0; 85 | S = spDetectMex('sticky',S,convTri(I,1),E,p); 86 | 87 | end 88 | 89 | % add or remove superpixel boundaries as necessary 90 | if(o.bounds~=b), S = spDetectMex('boundaries',S,E,o.bounds,o.nThreads); end 91 | 92 | % optionally merge superpixels 93 | if(o.merge>0 && o.bounds), S = spDetectMex('merge',S,E,o.merge); end 94 | 95 | % optionally create visualization 96 | if(nargout>=2), V=spDetectMex('visualize',S,I,o.bounds); end 97 | 98 | end 99 | --------------------------------------------------------------------------------