├── 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 | 
25 |
26 | 
27 |
28 | 
29 |
30 | 
31 |
32 | 
33 |
34 | 
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