├── results ├── test1.jpg ├── test2.png ├── test3.jpg ├── test4.jpg ├── test5.jpg ├── test6.jpg ├── test1_cut.png ├── test2_cut.png ├── test3_cut.png ├── test4_cut.png ├── test5_cut.png └── test6_cut.png ├── bin_graphcuts ├── bin │ └── bk_matlab.mexmaci64 ├── BK_Delete.m ├── BK_GetLabeling.m ├── BK_Minimize.m ├── BK_AddVars.m ├── BK_ListHandles.m ├── test2.jpg.html ├── BK_LoadLib.m ├── BK_Create.m ├── BK_SetPairwise.m ├── BK_SetUnary.m ├── BK_SetNeighbors.m ├── BK_BuildLib.m ├── graph.cpp ├── BK_UnitTest.m ├── block.h ├── energy.h ├── bk_matlab.cpp ├── graph.h └── maxflow.cpp ├── get_rgb_double.m ├── compute_unary.m ├── fit_gmm.m ├── select_back.m ├── update_gmm.m ├── assign_gauss.m ├── cut_Tu.m ├── README.md ├── grabcut.m └── compute_pairwise.m /results/test1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test1.jpg -------------------------------------------------------------------------------- /results/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test2.png -------------------------------------------------------------------------------- /results/test3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test3.jpg -------------------------------------------------------------------------------- /results/test4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test4.jpg -------------------------------------------------------------------------------- /results/test5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test5.jpg -------------------------------------------------------------------------------- /results/test6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test6.jpg -------------------------------------------------------------------------------- /results/test1_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test1_cut.png -------------------------------------------------------------------------------- /results/test2_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test2_cut.png -------------------------------------------------------------------------------- /results/test3_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test3_cut.png -------------------------------------------------------------------------------- /results/test4_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test4_cut.png -------------------------------------------------------------------------------- /results/test5_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test5_cut.png -------------------------------------------------------------------------------- /results/test6_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/results/test6_cut.png -------------------------------------------------------------------------------- /bin_graphcuts/bin/bk_matlab.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiumingzhang/grabcut/HEAD/bin_graphcuts/bin/bk_matlab.mexmaci64 -------------------------------------------------------------------------------- /bin_graphcuts/BK_Delete.m: -------------------------------------------------------------------------------- 1 | function BK_Delete(Handle) 2 | % BK_Delete Delete a BK object. 3 | % BK_Delete(Handle) deletes the object corresponding to Handle 4 | % and frees its memory. 5 | 6 | bk_matlab('bk_delete',int32(Handle)); 7 | end 8 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_GetLabeling.m: -------------------------------------------------------------------------------- 1 | function Labeling = BK_GetLabeling(Handle) 2 | % BK_GetLabeling Retrieve the current labeling 3 | % BK_GetLabeling(Handle) returns a column vector of all labels. 4 | 5 | BK_LoadLib(); 6 | Labeling = bk_matlab('bk_getlabeling',Handle); 7 | end 8 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_Minimize.m: -------------------------------------------------------------------------------- 1 | function Energy = BK_Minimize(Handle) 2 | % BK_Minimize Compute optimal labeling via graph cut 3 | % Returns the energy of the computed labeling. 4 | % The labeling itself can be retrieved via GCO_GetLabeling. 5 | % 6 | 7 | BK_LoadLib(); 8 | Energy = bk_matlab('bk_minimize',Handle); 9 | end 10 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_AddVars.m: -------------------------------------------------------------------------------- 1 | function id = BK_AddVars(Handle,Count) 2 | % BK_AddVars 3 | % id = BK_AddVars(Handle,Count) adds Count variables to the 4 | % binary energy with consecutive ids, returning the first id. 5 | 6 | if (nargin < 2), error('expected two input arguments'); end 7 | 8 | BK_LoadLib(); 9 | id = bk_matlab('bk_addvars',Handle,int32(Count)); 10 | end 11 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_ListHandles.m: -------------------------------------------------------------------------------- 1 | function Handles = BK_ListHandles() 2 | % BK_ListHandles Retrieve handles to all current BK instances 3 | % Useful for cleaning up BK instances that are using memory, 4 | % particularly when a script was interrupted. 5 | % Example: 6 | % BK_Delete(BK_ListHandles); % delete all BK instances 7 | 8 | BK_LoadLib(); 9 | Handles = bk_matlab('bk_listhandles'); 10 | end 11 | -------------------------------------------------------------------------------- /bin_graphcuts/test2.jpg.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /get_rgb_double.m: -------------------------------------------------------------------------------- 1 | function rgb = get_rgb_double(im, x, y) 2 | %GET_RGB_DOUBLE Part of GrabCut. Obtain a RGB tuple in double 3 | % 4 | % Inputs: 5 | % - im: 2D image 6 | % - x: horizontal position 7 | % - y: vertical position 8 | % 9 | % Output: 10 | % - rgb: double 3-vector 11 | % 12 | % Author: 13 | % Xiuming Zhang 14 | % GitHub: xiumingzhang 15 | % Dept. of ECE, National University of Singapore 16 | % April 2015 17 | % 18 | 19 | r = im(y, x, 1); 20 | g = im(y, x, 2); 21 | b = im(y, x, 3); 22 | 23 | rgb = double([r g b]); 24 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_LoadLib.m: -------------------------------------------------------------------------------- 1 | function BK_LoadLib() 2 | % BK_LoadLib Attempt to load the BK_MATLAB library. 3 | % BK_LoadLib is used internally by all other BK_MATLAB commands 4 | % to compile (if necessary), load, and bind the wrapper library. 5 | 6 | if (isempty(getenv('BK_MATLAB'))) 7 | BK_BuildLib(struct('Force',false)); 8 | if (exist('bk_matlab') ~= 3) 9 | error('Failed to load bk_matlab library'); 10 | end 11 | warning on BK:int32; 12 | setenv('BK_MATLAB','LOADED'); % environment variables 10x faster than 'exists' 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_Create.m: -------------------------------------------------------------------------------- 1 | function Handle = BK_Create(NumVars,MaxEdges) 2 | % BK_Create Create a BK object. 3 | % Handle = BK_Create() creates a new BK object and returns a 'handle' 4 | % to uniquely identify it. Use BK_AddVars to add variables. 5 | % 6 | % Handle = BK_Create(NumVars) creates a new BK object with NumVars 7 | % variables already created. 8 | % 9 | % Handle = BK_Create(NumVars,MaxEdges) pre-allocates memory for up to m edges. 10 | % 11 | % Call BK_Delete(Handle) to delete the object and free its memory. 12 | % Call BK_Delete(BK_ListHandles) to delete all BK objects. 13 | 14 | BK_LoadLib(); 15 | if (nargin < 1), NumVars = 0; end 16 | if (nargin < 2), MaxEdges = 0; end 17 | Handle = bk_matlab('bk_create',int32(NumVars),int32(MaxEdges)); 18 | end 19 | -------------------------------------------------------------------------------- /compute_unary.m: -------------------------------------------------------------------------------- 1 | function D_min = compute_unary(rgb_pt, gmm_params) 2 | %COMPUTE_UNARY Part of GrabCut. Compute unary (data) term 3 | % 4 | % Inputs: 5 | % - rgb_pt: a RGB point 6 | % - gmm_params: GMM parameters 7 | % 8 | % Output: 9 | % - D_min: minimum D 10 | % 11 | % Author: 12 | % Xiuming Zhang 13 | % GitHub: xiumingzhang 14 | % Dept. of ECE, National University of Singapore 15 | % April 2015 16 | % 17 | 18 | % The pixel's D to all Gaussians in this GMM 19 | pix_D = zeros(size(gmm_params, 1), 1); 20 | 21 | % For every Gaussian 22 | for idx = 1:size(gmm_params, 1) 23 | pi_coeff = gmm_params{idx, 1}; 24 | mu = gmm_params{idx, 2}; 25 | sigma = gmm_params{idx, 3}; 26 | 27 | val = -log(mvnpdf(rgb_pt, mu, sigma))-log(pi_coeff)-1.5*log(2*pi); 28 | pix_D(idx) = val; 29 | end 30 | 31 | D_min = min(pix_D); 32 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_SetPairwise.m: -------------------------------------------------------------------------------- 1 | function BK_SetPairwise(Handle,Edges) 2 | % BK_SetPairwise Set full pairwise potentials of all sites. 3 | % BK_SetPairwise(Handle,Edges) determines which sites are neighbors 4 | % and what their specific interaction potential is. 5 | % Edges is a dense NumEdges-by-6 matrix of doubles 6 | % (or int32 of CostType is 'int32'; see BK_BuildLib). 7 | % Each row is of the format [i,j,e00,e01,e10,e11] where i and j 8 | % are neighbours and the four coefficients define the interaction 9 | % potential. 10 | % SetNeighbors cannot currently be called after Minimize. 11 | 12 | BK_LoadLib(); 13 | if (size(Edges,2) ~= 6) % Check [i,j,e00,e01,e10,e11] format 14 | error('Edges must be of size [ NumEdges 6 ]'); 15 | end 16 | bk_matlab('bk_setpairwise',Handle,Edges); 17 | end 18 | -------------------------------------------------------------------------------- /fit_gmm.m: -------------------------------------------------------------------------------- 1 | function gmm_param = fit_gmm(rgb_pts, labels) 2 | %FIT_GMM Part of GrabCut: Fit a GMM with K Gaussians, 3 | % where K=numel(unique(labels)) 4 | % 5 | % Inputs: 6 | % - rgb_pts: Nx3 matrix holding N points in RGB space 7 | % - labels: Nx1 label vector 8 | % 9 | % Output: 10 | % - gmm_param: Kx3 parameter matrix, 11 | % column 1 for \pi, 2 for \mu, 3 for \sigma 12 | % 13 | % Author: 14 | % Xiuming Zhang 15 | % GitHub: xiumingzhang 16 | % Dept. of ECE, National University of Singapore 17 | % April 2015 18 | % 19 | 20 | no_gauss = numel(unique(labels)); 21 | 22 | gmm_param = cell(no_gauss, 3); 23 | 24 | % For each Gaussian 25 | for idx = 1:no_gauss 26 | pts = rgb_pts(labels==idx, :); 27 | gmm_param{idx, 1} = size(pts, 1)/size(rgb_pts, 1); % pi 28 | gmm_param{idx, 2} = mean(pts, 1); 29 | gmm_param{idx, 3} = cov(pts); 30 | end 31 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_SetUnary.m: -------------------------------------------------------------------------------- 1 | function BK_SetUnary(Handle,Costs) 2 | % BK_SetUnary Set the unary cost of individual variables. 3 | % BK_SetUnary(Handle,Costs) accepts a 2-by-NumVars 4 | % int32 matrix where Costs(k,i) is the cost of assigning 5 | % label k to site i. In this case, the MATLAB matrix is pointed to 6 | % by the C++ code, and so a DataCost array is not copied. 7 | % 8 | % TODO: document behaviour for dynamic graph cuts 9 | % 10 | 11 | BK_LoadLib(); 12 | if (nargin ~= 2), error('Expected 2 arguments'); end 13 | if (~isnumeric(Costs)), error('Costs must be numeric'); end 14 | if (~isreal(Costs)), error('Costs cannot be complex'); end 15 | NumSites = bk_matlab('bk_getnumsites', Handle); 16 | if (any(size(Costs) ~= [ 2 NumSites ])) 17 | error('Costs size must be [ 2 NumSites ]'); 18 | end 19 | if (~isa(Costs,'int32') && strcmp(bk_matlab('bk_getcosttype'),'int32')) 20 | if (NumSites*2 > 200 || any(any(floor(Costs) ~= Costs))) 21 | warning('BK:int32','Costs converted to int32'); 22 | end 23 | Costs = int32(Costs); 24 | end 25 | bk_matlab('bk_setunary',Handle,Costs); 26 | end 27 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_SetNeighbors.m: -------------------------------------------------------------------------------- 1 | function BK_SetNeighbor(Handle,Weights) 2 | % BK_SetNeighbors Set sparse pairwise connectivity of all sites. 3 | % BK_SetNeighbors(Handle,Weights) determines which sites are 4 | % neighbors and thereby have a weighted Potts interaction. 5 | % Weights is a sparse NumSites-by-NumSites matrix of doubles, where 6 | % Weights(i,j) > 0 indicates that sites i and j are neighbors 7 | % with a Sparse potential of the given strength. 8 | % IMPORTANT: only the upper-triangular area of Weights is consulted 9 | % because the connectivity is undirected. 10 | % SetNeighbors cannot currently be called after Minimize. 11 | 12 | BK_LoadLib(); 13 | NumSites = bk_matlab('bk_getnumsites',Handle); 14 | if (any(size(Weights) ~= [ NumSites NumSites ])) 15 | error('Neighbors must be of size [ NumSites NumSites ]'); 16 | end 17 | if (~issparse(Weights)) 18 | if (NumSites > 100) 19 | warning('Sparsifying the Neighbors matrix (performance warning)'); 20 | end 21 | if (~isa(Weights,'double')) 22 | error('Neighbors matrix must be of type double, but with integral values'); 23 | end 24 | Weights = sparse(Weights); 25 | end 26 | bk_matlab('bk_setneighbors',Handle,Weights); 27 | end 28 | -------------------------------------------------------------------------------- /select_back.m: -------------------------------------------------------------------------------- 1 | function [im_1d, alpha, im_sub] = select_back(im_in) 2 | %SELECT_BACK Part of GrabCut: User draws a rectangle, out of which 3 | %becomes background 4 | % 5 | % Inputs: 6 | % - im_in: input color image, e.g., a 100x100x3 matrix 7 | % 8 | % Output: 9 | % - im_1d: image in 1D, columns in 2D images are concatenated 10 | % - alpha: labels in 1D, same convention as image 11 | % - im_sub: subimage for Graph Cut 12 | % 13 | % Author: 14 | % Xiuming Zhang 15 | % GitHub: xiumingzhang 16 | % Dept. of ECE, National University of Singapore 17 | % April 2015 18 | % 19 | 20 | % Get image dimensions 21 | [im_h, im_w, ~] = size(im_in); 22 | % Compute #. nodes (pixels) 23 | no_nodes = im_h*im_w; 24 | 25 | % Select rectangle 26 | imshow(im_in); 27 | rect = getrect; 28 | rect = int32(rect); 29 | 30 | % Set labels 31 | alpha_2d = zeros(im_h, im_w); % 1 for foreground, 0 for background 32 | xmin = max(rect(1), 1); 33 | ymin = max(rect(2), 1); 34 | xmax = min(xmin+rect(3), im_w); 35 | ymax = min(ymin+rect(4), im_h); 36 | 37 | alpha_2d(ymin:ymax, xmin:xmax) = 1; 38 | 39 | im_sub = im_in(ymin:ymax, xmin:xmax, :); 40 | 41 | % Serialize the 2D image into 1D 42 | 43 | im_1d = zeros(no_nodes, 3); 44 | alpha = zeros(no_nodes, 1); 45 | for idx = 1:size(im_in, 2) 46 | im_1d((idx-1)*im_h+1:idx*im_h, :) = im_in(:, idx, :); 47 | alpha((idx-1)*im_h+1:idx*im_h) = alpha_2d(:, idx); 48 | end 49 | -------------------------------------------------------------------------------- /update_gmm.m: -------------------------------------------------------------------------------- 1 | function [gmm_U, gmm_B] = update_gmm(im_1d, pix_U, k_U, pix_B, k_B) 2 | %UPDATE_GMM Part of GrabCut. Update the GMM parameters with newly assigned 3 | %data 4 | % 5 | % Inputs: 6 | % - im_1d: 1D RGB image 7 | % - pix_U: Logical indices for foreground 8 | % - k_U: their Gaussian memberships in the GMM 9 | % - pix_B: Logical indices for background 10 | % - k_B: their Gaussian memberships in the GMM 11 | % 12 | % Output: 13 | % - gmm_U: updated GMM for T_U pixels 14 | % - gmm_B: updated GMM for T_B pixels 15 | % 16 | % Author: 17 | % Xiuming Zhang 18 | % GitHub: xiumingzhang 19 | % Dept. of ECE, National University of Singapore 20 | % April 2015 21 | % 22 | 23 | %----------- T_U 24 | 25 | rgb_pts = im_1d(pix_U, :); 26 | 27 | no_gauss = numel(unique(k_U)); 28 | 29 | gmm_U = cell(no_gauss, 3); 30 | 31 | % For each Gaussian 32 | for idx = 1:no_gauss 33 | pts = rgb_pts(k_U==idx, :); 34 | gmm_U{idx, 1} = size(pts, 1)/size(rgb_pts, 1); % pi 35 | gmm_U{idx, 2} = mean(pts, 1); 36 | gmm_U{idx, 3} = cov(pts); 37 | end 38 | 39 | %----------- T_B 40 | 41 | rgb_pts = im_1d(pix_B, :); 42 | 43 | no_gauss = numel(unique(k_B)); 44 | 45 | gmm_B = cell(no_gauss, 3); 46 | 47 | % For each Gaussian 48 | for idx = 1:no_gauss 49 | pts = rgb_pts(k_B==idx, :); 50 | gmm_B{idx, 1} = size(pts, 1)/size(rgb_pts, 1); % pi 51 | gmm_B{idx, 2} = mean(pts, 1); 52 | gmm_B{idx, 3} = cov(pts); 53 | end 54 | 55 | -------------------------------------------------------------------------------- /assign_gauss.m: -------------------------------------------------------------------------------- 1 | function [k_U, k_B] = assign_gauss(im_1d, pix_U, gmm_U, pix_B, gmm_B) 2 | %ASSIGN_GAUSS Assign pixels in T_U and T_B to the most probable Gaussians 3 | %in gmm_U and gmm_B, repspectively. 4 | % 5 | % Inputs: 6 | % - im_1d: Nx3 RGB points 7 | % - pix_U: Logical indices for foreground 8 | % - gmm_U: GMM for foreground 9 | % - pix_B: Logical indices for background 10 | % - gmm_B: GMM for background 11 | % 12 | % Output: 13 | % - k_U: new label assignments for the T_U pixels 14 | % - k_B: new label assignments for the T_B pixels 15 | % 16 | % Author: 17 | % Xiuming Zhang 18 | % GitHub: xiumingzhang 19 | % Dept. of ECE, National University of Singapore 20 | % April 2015 21 | % 22 | 23 | %-------------- T_U 24 | 25 | % Each row holds a T_U pixel's D to all Gaussians in the T_U GMM 26 | pix_D = zeros(sum(pix_U), size(gmm_U, 1)); 27 | 28 | rgb_pts = im_1d(pix_U, :); 29 | 30 | % For every Gaussian 31 | for idx = 1:size(gmm_U, 1) 32 | pi_coeff = gmm_U{idx, 1}; 33 | mu = gmm_U{idx, 2}; 34 | sigma = gmm_U{idx, 3}; 35 | 36 | col = -log(mvnpdf(rgb_pts, mu, sigma))-log(pi_coeff)-1.5*log(2*pi); 37 | pix_D(:, idx) = col; 38 | end 39 | 40 | [~, k_U] = min(pix_D, [], 2); 41 | 42 | %-------------- T_B 43 | 44 | % Each row holds a T_U pixel's D to all Gaussians in the T_U GMM 45 | pix_D = zeros(sum(pix_B), size(gmm_B, 1)); 46 | 47 | rgb_pts = im_1d(pix_B, :); 48 | 49 | % For every Gaussian 50 | for idx = 1:size(gmm_B, 1) 51 | pi_coeff = gmm_B{idx, 1}; 52 | mu = gmm_B{idx, 2}; 53 | sigma = gmm_B{idx, 3}; 54 | 55 | col = -log(mvnpdf(rgb_pts, mu, sigma))-log(pi_coeff)-1.5*log(2*pi); 56 | pix_D(:, idx) = col; 57 | end 58 | 59 | [~, k_B] = min(pix_D, [], 2); 60 | 61 | -------------------------------------------------------------------------------- /cut_Tu.m: -------------------------------------------------------------------------------- 1 | function [pix_U_new, E] = cut_Tu(pix_U, im_sub, alpha, gmm_U, gmm_B, pairwise) 2 | %CUT_TU Part of GrabCut. Relabel with Graph Cut the pixels in T_U as 3 | %either still T_U (1) or T_B (0) 4 | % 5 | % Inputs: 6 | % - pix_U: logical indices for forground 7 | % - im_sub: 2D subimage, on which Graph Cut is performed 8 | % - alpha: initial foreground indices 9 | % - gmm_U: parameters of the T_U GMM 10 | % - gmm_B: parameters of the T_B GMM 11 | % - pairwise: pairwise term computed beforehand 12 | % 13 | % Output: 14 | % - pix_U: IDs of pixels who are still in T_U after Graph Cut 15 | % - E: energy after Graph Cut 16 | % 17 | % Author: 18 | % Xiuming Zhang 19 | % GitHub: xiumingzhang 20 | % Dept. of ECE, National University of Singapore 21 | % April 2015 22 | % 23 | 24 | % Get image dimensions 25 | [im_h, im_w, ~] = size(im_sub); 26 | % Compute #. nodes (pixels) 27 | no_nodes = im_h*im_w; 28 | 29 | %------------------- Compute MRF edge values 30 | 31 | unary = zeros(2, no_nodes); 32 | 33 | % Loop through all the pixels (nodes) and set pairwise and label_costs 34 | fprintf('Computing unary terms...\n'); 35 | for y = 1:im_h 36 | fprintf('%d/%d ', y, im_h); 37 | for x = 1:im_w 38 | % Current node 39 | color = get_rgb_double(im_sub, x, y); 40 | node = (x-1)*im_h+y; 41 | 42 | %------ Compute data term 43 | % 1 for foreground 44 | % 2 for backround 45 | unary(1, node) = compute_unary(color, gmm_U); 46 | unary(2, node) = compute_unary(color, gmm_B); 47 | end 48 | end 49 | 50 | %------------------- Create MRF 51 | 52 | mrf = BK_Create(no_nodes); 53 | % Nodes are arranged in 1-D, following this order 54 | % (1, 1), (2, 1), ..., (573, 1), (1, 2), (2, 2), ... 55 | 56 | %------------------- Set unary 57 | 58 | BK_SetUnary(mrf, unary); 59 | 60 | %------------------- Set pairwise 61 | 62 | BK_SetPairwise(mrf, pairwise); 63 | 64 | %------------------- Graphcuts 65 | 66 | E = BK_Minimize(mrf); 67 | gc_labels = BK_GetLabeling(mrf); 68 | 69 | BK_Delete(mrf); 70 | 71 | %------------------- Kick out T_B pixels from T_U 72 | 73 | pix_U_new = pix_U; 74 | 75 | % Replace initial labels with Graph Cut labels 76 | alpha(alpha==1) = gc_labels; 77 | 78 | % Remove background pixels from T_U 79 | pix_U_new(alpha==2) = 0; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grabcut 2 | A MATLAB Implementation of GrabCut (Excluding Border Matting and User Editing) 3 | 4 | This project implements 5 | 6 | @article{rother2004grabcut, 7 | title={Grabcut: Interactive foreground extraction using iterated graph cuts}, 8 | author={Rother, Carsten and Kolmogorov, Vladimir and Blake, Andrew}, 9 | journal={ACM Transactions on Graphics (TOG)}, 10 | volume={23}, 11 | number={3}, 12 | pages={309--314}, 13 | year={2004}, 14 | publisher={ACM} 15 | } 16 | 17 | in MATLAB, and it *excludes* border matting and user editing. That is, it implements everything up to 18 | > Iterative minimisation 4. Repeat from step 1 until convergence 19 | 20 | as in Figure 3 in the original paper. 21 | 22 | ## Results 23 | 24 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test1.jpg)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test1_cut.png) 25 | 26 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test2.png)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test2_cut.png) 27 | 28 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test3.jpg)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test3_cut.png) 29 | 30 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test4.jpg)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test4_cut.png) 31 | 32 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test5.jpg)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test5_cut.png) 33 | 34 | ![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test6.jpg)![](https://raw.githubusercontent.com/xiumingzhang/grabcut/master/results/test6_cut.png) 35 | 36 | ## Example Usage 37 | 38 | GAMMA = 20; 39 | % Inputs and parameters 40 | im_in = imread('./grabcut/results/test4.jpg'); 41 | % GrabCut 42 | im_out = grabcut(im_in, GAMMA); 43 | imwrite(im_out, './grabcut/results/test4_out.jpg'); 44 | 45 | ### Acknowledgement 46 | 47 | The author would like to thank the Computer Vision Research Group at the University of Western Ontario for making their implementation of the max-flow/min-cut algorithm publicly available [here](http://vision.csd.uwo.ca/wiki/vision/upload/d/d7/Bk_matlab.zip). 48 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_BuildLib.m: -------------------------------------------------------------------------------- 1 | function BK_BuildLib(Options) 2 | % BK_BuildLib Attempt to compile and link the BK_MATLAB library. 3 | % BK_BuildLib is used internally by all other BK_MATLAB commands 4 | % to recompile the wrapper library if it is not yet built. 5 | % 6 | % YOU DO NOT NEED TO EXPLICITLY CALL THIS FUNCTION, unless you want to 7 | % customise the build settings via BK_BuildLib(Options). 8 | % Default options: 9 | % Options.Debug=0 % optimised, detailed checking disabled 10 | % Options.CostType='double' % type of energy terms 11 | % 12 | % Example: 13 | % % Enable detailed assertions (e.g. than energy does not go up 14 | % % during expansion) and use 32-bit energy counters (slightly faster) 15 | % BK_BuildLib(struct('Debug',1,'CostType','int32')); 16 | % 17 | 18 | if (nargin < 1) 19 | Options = struct(); 20 | end 21 | if (~isfield(Options,'Debug')), Options.Debug = 0; end 22 | if (~isfield(Options,'CostType')), Options.CostType = 'double'; end 23 | if (~isfield(Options,'Force')), Options.Force = 1; end 24 | 25 | MEXFLAGS = ''; 26 | if (strcmp(computer(),'GLNXA64') || strcmp(computer(),'PCWIN64') || strcmp(computer(),'MACI64')) 27 | MEXFLAGS = [MEXFLAGS ' -largeArrayDims -DA64BITS']; 28 | end 29 | if (Options.Debug) 30 | MEXFLAGS = [MEXFLAGS ' -g']; 31 | end 32 | if (strcmp(Options.CostType,'double')) 33 | MEXFLAGS = [MEXFLAGS ' -DBK_COSTTYPE=0']; 34 | elseif (strcmp(Options.CostType,'int32')) 35 | MEXFLAGS = [MEXFLAGS ' -DBK_COSTTYPE=1']; 36 | end 37 | if (strcmp(computer(),'PCWIN')) % link with libut for user interruptibility 38 | MEXFLAGS = [MEXFLAGS ' -D_WIN32 "' matlabroot() '\extern\lib\win32\microsoft\libut.lib"' ]; 39 | elseif (strcmp(computer(),'PCWIN64')) 40 | MEXFLAGS = [MEXFLAGS ' -D_WIN64 "' matlabroot() '\extern\lib\win64\microsoft\libut.lib"' ]; 41 | else 42 | MEXFLAGS = [MEXFLAGS ' -lut' ]; 43 | end 44 | 45 | LIB_NAME = 'bk_matlab'; 46 | BKDIR = fileparts(mfilename('fullpath')); 47 | OUTDIR = [ BKDIR filesep 'bin' ]; 48 | [status msg msgid] = mkdir(BKDIR, 'bin'); % Create bin directory 49 | addpath(OUTDIR); % and add it to search path 50 | if (~Options.Force && exist('bk_matlab')==3) 51 | return; 52 | end 53 | clear bk_matlab; 54 | 55 | mexcmd = ['mex ' MEXFLAGS ' -outdir ''' OUTDIR ''' -output ' LIB_NAME ' ' ]; 56 | 57 | % Append all source file names to the MEX command string 58 | SRCCPP = { 59 | [BKDIR filesep 'bk_matlab.cpp'] 60 | }; 61 | for f=1:length(SRCCPP) 62 | mexcmd = [mexcmd ' ''' SRCCPP{f} ''' ']; 63 | end 64 | 65 | eval(mexcmd); % compile and link in one step 66 | 67 | end 68 | -------------------------------------------------------------------------------- /grabcut.m: -------------------------------------------------------------------------------- 1 | function im_out = grabcut(im_in, gamma) 2 | %GRABCUT Foreground extraction with GrabCut 3 | % 4 | % Inputs: 5 | % - im_in: input image, e.g., a 100x100x3 matrix 6 | % - gamma: gamma parameter 7 | % 8 | % Output: 9 | % - im_out: the extracted foreground 10 | % 11 | % Author: 12 | % Xiuming Zhang 13 | % GitHub: xiumingzhang 14 | % Dept. of ECE, National University of Singapore 15 | % April 2015 16 | % 17 | 18 | % Convergence criterion 19 | E_CHANGE_THRES = 0.0001; 20 | 21 | %%% Get image dimensions 22 | [im_h, ~, ~] = size(im_in); 23 | 24 | %--------------------------- I. Initialization 25 | 26 | %%% User indicates background 27 | [im_1d, alpha, im_sub] = select_back(im_in); 28 | 29 | pix_U = alpha==1; 30 | T_U = im_1d(pix_U, :); 31 | pix_B = ~pix_U; 32 | T_B = im_1d(pix_B, :); 33 | 34 | %%% Initialize GMM 35 | no_gauss = 5; % 5 Gaussians in each GMM 36 | % Background 37 | k_B = kmeans(T_B, no_gauss, 'Distance', 'cityblock', 'Replicates', 5); 38 | gmm_B = fit_gmm(T_B, k_B); 39 | % Foreground 40 | k_U = kmeans(T_U, no_gauss, 'Distance', 'cityblock', 'Replicates', 5); 41 | gmm_U = fit_gmm(T_U, k_U); 42 | 43 | %--------------------------- II. Iterative Minimization 44 | 45 | %%% Compute pairwise in one shot 46 | pairwise = compute_pairwise(im_sub, gamma); 47 | fprintf('Pairwise terms computed in one shot\n'); 48 | 49 | isConverged = 0; 50 | E_prev = +Inf; 51 | iter = 0; 52 | while ~isConverged 53 | 54 | %------- 1. Assign GMM components to pixels 55 | 56 | [k_U, k_B] = assign_gauss(im_1d, pix_U, gmm_U, pix_B, gmm_B); 57 | 58 | %------- 2. Learn GMM parameters from data 59 | 60 | [gmm_U, gmm_B] = update_gmm(im_1d, pix_U, k_U, pix_B, k_B); 61 | 62 | %------- 3. Estimate segmentation: use min cut to solve 63 | 64 | [pix_U, E] = cut_Tu(pix_U, im_sub, alpha, gmm_U, gmm_B, pairwise); 65 | 66 | %%% Report progress 67 | E_change = (E_prev-E)/E_prev; 68 | iter = iter+1; 69 | fprintf('\n'); 70 | fprintf('Iter %i done, E drops by %.3f%% (converged when < %.3f%%)\n', iter, E_change*100, E_CHANGE_THRES*100); 71 | 72 | %%% Check convergence 73 | if E_change < E_CHANGE_THRES 74 | isConverged = 1; 75 | end 76 | 77 | %%% Update for next iteration 78 | pix_B = ~pix_U; 79 | E_prev = E; 80 | 81 | %------- Display current result 82 | 83 | im_out = im_in; 84 | im_out_1d = im_1d; 85 | % Set background to white 86 | im_out_1d(pix_B, :) = 255; 87 | % Assemble the 1D image back into 2D 88 | for idx = 1:size(im_out, 2) 89 | im_out(:, idx, :) = im_out_1d((idx-1)*im_h+1:idx*im_h, :); 90 | end 91 | imshow(im_out); 92 | drawnow; 93 | 94 | end 95 | 96 | -------------------------------------------------------------------------------- /compute_pairwise.m: -------------------------------------------------------------------------------- 1 | function pairwise = compute_pairwise(im_sub, gamma) 2 | %COMOUTE_PAIRWISE Part of GrabCut. Compute the pairwise terms. 3 | % 4 | % Inputs: 5 | % - im_sub: 2D subimage, on which Graph Cut is performed 6 | % - gamma: gamma parameter 7 | % 8 | % Output: 9 | % - pairwise: a dense no_edgesx6 matrix of doubles. Each row is of the 10 | %format [i, j, e00, e01, e10, e11] where i and j are neighbours and the four 11 | %coefficients define the interaction potential 12 | % 13 | % Author: 14 | % Xiuming Zhang 15 | % GitHub: xiumingzhang 16 | % Dept. of ECE, National University of Singapore 17 | % April 2015 18 | % 19 | 20 | % Get image dimensions 21 | [im_h, im_w, ~] = size(im_sub); 22 | 23 | %------- Compute \beta 24 | 25 | beta = compute_beta(im_sub); 26 | 27 | %------- Set pairwise 28 | 29 | pairwise = zeros((im_h-1)*(im_w-1)*2+(im_h-1)+(im_w-1), 6); 30 | 31 | % Loop through all the pixels (nodes) and set pairwise 32 | idx = 1; 33 | for y = 1:im_h 34 | for x = 1:im_w 35 | % Current node 36 | node = (x-1)*im_h+y; 37 | color = get_rgb_double(im_sub, x, y); 38 | 39 | % Right neighbor 40 | if x < im_w % Has a right neighbor 41 | node_r = (x+1-1)*im_h+y; 42 | color_r = get_rgb_double(im_sub, x+1, y); 43 | pairwise(idx, 1) = node; 44 | pairwise(idx, 2) = node_r; 45 | pairwise(idx, 3) = compute_V(color, 0, color_r, 0, gamma, beta); 46 | pairwise(idx, 4) = compute_V(color, 0, color_r, 1, gamma, beta); 47 | pairwise(idx, 5) = compute_V(color, 1, color_r, 0, gamma, beta); 48 | pairwise(idx, 6) = compute_V(color, 1, color_r, 1, gamma, beta); 49 | idx = idx+1; 50 | end 51 | 52 | % Down neighbor 53 | if y < im_h % Has a down neighbor 54 | node_d = (x-1)*im_h+y+1; 55 | color_d = get_rgb_double(im_sub, x, y+1); 56 | pairwise(idx, 1) = node; 57 | pairwise(idx, 2) = node_d; 58 | pairwise(idx, 3) = compute_V(color, 0, color_d, 0, gamma, beta); 59 | pairwise(idx, 4) = compute_V(color, 0, color_d, 1, gamma, beta); 60 | pairwise(idx, 5) = compute_V(color, 1, color_d, 0, gamma, beta); 61 | pairwise(idx, 6) = compute_V(color, 1, color_d, 1, gamma, beta); 62 | idx = idx+1; 63 | end 64 | end 65 | end 66 | 67 | end 68 | 69 | 70 | function beta = compute_beta(im_sub) 71 | 72 | % Get image dimensions 73 | [im_h, im_w, ~] = size(im_sub); 74 | 75 | beta_sum = 0; 76 | cnt = 0; 77 | 78 | for y = 1:im_h 79 | for x = 1:im_w 80 | % Current node 81 | color = get_rgb_double(im_sub, x, y); 82 | 83 | % Right neighbor 84 | if x < im_w % Has a right neighbor 85 | color_r = get_rgb_double(im_sub, x+1, y); 86 | beta_sum = beta_sum+norm(color-color_r)^2; 87 | cnt = cnt+1; 88 | end 89 | % Down neighbor 90 | if y < im_h % Has a down neighbor 91 | color_d = get_rgb_double(im_sub, x, y+1); 92 | beta_sum = beta_sum+norm(color-color_d)^2; 93 | cnt = cnt+1; 94 | end 95 | end 96 | end 97 | 98 | beta = 1/(2*(beta_sum/cnt)); 99 | 100 | end 101 | 102 | 103 | function V = compute_V(color1, label1, color2, label2, gamma, beta) 104 | 105 | V = gamma*double(label1~=label2)*exp(-beta*(norm(color1-color2)^2)); 106 | 107 | end 108 | -------------------------------------------------------------------------------- /bin_graphcuts/graph.cpp: -------------------------------------------------------------------------------- 1 | /* graph.cpp */ 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include "graph.h" 8 | 9 | 10 | template 11 | Graph::Graph(int node_num_max, int edge_num_max, void (*err_function)(char *)) 12 | : node_num(0), 13 | nodeptr_block(NULL), 14 | error_function(err_function) 15 | { 16 | if (node_num_max < 16) node_num_max = 16; 17 | if (edge_num_max < 16) edge_num_max = 16; 18 | 19 | nodes = (node*) malloc(node_num_max*sizeof(node)); 20 | arcs = (arc*) malloc(2*edge_num_max*sizeof(arc)); 21 | if (!nodes || !arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 22 | 23 | node_last = nodes; 24 | node_max = nodes + node_num_max; 25 | arc_last = arcs; 26 | arc_max = arcs + 2*edge_num_max; 27 | 28 | maxflow_iteration = 0; 29 | flow = 0; 30 | } 31 | 32 | template 33 | Graph::~Graph() 34 | { 35 | if (nodeptr_block) 36 | { 37 | delete nodeptr_block; 38 | nodeptr_block = NULL; 39 | } 40 | free(nodes); 41 | free(arcs); 42 | } 43 | 44 | template 45 | void Graph::reset() 46 | { 47 | node_last = nodes; 48 | arc_last = arcs; 49 | node_num = 0; 50 | 51 | if (nodeptr_block) 52 | { 53 | delete nodeptr_block; 54 | nodeptr_block = NULL; 55 | } 56 | 57 | maxflow_iteration = 0; 58 | flow = 0; 59 | } 60 | 61 | template 62 | void Graph::reallocate_nodes(int num) 63 | { 64 | int node_num_max = (int)(node_max - nodes); 65 | node* nodes_old = nodes; 66 | 67 | node_num_max += node_num_max / 2; 68 | if (node_num_max < node_num + num) node_num_max = node_num + num; 69 | nodes = (node*) realloc(nodes_old, node_num_max*sizeof(node)); 70 | if (!nodes) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 71 | 72 | node_last = nodes + node_num; 73 | node_max = nodes + node_num_max; 74 | 75 | if (nodes != nodes_old) 76 | { 77 | arc* a; 78 | for (a=arcs; ahead = (node*) ((char*)a->head + (((char*) nodes) - ((char*) nodes_old))); 81 | } 82 | } 83 | } 84 | 85 | template 86 | void Graph::reallocate_arcs() 87 | { 88 | int arc_num_max = (int)(arc_max - arcs); 89 | int arc_num = (int)(arc_last - arcs); 90 | arc* arcs_old = arcs; 91 | 92 | arc_num_max += arc_num_max / 2; if (arc_num_max & 1) arc_num_max ++; 93 | arcs = (arc*) realloc(arcs_old, arc_num_max*sizeof(arc)); 94 | if (!arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 95 | 96 | arc_last = arcs + arc_num; 97 | arc_max = arcs + arc_num_max; 98 | 99 | if (arcs != arcs_old) 100 | { 101 | node* i; 102 | arc* a; 103 | for (i=nodes; ifirst) i->first = (arc*) ((char*)i->first + (((char*) arcs) - ((char*) arcs_old))); 106 | } 107 | for (a=arcs; anext) a->next = (arc*) ((char*)a->next + (((char*) arcs) - ((char*) arcs_old))); 110 | a->sister = (arc*) ((char*)a->sister + (((char*) arcs) - ((char*) arcs_old))); 111 | } 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /bin_graphcuts/BK_UnitTest.m: -------------------------------------------------------------------------------- 1 | function BK_UnitTest 2 | % BK_UnitTest Compile, load, and test the BK_MATLAB library. 3 | % BK_UnitTest will make sure the wrapper compiles on the target 4 | % platform and then exercises the library to look for silly bugs. 5 | 6 | function Assert(cond,msg) % for older MATLAB without assert() 7 | if (exist('assert') == 5) 8 | if (nargin < 2) 9 | assert(cond); 10 | else 11 | assert(cond,msg); 12 | end 13 | elseif (~cond) 14 | if (nargin < 2) 15 | msg = 'Assertion failed'; 16 | end 17 | error(msg); 18 | end 19 | end 20 | 21 | function D = Sparse2Dense(S) 22 | [i,j,s] = find(S); 23 | z = zeros(size(s,1),1); 24 | D = [i,j,z,s,s,z]; % [i,j,e00,e01,e10,e11] 25 | end 26 | 27 | BK_BuildLib; disp('BuildLib PASSED'); 28 | BK_LoadLib; disp('LoadLib PASSED'); 29 | 30 | 31 | % Basic tests with no Create/Delete 32 | caught=false; try BK_Delete(10); catch, caught=true; end, Assert(caught,'Expected an exception'); 33 | caught=false; try h = BK_Create(-1); catch, caught=true; end, Assert(caught,'Expected an exception'); 34 | caught=false; try h = BK_Create(1,-1); catch, caught=true; end, Assert(caught,'Expected an exception'); 35 | h1 = BK_Create(5); 36 | h2 = BK_Create(3); 37 | Assert(all(BK_ListHandles == [h1; h2])); 38 | BK_Delete(h1); 39 | caught=false; try BK_GetLabeling(h1); catch, caught=true; end, Assert(caught,'Expected an exception'); 40 | caught=false; try BK_Delete(h1); catch, caught=true; end, Assert(caught,'Expected an exception'); 41 | Assert(all(BK_ListHandles == [h2])); 42 | BK_Delete(h2); 43 | Assert(isempty(BK_ListHandles)); 44 | caught=false; try BK_Delete(h2); catch, caught=true; end, Assert(caught,'Expected an exception'); 45 | disp('Create/Delete PASSED'); 46 | disp('ListHandles PASSED'); 47 | 48 | 49 | % Test with NO costs 50 | h = BK_Create(3); 51 | e = BK_Minimize(h); 52 | Assert(e == 0); 53 | e = BK_Minimize(h); 54 | Assert(e == 0); 55 | BK_Delete(h); 56 | disp('Expansion-000 PASSED'); 57 | 58 | 59 | 60 | 61 | % Test with DATA cost only 62 | h = BK_Create(5); 63 | dc = [1 2 5 10 0; 64 | 3 1 2 5 4]; 65 | 66 | caught=false; try BK_SetUnary(h,[dc [0 0]']); catch, caught=true; end, Assert(caught,'Expected an exception'); 67 | caught=false; try BK_SetUnary(h,dc(:,1:end-1)); catch, caught=true; end, Assert(caught,'Expected an exception'); 68 | caught=false; try BK_SetUnary(h,dc(1:end-1,:)); catch, caught=true; end, Assert(caught,'Expected an exception'); 69 | 70 | BK_SetUnary(h,dc); 71 | e = BK_Minimize(h); 72 | Assert(e == 9); 73 | Assert(all(BK_GetLabeling(h) == [1 2 2 2 1]')); 74 | BK_Delete(h); 75 | 76 | disp('Expansion-D00 PASSED'); 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | % Test with DATA+SMOOTH costs 85 | h = BK_Create(); 86 | BK_AddVars(h,5); 87 | 88 | caught=false; try BK_SetNeighbors(h,eye(4)); catch, caught=true; end, Assert(caught,'Expected an exception'); 89 | caught=false; try BK_SetNeighbors(h,zeros(6)); catch, caught=true; end, Assert(caught,'Expected an exception'); 90 | 91 | BK_SetUnary(h,dc); 92 | BK_SetNeighbors(h, [0 2 0 0 0; 93 | 0 0 1 0 0; 94 | 0 0 0 5 0; 95 | 0 0 0 0 5; 96 | 0 0 0 0 0]); 97 | 98 | e = BK_Minimize(h); 99 | Assert(e == 15); 100 | Assert(all(BK_GetLabeling(h) == [1 1 2 2 2]')); 101 | 102 | disp('Expansion-DS0 (sparse) PASSED'); 103 | 104 | 105 | % Test with DATA+SMOOTH costs 106 | h = BK_Create(); 107 | BK_AddVars(h,5); 108 | 109 | BK_SetUnary(h,dc); 110 | BK_SetPairwise(h, Sparse2Dense([0 2 0 0 0; 111 | 0 0 1 0 0; 112 | 0 0 0 5 0; 113 | 0 0 0 0 5; 114 | 0 0 0 0 0])); 115 | 116 | e = BK_Minimize(h); 117 | Assert(e == 15); 118 | Assert(all(BK_GetLabeling(h) == [1 1 2 2 2]')); 119 | 120 | disp('Expansion-DS0 (dense) PASSED'); 121 | 122 | 123 | 124 | 125 | 126 | nb = sparse([0 5; 0 0;]); 127 | 128 | dc1 = [2 0; 129 | 0 2]; 130 | 131 | dc2 = [1 0; 132 | 0 1]; 133 | 134 | %%%%%%%%%%%% 135 | h = BK_Create(2); 136 | BK_SetNeighbors(h,nb); 137 | BK_SetUnary(h,dc1); 138 | eh1 = BK_Minimize(h); 139 | lh1 = BK_GetLabeling(h); 140 | BK_SetUnary(h,dc2); 141 | eh2 = BK_Minimize(h); 142 | lh2 = BK_GetLabeling(h); 143 | 144 | %%%%%%%%%%%% 145 | g = BK_Create(2); 146 | BK_SetNeighbors(g,nb); 147 | BK_SetUnary(g,dc2); 148 | eg2 = BK_Minimize(g); 149 | lg2 = BK_GetLabeling(g); 150 | 151 | Assert(eh2 == eg2); 152 | 153 | BK_Delete(h); 154 | BK_Delete(g); 155 | 156 | disp('Dynamic (small) PASSED'); 157 | 158 | 159 | 160 | 161 | 162 | % Test large scale, and incremental data costs DATA+SMOOTH costs 163 | rand('twister', 987+4); % get the same random stream each time 164 | wd = 128; ht = 96; 165 | noise1 = rand([wd,ht])*20; 166 | noise2 = rand([wd,ht])*20; 167 | H = fspecial('disk',5); 168 | noise1 = imfilter(noise1,H,'replicate'); 169 | noise2 = imfilter(noise2,H,'replicate'); 170 | noise = [noise1(:)';noise2(:)']; 171 | nb = sparse(wd*ht,wd*ht); 172 | for y=1:ht % set up a grid-like neighbourhood, arbitrarily 173 | for x=1:wd 174 | if (x < wd), nb((y-1)*wd+x,(y-1)*wd+x+1) = 1; end 175 | if (y < ht), nb((y-1)*wd+x, y *wd+x ) = 1; end 176 | end 177 | end 178 | distmap = [zeros(ht,wd/2) ones(ht,wd/2)]; 179 | distmap = bwdist(distmap); 180 | distmap = distmap / max(distmap(:)); 181 | distmap1 = distmap; 182 | distmap2 = flipdim(distmap,2); 183 | distmap = [distmap1(:)'; distmap2(:)']; 184 | 185 | hinc = BK_Create(wd*ht,2*wd*ht); 186 | BK_SetNeighbors(hinc,nb); 187 | 188 | time_inc = []; 189 | time_new = []; 190 | 191 | figure; 192 | 193 | lambda = 16; 194 | while (lambda >= 1) 195 | newdc = double(noise+lambda*distmap); 196 | 197 | BK_SetUnary(hinc,newdc); 198 | tic; 199 | e_inc = BK_Minimize(hinc); 200 | time_inc(end+1) = toc; 201 | lab = BK_GetLabeling(hinc); 202 | 203 | imagesc(reshape(lab,[wd,ht])); 204 | drawnow; 205 | 206 | hnew = BK_Create(wd*ht,2*wd*ht); 207 | BK_SetNeighbors(hnew,nb); 208 | BK_SetUnary(hnew,newdc); 209 | tic; 210 | e_new = BK_Minimize(hnew); 211 | time_new(end+1) = toc; 212 | BK_Delete(hnew); 213 | 214 | Assert(abs(e_inc - e_new) < 1e-6); 215 | lambda = lambda*0.9; 216 | end 217 | 218 | BK_Delete(hinc); 219 | fprintf('Dynamic PASSED (%.3fsec normal, %.3fsec dynamic)\n',sum(time_new),sum(time_inc)); 220 | 221 | figure; plot(time_new,'-or'); hold on; plot(time_inc,'-xb'); 222 | 223 | 224 | end 225 | 226 | -------------------------------------------------------------------------------- /bin_graphcuts/block.h: -------------------------------------------------------------------------------- 1 | /* block.h */ 2 | /* 3 | Template classes Block and DBlock 4 | Implement adding and deleting items of the same type in blocks. 5 | 6 | If there there are many items then using Block or DBlock 7 | is more efficient than using 'new' and 'delete' both in terms 8 | of memory and time since 9 | (1) On some systems there is some minimum amount of memory 10 | that 'new' can allocate (e.g., 64), so if items are 11 | small that a lot of memory is wasted. 12 | (2) 'new' and 'delete' are designed for items of varying size. 13 | If all items has the same size, then an algorithm for 14 | adding and deleting can be made more efficient. 15 | (3) All Block and DBlock functions are inline, so there are 16 | no extra function calls. 17 | 18 | Differences between Block and DBlock: 19 | (1) DBlock allows both adding and deleting items, 20 | whereas Block allows only adding items. 21 | (2) Block has an additional operation of scanning 22 | items added so far (in the order in which they were added). 23 | (3) Block allows to allocate several consecutive 24 | items at a time, whereas DBlock can add only a single item. 25 | 26 | Note that no constructors or destructors are called for items. 27 | 28 | Example usage for items of type 'MyType': 29 | 30 | /////////////////////////////////////////////////// 31 | #include "block.h" 32 | #define BLOCK_SIZE 1024 33 | typedef struct { int a, b; } MyType; 34 | MyType *ptr, *array[10000]; 35 | 36 | ... 37 | 38 | Block *block = new Block(BLOCK_SIZE); 39 | 40 | // adding items 41 | for (int i=0; i New(); 44 | ptr -> a = ptr -> b = rand(); 45 | } 46 | 47 | // reading items 48 | for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) 49 | { 50 | printf("%d %d\n", ptr->a, ptr->b); 51 | } 52 | 53 | delete block; 54 | 55 | ... 56 | 57 | DBlock *dblock = new DBlock(BLOCK_SIZE); 58 | 59 | // adding items 60 | for (int i=0; i New(); 63 | } 64 | 65 | // deleting items 66 | for (int i=0; i Delete(array[i]); 69 | } 70 | 71 | // adding items 72 | for (int i=0; i New(); 75 | } 76 | 77 | delete dblock; 78 | 79 | /////////////////////////////////////////////////// 80 | 81 | Note that DBlock deletes items by marking them as 82 | empty (i.e., by adding them to the list of free items), 83 | so that this memory could be used for subsequently 84 | added items. Thus, at each moment the memory allocated 85 | is determined by the maximum number of items allocated 86 | simultaneously at earlier moments. All memory is 87 | deallocated only when the destructor is called. 88 | */ 89 | 90 | #ifndef __BLOCK_H__ 91 | #define __BLOCK_H__ 92 | 93 | #include 94 | 95 | /***********************************************************************/ 96 | /***********************************************************************/ 97 | /***********************************************************************/ 98 | 99 | template class Block 100 | { 101 | public: 102 | /* Constructor. Arguments are the block size and 103 | (optionally) the pointer to the function which 104 | will be called if allocation failed; the message 105 | passed to this function is "Not enough memory!" */ 106 | Block(int size, void (*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } 107 | 108 | /* Destructor. Deallocates all items added so far */ 109 | ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } 110 | 111 | /* Allocates 'num' consecutive items; returns pointer 112 | to the first item. 'num' cannot be greater than the 113 | block size since items must fit in one block */ 114 | Type *New(int num = 1) 115 | { 116 | Type *t; 117 | 118 | if (!last || last->current + num > last->last) 119 | { 120 | if (last && last->next) last = last -> next; 121 | else 122 | { 123 | block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; 124 | if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 125 | if (last) last -> next = next; 126 | else first = next; 127 | last = next; 128 | last -> current = & ( last -> data[0] ); 129 | last -> last = last -> current + block_size; 130 | last -> next = NULL; 131 | } 132 | } 133 | 134 | t = last -> current; 135 | last -> current += num; 136 | return t; 137 | } 138 | 139 | /* Returns the first item (or NULL, if no items were added) */ 140 | Type *ScanFirst() 141 | { 142 | for (scan_current_block=first; scan_current_block; scan_current_block = scan_current_block->next) 143 | { 144 | scan_current_data = & ( scan_current_block -> data[0] ); 145 | if (scan_current_data < scan_current_block -> current) return scan_current_data ++; 146 | } 147 | return NULL; 148 | } 149 | 150 | /* Returns the next item (or NULL, if all items have been read) 151 | Can be called only if previous ScanFirst() or ScanNext() 152 | call returned not NULL. */ 153 | Type *ScanNext() 154 | { 155 | while (scan_current_data >= scan_current_block -> current) 156 | { 157 | scan_current_block = scan_current_block -> next; 158 | if (!scan_current_block) return NULL; 159 | scan_current_data = & ( scan_current_block -> data[0] ); 160 | } 161 | return scan_current_data ++; 162 | } 163 | 164 | /* Marks all elements as empty */ 165 | void Reset() 166 | { 167 | block *b; 168 | if (!first) return; 169 | for (b=first; ; b=b->next) 170 | { 171 | b -> current = & ( b -> data[0] ); 172 | if (b == last) break; 173 | } 174 | last = first; 175 | } 176 | 177 | /***********************************************************************/ 178 | 179 | private: 180 | 181 | typedef struct block_st 182 | { 183 | Type *current, *last; 184 | struct block_st *next; 185 | Type data[1]; 186 | } block; 187 | 188 | int block_size; 189 | block *first; 190 | block *last; 191 | 192 | block *scan_current_block; 193 | Type *scan_current_data; 194 | 195 | void (*error_function)(char *); 196 | }; 197 | 198 | /***********************************************************************/ 199 | /***********************************************************************/ 200 | /***********************************************************************/ 201 | 202 | template class DBlock 203 | { 204 | public: 205 | /* Constructor. Arguments are the block size and 206 | (optionally) the pointer to the function which 207 | will be called if allocation failed; the message 208 | passed to this function is "Not enough memory!" */ 209 | DBlock(int size, void (*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } 210 | 211 | /* Destructor. Deallocates all items added so far */ 212 | ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } 213 | 214 | /* Allocates one item */ 215 | Type *New() 216 | { 217 | block_item *item; 218 | 219 | if (!first_free) 220 | { 221 | block *next = first; 222 | first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; 223 | if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 224 | first_free = & (first -> data[0] ); 225 | for (item=first_free; item next_free = item + 1; 227 | item -> next_free = NULL; 228 | first -> next = next; 229 | } 230 | 231 | item = first_free; 232 | first_free = item -> next_free; 233 | return (Type *) item; 234 | } 235 | 236 | /* Deletes an item allocated previously */ 237 | void Delete(Type *t) 238 | { 239 | ((block_item *) t) -> next_free = first_free; 240 | first_free = (block_item *) t; 241 | } 242 | 243 | /***********************************************************************/ 244 | 245 | private: 246 | 247 | typedef union block_item_st 248 | { 249 | Type t; 250 | block_item_st *next_free; 251 | } block_item; 252 | 253 | typedef struct block_st 254 | { 255 | struct block_st *next; 256 | block_item data[1]; 257 | } block; 258 | 259 | int block_size; 260 | block *first; 261 | block_item *first_free; 262 | 263 | void (*error_function)(char *); 264 | }; 265 | 266 | 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /bin_graphcuts/energy.h: -------------------------------------------------------------------------------- 1 | /* energy.h */ 2 | /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2003. */ 3 | 4 | /* 5 | This software implements an energy minimization technique described in 6 | 7 | What Energy Functions can be Minimized via Graph Cuts? 8 | Vladimir Kolmogorov and Ramin Zabih. 9 | To appear in IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI). 10 | Earlier version appeared in European Conference on Computer Vision (ECCV), May 2002. 11 | 12 | More specifically, it computes the global minimum of a function E of binary 13 | variables x_1, ..., x_n which can be written as a sum of terms involving 14 | at most three variables at a time: 15 | 16 | E(x_1, ..., x_n) = \sum_{i} E^{i} (x_i) 17 | + \sum_{i,j} E^{i,j} (x_i, x_j) 18 | + \sum_{i,j,k} E^{i,j,k}(x_i, x_j, x_k) 19 | 20 | The method works only if each term is "regular". Definitions of regularity 21 | for terms E^{i}, E^{i,j}, E^{i,j,k} are given below as comments to functions 22 | add_term1(), add_term2(), add_term3(). 23 | 24 | This software can be used only for research purposes. IF YOU USE THIS SOFTWARE, 25 | YOU SHOULD CITE THE AFOREMENTIONED PAPER IN ANY RESULTING PUBLICATION. 26 | 27 | In order to use it, you will also need a MAXFLOW software which can be 28 | obtained from http://www.cs.cornell.edu/People/vnk/software.html 29 | 30 | 31 | Example usage 32 | (Minimizes the following function of 3 binary variables: 33 | E(x, y, z) = x - 2*y + 3*(1-z) - 4*x*y + 5*|y-z|): 34 | 35 | /////////////////////////////////////////////////// 36 | 37 | #include 38 | #include "energy.h" 39 | 40 | void main() 41 | { 42 | // Minimize the following function of 3 binary variables: 43 | // E(x, y, z) = x - 2*y + 3*(1-z) - 4*x*y + 5*|y-z| 44 | 45 | Energy::Var varx, vary, varz; 46 | Energy *e = new Energy(); 47 | 48 | varx = e -> add_variable(); 49 | vary = e -> add_variable(); 50 | varz = e -> add_variable(); 51 | 52 | e -> add_term1(varx, 0, 1); // add term x 53 | e -> add_term1(vary, 0, -2); // add term -2*y 54 | e -> add_term1(varz, 3, 0); // add term 3*(1-z) 55 | 56 | e -> add_term2(x, y, 0, 0, 0, -4); // add term -4*x*y 57 | e -> add_term2(y, z, 0, 5, 5, 0); // add term 5*|y-z| 58 | 59 | Energy::TotalValue Emin = e -> minimize(); 60 | 61 | printf("Minimum = %d\n", Emin); 62 | printf("Optimal solution:\n"); 63 | printf("x = %d\n", e->get_var(varx)); 64 | printf("y = %d\n", e->get_var(vary)); 65 | printf("z = %d\n", e->get_var(varz)); 66 | 67 | delete e; 68 | } 69 | 70 | /////////////////////////////////////////////////// 71 | */ 72 | 73 | #ifndef __ENERGY_H__ 74 | #define __ENERGY_H__ 75 | 76 | #include 77 | #include "graph.h" 78 | 79 | template class Energy: public Graph 80 | { 81 | typedef Graph GraphT; 82 | public: 83 | typedef typename GraphT::node_id Var; 84 | 85 | /* Types of energy values. 86 | Value is a type of a value in a single term 87 | TotalValue is a type of a value of the total energy. 88 | By default Value = short, TotalValue = int. 89 | To change it, change the corresponding types in graph.h */ 90 | typedef captype Value; 91 | typedef flowtype TotalValue; 92 | 93 | /* interface functions */ 94 | 95 | /* Constructor. Optional argument is the pointer to the 96 | function which will be called if an error occurs; 97 | an error message is passed to this function. If this 98 | argument is omitted, exit(1) will be called. */ 99 | Energy(int var_num_max, int edge_num_max, void (*err_function)(char *) = NULL); 100 | 101 | /* Destructor */ 102 | ~Energy(); 103 | 104 | /* Adds a new binary variable */ 105 | Var add_variable(int num=1); 106 | 107 | /* Adds a constant E to the energy function */ 108 | void add_constant(Value E); 109 | 110 | /* Adds a new term E(x) of one binary variable 111 | to the energy function, where 112 | E(0) = E0, E(1) = E1 113 | E0 and E1 can be arbitrary */ 114 | bool add_term1(Var x, 115 | Value E0, Value E1); 116 | 117 | /* Adds a new term E(x,y) of two binary variables 118 | to the energy function, where 119 | E(0,0) = E00, E(0,1) = E01 120 | E(1,0) = E10, E(1,1) = E11 121 | The term must be regular, i.e. E00 + E11 <= E01 + E10 */ 122 | void add_term2(Var x, Var y, 123 | Value E00, Value E01, 124 | Value E10, Value E11); 125 | 126 | void add_term2(Var x, Var y, Value W); 127 | 128 | /* Adds a new term E(x,y,z) of three binary variables 129 | to the energy function, where 130 | E(0,0,0) = E000, E(0,0,1) = E001 131 | E(0,1,0) = E010, E(0,1,1) = E011 132 | E(1,0,0) = E100, E(1,0,1) = E101 133 | E(1,1,0) = E110, E(1,1,1) = E111 134 | The term must be regular. It means that if one 135 | of the variables is fixed (for example, y=1), then 136 | the resulting function of two variables must be regular. 137 | Since there are 6 ways to fix one variable 138 | (3 variables times 2 binary values - 0 and 1), 139 | this is equivalent to 6 inequalities */ 140 | void add_term3(Var x, Var y, Var z, 141 | Value E000, Value E001, 142 | Value E010, Value E011, 143 | Value E100, Value E101, 144 | Value E110, Value E111); 145 | 146 | /* After the energy function has been constructed, 147 | call this function to minimize it. 148 | Returns the minimum of the function */ 149 | TotalValue minimize(bool incremental = false, Block* changed_list = NULL); 150 | 151 | /* After 'minimize' has been called, this function 152 | can be used to determine the value of variable 'x' 153 | in the optimal solution. 154 | Returns either 0 or 1 */ 155 | int get_var(Var x); 156 | 157 | int var_num() { return GraphT::get_node_num(); } 158 | 159 | /***********************************************************************/ 160 | /***********************************************************************/ 161 | /***********************************************************************/ 162 | 163 | private: 164 | /* internal variables and functions */ 165 | 166 | TotalValue Econst; 167 | void (*error_function)(char *); /* this function is called if a error occurs, 168 | with a corresponding error message 169 | (or exit(1) is called if it's NULL) */ 170 | }; 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | /***********************************************************************/ 187 | /************************ Implementation ******************************/ 188 | /***********************************************************************/ 189 | 190 | template 191 | inline Energy::Energy(int var_num_max, int edge_num_max, void (*err_function)(char *)) : Graph(var_num_max, edge_num_max, err_function) 192 | { 193 | Econst = 0; 194 | error_function = err_function; 195 | } 196 | 197 | template 198 | inline Energy::~Energy() {} 199 | 200 | template 201 | inline typename Energy::Var Energy::add_variable(int num) 202 | { return GraphT::add_node(num); } 203 | 204 | template 205 | inline void Energy::add_constant(Value A) { Econst += A; } 206 | 207 | template 208 | inline bool Energy::add_term1(Var x, 209 | Value A, Value B) 210 | { 211 | return GraphT::add_tweights(x, B, A); 212 | } 213 | 214 | template 215 | inline void Energy::add_term2(Var x, Var y, 216 | Value A, Value B, 217 | Value C, Value D) 218 | { 219 | /* 220 | E = A A + 0 B-A 221 | D D C-D 0 222 | Add edges for the first term 223 | */ 224 | GraphT::add_tweights(x, D, A); 225 | B -= A; C -= D; 226 | 227 | /* now need to represent 228 | 0 B 229 | C 0 230 | */ 231 | 232 | assert(B + C >= 0); /* check regularity */ 233 | if (B < 0) 234 | { 235 | /* Write it as 236 | B B + -B 0 + 0 0 237 | 0 0 -B 0 B+C 0 238 | */ 239 | GraphT::add_tweights(x, 0, B); /* first term */ 240 | GraphT::add_tweights(y, 0, -B); /* second term */ 241 | GraphT::add_edge(x, y, 0, B+C); /* third term */ 242 | } 243 | else if (C < 0) 244 | { 245 | /* Write it as 246 | -C -C + C 0 + 0 B+C 247 | 0 0 C 0 0 0 248 | */ 249 | GraphT::add_tweights(x, 0, -C); /* first term */ 250 | GraphT::add_tweights(y, 0, C); /* second term */ 251 | GraphT::add_edge(x, y, B+C, 0); /* third term */ 252 | } 253 | else /* B >= 0, C >= 0 */ 254 | { 255 | GraphT::add_edge(x, y, B, C); 256 | } 257 | } 258 | 259 | template 260 | inline void Energy::add_term2(Var x, Var y, 261 | Value W) 262 | { 263 | GraphT::add_edge(x, y, W, W); 264 | } 265 | 266 | template 267 | inline void Energy::add_term3(Var x, Var y, Var z, 268 | Value E000, Value E001, 269 | Value E010, Value E011, 270 | Value E100, Value E101, 271 | Value E110, Value E111) 272 | { 273 | register Value pi = (E000 + E011 + E101 + E110) - (E100 + E010 + E001 + E111); 274 | register Value delta; 275 | register Var u; 276 | 277 | if (pi >= 0) 278 | { 279 | Econst += E111 - (E011 + E101 + E110); 280 | 281 | GraphT::add_tweights(x, E101, E001); 282 | GraphT::add_tweights(y, E110, E100); 283 | GraphT::add_tweights(z, E011, E010); 284 | 285 | delta = (E010 + E001) - (E000 + E011); /* -pi(E[x=0]) */ 286 | assert(delta >= 0); /* check regularity */ 287 | GraphT::add_edge(y, z, delta, 0); 288 | 289 | delta = (E100 + E001) - (E000 + E101); /* -pi(E[y=0]) */ 290 | assert(delta >= 0); /* check regularity */ 291 | GraphT::add_edge(z, x, delta, 0); 292 | 293 | delta = (E100 + E010) - (E000 + E110); /* -pi(E[z=0]) */ 294 | assert(delta >= 0); /* check regularity */ 295 | GraphT::add_edge(x, y, delta, 0); 296 | 297 | if (pi > 0) 298 | { 299 | u = add_variable(); 300 | GraphT::add_edge(x, u, pi, 0); 301 | GraphT::add_edge(y, u, pi, 0); 302 | GraphT::add_edge(z, u, pi, 0); 303 | GraphT::add_tweights(u, 0, pi); 304 | } 305 | } 306 | else 307 | { 308 | Econst += E000 - (E100 + E010 + E001); 309 | 310 | GraphT::add_tweights(x, E110, E010); 311 | GraphT::add_tweights(y, E011, E001); 312 | GraphT::add_tweights(z, E101, E100); 313 | 314 | delta = (E110 + E101) - (E100 + E111); /* -pi(E[x=1]) */ 315 | assert(delta >= 0); /* check regularity */ 316 | GraphT::add_edge(z, y, delta, 0); 317 | 318 | delta = (E110 + E011) - (E010 + E111); /* -pi(E[y=1]) */ 319 | assert(delta >= 0); /* check regularity */ 320 | GraphT::add_edge(x, z, delta, 0); 321 | 322 | delta = (E101 + E011) - (E001 + E111); /* -pi(E[z=1]) */ 323 | assert(delta >= 0); /* check regularity */ 324 | GraphT::add_edge(y, x, delta, 0); 325 | 326 | u = add_variable(); 327 | GraphT::add_edge(u, x, -pi, 0); 328 | GraphT::add_edge(u, y, -pi, 0); 329 | GraphT::add_edge(u, z, -pi, 0); 330 | GraphT::add_tweights(u, -pi, 0); 331 | } 332 | } 333 | 334 | template 335 | inline typename Energy::TotalValue Energy::minimize(bool incremental, Block* changed_list) 336 | { 337 | return Econst + GraphT::maxflow(incremental,changed_list); 338 | } 339 | 340 | template 341 | inline int Energy::get_var(Var x) { return (int) GraphT::what_segment(x); } 342 | 343 | #endif 344 | -------------------------------------------------------------------------------- /bin_graphcuts/bk_matlab.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "energy.h" 6 | #include "maxflow.cpp" 7 | #include "graph.cpp" 8 | 9 | #if !defined(MX_API_VER) || MX_API_VER < 0x07030000 10 | typedef int mwSize; 11 | typedef int mwIndex; 12 | #endif 13 | 14 | #if BK_COSTTYPE == 0 15 | typedef double EnergyType; 16 | typedef double EnergyTermType; 17 | mxClassID cEnergyTermClassID = mxDOUBLE_CLASS; 18 | mxClassID cEnergyClassID = mxDOUBLE_CLASS; 19 | const char* cCostTypeName = "double"; 20 | #elif BK_COSTTYPE == 1 21 | typedef int EnergyType; 22 | typedef long long EnergyTermType; 23 | mxClassID cEnergyTermClassID = mxINT32_CLASS; 24 | mxClassID cEnergyClassID = mxINT64_CLASS; 25 | const char* cCostTypeName = "int32"; 26 | #endif 27 | 28 | typedef Energy BKEnergy; 29 | mxClassID cLabelClassID = mxUINT8_CLASS; 30 | mxClassID cSiteClassID = mxINT32_CLASS; 31 | typedef unsigned char LabelID; 32 | typedef int SiteID; 33 | 34 | // MATLAB 2014a: mxCreateReference is NO LONGER AVAILABLE :( 35 | //extern "C" mxArray *mxCreateReference(const mxArray*); // undocumented mex function 36 | 37 | #define BK_EXPORT(func) \ 38 | extern "C" void func(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]); \ 39 | FuncRegistry::Entry regentry_##func(#func,func); \ 40 | extern "C" void func(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 41 | 42 | #define MATLAB_ASSERT(expr,msg) if (!(expr)) { throw MatlabError(msg); } 43 | #define MATLAB_ASSERT_ARGCOUNT(nout, nin) \ 44 | MATLAB_ASSERT(nlhs >= nout, "Not enough output arguments, expected " #nout); \ 45 | MATLAB_ASSERT(nlhs <= nout, "Too many output arguments, expected " #nout); \ 46 | MATLAB_ASSERT(nrhs >= nin, "Not enough input arguments, expected " #nin); \ 47 | MATLAB_ASSERT(nrhs <= nin, "Too many input arguments, expected " #nin); 48 | #define MATLAB_ASSERT_INTYPE(arg, type) \ 49 | MATLAB_ASSERT(mxGetClassID(prhs[arg]) == type, "Expected " #type " for input argument " #arg); 50 | #define MATLAB_ASSERT_HANDLE(arg) \ 51 | MATLAB_ASSERT(mxGetClassID(prhs[arg]) == mxINT32_CLASS, "Expected valid handle for argument " #arg); 52 | 53 | struct MatlabError { 54 | MatlabError(const char* msg): msg(msg) { } 55 | const char* msg; 56 | }; 57 | 58 | struct FuncRegistry { 59 | typedef void (*Func)(int, mxArray*[], int, const mxArray*[]); 60 | typedef std::map LookupTable; 61 | static LookupTable sLookup; 62 | struct Entry { 63 | Entry(const char* name, Func ptr) { sLookup[name] = ptr; } 64 | }; 65 | }; 66 | FuncRegistry::LookupTable FuncRegistry::sLookup; 67 | 68 | 69 | struct BKInstanceInfo { 70 | BKInstanceInfo(): bk(0), dc(0), pc(0), nb(0), changed_list(0), was_minimized(false) { } 71 | ~BKInstanceInfo() { 72 | if (nb) mxDestroyArray(nb); 73 | if (pc) mxDestroyArray(pc); 74 | if (dc) mxDestroyArray(dc); 75 | if (bk) delete bk; 76 | if (changed_list) delete changed_list; 77 | } 78 | BKEnergy* bk; 79 | mxArray* dc; 80 | mxArray* pc; 81 | mxArray* nb; 82 | Block* changed_list; 83 | bool was_minimized; 84 | private: 85 | }; 86 | 87 | typedef std::map BKInstanceMap; 88 | 89 | static int gNextInstanceID = 10001; // some start id for the first GC object 90 | static BKInstanceMap gInstanceMap; 91 | 92 | BKInstanceMap::mapped_type& sGetBKInstance(int id) { 93 | BKInstanceMap::iterator it = gInstanceMap.find(id); 94 | MATLAB_ASSERT(it != gInstanceMap.end(), "Invalid handle; no such bkptimization object"); 95 | return it->second; 96 | } 97 | 98 | struct BKexception { 99 | BKexception(char* msg): message(msg) { } 100 | char* message; 101 | }; 102 | 103 | void handleError(char *message) 104 | { 105 | throw BKexception(message); 106 | } 107 | 108 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 109 | { 110 | if (nrhs == 0 || !mxIsChar(prhs[0])) 111 | mexErrMsgTxt("Do not use bk_matlab() directly, instead use the bk functions such as BK_Create"); 112 | mwSize nameLen = mxGetN(prhs[0])*sizeof(mxChar)+1; 113 | char funcName[512]; 114 | mxGetString(prhs[0], funcName, nameLen); 115 | FuncRegistry::LookupTable::const_iterator it = FuncRegistry::sLookup.find(funcName); 116 | if (it == FuncRegistry::sLookup.end()) 117 | mexErrMsgTxt("Specified function does not exist within bk_matlab module"); 118 | try { 119 | it->second(nlhs, plhs, nrhs-1, prhs+1); 120 | } catch (BKexception err) { 121 | mexErrMsgTxt(err.message); 122 | } catch (MatlabError err) { 123 | mexErrMsgTxt(err.msg); 124 | } 125 | } 126 | 127 | 128 | BK_EXPORT(bk_create) 129 | { 130 | int instanceID = 0; 131 | try { 132 | MATLAB_ASSERT_ARGCOUNT(1,2); 133 | MATLAB_ASSERT_INTYPE(0,cSiteClassID); 134 | MATLAB_ASSERT_INTYPE(1,cSiteClassID); 135 | SiteID numSites = *(SiteID*)mxGetData(prhs[0]); MATLAB_ASSERT(numSites >= 0, "Number of variables must be non-negative"); 136 | SiteID maxEdges = *(SiteID*)mxGetData(prhs[1]); MATLAB_ASSERT(maxEdges >= 0, "Number of edges must be non-negative"); 137 | instanceID = gNextInstanceID++; 138 | BKInstanceInfo& gcinstance = gInstanceMap[instanceID]; 139 | gcinstance.bk = new BKEnergy(numSites,maxEdges,handleError); 140 | gcinstance.bk->add_variable(numSites); 141 | gcinstance.was_minimized = false; 142 | mwSize outSize = 1; 143 | plhs[0] = mxCreateNumericArray(1, &outSize, mxINT32_CLASS, mxREAL); 144 | *(int*)mxGetData(plhs[0]) = instanceID; 145 | } catch (MatlabError) { 146 | if (instanceID) 147 | gInstanceMap.erase(instanceID); 148 | throw; 149 | } 150 | } 151 | 152 | BK_EXPORT(bk_delete) 153 | { 154 | MATLAB_ASSERT_HANDLE(0); 155 | const int* instanceIDs = (int*)mxGetData(prhs[0]); 156 | MATLAB_ASSERT(mxGetN(prhs[0]) == 1 || mxGetM(prhs[0]) == 1, "Input must be a scalar or a vector"); 157 | mwIndex count = mxGetNumberOfElements(prhs[0]); 158 | for (mwIndex i = 0; i < count; ++i) { 159 | MATLAB_ASSERT(gInstanceMap.find(instanceIDs[i]) != gInstanceMap.end(), "Invalid handle (no such BK object)"); 160 | gInstanceMap.erase(instanceIDs[i]); 161 | } 162 | } 163 | 164 | BK_EXPORT(bk_listhandles) 165 | { 166 | MATLAB_ASSERT_ARGCOUNT(1,0); 167 | mwSize outSize = (mwSize)gInstanceMap.size(); 168 | plhs[0] = mxCreateNumericArray(1, &outSize, mxINT32_CLASS, mxREAL); 169 | int* instanceIDs = (int*)mxGetData(plhs[0]); 170 | for (BKInstanceMap::const_iterator i = gInstanceMap.begin(); i != gInstanceMap.end(); ++i) 171 | *(instanceIDs++) = i->first; 172 | } 173 | 174 | BK_EXPORT(bk_setunary) 175 | { 176 | MATLAB_ASSERT_ARGCOUNT(0,2); 177 | MATLAB_ASSERT_HANDLE(0); 178 | MATLAB_ASSERT_INTYPE(1,cEnergyTermClassID); 179 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 180 | const mxArray* dc = prhs[1]; 181 | 182 | BKEnergy* bk = gcinstance.bk; 183 | SiteID varcount = bk->var_num(); 184 | // Dense data costs 185 | MATLAB_ASSERT(mxGetN(dc) == varcount && mxGetM(dc) == 2, 186 | "Numeric data cost must be 2 x NumSites in size"); 187 | // Increment reference count on this array to avoid copy. 188 | // This way the BK object refers directly to the Matlab storage. 189 | // If the user modifies their original variable, Matlab will do a lazy copy 190 | // before modifying, thus making sure the pointer used here is valid and 191 | // still points to the original data. 192 | EnergyTermType* newcosts = (EnergyTermType*)mxGetData(dc); 193 | if (!gcinstance.was_minimized || !gcinstance.dc) { 194 | for (SiteID i = 0; i < varcount; ++i) 195 | bk->add_term1(i,newcosts[2*i],newcosts[2*i+1]); 196 | } else { 197 | EnergyTermType* oldcosts = (EnergyTermType*)mxGetData(gcinstance.dc); 198 | for (SiteID i = 0; i < varcount; ++i) { 199 | bool mark = bk->add_term1(i,newcosts[2*i]-oldcosts[2*i],newcosts[2*i+1]-oldcosts[2*i+1]); 200 | if (mark) 201 | bk->mark_node(i); 202 | } 203 | } 204 | if (gcinstance.dc) 205 | mxDestroyArray(gcinstance.dc); 206 | 207 | // Make a copy since MATLAB no longer supports incrementing reference 208 | // count from mex extensions... I miss you mxCreateReference :( 209 | mxArray* dc_copy = mxDuplicateArray(dc); 210 | mexMakeArrayPersistent(dc_copy); 211 | gcinstance.dc = dc_copy; 212 | } 213 | 214 | BK_EXPORT(bk_setneighbors) 215 | { 216 | MATLAB_ASSERT_ARGCOUNT(0,2); 217 | MATLAB_ASSERT_HANDLE(0); 218 | MATLAB_ASSERT_INTYPE(1,mxDOUBLE_CLASS); 219 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 220 | BKEnergy* bk = gcinstance.bk; 221 | const mxArray* nb = prhs[1]; 222 | MATLAB_ASSERT(mxIsSparse(nb), "SetNeighbors expects a sparse matrix") 223 | MATLAB_ASSERT(mxGetN(nb) == gcinstance.bk->var_num() && mxGetM(nb) == gcinstance.bk->var_num(), 224 | "Sparse neighbours array must be NumSites x NumSites in size"); 225 | MATLAB_ASSERT(!gcinstance.was_minimized, "Cannot call SetNeighbors after Minimize"); 226 | MATLAB_ASSERT(!gcinstance.nb, "Cannot call SetNeighbors twice on the same instance"); 227 | MATLAB_ASSERT(!gcinstance.pc, "Cannot call SetNeighbors after SetPairwise"); 228 | 229 | mwIndex n = (mwIndex)mxGetN(nb); 230 | const mwIndex* ir = mxGetIr(nb); 231 | const mwIndex* jc = mxGetJc(nb); 232 | double* pr = mxGetPr(nb); 233 | mwIndex count = 0; 234 | bool warned = false; 235 | for (mwIndex c = 0; c < n; ++c) { 236 | mwIndex rowStart = jc[c]; 237 | mwIndex rowEnd = jc[c+1]; 238 | for (mwIndex ri = rowStart; ri < rowEnd; ++ri) { 239 | mwIndex r = ir[ri]; 240 | MATLAB_ASSERT(r != c, "A site cannot neighbor itself; make sure diagonal is all zero"); 241 | 242 | double dw = pr[count++]; 243 | #if BK_COSTTYPE == 1 244 | if ((double) ((int)dw) != dw && !warned) { 245 | mexWarnMsgTxt("Non-integer weight detected; rounding to int32"); 246 | warned = true; 247 | } 248 | #endif 249 | if (r < c) { 250 | bk->add_term2((SiteID)r, (SiteID)c, (EnergyTermType)dw); 251 | } 252 | } 253 | } 254 | // TODO: support assigning new neighbour weights via dynamic graph cuts 255 | //gcinstance.nb = mxCreateReference(nb); 256 | } 257 | 258 | BK_EXPORT(bk_setpairwise) 259 | { 260 | MATLAB_ASSERT_ARGCOUNT(0,2); 261 | MATLAB_ASSERT_HANDLE(0); 262 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 263 | BKEnergy* bk = gcinstance.bk; 264 | const mxArray* pc = prhs[1]; 265 | MATLAB_ASSERT_INTYPE(1,cEnergyTermClassID); 266 | MATLAB_ASSERT(!mxIsSparse(pc), "Pairwise cost array must be dense, not sparse") 267 | MATLAB_ASSERT(mxGetN(pc) == 6, "Pairwise cost array must be NumEdges x 6 in size"); 268 | MATLAB_ASSERT(!gcinstance.was_minimized, "Cannot call SetNeighbors after Minimize"); 269 | MATLAB_ASSERT(!gcinstance.nb, "Cannot call SetPairwise after SetNeighbors"); 270 | MATLAB_ASSERT(!gcinstance.pc, "Cannot call SetPairwise twice on the same instance"); 271 | 272 | mwIndex m = (mwIndex)mxGetM(pc); 273 | EnergyTermType* pcdata = (EnergyTermType*)mxGetData(pc); 274 | 275 | #if BK_COSTTYPE == 1 276 | int numsites = bk->var_num(); 277 | for (mwIndex row = 0; row < m; ++row) { 278 | EnergyTermType i = pcdata[row+0*m]; 279 | EnergyTermType j = pcdata[row+1*m]; 280 | MATLAB_ASSERT((double)((SiteID)i) == i, "Non-integer SiteID detected"); 281 | MATLAB_ASSERT((double)((SiteID)j) == j, "Non-integer SiteID detected"); 282 | MATLAB_ASSERT((SiteID)i >= 1 && (SiteID)i <= numsites, "SiteID was outside range 1..NumSites"); 283 | MATLAB_ASSERT((SiteID)j >= 1 && (SiteID)j <= numsites, "SiteID was outside range 1..NumSites"); 284 | } 285 | #endif 286 | 287 | for (mwIndex row = 0; row < m; ++row) { 288 | SiteID i = (SiteID)pcdata[row+0*m]-1; 289 | SiteID j = (SiteID)pcdata[row+1*m]-1; 290 | EnergyTermType e00 = pcdata[row+2*m]; 291 | EnergyTermType e01 = pcdata[row+3*m]; 292 | EnergyTermType e10 = pcdata[row+4*m]; 293 | EnergyTermType e11 = pcdata[row+5*m]; 294 | 295 | bk->add_term2(i, j, e00, e01, e10, e11); 296 | } 297 | // TODO: support assigning new pairwise costs via dynamic graph cuts 298 | //gcinstance.pc = mxCreateReference(pc); 299 | } 300 | 301 | BK_EXPORT(bk_minimize) 302 | { 303 | MATLAB_ASSERT_ARGCOUNT(1,1); 304 | MATLAB_ASSERT_HANDLE(0); 305 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 306 | EnergyType energy; 307 | if (!gcinstance.was_minimized) { 308 | energy = gcinstance.bk->minimize(); 309 | gcinstance.was_minimized = true; 310 | } else { 311 | if (!gcinstance.changed_list) 312 | gcinstance.changed_list = new Block(256); 313 | energy = gcinstance.bk->minimize(true,gcinstance.changed_list); 314 | } 315 | mwSize outdim = 1; 316 | plhs[0] = mxCreateNumericArray(1, &outdim, cEnergyClassID, mxREAL); 317 | *(EnergyType*)mxGetData(plhs[0]) = energy; 318 | } 319 | 320 | BK_EXPORT(bk_getcosttype) 321 | { 322 | plhs[0] = mxCreateString(cCostTypeName); 323 | } 324 | 325 | BK_EXPORT(bk_addvars) 326 | { 327 | MATLAB_ASSERT_ARGCOUNT(1,2); 328 | MATLAB_ASSERT_HANDLE(0); 329 | MATLAB_ASSERT_INTYPE(1,cSiteClassID); 330 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 331 | SiteID varnum_old = gcinstance.bk->var_num(); 332 | SiteID count = *(SiteID* )mxGetData(prhs[1]); MATLAB_ASSERT(count >= 1, "Number of new variables must be positive"); 333 | SiteID id = gcinstance.bk->add_variable(count); 334 | if (gcinstance.dc) { 335 | // need to extend the old datacost table and fill the new region with zeros 336 | SiteID varnum_new = gcinstance.bk->var_num(); 337 | mwSize dcdim[2] = {2,varnum_new}; 338 | mxArray* newdch = mxCreateNumericArray(2, dcdim, cEnergyTermClassID, mxREAL); 339 | EnergyTermType* newdc = (EnergyTermType*)mxGetData(newdch); 340 | EnergyTermType* olddc = (EnergyTermType*)mxGetData(gcinstance.dc); 341 | for (SiteID i = 0; i < varnum_old; ++i) { 342 | newdc[2*i+0] = olddc[2*i+0]; 343 | newdc[2*i+1] = olddc[2*i+1]; 344 | } 345 | for (SiteID i = varnum_old; i < varnum_new; ++i) { 346 | newdc[2*i+0] = 0; 347 | newdc[2*i+1] = 0; 348 | } 349 | mxDestroyArray(gcinstance.dc); 350 | gcinstance.dc = newdch; 351 | } 352 | mwSize outdim = 1; 353 | plhs[0] = mxCreateNumericArray(1, &outdim, cSiteClassID, mxREAL); 354 | *(SiteID*)mxGetData(plhs[0]) = id+1; // convert C index to Matlab index 355 | } 356 | 357 | BK_EXPORT(bk_getlabeling) 358 | { 359 | MATLAB_ASSERT_ARGCOUNT(1,1); 360 | MATLAB_ASSERT_HANDLE(0); 361 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 362 | MATLAB_ASSERT(gcinstance.was_minimized,"GetLabeling can only be called after Minimize"); 363 | mwSize mlcount = (mwSize)gcinstance.bk->var_num(); 364 | plhs[0] = mxCreateNumericArray(1, &mlcount, cLabelClassID, mxREAL); 365 | LabelID* labeling = (LabelID*)mxGetData(plhs[0]); 366 | // if no changed list, straight-forward update 367 | if (!gcinstance.changed_list) { 368 | for ( SiteID i = 0; i < mlcount; ++i ) 369 | labeling[i] = gcinstance.bk->get_var(i)+1; // convert C index to Matlab index 370 | } else { 371 | for (BKEnergy::node_id* ptr = gcinstance.changed_list->ScanFirst(); ptr; ptr = gcinstance.changed_list->ScanNext()) { 372 | BKEnergy::node_id i = *ptr; MATLAB_ASSERT(i>=0 && iremove_from_changed_list(i); 374 | } 375 | for ( SiteID i = 0; i < mlcount; ++i ) 376 | labeling[i] = gcinstance.bk->get_var(i)+1; // convert C index to Matlab index 377 | gcinstance.changed_list->Reset(); 378 | } 379 | } 380 | 381 | BK_EXPORT(bk_getnumsites) 382 | { 383 | MATLAB_ASSERT_ARGCOUNT(1,1); 384 | MATLAB_ASSERT_HANDLE(0); 385 | BKInstanceInfo& gcinstance = sGetBKInstance(*(int*)mxGetData(prhs[0])); 386 | mwSize outdim = 1; 387 | plhs[0] = mxCreateNumericArray(1, &outdim, cSiteClassID, mxREAL); 388 | *(SiteID*)mxGetData(plhs[0]) = gcinstance.bk->var_num(); 389 | } 390 | 391 | -------------------------------------------------------------------------------- /bin_graphcuts/graph.h: -------------------------------------------------------------------------------- 1 | /* graph.h */ 2 | /* 3 | This software library implements the maxflow algorithm 4 | described in 5 | 6 | "An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision." 7 | Yuri Boykov and Vladimir Kolmogorov. 8 | In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), 9 | September 2004 10 | 11 | This algorithm was developed by Yuri Boykov and Vladimir Kolmogorov 12 | at Siemens Corporate Research. To make it available for public use, 13 | it was later reimplemented by Vladimir Kolmogorov based on open publications. 14 | 15 | If you use this software for research purposes, you should cite 16 | the aforementioned paper in any resulting publication. 17 | 18 | ---------------------------------------------------------------------- 19 | 20 | REUSING TREES: 21 | 22 | Starting with version 3.0, there is a also an option of reusing search 23 | trees from one maxflow computation to the next, as described in 24 | 25 | "Efficiently Solving Dynamic Markov Random Fields Using Graph Cuts." 26 | Pushmeet Kohli and Philip H.S. Torr 27 | International Conference on Computer Vision (ICCV), 2005 28 | 29 | If you use this option, you should cite 30 | the aforementioned paper in any resulting publication. 31 | */ 32 | 33 | 34 | 35 | /* 36 | For description, license, example usage see README.TXT. 37 | */ 38 | 39 | #ifndef __GRAPH_H__ 40 | #define __GRAPH_H__ 41 | 42 | #include 43 | #include "block.h" 44 | 45 | #include 46 | // NOTE: in UNIX you need to use -DNDEBUG preprocessor option to supress assert's!!! 47 | 48 | 49 | 50 | // captype: type of edge capacities (excluding t-links) 51 | // tcaptype: type of t-links (edges between nodes and terminals) 52 | // flowtype: type of total flow 53 | // 54 | // Current instantiations are in instances.inc 55 | template class Graph 56 | { 57 | public: 58 | typedef enum 59 | { 60 | SOURCE = 0, 61 | SINK = 1 62 | } termtype; // terminals 63 | typedef int node_id; 64 | 65 | ///////////////////////////////////////////////////////////////////////// 66 | // BASIC INTERFACE FUNCTIONS // 67 | // (should be enough for most applications) // 68 | ///////////////////////////////////////////////////////////////////////// 69 | 70 | // Constructor. 71 | // The first argument gives an estimate of the maximum number of nodes that can be added 72 | // to the graph, and the second argument is an estimate of the maximum number of edges. 73 | // The last (optional) argument is the pointer to the function which will be called 74 | // if an error occurs; an error message is passed to this function. 75 | // If this argument is omitted, exit(1) will be called. 76 | // 77 | // IMPORTANT: It is possible to add more nodes to the graph than node_num_max 78 | // (and node_num_max can be zero). However, if the count is exceeded, then 79 | // the internal memory is reallocated (increased by 50%) which is expensive. 80 | // Also, temporarily the amount of allocated memory would be more than twice than needed. 81 | // Similarly for edges. 82 | // If you wish to avoid this overhead, you can download version 2.2, where nodes and edges are stored in blocks. 83 | Graph(int node_num_max, int edge_num_max, void (*err_function)(char *) = NULL); 84 | 85 | // Destructor 86 | ~Graph(); 87 | 88 | // Adds node(s) to the graph. By default, one node is added (num=1); then first call returns 0, second call returns 1, and so on. 89 | // If num>1, then several nodes are added, and node_id of the first one is returned. 90 | // IMPORTANT: see note about the constructor 91 | node_id add_node(int num = 1); 92 | 93 | // Adds a bidirectional edge between 'i' and 'j' with the weights 'cap' and 'rev_cap'. 94 | // IMPORTANT: see note about the constructor 95 | void add_edge(node_id i, node_id j, captype cap, captype rev_cap); 96 | 97 | // Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights. 98 | // Can be called multiple times for each node. 99 | // Weights can be negative. 100 | // Returns true if the residual capacity actually changed (useful for determining if a node must be 'marked' when re-using search trees) 101 | // NOTE: the number of such edges is not counted in edge_num_max. 102 | // No internal memory is allocated by this call. 103 | bool add_tweights(node_id i, tcaptype cap_source, tcaptype cap_sink); 104 | 105 | 106 | // Computes the maxflow. Can be called several times. 107 | // FOR DESCRIPTION OF reuse_trees, SEE mark_node(). 108 | // FOR DESCRIPTION OF changed_list, SEE remove_from_changed_list(). 109 | flowtype maxflow(bool reuse_trees = false, Block* changed_list = NULL); 110 | 111 | // After the maxflow is computed, this function returns to which 112 | // segment the node 'i' belongs (Graph::SOURCE or Graph::SINK). 113 | // 114 | // Occasionally there may be several minimum cuts. If a node can be assigned 115 | // to both the source and the sink, then default_segm is returned. 116 | termtype what_segment(node_id i, termtype default_segm = SOURCE); 117 | 118 | 119 | 120 | ////////////////////////////////////////////// 121 | // ADVANCED INTERFACE FUNCTIONS // 122 | // (provide access to the graph) // 123 | ////////////////////////////////////////////// 124 | 125 | private: 126 | struct node; 127 | struct arc; 128 | 129 | public: 130 | 131 | //////////////////////////// 132 | // 1. Reallocating graph. // 133 | //////////////////////////// 134 | 135 | // Removes all nodes and edges. 136 | // After that functions add_node() and add_edge() must be called again. 137 | // 138 | // Advantage compared to deleting Graph and allocating it again: 139 | // no calls to delete/new (which could be quite slow). 140 | // 141 | // If the graph structure stays the same, then an alternative 142 | // is to go through all nodes/edges and set new residual capacities 143 | // (see functions below). 144 | void reset(); 145 | 146 | //////////////////////////////////////////////////////////////////////////////// 147 | // 2. Functions for getting pointers to arcs and for reading graph structure. // 148 | // NOTE: adding new arcs may invalidate these pointers (if reallocation // 149 | // happens). So it's best not to add arcs while reading graph structure. // 150 | //////////////////////////////////////////////////////////////////////////////// 151 | 152 | // The following two functions return arcs in the same order that they 153 | // were added to the graph. NOTE: for each call add_edge(i,j,cap,cap_rev) 154 | // the first arc returned will be i->j, and the second j->i. 155 | // If there are no more arcs, then the function can still be called, but 156 | // the returned arc_id is undetermined. 157 | typedef arc* arc_id; 158 | arc_id get_first_arc(); 159 | arc_id get_next_arc(arc_id a); 160 | 161 | // other functions for reading graph structure 162 | int get_node_num() { return node_num; } 163 | int get_arc_num() { return (int)(arc_last - arcs); } 164 | void get_arc_ends(arc_id a, node_id& i, node_id& j); // returns i,j to that a = i->j 165 | 166 | /////////////////////////////////////////////////// 167 | // 3. Functions for reading residual capacities. // 168 | /////////////////////////////////////////////////// 169 | 170 | // returns residual capacity of SOURCE->i minus residual capacity of i->SINK 171 | tcaptype get_trcap(node_id i); 172 | // returns residual capacity of arc a 173 | captype get_rcap(arc* a); 174 | 175 | ///////////////////////////////////////////////////////////////// 176 | // 4. Functions for setting residual capacities. // 177 | // NOTE: If these functions are used, the value of the flow // 178 | // returned by maxflow() will not be valid! // 179 | ///////////////////////////////////////////////////////////////// 180 | 181 | void set_trcap(node_id i, tcaptype trcap); 182 | void set_rcap(arc* a, captype rcap); 183 | 184 | //////////////////////////////////////////////////////////////////// 185 | // 5. Functions related to reusing trees & list of changed nodes. // 186 | //////////////////////////////////////////////////////////////////// 187 | 188 | // If flag reuse_trees is true while calling maxflow(), then search trees 189 | // are reused from previous maxflow computation. 190 | // In this case before calling maxflow() the user must 191 | // specify which parts of the graph have changed by calling mark_node(): 192 | // add_tweights(i),set_trcap(i) => call mark_node(i) 193 | // add_edge(i,j),set_rcap(a) => call mark_node(i); mark_node(j) 194 | // 195 | // This option makes sense only if a small part of the graph is changed. 196 | // The initialization procedure goes only through marked nodes then. 197 | // 198 | // mark_node(i) can either be called before or after graph modification. 199 | // Can be called more than once per node, but calls after the first one 200 | // do not have any effect. 201 | // 202 | // NOTE: 203 | // - This option cannot be used in the first call to maxflow(). 204 | // - It is not necessary to call mark_node() if the change is ``not essential'', 205 | // i.e. sign(trcap) is preserved for a node and zero/nonzero status is preserved for an arc. 206 | // - To check that you marked all necessary nodes, you can call maxflow(false) after calling maxflow(true). 207 | // If everything is correct, the two calls must return the same value of flow. (Useful for debugging). 208 | void mark_node(node_id i); 209 | 210 | // If changed_list is not NULL while calling maxflow(), then the algorithm 211 | // keeps a list of nodes which could potentially have changed their segmentation label. 212 | // Nodes which are not in the list are guaranteed to keep their old segmentation label (SOURCE or SINK). 213 | // Example usage: 214 | // 215 | // typedef Graph G; 216 | // G* g = new Graph(nodeNum, edgeNum); 217 | // Block* changed_list = new Block(128); 218 | // 219 | // ... // add nodes and edges 220 | // 221 | // g->maxflow(); // first call should be without arguments 222 | // for (int iter=0; iter<10; iter++) 223 | // { 224 | // ... // change graph, call mark_node() accordingly 225 | // 226 | // g->maxflow(true, changed_list); 227 | // G::node_id* ptr; 228 | // for (ptr=changed_list->ScanFirst(); ptr; ptr=changed_list->ScanNext()) 229 | // { 230 | // G::node_id i = *ptr; assert(i>=0 && iremove_from_changed_list(i); 232 | // // do something with node i... 233 | // if (g->what_segment(i) == G::SOURCE) { ... } 234 | // } 235 | // changed_list->Reset(); 236 | // } 237 | // delete changed_list; 238 | // 239 | // NOTE: 240 | // - If changed_list option is used, then reuse_trees must be used as well. 241 | // - In the example above, the user may omit calls g->remove_from_changed_list(i) and changed_list->Reset() in a given iteration. 242 | // Then during the next call to maxflow(true, &changed_list) new nodes will be added to changed_list. 243 | // - If the next call to maxflow() does not use option reuse_trees, then calling remove_from_changed_list() 244 | // is not necessary. ("changed_list->Reset()" or "delete changed_list" should still be called, though). 245 | void remove_from_changed_list(node_id i) 246 | { 247 | assert(i>=0 && i* g0); 252 | 253 | 254 | int get_node_num() const { return node_num; } 255 | 256 | 257 | ///////////////////////////////////////////////////////////////////////// 258 | ///////////////////////////////////////////////////////////////////////// 259 | ///////////////////////////////////////////////////////////////////////// 260 | 261 | private: 262 | // internal variables and functions 263 | 264 | struct node 265 | { 266 | arc *first; // first outcoming arc 267 | 268 | arc *parent; // node's parent 269 | node *next; // pointer to the next active node 270 | // (or to itself if it is the last node in the list) 271 | int TS; // timestamp showing when DIST was computed 272 | int DIST; // distance to the terminal 273 | int is_sink : 1; // flag showing whether the node is in the source or in the sink tree (if parent!=NULL) 274 | int is_marked : 1; // set by mark_node() 275 | int is_in_changed_list : 1; // set by maxflow if 276 | 277 | tcaptype tr_cap; // if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node 278 | // otherwise -tr_cap is residual capacity of the arc node->SINK 279 | 280 | }; 281 | 282 | struct arc 283 | { 284 | node *head; // node the arc points to 285 | arc *next; // next arc with the same originating node 286 | arc *sister; // reverse arc 287 | 288 | captype r_cap; // residual capacity 289 | }; 290 | 291 | struct nodeptr 292 | { 293 | node *ptr; 294 | nodeptr *next; 295 | }; 296 | static const int NODEPTR_BLOCK_SIZE = 128; 297 | 298 | node *nodes, *node_last, *node_max; // node_last = nodes+node_num, node_max = nodes+node_num_max; 299 | arc *arcs, *arc_last, *arc_max; // arc_last = arcs+2*edge_num, arc_max = arcs+2*edge_num_max; 300 | 301 | int node_num; 302 | 303 | DBlock *nodeptr_block; 304 | 305 | void (*error_function)(char *); // this function is called if a error occurs, 306 | // with a corresponding error message 307 | // (or exit(1) is called if it's NULL) 308 | 309 | flowtype flow; // total flow 310 | 311 | // reusing trees & list of changed pixels 312 | int maxflow_iteration; // counter 313 | Block *changed_list; 314 | 315 | ///////////////////////////////////////////////////////////////////////// 316 | 317 | node *queue_first[2], *queue_last[2]; // list of active nodes 318 | nodeptr *orphan_first, *orphan_last; // list of pointers to orphans 319 | int TIME; // monotonically increasing global counter 320 | 321 | ///////////////////////////////////////////////////////////////////////// 322 | 323 | void reallocate_nodes(int num); // num is the number of new nodes 324 | void reallocate_arcs(); 325 | 326 | // functions for processing active list 327 | void set_active(node *i); 328 | node *next_active(); 329 | 330 | // functions for processing orphans list 331 | void set_orphan_front(node* i); // add to the beginning of the list 332 | void set_orphan_rear(node* i); // add to the end of the list 333 | 334 | void add_to_changed_list(node* i); 335 | 336 | void maxflow_init(); // called if reuse_trees == false 337 | void maxflow_reuse_trees_init(); // called if reuse_trees == true 338 | void augment(arc *middle_arc); 339 | void process_source_orphan(node *i); 340 | void process_sink_orphan(node *i); 341 | 342 | void test_consistency(node* current_node=NULL); // debug function 343 | }; 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | /////////////////////////////////////// 356 | // Implementation - inline functions // 357 | /////////////////////////////////////// 358 | 359 | 360 | 361 | template 362 | inline typename Graph::node_id Graph::add_node(int num) 363 | { 364 | assert(num > 0); 365 | 366 | if (node_last + num > node_max) reallocate_nodes(num); 367 | 368 | if (num == 1) 369 | { 370 | node_last -> first = NULL; 371 | node_last -> tr_cap = 0; 372 | node_last -> is_marked = 0; 373 | node_last -> is_in_changed_list = 0; 374 | 375 | node_last ++; 376 | return node_num ++; 377 | } 378 | else 379 | { 380 | memset(node_last, 0, num*sizeof(node)); 381 | 382 | node_id i = node_num; 383 | node_num += num; 384 | node_last += num; 385 | return i; 386 | } 387 | } 388 | 389 | template 390 | inline bool Graph::add_tweights(node_id i, tcaptype cap_source, tcaptype cap_sink) 391 | { 392 | assert(i >= 0 && i < node_num); 393 | tcaptype oldcap = nodes[i].tr_cap; 394 | 395 | tcaptype delta = nodes[i].tr_cap; 396 | if (delta > 0) cap_source += delta; 397 | else cap_sink -= delta; 398 | flow += (cap_source < cap_sink) ? cap_source : cap_sink; 399 | nodes[i].tr_cap = cap_source - cap_sink; 400 | return !((nodes[i].tr_cap > 0 && oldcap > 0)||(nodes[i].tr_cap < 0 && oldcap < 0)); 401 | } 402 | 403 | template 404 | inline void Graph::add_edge(node_id _i, node_id _j, captype cap, captype rev_cap) 405 | { 406 | assert(_i >= 0 && _i < node_num); 407 | assert(_j >= 0 && _j < node_num); 408 | assert(_i != _j); 409 | assert(cap >= 0); 410 | assert(rev_cap >= 0); 411 | 412 | if (arc_last == arc_max) reallocate_arcs(); 413 | 414 | arc *a = arc_last ++; 415 | arc *a_rev = arc_last ++; 416 | 417 | node* i = nodes + _i; 418 | node* j = nodes + _j; 419 | 420 | a -> sister = a_rev; 421 | a_rev -> sister = a; 422 | a -> next = i -> first; 423 | i -> first = a; 424 | a_rev -> next = j -> first; 425 | j -> first = a_rev; 426 | a -> head = j; 427 | a_rev -> head = i; 428 | a -> r_cap = cap; 429 | a_rev -> r_cap = rev_cap; 430 | } 431 | 432 | template 433 | inline typename Graph::arc* Graph::get_first_arc() 434 | { 435 | return arcs; 436 | } 437 | 438 | template 439 | inline typename Graph::arc* Graph::get_next_arc(arc* a) 440 | { 441 | return a + 1; 442 | } 443 | 444 | template 445 | inline void Graph::get_arc_ends(arc* a, node_id& i, node_id& j) 446 | { 447 | assert(a >= arcs && a < arc_last); 448 | i = (node_id) (a->sister->head - nodes); 449 | j = (node_id) (a->head - nodes); 450 | } 451 | 452 | template 453 | inline tcaptype Graph::get_trcap(node_id i) 454 | { 455 | assert(i>=0 && i 460 | inline captype Graph::get_rcap(arc* a) 461 | { 462 | assert(a >= arcs && a < arc_last); 463 | return a->r_cap; 464 | } 465 | 466 | template 467 | inline void Graph::set_trcap(node_id i, tcaptype trcap) 468 | { 469 | assert(i>=0 && i 474 | inline void Graph::set_rcap(arc* a, captype rcap) 475 | { 476 | assert(a >= arcs && a < arc_last); 477 | a->r_cap = rcap; 478 | } 479 | 480 | 481 | template 482 | inline typename Graph::termtype Graph::what_segment(node_id i, termtype default_segm) 483 | { 484 | if (nodes[i].parent) 485 | { 486 | return (nodes[i].is_sink) ? SINK : SOURCE; 487 | } 488 | else 489 | { 490 | return default_segm; 491 | } 492 | } 493 | 494 | template 495 | inline void Graph::mark_node(node_id _i) 496 | { 497 | node* i = nodes + _i; 498 | if (!i->next) 499 | { 500 | /* it's not in the list yet */ 501 | if (queue_last[1]) queue_last[1] -> next = i; 502 | else queue_first[1] = i; 503 | queue_last[1] = i; 504 | i -> next = i; 505 | } 506 | i->is_marked = 1; 507 | } 508 | 509 | 510 | #endif 511 | -------------------------------------------------------------------------------- /bin_graphcuts/maxflow.cpp: -------------------------------------------------------------------------------- 1 | /* maxflow.cpp */ 2 | 3 | 4 | #include 5 | #include "graph.h" 6 | 7 | 8 | /* 9 | special constants for node->parent 10 | */ 11 | #define TERMINAL ( (arc *) 1 ) /* to terminal */ 12 | #define ORPHAN ( (arc *) 2 ) /* orphan */ 13 | 14 | 15 | #define INFINITE_D ((int)(((unsigned)-1)/2)) /* infinite distance to the terminal */ 16 | 17 | /***********************************************************************/ 18 | 19 | /* 20 | Functions for processing active list. 21 | i->next points to the next node in the list 22 | (or to i, if i is the last node in the list). 23 | If i->next is NULL iff i is not in the list. 24 | 25 | There are two queues. Active nodes are added 26 | to the end of the second queue and read from 27 | the front of the first queue. If the first queue 28 | is empty, it is replaced by the second queue 29 | (and the second queue becomes empty). 30 | */ 31 | 32 | 33 | template 34 | inline void Graph::set_active(node *i) 35 | { 36 | if (!i->next) 37 | { 38 | /* it's not in the list yet */ 39 | if (queue_last[1]) queue_last[1] -> next = i; 40 | else queue_first[1] = i; 41 | queue_last[1] = i; 42 | i -> next = i; 43 | } 44 | } 45 | 46 | /* 47 | Returns the next active node. 48 | If it is connected to the sink, it stays in the list, 49 | otherwise it is removed from the list 50 | */ 51 | template 52 | inline typename Graph::node* Graph::next_active() 53 | { 54 | node *i; 55 | 56 | while ( 1 ) 57 | { 58 | if (!(i=queue_first[0])) 59 | { 60 | queue_first[0] = i = queue_first[1]; 61 | queue_last[0] = queue_last[1]; 62 | queue_first[1] = NULL; 63 | queue_last[1] = NULL; 64 | if (!i) return NULL; 65 | } 66 | 67 | /* remove it from the active list */ 68 | if (i->next == i) queue_first[0] = queue_last[0] = NULL; 69 | else queue_first[0] = i -> next; 70 | i -> next = NULL; 71 | 72 | /* a node in the list is active iff it has a parent */ 73 | if (i->parent) return i; 74 | } 75 | } 76 | 77 | /***********************************************************************/ 78 | 79 | template 80 | inline void Graph::set_orphan_front(node *i) 81 | { 82 | nodeptr *np; 83 | i -> parent = ORPHAN; 84 | np = nodeptr_block -> New(); 85 | np -> ptr = i; 86 | np -> next = orphan_first; 87 | orphan_first = np; 88 | } 89 | 90 | template 91 | inline void Graph::set_orphan_rear(node *i) 92 | { 93 | nodeptr *np; 94 | i -> parent = ORPHAN; 95 | np = nodeptr_block -> New(); 96 | np -> ptr = i; 97 | if (orphan_last) orphan_last -> next = np; 98 | else orphan_first = np; 99 | orphan_last = np; 100 | np -> next = NULL; 101 | } 102 | 103 | /***********************************************************************/ 104 | 105 | template 106 | inline void Graph::add_to_changed_list(node *i) 107 | { 108 | if (changed_list && !i->is_in_changed_list) 109 | { 110 | node_id* ptr = changed_list->New(); 111 | *ptr = (node_id)(i - nodes); 112 | i->is_in_changed_list = true; 113 | } 114 | } 115 | 116 | /***********************************************************************/ 117 | 118 | template 119 | void Graph::maxflow_init() 120 | { 121 | node *i; 122 | 123 | queue_first[0] = queue_last[0] = NULL; 124 | queue_first[1] = queue_last[1] = NULL; 125 | orphan_first = NULL; 126 | 127 | TIME = 0; 128 | 129 | for (i=nodes; i next = NULL; 132 | i -> is_marked = 0; 133 | i -> is_in_changed_list = 0; 134 | i -> TS = TIME; 135 | if (i->tr_cap > 0) 136 | { 137 | /* i is connected to the source */ 138 | i -> is_sink = 0; 139 | i -> parent = TERMINAL; 140 | set_active(i); 141 | i -> DIST = 1; 142 | } 143 | else if (i->tr_cap < 0) 144 | { 145 | /* i is connected to the sink */ 146 | i -> is_sink = 1; 147 | i -> parent = TERMINAL; 148 | set_active(i); 149 | i -> DIST = 1; 150 | } 151 | else 152 | { 153 | i -> parent = NULL; 154 | } 155 | } 156 | } 157 | 158 | template 159 | void Graph::maxflow_reuse_trees_init() 160 | { 161 | node* i; 162 | node* j; 163 | node* queue = queue_first[1]; 164 | arc* a; 165 | nodeptr* np; 166 | 167 | queue_first[0] = queue_last[0] = NULL; 168 | queue_first[1] = queue_last[1] = NULL; 169 | orphan_first = orphan_last = NULL; 170 | 171 | TIME ++; 172 | 173 | while ((i=queue)) 174 | { 175 | queue = i->next; 176 | if (queue == i) queue = NULL; 177 | i->next = NULL; 178 | i->is_marked = 0; 179 | set_active(i); 180 | 181 | if (i->tr_cap == 0) 182 | { 183 | if (i->parent) set_orphan_rear(i); 184 | continue; 185 | } 186 | 187 | if (i->tr_cap > 0) 188 | { 189 | if (!i->parent || i->is_sink) 190 | { 191 | i->is_sink = 0; 192 | for (a=i->first; a; a=a->next) 193 | { 194 | j = a->head; 195 | if (!j->is_marked) 196 | { 197 | if (j->parent == a->sister) set_orphan_rear(j); 198 | if (j->parent && j->is_sink && a->r_cap > 0) set_active(j); 199 | } 200 | } 201 | add_to_changed_list(i); 202 | } 203 | } 204 | else 205 | { 206 | if (!i->parent || !i->is_sink) 207 | { 208 | i->is_sink = 1; 209 | for (a=i->first; a; a=a->next) 210 | { 211 | j = a->head; 212 | if (!j->is_marked) 213 | { 214 | if (j->parent == a->sister) set_orphan_rear(j); 215 | if (j->parent && !j->is_sink && a->sister->r_cap > 0) set_active(j); 216 | } 217 | } 218 | add_to_changed_list(i); 219 | } 220 | } 221 | i->parent = TERMINAL; 222 | i -> TS = TIME; 223 | i -> DIST = 1; 224 | } 225 | 226 | //test_consistency(); 227 | 228 | /* adoption */ 229 | while ((np=orphan_first)) 230 | { 231 | orphan_first = np -> next; 232 | i = np -> ptr; 233 | nodeptr_block -> Delete(np); 234 | if (!orphan_first) orphan_last = NULL; 235 | if (i->is_sink) process_sink_orphan(i); 236 | else process_source_orphan(i); 237 | } 238 | /* adoption end */ 239 | 240 | //test_consistency(); 241 | } 242 | 243 | template 244 | void Graph::augment(arc *middle_arc) 245 | { 246 | node *i; 247 | arc *a; 248 | tcaptype bottleneck; 249 | 250 | 251 | /* 1. Finding bottleneck capacity */ 252 | /* 1a - the source tree */ 253 | bottleneck = middle_arc -> r_cap; 254 | for (i=middle_arc->sister->head; ; i=a->head) 255 | { 256 | a = i -> parent; 257 | if (a == TERMINAL) break; 258 | if (bottleneck > a->sister->r_cap) bottleneck = a -> sister -> r_cap; 259 | } 260 | if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; 261 | /* 1b - the sink tree */ 262 | for (i=middle_arc->head; ; i=a->head) 263 | { 264 | a = i -> parent; 265 | if (a == TERMINAL) break; 266 | if (bottleneck > a->r_cap) bottleneck = a -> r_cap; 267 | } 268 | if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; 269 | 270 | 271 | /* 2. Augmenting */ 272 | /* 2a - the source tree */ 273 | middle_arc -> sister -> r_cap += bottleneck; 274 | middle_arc -> r_cap -= bottleneck; 275 | for (i=middle_arc->sister->head; ; i=a->head) 276 | { 277 | a = i -> parent; 278 | if (a == TERMINAL) break; 279 | a -> r_cap += bottleneck; 280 | a -> sister -> r_cap -= bottleneck; 281 | if (!a->sister->r_cap) 282 | { 283 | set_orphan_front(i); // add i to the beginning of the adoption list 284 | } 285 | } 286 | i -> tr_cap -= bottleneck; 287 | if (!i->tr_cap) 288 | { 289 | set_orphan_front(i); // add i to the beginning of the adoption list 290 | } 291 | /* 2b - the sink tree */ 292 | for (i=middle_arc->head; ; i=a->head) 293 | { 294 | a = i -> parent; 295 | if (a == TERMINAL) break; 296 | a -> sister -> r_cap += bottleneck; 297 | a -> r_cap -= bottleneck; 298 | if (!a->r_cap) 299 | { 300 | set_orphan_front(i); // add i to the beginning of the adoption list 301 | } 302 | } 303 | i -> tr_cap += bottleneck; 304 | if (!i->tr_cap) 305 | { 306 | set_orphan_front(i); // add i to the beginning of the adoption list 307 | } 308 | 309 | 310 | flow += bottleneck; 311 | } 312 | 313 | /***********************************************************************/ 314 | 315 | template 316 | void Graph::process_source_orphan(node *i) 317 | { 318 | node *j; 319 | arc *a0, *a0_min = NULL, *a; 320 | int d, d_min = INFINITE_D; 321 | 322 | /* trying to find a new parent */ 323 | for (a0=i->first; a0; a0=a0->next) 324 | if (a0->sister->r_cap) 325 | { 326 | j = a0 -> head; 327 | if (!j->is_sink && (a=j->parent)) 328 | { 329 | /* checking the origin of j */ 330 | d = 0; 331 | while ( 1 ) 332 | { 333 | if (j->TS == TIME) 334 | { 335 | d += j -> DIST; 336 | break; 337 | } 338 | a = j -> parent; 339 | d ++; 340 | if (a==TERMINAL) 341 | { 342 | j -> TS = TIME; 343 | j -> DIST = 1; 344 | break; 345 | } 346 | if (a==ORPHAN) { d = INFINITE_D; break; } 347 | j = a -> head; 348 | } 349 | if (dhead; j->TS!=TIME; j=j->parent->head) 358 | { 359 | j -> TS = TIME; 360 | j -> DIST = d --; 361 | } 362 | } 363 | } 364 | } 365 | 366 | if (i->parent = a0_min) 367 | { 368 | i -> TS = TIME; 369 | i -> DIST = d_min + 1; 370 | } 371 | else 372 | { 373 | /* no parent is found */ 374 | add_to_changed_list(i); 375 | 376 | /* process neighbors */ 377 | for (a0=i->first; a0; a0=a0->next) 378 | { 379 | j = a0 -> head; 380 | if (!j->is_sink && (a=j->parent)) 381 | { 382 | if (a0->sister->r_cap) set_active(j); 383 | if (a!=TERMINAL && a!=ORPHAN && a->head==i) 384 | { 385 | set_orphan_rear(j); // add j to the end of the adoption list 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | template 393 | void Graph::process_sink_orphan(node *i) 394 | { 395 | node *j; 396 | arc *a0, *a0_min = NULL, *a; 397 | int d, d_min = INFINITE_D; 398 | 399 | /* trying to find a new parent */ 400 | for (a0=i->first; a0; a0=a0->next) 401 | if (a0->r_cap) 402 | { 403 | j = a0 -> head; 404 | if (j->is_sink && (a=j->parent)) 405 | { 406 | /* checking the origin of j */ 407 | d = 0; 408 | while ( 1 ) 409 | { 410 | if (j->TS == TIME) 411 | { 412 | d += j -> DIST; 413 | break; 414 | } 415 | a = j -> parent; 416 | d ++; 417 | if (a==TERMINAL) 418 | { 419 | j -> TS = TIME; 420 | j -> DIST = 1; 421 | break; 422 | } 423 | if (a==ORPHAN) { d = INFINITE_D; break; } 424 | j = a -> head; 425 | } 426 | if (dhead; j->TS!=TIME; j=j->parent->head) 435 | { 436 | j -> TS = TIME; 437 | j -> DIST = d --; 438 | } 439 | } 440 | } 441 | } 442 | 443 | if (i->parent = a0_min) 444 | { 445 | i -> TS = TIME; 446 | i -> DIST = d_min + 1; 447 | } 448 | else 449 | { 450 | /* no parent is found */ 451 | add_to_changed_list(i); 452 | 453 | /* process neighbors */ 454 | for (a0=i->first; a0; a0=a0->next) 455 | { 456 | j = a0 -> head; 457 | if (j->is_sink && (a=j->parent)) 458 | { 459 | if (a0->r_cap) set_active(j); 460 | if (a!=TERMINAL && a!=ORPHAN && a->head==i) 461 | { 462 | set_orphan_rear(j); // add j to the end of the adoption list 463 | } 464 | } 465 | } 466 | } 467 | } 468 | 469 | /***********************************************************************/ 470 | 471 | template 472 | flowtype Graph::maxflow(bool reuse_trees, Block* _changed_list) 473 | { 474 | node *i, *j, *current_node = NULL; 475 | arc *a; 476 | nodeptr *np, *np_next; 477 | 478 | if (!nodeptr_block) 479 | { 480 | nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); 481 | } 482 | 483 | changed_list = _changed_list; 484 | if (maxflow_iteration == 0 && reuse_trees) { if (error_function) (*error_function)("reuse_trees cannot be used in the first call to maxflow()!"); exit(1); } 485 | if (changed_list && !reuse_trees) { if (error_function) (*error_function)("changed_list cannot be used without reuse_trees!"); exit(1); } 486 | 487 | if (reuse_trees) maxflow_reuse_trees_init(); 488 | else maxflow_init(); 489 | 490 | // main loop 491 | while ( 1 ) 492 | { 493 | // test_consistency(current_node); 494 | 495 | if ((i=current_node)) 496 | { 497 | i -> next = NULL; /* remove active flag */ 498 | if (!i->parent) i = NULL; 499 | } 500 | if (!i) 501 | { 502 | if (!(i = next_active())) break; 503 | } 504 | 505 | /* growth */ 506 | if (!i->is_sink) 507 | { 508 | /* grow source tree */ 509 | for (a=i->first; a; a=a->next) 510 | if (a->r_cap) 511 | { 512 | j = a -> head; 513 | if (!j->parent) 514 | { 515 | j -> is_sink = 0; 516 | j -> parent = a -> sister; 517 | j -> TS = i -> TS; 518 | j -> DIST = i -> DIST + 1; 519 | set_active(j); 520 | add_to_changed_list(j); 521 | } 522 | else if (j->is_sink) break; 523 | else if (j->TS <= i->TS && 524 | j->DIST > i->DIST) 525 | { 526 | /* heuristic - trying to make the distance from j to the source shorter */ 527 | j -> parent = a -> sister; 528 | j -> TS = i -> TS; 529 | j -> DIST = i -> DIST + 1; 530 | } 531 | } 532 | } 533 | else 534 | { 535 | /* grow sink tree */ 536 | for (a=i->first; a; a=a->next) 537 | if (a->sister->r_cap) 538 | { 539 | j = a -> head; 540 | if (!j->parent) 541 | { 542 | j -> is_sink = 1; 543 | j -> parent = a -> sister; 544 | j -> TS = i -> TS; 545 | j -> DIST = i -> DIST + 1; 546 | set_active(j); 547 | add_to_changed_list(j); 548 | } 549 | else if (!j->is_sink) { a = a -> sister; break; } 550 | else if (j->TS <= i->TS && 551 | j->DIST > i->DIST) 552 | { 553 | /* heuristic - trying to make the distance from j to the sink shorter */ 554 | j -> parent = a -> sister; 555 | j -> TS = i -> TS; 556 | j -> DIST = i -> DIST + 1; 557 | } 558 | } 559 | } 560 | 561 | TIME ++; 562 | 563 | if (a) 564 | { 565 | i -> next = i; /* set active flag */ 566 | current_node = i; 567 | 568 | /* augmentation */ 569 | augment(a); 570 | /* augmentation end */ 571 | 572 | /* adoption */ 573 | while ((np=orphan_first)) 574 | { 575 | np_next = np -> next; 576 | np -> next = NULL; 577 | 578 | while ((np=orphan_first)) 579 | { 580 | orphan_first = np -> next; 581 | i = np -> ptr; 582 | nodeptr_block -> Delete(np); 583 | if (!orphan_first) orphan_last = NULL; 584 | if (i->is_sink) process_sink_orphan(i); 585 | else process_source_orphan(i); 586 | } 587 | 588 | orphan_first = np_next; 589 | } 590 | /* adoption end */ 591 | } 592 | else current_node = NULL; 593 | } 594 | // test_consistency(); 595 | 596 | if (!reuse_trees || (maxflow_iteration % 64) == 0) 597 | { 598 | delete nodeptr_block; 599 | nodeptr_block = NULL; 600 | } 601 | 602 | maxflow_iteration ++; 603 | return flow; 604 | } 605 | 606 | /***********************************************************************/ 607 | 608 | 609 | template 610 | void Graph::test_consistency(node* current_node) 611 | { 612 | node *i; 613 | arc *a; 614 | int r; 615 | int num1 = 0, num2 = 0; 616 | 617 | // test whether all nodes i with i->next!=NULL are indeed in the queue 618 | for (i=nodes; inext || i==current_node) num1 ++; 621 | } 622 | for (r=0; r<3; r++) 623 | { 624 | i = (r == 2) ? current_node : queue_first[r]; 625 | if (i) 626 | for ( ; ; i=i->next) 627 | { 628 | num2 ++; 629 | if (i->next == i) 630 | { 631 | if (r<2) assert(i == queue_last[r]); 632 | else assert(i == current_node); 633 | break; 634 | } 635 | } 636 | } 637 | assert(num1 == num2); 638 | 639 | for (i=nodes; iparent == NULL) {} 643 | else if (i->parent == ORPHAN) {} 644 | else if (i->parent == TERMINAL) 645 | { 646 | if (!i->is_sink) assert(i->tr_cap > 0); 647 | else assert(i->tr_cap < 0); 648 | } 649 | else 650 | { 651 | if (!i->is_sink) assert (i->parent->sister->r_cap > 0); 652 | else assert (i->parent->r_cap > 0); 653 | } 654 | // test whether passive nodes in search trees have neighbors in 655 | // a different tree through non-saturated edges 656 | if (i->parent && !i->next) 657 | { 658 | if (!i->is_sink) 659 | { 660 | assert(i->tr_cap >= 0); 661 | for (a=i->first; a; a=a->next) 662 | { 663 | if (a->r_cap > 0) assert(a->head->parent && !a->head->is_sink); 664 | } 665 | } 666 | else 667 | { 668 | assert(i->tr_cap <= 0); 669 | for (a=i->first; a; a=a->next) 670 | { 671 | if (a->sister->r_cap > 0) assert(a->head->parent && a->head->is_sink); 672 | } 673 | } 674 | } 675 | // test marking invariants 676 | if (i->parent && i->parent!=ORPHAN && i->parent!=TERMINAL) 677 | { 678 | assert(i->TS <= i->parent->head->TS); 679 | if (i->TS == i->parent->head->TS) assert(i->DIST > i->parent->head->DIST); 680 | } 681 | } 682 | } 683 | 684 | template 685 | void Graph::Copy(Graph* g0) 686 | { 687 | node* i; 688 | arc* a; 689 | 690 | reset(); 691 | 692 | if (node_max < nodes + g0->node_num) 693 | { 694 | free(nodes); 695 | nodes = node_last = (node*) malloc(g0->node_num*sizeof(node)); 696 | node_max = nodes + g0->node_num; 697 | } 698 | if (arc_max < arcs + (g0->arc_last - g0->arcs)) 699 | { 700 | free(arcs); 701 | arcs = arc_last = (arc*) malloc((g0->arc_last - g0->arcs)*sizeof(arc)); 702 | arc_max = arcs + (g0->arc_last - g0->arcs); 703 | } 704 | 705 | node_num = g0->node_num; 706 | node_last = nodes + node_num; 707 | memcpy(nodes, g0->nodes, node_num*sizeof(node)); 708 | for (i=nodes; ifirst) i->first = (arc*)((char*)arcs + (((char*)i->first) - ((char*)g0->arcs))); 711 | if (i->parent && i->parent!=TERMINAL && i->parent!=ORPHAN) i->parent = (arc*)((char*)arcs + (((char*)i->parent) - ((char*)g0->arcs))); 712 | if (i->next) i->next = (node*)((char*)nodes + (((char*)i->next) - ((char*)g0->nodes))); 713 | } 714 | 715 | arc_last = arcs + (g0->arc_last - g0->arcs); 716 | memcpy(arcs, g0->arcs, (g0->arc_last - g0->arcs)*sizeof(arc)); 717 | for (a=arcs; ahead = (node*)((char*)nodes + (((char*)a->head) - ((char*)g0->nodes))); 720 | if (a->next) a->next = (arc*)((char*)arcs + (((char*)a->next) - ((char*)g0->arcs))); 721 | a->sister = (arc*)((char*)arcs + (((char*)a->sister) - ((char*)g0->arcs))); 722 | } 723 | 724 | error_function = g0->error_function; 725 | flow = g0->flow; 726 | maxflow_iteration = g0->maxflow_iteration; 727 | 728 | queue_first[0] = (g0->queue_first[0]==NULL) ? NULL : (node*)((char*)nodes + (((char*)g0->queue_first[0]) - ((char*)g0->nodes))); 729 | queue_first[1] = (g0->queue_first[1]==NULL) ? NULL : (node*)((char*)nodes + (((char*)g0->queue_first[1]) - ((char*)g0->nodes))); 730 | queue_last[0] = (g0->queue_last[0]==NULL) ? NULL : (node*)((char*)nodes + (((char*)g0->queue_last[0]) - ((char*)g0->nodes))); 731 | queue_last[1] = (g0->queue_last[1]==NULL) ? NULL : (node*)((char*)nodes + (((char*)g0->queue_last[1]) - ((char*)g0->nodes))); 732 | TIME = g0->TIME; 733 | } 734 | --------------------------------------------------------------------------------