├── README.md ├── applications ├── L1IntensitySegmentation2D.m ├── atlasProbabilityMapsRegularization3D.m ├── interactiveGraphCutSegmentation3D.m └── smoothManualSegmentations2D.m ├── compile.m ├── data ├── brain_1125.mat ├── brain_prob_MNI.mat └── natural_imgs.mat ├── license.md ├── maxflow ├── asetsBinaryMF2D.m ├── asetsBinaryMF2D_mex.c ├── asetsBinaryMF3D.m ├── asetsBinaryMF3D_mex.c ├── asetsDAGMF2D.m ├── asetsDAGMF3D.m ├── asetsHMF2D.m ├── asetsHMF3D.m ├── asetsIshikawa2D.m ├── asetsIshikawa2D_mex.c ├── asetsIshikawa3D.m ├── asetsIshikawa3D_mex.c ├── asetsPotts2D.m ├── asetsPotts2D_mex.c ├── asetsPotts2D_starShape.m ├── asetsPotts3D.m └── asetsPotts3D_mex.c ├── tests ├── runTestBinaryMF.m ├── runTestHMFLinearModel.m ├── runTestIshikawa.m └── runTestPotts.m ├── todo.txt └── tutorials ├── t01_graphCutSegmentation_binary.m ├── t02_graphCutSegmentation_potts.m └── t03_usingCUDA.m /README.md: -------------------------------------------------------------------------------- 1 | ## [ASETS](http://www.advancedsegmentationtools.org) Matlab repository 2 | 3 | ### License: 4 | BSD (see license.md) 5 | 6 | ### Developers: 7 | - Martin Rajchl (@mrajchl), Imperial College London (UK) 8 | - John SH. Baxter (@jshbaxter), Robarts Research Institute (CAN) 9 | - Jing Yuan, Robarts Research Institute (CAN) 10 | 11 | ### Features: 12 | - Fast parallel continuous max flow solvers in 2D/3D 13 | - Binary max flow 14 | - Multi-region (Potts model, Ishikawa model, Hierarchical Max Flow) 15 | - In two different implementations (full flow and pseudo flow solvers) 16 | - Implemented in multiple languages 17 | - Matlab/mex/C 18 | - Matlab/CUDA 19 | - Tutorials 20 | - T01 Binary graph cuts 21 | - T02 Multi-region color image segmentation with the Potts model 22 | - T03 Using different max flow implementations (Matlab, C, CUDA) 23 | 24 | - Application examples for (medical) image segmentation: 25 | - Interactive max flow graph cuts (3D) 26 | - Regularization of probabilistic label maps as in atlas-based segmentation (3D) 27 | - High-performance multi-phase levelsets (3D) 28 | - Post-processing of flawed manual segmentations with constrast sensitive regularization (2D) 29 | - L1 intensity segmentation (2D) 30 | 31 | ### Overview of folder structure: 32 | *./*: Compile scripts, readme, license and todo list 33 | *./applications*: Contains examples of typical applications in image segmentation and analysis 34 | *./data*: Example data to run the applications 35 | *./lib*: Is created by compile.m and contains the compiled C/mex files 36 | *./maxflow*: Optimization code in C/mex and Matlab 37 | *./tests*: Test scripts to compare different implementations against each other 38 | *./tutorials*: Contains available tutorials 39 | 40 | ### Compile/Installation instructions: 41 | To compile the C/mex code run: 42 | ```matlab 43 | compile.m 44 | ``` 45 | which creates the folder *./lib*. For testing purposes run any script in *./tests*. 46 | 47 | ### Tests: 48 | - Matlab 2014a, 64-bit Linux (Ubuntu 12.04 LTS) 49 | - Matlab 2015a, 64-bit Windows 7 50 | - Matlab 2012a, 32-bit WinXP 51 | 52 | -------------------------------------------------------------------------------- /applications/L1IntensitySegmentation2D.m: -------------------------------------------------------------------------------- 1 | function [] = L1IntensitySegmentation2D() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Example application: Intensity segmentation with an L1 data term 5 | % 6 | % References: 7 | % [1] Yuan, J.; Bae, E.; Tai, X.-C.; Boykov, Y. 8 | % A Continuous Max-Flow Approach to Potts Model 9 | % ECCV, 2010 10 | % 11 | 12 | close all; 13 | clear all; 14 | 15 | % include max-flow solver 16 | addpath(['..', filesep, 'maxflow']); 17 | addpath(['..', filesep, 'lib']); 18 | 19 | % flags 20 | visualizationFLAG = 1; 21 | 22 | % load image 23 | load(['..', filesep, 'data', filesep, 'brain_1125.mat'], 'img', 'man_s40_flawed'); 24 | 25 | img = img(:,:,40); 26 | 27 | % parameters 28 | alpha1 = 0.025; 29 | numberOfLabels = 4; 30 | [r, c] = size(img); 31 | 32 | % alloc a cost function Ct for each label i, int lId 33 | Ct = zeros(r,c, numberOfLabels); 34 | alpha = alpha1.*ones(r,c, numberOfLabels); 35 | 36 | % normalize image 37 | img = (img - min(img(:)))/ (max(img(:)) - min(img(:)) ); 38 | 39 | % assign models of mean intensity for each of the N regions 40 | imgModels = [0, 0.25, 0.5, 0.9]; 41 | 42 | % compute intensity L1 data term 43 | for i=1:numberOfLabels 44 | Ct(:,:,i) = abs(img - imgModels(i)); 45 | end 46 | 47 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 48 | pars = [r; c; numberOfLabels; 200; 1e-11; 0.25; 0.11]; 49 | 50 | % call 2D max-flow optimizer 51 | [u, erriter, i, timet] = asetsPotts2D(Ct, alpha, pars); 52 | 53 | % maj vote to discretize continuous labels 54 | [uu,I] = max(u, [], 3); 55 | 56 | % visualize 57 | if(visualizationFLAG) 58 | 59 | figure(); 60 | subplot(1,2,1); imshow(img,[]); title('img'); 61 | subplot(1,2,2); imshow(I,[]); title('L1 intensity seg'); 62 | 63 | end 64 | 65 | 66 | end 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /applications/atlasProbabilityMapsRegularization3D.m: -------------------------------------------------------------------------------- 1 | function [] = atlasProbabilityMapsRegularization3D() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Example application: Graph cut post processing of probabilistic labels. 5 | % 6 | % Probabilistic label maps typically generated by multi-atlas 7 | % segmentation frameworks are regularized via multi-region graph cut as 8 | % a post-processing step. 9 | % 10 | % References: 11 | % [1] Rajchl M., et al. (2015). Hierarchical Max-Flow Segmentation 12 | % Framework For Multi-Atlas Segmentation with Kohonen Self-Organizing 13 | % Map Based Gaussian Mixture Modeling. Medical Image Analysis 14 | % 15 | % [2] Qiu, W. et al. (2014). Cerebral Ventricle Segmentation from 3D 16 | % Pre-term IVH Neonate MR Images Using Atlas-Based Convex Optimization. 17 | % Computer-Assisted and Robotic Endoscopy, pp.46–54. 18 | 19 | 20 | close all; 21 | clear all; 22 | 23 | % include max-flow solver 24 | addpath(['..', filesep, 'maxflow']); 25 | addpath(['..', filesep, 'lib']); 26 | 27 | % flags 28 | useCUDAFLAG = 1; 29 | visualizationFLAG = 1; 30 | 31 | % load probability maps 32 | load(['..', filesep, 'data', filesep, 'brain_prob_MNI.mat']); 33 | 34 | % parameters 35 | alpha1 = 0.05; 36 | numberOfLabels = 4; 37 | [r, c, s] = size(bg); 38 | 39 | % alloc a cost function Ct for each label i, int lId 40 | Ct = zeros(r,c,s, numberOfLabels); 41 | alpha = alpha1.*ones(r,c,s, numberOfLabels); 42 | 43 | % compute the likelihood from the probabilities \in [0,1] as data term 44 | epsilon = 1e-10; 45 | 46 | % brain mask 47 | Ct(:,:,:,1) = -log(bg + epsilon); 48 | % cerebro-spinal fluid 49 | Ct(:,:,:,2) = -log(csf + epsilon); 50 | % white matter 51 | Ct(:,:,:,3) = -log(wm + epsilon); 52 | % gray matter 53 | Ct(:,:,:,4) = -log(gm + epsilon); 54 | 55 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 56 | pars = [r; c; s; numberOfLabels; 200; 1e-11; 0.25; 0.11]; 57 | 58 | % call 3D max-flow optimizer with CUDA if possible 59 | if(gpuDeviceCount && useCUDAFLAG) 60 | [u, erriter, i, timet] = asetsPotts3D(gpuArray(Ct), gpuArray(alpha), pars); 61 | else 62 | [u, erriter, i, timet] = asetsPotts3D(Ct, alpha, pars); 63 | end 64 | 65 | % maj vote to discretize continuous labels 66 | [uu,I] = max(u, [], 4); 67 | 68 | % visualize 69 | if(visualizationFLAG) 70 | 71 | % compute mid slices in each direction 72 | vis_r = idivide(r,uint8(2)); 73 | vis_c = idivide(c,uint8(2)); 74 | vis_s = idivide(s,uint8(2)); 75 | 76 | figure(); 77 | for i=1:(numberOfLabels) 78 | subplot(4,numberOfLabels,i); imshow(Ct(:,:,vis_s,i),[0 1]); 79 | subplot(4,numberOfLabels,i+numberOfLabels); imshow(squeeze(u(vis_r,:,:,i)),[0 1]); 80 | subplot(4,numberOfLabels,i+2*numberOfLabels); imshow(squeeze(u(:,vis_c,:,i)),[0 1]); 81 | subplot(4,numberOfLabels,i+3*numberOfLabels); imshow(squeeze(u(:,:,vis_s,i)),[0 1]); 82 | end 83 | 84 | % view resulting labeling functions from each implementation 85 | figure(); 86 | subplot(1,3,1); imshow(squeeze(I(vis_r,:,:)),[1 numberOfLabels]); 87 | subplot(1,3,2); imshow(squeeze(I(:,vis_c,:)),[1 numberOfLabels]); 88 | subplot(1,3,3); imshow(squeeze(I(:,:,vis_s)),[1 numberOfLabels]); 89 | 90 | colormap('jet'); 91 | 92 | end 93 | 94 | 95 | end 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /applications/interactiveGraphCutSegmentation3D.m: -------------------------------------------------------------------------------- 1 | function [] = interactiveGraphCutSegmentation3D() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Example application: Interactive graph cut segmentation 5 | % 6 | % A log-likelihood cost of intensities sampled via user scribbles is used 7 | % to segment the background, cerebro-spinal fluid (csf), white matter 8 | % (wm) and gray matter (gm) from a T1-weighted brain MR image. 9 | % 10 | % References: 11 | % [1] Rajchl M., et al. (2014). Interactive Hierarchical Max-Flow 12 | % Segmentation of Scar Tissue from Late-Enhancement Cardiac MR Images. 13 | % IEEE Transactions on Medical Imaging 33(1), 159-172. 14 | % 15 | % [2] Baxter, JSH. et al (2015). Optimization-Based Interactive 16 | % Segmentation Interface for Multi-Region Problems. 17 | % SPIE Medical Imaging. 18 | 19 | close all; 20 | clear all; 21 | 22 | % include max-flow solver 23 | addpath(['..', filesep, 'maxflow']); 24 | addpath(['..', filesep, 'lib']); 25 | 26 | % flags 27 | useCUDAFLAG = 1; 28 | visualizationFLAG = 1; 29 | 30 | % load image and user scribbles 31 | load(['..', filesep, 'data', filesep, 'brain_1125.mat'], 'img', 'scribbles'); 32 | 33 | labelIds = unique(scribbles(scribbles ~= 0)); 34 | 35 | % parameters 36 | alpha1 = 0.025; 37 | numberOfLabels = length(labelIds); 38 | [r, c, s] = size(img); 39 | 40 | % alloc a cost function Ct for each label i, int lId 41 | Ct = zeros(r,c,s, numberOfLabels); 42 | alpha = alpha1.*ones(r,c,s, numberOfLabels); 43 | 44 | % compute the likelihood from the probabilities \in [0,1] as data term 45 | epsilon = 1e-10; 46 | 47 | for i=1:numberOfLabels 48 | Ct(:,:,:,i) = computeLogLikelihoodCost(img, scribbles == i, epsilon); 49 | end 50 | 51 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 52 | pars = [r; c; s; numberOfLabels; 200; 1e-11; 0.25; 0.11]; 53 | 54 | % call 3D max-flow optimizer with CUDA if possible 55 | if(gpuDeviceCount && useCUDAFLAG) 56 | [u, erriter, i, timet] = asetsPotts3D(gpuArray(Ct), gpuArray(alpha), pars); 57 | else 58 | [u, erriter, i, timet] = asetsPotts3D(Ct, alpha, pars); 59 | end 60 | % maj vote to discretize continuous labels 61 | [uu,I] = max(u, [], 4); 62 | 63 | % visualize 64 | if(visualizationFLAG) 65 | 66 | % compute mid slices in each direction 67 | vis_r = idivide(r,uint8(2)); 68 | vis_c = idivide(c,uint8(2)); 69 | vis_s = idivide(s,uint8(2)); 70 | 71 | figure(); 72 | nVis = numberOfLabels+2; 73 | for i=1:nVis 74 | switch i 75 | case 1 76 | subplot(3,nVis,i); imshow(squeeze(img(vis_r,:,:)),[]); title('img'); 77 | subplot(3,nVis,i+nVis); imshow(squeeze(img(:,vis_c,:)),[]); 78 | subplot(3,nVis,i+2*nVis); imshow(squeeze(img(:,:,vis_s)),[]); 79 | case 2 80 | subplot(3,nVis,i); imshow(squeeze(I(vis_r,:,:)),[1 numberOfLabels]); title('seg'); 81 | subplot(3,nVis,i+nVis); imshow(squeeze(I(:,vis_c,:)),[1 numberOfLabels]); 82 | subplot(3,nVis,i+2*nVis); imshow(squeeze(I(:,:,vis_s)),[1 numberOfLabels]); 83 | otherwise 84 | subplot(3,nVis,i); imshow(squeeze(u(vis_r,:,:,i-2)),[0 1]); title(['u_',num2str(i-2)]); 85 | subplot(3,nVis,i+nVis); imshow(squeeze(u(:,vis_c,:,i-2)),[0 1]); 86 | subplot(3,nVis,i+2*nVis); imshow(squeeze(u(:,:,vis_s,i-2)),[0 1]); 87 | end 88 | end 89 | 90 | end 91 | 92 | 93 | end 94 | 95 | 96 | function [cost] = computeLogLikelihoodCost(img, lbl, epsilon) 97 | 98 | img = single(img); 99 | 100 | nBins = 256; 101 | 102 | minI = min(img(:)); 103 | maxI = max(img(:)); 104 | 105 | % normalize image to 8 bit 106 | img_n = ((img - minI)/ (maxI - minI)).*255.0; 107 | 108 | % compute histogram 109 | [binCounts] = histc(img_n(lbl == 1),linspace(0,255,nBins)); 110 | 111 | % normalize to compute the probabilities 112 | binCounts = binCounts./sum(binCounts(:)); 113 | 114 | % compute LL 115 | P = binCounts( uint16(img_n/ (256/nBins)) + 1); 116 | cost = -log10(P + epsilon); 117 | 118 | end 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /applications/smoothManualSegmentations2D.m: -------------------------------------------------------------------------------- 1 | function [] = smoothManualSegmentations2D() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Example application: Regularization of manual segmentations 5 | % 6 | % Flawed manual segmentations are regularized [1] with constant and 7 | % contrast sensitive regularization terms [2]. 8 | % 9 | % References: 10 | % 11 | % [1] Yuan, J.; Bae, E.; Tai, X.-C.; Boykov, Y. 12 | % A Continuous Max-Flow Approach to Potts Model 13 | % ECCV, 2010 14 | % 15 | % [2] Rajchl M., et al. (2014). Interactive Hierarchical Max-Flow 16 | % Segmentation of Scar Tissue from Late-Enhancement Cardiac MR Images. 17 | % IEEE Transactions on Medical Imaging 33(1), 159–172. 18 | 19 | 20 | close all; 21 | clear all; 22 | 23 | % include max-flow solver 24 | addpath(['..', filesep, 'maxflow']); 25 | addpath(['..', filesep, 'lib']); 26 | 27 | % flags 28 | visualizationFLAG = 1; 29 | 30 | % load image and user scribbles 31 | load(['..', filesep, 'data', filesep, 'brain_1125.mat'], 'img', 'man_s40_flawed'); 32 | 33 | man = man_s40_flawed(:,:,40); 34 | img = img(:,:,40); 35 | 36 | 37 | labelIds = sort(unique(man),'ascend'); 38 | 39 | % parameters 40 | numberOfLabels = length(labelIds); 41 | [r, c] = size(img); 42 | 43 | % alloc a cost function Ct for each label i, int lId 44 | Ct = zeros(r,c, numberOfLabels); 45 | 46 | % cast to float for smoothing 47 | man = single(man); 48 | 49 | % create a Gaussian kernel 50 | hs=fspecial('gaussian',[5,5],1.5); 51 | 52 | % compute data term from Gaussian smoothed manual segmentations 53 | for i=1:numberOfLabels 54 | inv_bin_label = (1- single(man == labelIds(i))); 55 | Ct(:,:,i) = imfilter(inv_bin_label,hs,'replicate'); 56 | end 57 | 58 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 59 | pars = [r; c; numberOfLabels; 300; 1e-11; 0.25; 0.11]; 60 | 61 | 62 | % Regularize with constant regularization term alpha 63 | alpha1 = 0.2.*ones(r,c, numberOfLabels); 64 | 65 | % call 2D max-flow optimizer 66 | [u, erriter, i, timet] = asetsPotts2D(gpuArray(Ct), gpuArray(alpha1), gpuArray(pars)); 67 | 68 | % maj vote to discretize continuous labels 69 | [uu,I] = max(u, [], 3); 70 | 71 | 72 | 73 | % Regularize with contrast sensitive regularization term alpha(x) 74 | img = (img - min(img(:)))/(max(img(:)) - min(img(:))); 75 | % compute gradient magnitude from image 76 | [gx, gy] = gradient(img); 77 | gm = sqrt(gx.^2 + gy.^2); 78 | 79 | % build contrast sensitive regularization as in [2] 80 | for i=1:numberOfLabels 81 | alpha2(:,:,i) = 0.0 + 0.2.* exp(-10*gm); 82 | end 83 | 84 | % call 3D max-flow optimizer 85 | [u2, erriter2, i2, timet2] = asetsPotts2D(single(Ct), single(alpha2), single(pars)); 86 | 87 | % maj vote to discretize continuous labels 88 | [uu2,I2] = max(u2, [], 3); 89 | 90 | 91 | % visualize 92 | if(visualizationFLAG) 93 | 94 | figure(); 95 | subplot(2,3,1); imshow(img,[]); title('img'); 96 | subplot(2,3,2); imshow(alpha1(:,:,1),[]); title ('alpha const.'); 97 | subplot(2,3,3); imshow(alpha2(:,:,1),[]); title ('alpha constrast sens.'); 98 | subplot(2,3,4); imshow(man,[]); title('before'); 99 | subplot(2,3,5); imshow(labelIds(I),[]); title('constant reg.'); 100 | subplot(2,3,6); imshow(labelIds(I2),[]); title('contrast sensitive reg.'); 101 | 102 | end 103 | 104 | 105 | end 106 | -------------------------------------------------------------------------------- /compile.m: -------------------------------------------------------------------------------- 1 | % Martin Rajchl, Imperial College London, 2015 2 | % compile script for mex/c code solvers in asetsMaxFlow 3 | 4 | % compile the mex implementations before running 5 | basePath = pwd(); 6 | libDir = [basePath, filesep, 'lib']; 7 | mkdir(libDir) 8 | 9 | % compile 10 | cd(libDir); 11 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsBinaryMF2D_mex.c']); 12 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsBinaryMF3D_mex.c']); 13 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsPotts2D_mex.c']); 14 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsPotts3D_mex.c']); 15 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsIshikawa2D_mex.c']); 16 | mex ([basePath, filesep, 'maxflow', filesep ,'asetsIshikawa3D_mex.c']); 17 | cd(basePath); 18 | -------------------------------------------------------------------------------- /data/brain_1125.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASETS/asetsMatlabMaxFlow/5ae80209a6c0be0c2db51d06085b7a7c16d3b338/data/brain_1125.mat -------------------------------------------------------------------------------- /data/brain_prob_MNI.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASETS/asetsMatlabMaxFlow/5ae80209a6c0be0c2db51d06085b7a7c16d3b338/data/brain_prob_MNI.mat -------------------------------------------------------------------------------- /data/natural_imgs.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASETS/asetsMatlabMaxFlow/5ae80209a6c0be0c2db51d06085b7a7c16d3b338/data/natural_imgs.mat -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, M Rajchl, JSH Baxter, J. Yuan 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the name of the ASeTs nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /maxflow/asetsBinaryMF2D.m: -------------------------------------------------------------------------------- 1 | function [u, erriter, i, timet] = asetsBinaryMF2D(Cs, Ct, alpha, pars) 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % 5 | % Re-implementation with of [1] with CUDA capability as in [2] 6 | % 7 | % [1] Yuan, J.; Bae, E.; Tai, X,-C.; 8 | % A study on continuous max-flow and min-cut approaches 9 | % IEEE CVPR, 2010 10 | % 11 | % [2] Rajchl, M.; Yuan, J.; Peters, TM. 12 | % Real-time segmentation in 4D ultrasound with 13 | % continuous max-flow 14 | % SPIE Medical Imaging 2012, 83141F-83141F-8 15 | 16 | 17 | if(nargin < 3) 18 | error('Not enough args. Exiting...'); 19 | end 20 | 21 | % setup 22 | rows = pars(1); 23 | cols = pars(2); 24 | iterNum= pars(3); 25 | beta = pars(4); 26 | cc = pars(5); 27 | steps = pars(6); 28 | 29 | imgSize = rows*cols; 30 | 31 | % allocate buffers 32 | u = zeros(rows,cols,'like', Ct); 33 | ps = zeros(rows,cols,'like', Ct); 34 | pt = zeros(rows,cols,'like', Ct); 35 | 36 | pp1 = zeros(rows, cols+1, 'like', Ct); 37 | pp2 = zeros(rows+1, cols, 'like', Ct); 38 | divp = zeros(rows,cols, 'like', Ct); 39 | 40 | erriter = zeros(iterNum,1, 'like', Ct); 41 | 42 | % initialize the flow buffers for faster convergence 43 | u = double((Cs-Ct) >= 0); 44 | ps = min(Cs, Ct); 45 | pt = ps; 46 | 47 | tic 48 | for i = 1:iterNum 49 | 50 | % update the spatial flow field p = (pp1, pp2): 51 | % compute the gradient descent 52 | pts = divp - (ps - pt + u/cc); 53 | pp1(:,2:cols) = pp1(:,2:cols) + steps*(pts(:,2:cols) - pts(:,1:cols-1)); 54 | pp2(2:rows,:) = pp2(2:rows,:) + steps*(pts(2:rows,:) - pts(1:rows-1,:)); 55 | 56 | % projection step |p(x)| <= alpha(x) 57 | gk = sqrt((pp1(:,1:cols).^2 + pp1(:,2:cols+1).^2 + pp2(1:rows,:).^2 + pp2(2:rows+1,:).^2)*0.5); 58 | gk = double(gk <= alpha) + double(~(gk <= alpha)).*(gk ./ alpha); 59 | gk = 1 ./ gk; 60 | 61 | pp1(:,2:cols) = (0.5*(gk(:,2:cols) + gk(:,1:cols-1))).*pp1(:,2:cols); 62 | pp2(2:rows,:) = (0.5*(gk(2:rows,:) + gk(1:rows-1,:))).*pp2(2:rows,:); 63 | 64 | divp = pp1(:,2:cols+1)-pp1(:,1:cols)+pp2(2:rows+1,:)-pp2(1:rows,:); 65 | 66 | % update the source flow ps 67 | pts = divp + pt - u/cc + 1/cc; 68 | ps = min(pts, Cs); 69 | 70 | % update the sink flow pt 71 | pts = - divp + ps + u/cc; 72 | pt = min(pts, Ct); 73 | 74 | % update the multiplier u 75 | 76 | erru = cc*(divp + pt - ps); 77 | u = u - erru; 78 | 79 | % evaluate the avarage error 80 | 81 | erriter(i) = sum(sum(abs(erru)))/imgSize; 82 | 83 | if (erriter(i) < beta) 84 | break; 85 | end 86 | 87 | end 88 | 89 | if(strcmp(class(u), 'gpuArray')) 90 | u = gather(u); 91 | erriter = gather(erriter); 92 | end 93 | 94 | timet = toc; 95 | 96 | msg = sprintf('number of iterations = %u; time = %f \n', i, timet); 97 | disp(msg); 98 | 99 | 100 | end -------------------------------------------------------------------------------- /maxflow/asetsBinaryMF2D_mex.c: -------------------------------------------------------------------------------- 1 | /* Martin Rajchl, Imperial College London, 2015 2 | * 3 | * Re-implementation with of [1] 4 | * 5 | * [1] Yuan, J.; Bae, E.; Tai, X,-C.; 6 | * A study on continuous max-flow and min-cut approaches 7 | * IEEE CVPR, 2010 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MIN(a,b) (((a)<(b))?(a):(b)) 18 | #define MAX(a,b) (((a)>(b))?(a):(b)) 19 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 20 | 21 | void runMaxFlow( float *alpha, float *Cs, float *Ct, 22 | int Nx, int Ny, int nLab, int maxIt, 23 | float errbound, float cc, float steps, 24 | float *u, float *cvg, int *itNum); 25 | 26 | void init(float *Cs, float *Ct, float *ps, float *pt, float *u, int Nx, int Ny); 27 | 28 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, float cc); 29 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps); 30 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps); 31 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny); 32 | void updateBX(float *bx, float *gk, int Nx, int Ny); 33 | void updateBY(float *by, float *gk, int Nx, int Ny); 34 | float updateDIVPSPTU(float *dv, float *bx, float *by, float *ps, float *pt, float *Cs, float *Ct, float *u, int Nx, int Ny, float cc); 35 | 36 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 37 | int iNbIn, const mxArray *pmxIn[]) 38 | { 39 | /* vars in */ 40 | float *alpha, *Cs, *Ct, *pars; 41 | int Nx, Ny, nLab, maxIt; 42 | float errBound, cc, steps; 43 | 44 | /* vars out */ 45 | float *u, *cvg; 46 | int *itNum; 47 | double *runTime; 48 | int nDim; 49 | int dim[4]; 50 | 51 | /* others */ 52 | time_t start_time, end_time; 53 | 54 | /* compute Max-Flow */ 55 | start_time = clock(); 56 | 57 | /* Inputs */ 58 | Cs = mxGetData(pmxIn[0]); /* bound of sink flows */ 59 | Ct = mxGetData(pmxIn[1]); /* bound of sink flows */ 60 | alpha = mxGetData(pmxIn[2]); /* penalty parameters */ 61 | pars = mxGetData(pmxIn[3]); /* Vector of parameters */ 62 | 63 | /* 64 | *pfVecParameters Setting 65 | * [0] : number of columns 66 | * [1] : number of rows 67 | * [3] : the maximum iteration number 68 | * [4] : error criterion 69 | * [5] : cc for the step-size of ALM 70 | * [6] : steps for the step-size of projected-gradient of p 71 | */ 72 | 73 | /* pars */ 74 | Ny = (int) pars[0]; 75 | Nx = (int) pars[1]; 76 | maxIt = (int) pars[2]; 77 | errBound = (float) pars[3]; 78 | cc = (float) pars[4]; 79 | steps = (float) pars[5]; 80 | 81 | nLab = 1; 82 | 83 | /* Outputs */ 84 | /* outputs the computed u(x) */ 85 | dim[0] = Ny; 86 | dim[1] = Nx; 87 | nDim = 2; 88 | 89 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 90 | u = mxGetData(pmxOut[0]); 91 | 92 | /* outputs the convergence rate */ 93 | nDim = 2; 94 | dim[0] = maxIt; 95 | dim[1] = 1; 96 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 97 | cvg = mxGetData(pmxOut[1]); 98 | 99 | /* outputs the iteration number */ 100 | nDim = 2; 101 | dim[0] = 1; 102 | dim[1] = 1; 103 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 104 | itNum = mxGetData(pmxOut[2]); 105 | 106 | /* outputs the computation time */ 107 | nDim = 2; 108 | dim[0] = 1; 109 | dim[1] = 1; 110 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 111 | runTime = mxGetData(pmxOut[3]); 112 | 113 | 114 | /* compute Max-Flow */ 115 | start_time = clock(); 116 | 117 | 118 | runMaxFlow(alpha, Cs, Ct, 119 | Nx, Ny, nLab, maxIt, errBound, cc, steps, 120 | u, cvg, itNum); 121 | 122 | 123 | end_time = clock(); 124 | 125 | runTime[0] = difftime(end_time, start_time)/1000000; 126 | 127 | mexPrintf("binary max flow 2D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 128 | 129 | } 130 | 131 | 132 | void runMaxFlow( float *alpha, float *Cs, float *Ct, 133 | int Nx, int Ny, int nLab, int maxIt, 134 | float errBound, float cc, float steps, 135 | float *u, float *cvg, int *itNum){ 136 | 137 | 138 | float *bx, *by, *dv, *gk, *ps, *pt; 139 | int i; 140 | float total_err; 141 | 142 | /* alloc buffers */ 143 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny), sizeof(float) ); 144 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)), sizeof(float) ); 145 | dv = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 146 | gk = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 147 | ps = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 148 | pt = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 149 | if (!(bx || by || dv || gk || ps || pt)) 150 | mexPrintf("malloc error.\n"); 151 | 152 | init(Cs, Ct, ps, pt, u, Nx, Ny); 153 | 154 | /* iterate */ 155 | i = 0; 156 | for (i = 0; i < maxIt; i++){ 157 | 158 | /* update the spatial flow field p(x) = (bx(x),by(x)) */ 159 | updateP1(gk, dv, ps, pt, u, Nx, Ny, cc); 160 | updatePX(gk, bx, Nx, Ny, steps); 161 | updatePY(gk, by, Nx, Ny, steps); 162 | 163 | /* projection step to make |p(x,i)| <= alpha(x)*/ 164 | projStep(bx, by, alpha, gk, Nx, Ny); 165 | 166 | /* update the component bx, by */ 167 | updateBX(bx, gk, Nx, Ny); 168 | updateBY(by, gk, Nx, Ny); 169 | 170 | /* update ps(x)/pt(x) and the multiplier/labeling functions u(x) */ 171 | total_err = updateDIVPSPTU(dv, bx, by, ps, pt, Cs, Ct, u, Nx, Ny, cc); 172 | 173 | 174 | /* evaluate the convergence error */ 175 | cvg[i] = total_err / (float)(Nx*Ny); 176 | /* mexPrintf("it= %d, cvg = %f\n", i, cvg[i]); */ 177 | 178 | /* check if converged */ 179 | if (cvg[i] <= errBound) 180 | break; 181 | 182 | } 183 | /* update iteration number */ 184 | itNum[0] = i; 185 | 186 | /* free mem */ 187 | free( (float *) bx ); 188 | free( (float *) by ); 189 | free( (float *) dv ); 190 | free( (float *) gk ); 191 | free( (float *) ps ); 192 | free( (float *) pt ); 193 | 194 | } 195 | 196 | void init(float *Cs, float *Ct, float *ps, float *pt, float *u, int Nx, int Ny){ 197 | /* init */ 198 | int x, y; 199 | 200 | for (x=0; x < Nx; x++){ 201 | for (y=0; y < Ny; y++){ 202 | 203 | int g_idx = x*Ny + y; 204 | 205 | if(Cs[g_idx] - Ct[g_idx] >= 0){ 206 | u[g_idx] = 1.0f; 207 | } 208 | ps[g_idx] = MIN(Cs[g_idx], Ct[g_idx]); 209 | pt[g_idx] = ps[g_idx]; 210 | 211 | } 212 | 213 | } 214 | } 215 | 216 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, float cc){ 217 | 218 | int x = 0; 219 | int y = 0; 220 | for (x=0; x < Nx; x++){ 221 | for (y=0; y < Ny; y++){ 222 | 223 | int g_idx = x*Ny + y; 224 | 225 | gk[g_idx] = dv[g_idx] - (ps[g_idx] 226 | - pt[g_idx] + u[g_idx]/cc); 227 | 228 | } 229 | } 230 | } 231 | 232 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps){ 233 | 234 | int x = 0; 235 | int y = 0; 236 | for (x=1; x < Nx; x++){ 237 | for (y=0; y < Ny; y++){ 238 | 239 | int g_idx = x*Ny + y; 240 | 241 | bx[g_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[g_idx]; 242 | } 243 | } 244 | } 245 | 246 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps){ 247 | 248 | int x = 0; 249 | int y = 0; 250 | 251 | for(x = 0; x < Nx; x ++){ 252 | for(y = 1; y < Ny; y++){ 253 | 254 | int g_idx = x*Ny + y; 255 | 256 | by[g_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[g_idx]; 257 | } 258 | } 259 | } 260 | 261 | 262 | 263 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny){ 264 | 265 | float fpt; 266 | int x = 0; 267 | int y = 0; 268 | for (x=0; x< Nx; x++){ 269 | for (y=0; y< Ny; y++){ 270 | 271 | int g_idx = x*Ny + y; 272 | 273 | if( alpha[g_idx] <= 0 ){ 274 | mexErrMsgTxt("alpha(x) must be positive. Exiting..."); 275 | } 276 | 277 | fpt = sqrt((pow(bx[g_idx+Ny],2) + pow(bx[g_idx],2) + 278 | pow(by[g_idx+1],2) + pow(by[g_idx],2))*0.5); 279 | 280 | if (fpt > alpha[g_idx]) 281 | fpt = fpt / alpha[g_idx]; 282 | else 283 | fpt = 1; 284 | 285 | gk[g_idx] = 1/fpt; 286 | } 287 | } 288 | } 289 | 290 | void updateBX(float *bx, float *gk, int Nx, int Ny){ 291 | 292 | int x = 0; 293 | int y = 0; 294 | 295 | for (x=1; x< Nx; x++){ 296 | for (y=0; y< Ny; y++){ 297 | 298 | int g_idx = x*Ny + y; 299 | 300 | bx[g_idx] = (gk[g_idx] + gk[g_idx-Ny]) 301 | *0.5*bx[g_idx]; 302 | } 303 | } 304 | } 305 | 306 | void updateBY(float *by, float *gk, int Nx, int Ny){ 307 | 308 | int x = 0; 309 | int y = 0; 310 | 311 | for (x=0; x= 0); 41 | ps = min(Cs, Ct); 42 | pt = ps; 43 | 44 | tic 45 | for i = 1:iterNum 46 | 47 | % update the spatial flow field p = (pp1, pp2): 48 | % compute the gradient descent 49 | pts = divp - (ps - pt + u/cc); 50 | pp1(:,2:cols,:) = pp1(:,2:cols,:) + steps*(pts(:,2:cols,:) - pts(:,1:cols-1,:)); 51 | pp2(2:rows,:,:) = pp2(2:rows,:,:) + steps*(pts(2:rows,:,:) - pts(1:rows-1,:,:)); 52 | pp3(:,:,2:slices) = pp3(:,:,2:slices) + steps*(pts(:,:,2:slices) - pts(:,:,1:slices-1)); 53 | 54 | % projection step |p(x)| <= alpha(x) 55 | gk = sqrt((pp1(:,1:cols,:).^2 + pp1(:,2:cols+1,:).^2 ... 56 | + pp2(1:rows,:,:).^2 + pp2(2:rows+1,:,:).^2 ... 57 | + pp3(:,:,1:slices).^2 + pp3(:,:,2:slices+1).^2 ... 58 | )*0.5); 59 | gk = double(gk <= alpha) + double(~(gk <= alpha)).*(gk ./ alpha); 60 | gk = 1 ./ gk; 61 | 62 | pp1(:,2:cols,:) = (0.5*(gk(:,2:cols,:) + gk(:,1:cols-1,:))).*pp1(:,2:cols,:); 63 | pp2(2:rows,:,:) = (0.5*(gk(2:rows,:,:) + gk(1:rows-1,:,:))).*pp2(2:rows,:,:); 64 | pp3(:,:,2:slices) = (0.5*(gk(:,:,2:slices) + gk(:,:,1:slices-1))).*pp3(:,:,2:slices); 65 | 66 | divp = pp1(:,2:cols+1,:)-pp1(:,1:cols,:) ... 67 | + pp2(2:rows+1,:,:)-pp2(1:rows,:,:) ... 68 | + pp3(:,:,2:slices+1) - pp3(:,:,1:slices); 69 | 70 | % update the source flow ps 71 | pts = divp + pt - u/cc + 1/cc; 72 | ps = min(pts, Cs); 73 | 74 | % update the sink flow pt 75 | pts = - divp + ps + u/cc; 76 | pt = min(pts, Ct); 77 | 78 | % update the multiplier u 79 | 80 | erru = cc*(divp + pt - ps); 81 | u = u - erru; 82 | 83 | % evaluate the avarage error 84 | 85 | erriter(i) = sum(sum(sum(abs(erru))))/imgSize; 86 | 87 | if (erriter(i) < beta) 88 | break; 89 | end 90 | 91 | end 92 | 93 | if(strcmp(class(u), 'gpuArray')) 94 | u = gather(u); 95 | erriter = gather(erriter); 96 | end 97 | 98 | timet = toc; 99 | 100 | msg = sprintf('number of iterations = %u; time = %f \n', i, timet); 101 | disp(msg); 102 | 103 | 104 | end -------------------------------------------------------------------------------- /maxflow/asetsBinaryMF3D_mex.c: -------------------------------------------------------------------------------- 1 | /* Martin Rajchl, Imperial College London, 2015 2 | * 3 | * Re-implementation with of [1] 4 | * 5 | * [1] Yuan, J.; Bae, E.; Tai, X,-C.; 6 | * A study on continuous max-flow and min-cut approaches 7 | * IEEE CVPR, 2010 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MIN(a,b) (((a)<(b))?(a):(b)) 18 | #define MAX(a,b) (((a)>(b))?(a):(b)) 19 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 20 | 21 | void runMaxFlow( float *alpha, float *Cs, float *Ct, 22 | int Nx, int Ny, int Nz, int nLab, int maxIt, 23 | float errbound, float cc, float steps, 24 | float *u, float *cvg, int *itNum); 25 | 26 | void init(float *Cs, float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int Nz); 27 | 28 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, float cc); 29 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps); 30 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps); 31 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps); 32 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz); 33 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz); 34 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz); 35 | void updateBZ(float *bz, float *gk, int Nx, int Ny, int Nz); 36 | float updateDIVPSPTU(float *dv, float *bx, float *by, float *bz, float *ps, float *pt, float *Cs, float *Ct, float *u, int Nx, int Ny, int Nz, float cc); 37 | 38 | 39 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 40 | int iNbIn, const mxArray *pmxIn[]) 41 | { 42 | /* vars in */ 43 | float *alpha, *Cs, *Ct, *pars; 44 | int Nx, Ny, Nz, nLab, maxIt; 45 | float errBound, cc, steps; 46 | 47 | /* vars out */ 48 | float *u, *cvg; 49 | int *itNum; 50 | double *runTime; 51 | 52 | int dim[4]; 53 | int nDim; 54 | 55 | /* others */ 56 | time_t start_time, end_time; 57 | 58 | /* compute Max-Flow */ 59 | start_time = clock(); 60 | 61 | /* Inputs */ 62 | Cs = mxGetData(pmxIn[0]); /* bound of source flows */ 63 | Ct = mxGetData(pmxIn[1]); /* bound of sink flows */ 64 | alpha = mxGetData(pmxIn[2]); /* penalty parameters */ 65 | pars = mxGetData(pmxIn[3]); /* Vector of parameters */ 66 | 67 | /* 68 | *pfVecParameters Setting 69 | * [0] : number of columns 70 | * [1] : number of rows 71 | * [2] : number of slices 72 | * [4] : the maximum iteration number 73 | * [5] : error criterion 74 | * [6] : cc for the step-size of ALM 75 | * [7] : steps for the step-size of projected-gradient of p 76 | */ 77 | 78 | /* pars */ 79 | Ny = (int) pars[0]; 80 | Nx = (int) pars[1]; 81 | Nz = (int) pars[2]; 82 | maxIt = (int) pars[3]; 83 | errBound = (float) pars[4]; 84 | cc = (float) pars[5]; 85 | steps = (float) pars[6]; 86 | 87 | nLab = 1; 88 | 89 | /* Outputs */ 90 | /* outputs the computed u(x) */ 91 | dim[0] = Ny; 92 | dim[1] = Nx; 93 | dim[2] = Nz; 94 | nDim = 3; 95 | 96 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 97 | u = mxGetData(pmxOut[0]); 98 | 99 | /* outputs the convergence rate */ 100 | nDim = 2; 101 | dim[0] = maxIt; 102 | dim[1] = 1; 103 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 104 | cvg = mxGetData(pmxOut[1]); 105 | 106 | /* outputs the iteration number */ 107 | nDim = 2; 108 | dim[0] = 1; 109 | dim[1] = 1; 110 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 111 | itNum = mxGetData(pmxOut[2]); 112 | 113 | /* outputs the computation time */ 114 | nDim = 2; 115 | dim[0] = 1; 116 | dim[1] = 1; 117 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 118 | runTime = mxGetData(pmxOut[3]); 119 | 120 | 121 | /* compute Max-Flow */ 122 | start_time = clock(); 123 | 124 | runMaxFlow(alpha, Cs, Ct, 125 | Nx, Ny, Nz, nLab, maxIt, errBound, cc, steps, 126 | u, cvg, itNum); 127 | 128 | 129 | end_time = clock(); 130 | 131 | runTime[0] = difftime(end_time, start_time)/1000000; 132 | mexPrintf("binary max flow 3D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 133 | 134 | } 135 | 136 | 137 | void runMaxFlow( float *alpha, float *Cs, float *Ct, 138 | int Nx, int Ny, int Nz, int nLab, int maxIt, 139 | float errBound, float cc, float steps, 140 | float *u, float *cvg, int *itNum){ 141 | 142 | 143 | float *bx, *by, *bz, *dv, *gk, *ps, *pt; 144 | int i; 145 | float total_err; 146 | 147 | /* alloc buffers */ 148 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny*Nz), sizeof(float) ); 149 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)*Nz), sizeof(float) ); 150 | bz = (float *) calloc( (unsigned)(Nx*Ny*(Nz+1)), sizeof(float) ); 151 | dv = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 152 | gk = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 153 | ps = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 154 | pt = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 155 | if (!(bx || by || bz || dv || gk || ps || pt)) 156 | mexPrintf("malloc error.\n"); 157 | 158 | init(Cs, Ct, ps, pt, u, Nx, Ny, Nz); 159 | 160 | 161 | /* iterate */ 162 | i = 0; 163 | for (i = 0; i < maxIt; i++){ 164 | 165 | /* update the spatial flow field p(x) = (bx(x),by(x),bz(x)) */ 166 | updateP1(gk, dv, ps, pt, u, Nx, Ny, Nz, cc); 167 | updatePX(gk, bx, Nx, Ny, Nz, steps); 168 | updatePY(gk, by, Nx, Ny, Nz, steps); 169 | updatePZ(gk, bz, Nx, Ny, Nz, steps); 170 | 171 | /* projection step to make |p(x,i)| <= alpha(x)*/ 172 | projStep(bx, by, bz, alpha, gk, Nx, Ny, Nz); 173 | 174 | /* update the component bx, by, bz */ 175 | updateBX(bx, gk, Nx, Ny, Nz); 176 | updateBY(by, gk, Nx, Ny, Nz); 177 | updateBZ(bz, gk, Nx, Ny, Nz); 178 | 179 | /* update ps(x)/pt(x) and the multiplier/labeling functions u(x) */ 180 | total_err = updateDIVPSPTU(dv, bx, by, bz, ps, pt, Cs, Ct, u, Nx, Ny, Nz, cc); 181 | 182 | /* evaluate the convergence error */ 183 | cvg[i] = total_err / (float)(Nx*Ny*Nz); 184 | /*mexPrintf("it= %d, cvg = %f\n", i,cvg[i] ); */ 185 | 186 | /* check if converged */ 187 | if (cvg[i] <= errBound) 188 | break; 189 | 190 | } 191 | /* update iteration number */ 192 | itNum[0] = i; 193 | 194 | /* free mem */ 195 | free( (float *) bx ); 196 | free( (float *) by ); 197 | free( (float *) bz ); 198 | free( (float *) dv ); 199 | free( (float *) gk ); 200 | free( (float *) ps ); 201 | free( (float *) pt ); 202 | 203 | } 204 | 205 | void init(float *Cs, float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int Nz){ 206 | /* init */ 207 | int x, y, z; 208 | 209 | for (x=0; x < Nx; x++){ 210 | for (y=0; y < Ny; y++){ 211 | for (z=0; z < Nz; z++){ 212 | 213 | int g_idx = z*Nx*Ny + x*Ny + y; 214 | 215 | if(Cs[g_idx] - Ct[g_idx] >= 0){ 216 | u[g_idx] = 1.0f; 217 | } 218 | ps[g_idx] = MIN(Cs[g_idx], Ct[g_idx]); 219 | pt[g_idx] = ps[g_idx]; 220 | 221 | } 222 | } 223 | } 224 | } 225 | 226 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, float cc){ 227 | 228 | int x = 0; 229 | int y = 0; 230 | int z = 0; 231 | 232 | for (z=0; z < Nz; z++){ 233 | for (x=0; x < Nx; x++){ 234 | for (y=0; y < Ny; y++){ 235 | 236 | int g_idx = z*Nx*Ny + x*Ny + y; 237 | 238 | gk[g_idx] = dv[g_idx] - (ps[g_idx] 239 | - pt[g_idx] + u[g_idx]/cc); 240 | 241 | } 242 | } 243 | } 244 | } 245 | 246 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps){ 247 | 248 | int x = 0; 249 | int y = 0; 250 | int z = 0; 251 | for (z=0; z < Nz; z++){ 252 | for (x=1; x < Nx; x++){ 253 | for (y=0; y < Ny; y++){ 254 | 255 | 256 | int g_idx = z*Nx*Ny + x*Ny + y; 257 | 258 | bx[g_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[g_idx]; 259 | 260 | } 261 | } 262 | } 263 | 264 | } 265 | 266 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps){ 267 | 268 | int x = 0; 269 | int y = 0; 270 | int z = 0; 271 | for (z=0; z < Nz; z++){ 272 | for(x = 0; x < Nx; x ++){ 273 | for(y = 1; y < Ny; y++){ 274 | 275 | 276 | int g_idx = z*Nx*Ny + x*Ny + y; 277 | 278 | by[g_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[g_idx]; 279 | 280 | } 281 | } 282 | } 283 | } 284 | 285 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps){ 286 | 287 | int x = 0; 288 | int y = 0; 289 | int z = 0; 290 | for (z=1; z < Nz; z++){ 291 | for(x = 0; x < Nx; x ++){ 292 | for(y = 0; y < Ny; y++){ 293 | 294 | 295 | int g_idx = z*Nx*Ny + x*Ny + y; 296 | 297 | bz[g_idx] = steps*(gk[g_idx] - gk[g_idx-(Nx*Ny)]) + bz[g_idx]; 298 | } 299 | } 300 | } 301 | } 302 | 303 | 304 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz){ 305 | 306 | float fpt; 307 | int x = 0; 308 | int y = 0; 309 | int z = 0; 310 | for (z=0; z < Nz; z++){ 311 | for (x=0; x< Nx; x++){ 312 | for (y=0; y< Ny; y++){ 313 | 314 | 315 | int g_idx = z*Nx*Ny + x*Ny + y; 316 | 317 | if( alpha[g_idx] <= 0 ){ 318 | mexErrMsgTxt("alpha(x) must be positive. Exiting..."); 319 | } 320 | 321 | fpt = sqrt((pow(bx[g_idx+Ny],2) + pow(bx[g_idx],2) + 322 | pow(by[g_idx+1],2) + pow(by[g_idx],2) + 323 | pow(bz[g_idx+(Nx*Ny)],2) + pow(bz[g_idx],2) 324 | )*0.5); 325 | 326 | if (fpt > alpha[g_idx]) 327 | fpt = fpt / alpha[g_idx]; 328 | else 329 | fpt = 1; 330 | 331 | gk[g_idx] = 1/fpt; 332 | } 333 | } 334 | } 335 | } 336 | 337 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz){ 338 | 339 | int g_idx; 340 | int l_idx; 341 | int x = 0; 342 | int y = 0; 343 | int z = 0; 344 | for (z=0; z < Nz; z++){ 345 | for (x=1; x< Nx; x++){ 346 | for (y=0; y< Ny; y++){ 347 | 348 | 349 | g_idx = z*Nx*Ny + x*Ny + y; 350 | 351 | bx[g_idx] = (gk[g_idx] + gk[g_idx-Ny]) 352 | *0.5*bx[g_idx]; 353 | } 354 | } 355 | } 356 | } 357 | 358 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz){ 359 | 360 | int g_idx; 361 | int l_idx; 362 | int x = 0; 363 | int y = 0; 364 | int z = 0; 365 | for (z=0; z < Nz; z++){ 366 | for (x=0; x 1 ) 149 | h.pt = zeros(h.D,'like',h.Ct); 150 | for i = 1:length(h.C) 151 | h.pt = max( h.pt, h.C{1}.pt ); 152 | end 153 | else 154 | h.pt = h.Ct; 155 | end 156 | if( length(h.P) > 1 ) 157 | h.pn = h.pt; 158 | end 159 | h.div = zeros(h.D,'like',h.Ct); 160 | h.u = zeros(h.D,'like',h.Ct); 161 | h.px = zeros([h.D(1)-1 h.D(2)],'like',h.Ct); 162 | h.py = zeros([h.D(1) h.D(2)-1],'like',h.Ct); 163 | 164 | %normalize lengths for geodesic shape constraint 165 | if numel(h.lx) > 0 && numel(h.ly) > 0 166 | denom = (h.lx.^2+h.ly.^2).^0.5; 167 | mask = (denom > 0.001); 168 | h.lx(mask) = h.lx(mask) ./ denom(mask); 169 | h.lx(~mask) = 0; 170 | h.ly(mask) = h.ly(mask) ./ denom(mask); 171 | h.ly(~mask) = 0; 172 | end 173 | 174 | end 175 | 176 | %deinitialize buffers for full flow 177 | function DeInitializeFullFlow(h) 178 | for i = 1:length(h.C) 179 | h.C{i}.DeInitializeFullFlow(); 180 | end 181 | clear h.g; 182 | clear h.pt; 183 | clear h.pn; 184 | clear h.px; 185 | clear h.py; 186 | clear h.div; 187 | end 188 | 189 | 190 | %update labels (top-down) (not recursive) 191 | function UpdateLabels(h,cc) 192 | if length(h.P)>1 193 | h.u = h.u + cc*(h.pn - h.div - h.pt); 194 | elseif length(h.P)==1 195 | h.u = h.u + cc*(h.P{1}.pt - h.div - h.pt); 196 | end 197 | 198 | if ~isempty(h.Name) 199 | PlotResults(h.Name,h.px,h.py,h.pt,h.div,h.g,h.u) 200 | end 201 | end 202 | 203 | %push down sink flow step (not recursive) 204 | function PushDownFlows(h,cc) 205 | %push down flow 206 | for i = 1:length(h.C) 207 | if length(h.C{i}.P)>1 208 | h.C{i}.pn = h.C{i}.pn + h.wC(i)*h.pt; 209 | end 210 | end 211 | 212 | %figure out sink desired capacity 213 | if ~isempty(h.P) && ~isempty(h.C) 214 | if length(h.P)>1 215 | h.g = h.pn - h.div + h.u/cc; 216 | else 217 | h.g = h.P{1}.pt - h.div + h.u/cc; 218 | end 219 | elseif isempty(h.P) 220 | h.g = 1/cc; 221 | end 222 | end 223 | 224 | %push sink capacities up and set sink flows 225 | function PushCapacityUp(h,cc) 226 | %leaf node, so constrain output flow 227 | if isempty(h.C) 228 | if length(h.P) > 1 229 | h.pt = min(h.Ct, h.pn-h.div+h.u/cc); 230 | else 231 | h.pt = min(h.Ct, h.P{1}.pt-h.div+h.u/cc); 232 | end 233 | 234 | %source node, so accumulate desired in-flow from children and set 235 | %source flow accordingly 236 | elseif isempty(h.P) 237 | for i = 1:length(h.C) 238 | if length(h.C{i}.P)>1 239 | h.g = h.g + h.wC(i)*(h.C{i}.div + h.C{i}.pt + h.wC(i)*h.pt - h.C{i}.pn - h.C{i}.u/cc); 240 | else 241 | h.g = h.g + h.C{i}.div + h.C{i}.pt - h.C{i}.u/cc; 242 | end 243 | end 244 | h.pt = h.g / sum(h.wC .^2); 245 | 246 | %branch node, so accumulate desired in-flow from children and set 247 | %branch output flow accordingly 248 | else 249 | for i = 1:length(h.C) 250 | if length(h.C{i}.P)>1 251 | h.g = h.g + h.wC(i)*(h.C{i}.div + h.C{i}.pt + h.wC(i)*h.pt - h.C{i}.pn - h.C{i}.u/cc); 252 | else 253 | h.g = h.g + h.C{i}.div + h.C{i}.pt - h.C{i}.u/cc; 254 | end 255 | end 256 | h.pt = h.g / (1+sum(h.wC .^2)); 257 | end 258 | 259 | end 260 | 261 | %update spatial flows (not recursive) 262 | function UpdateSpatialFlows(h,steps,cc) 263 | if ~isempty(h.P) 264 | 265 | %gradient descent on flows 266 | if length(h.P) > 1 267 | h.g = steps*( h.div + h.pt - h.pn - h.u/cc ); 268 | else 269 | h.g = steps*( h.div + h.pt - h.P{1}.pt - h.u/cc ); 270 | end 271 | h.px = h.px + h.g(2:h.D(1),:)-h.g(1:h.D(1)-1,:); 272 | h.py = h.py + h.g(:,2:h.D(2))-h.g(:,1:h.D(2)-1); 273 | 274 | %find flow mag, exemption amounts, and correction 275 | if numel(h.lx) > 0 276 | 277 | %find exemption amount 278 | h.g(1:h.D(1)-1,:) = max((h.px>0).*h.px.*h.lx(1:h.D(1)-1,:),0); 279 | h.g(h.D(1),:)=0; 280 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + max((h.px<0).*h.px.*h.lx(2:h.D(1),:) ,0); 281 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1)+ max((h.py>0).*h.py.*h.ly(:,1:h.D(2)-1),0); 282 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + max((h.py<0).*h.py.*h.ly(:,2:h.D(2)) ,0); 283 | 284 | %find exemption amount 285 | ex = (h.px>0 & h.lx(1:h.D(1)-1,:,:)>0).*h.lx(1:h.D(1)-1,:,:).*h.g(1:h.D(1)-1,:,:); 286 | ex = ex + (h.px<0 & h.lx(2:h.D(1),:,:)<0).*h.lx(2:h.D(1),:,:).*h.g(2:h.D(1),:,:); 287 | 288 | ey = (h.py>0 & h.ly(:,1:h.D(2)-1,:)>0).*h.ly(:,1:h.D(2)-1,:).*h.g(:,1:h.D(2)-1,:); 289 | ey = ey + (h.py<0 & h.ly(:,2:h.D(2),:)<0).*h.ly(:,2:h.D(2),:).*h.g(:,2:h.D(2),:); 290 | 291 | %apply exemption 292 | h.px = h.px - ex; 293 | h.py = h.py - ey; 294 | 295 | %find flow mag 296 | h.g(1:h.D(1)-1,:) = h.px.^2; 297 | h.g(h.D(1),:)=0; 298 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 299 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 300 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 301 | h.g = h.g .^ 0.5; 302 | 303 | %correct for flow mag 304 | mask = (h.g <= h.alpha); 305 | if numel(h.alpha) == 1 306 | h.g(~mask) = h.alpha ./ h.g(~mask); 307 | else 308 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 309 | end 310 | h.g(mask) = 1; 311 | h.px = ex + 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 312 | h.py = ey + 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 313 | 314 | %no exemption vector, so a=0 315 | else 316 | 317 | %find flow mag 318 | h.g(1:h.D(1)-1,:) = h.px.^2; 319 | h.g(h.D(1),:)=0; 320 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 321 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 322 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 323 | h.g = h.g .^ 0.5; 324 | 325 | %correct for flow mag 326 | mask = (h.g <= h.alpha); 327 | if numel(h.alpha) == 1 328 | h.g(~mask) = h.alpha ./ h.g(~mask); 329 | else 330 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 331 | end 332 | h.g(mask) = 1; 333 | h.px = 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 334 | h.py = 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 335 | end 336 | 337 | %calculate divergence 338 | h.div(1:h.D(1)-1,:) = h.px; 339 | h.div(h.D(1),:) = 0; 340 | h.div(2:h.D(1),:) = h.div(2:h.D(1),:) - h.px; 341 | h.div(:,1:h.D(2)-1) = h.div(:,1:h.D(2)-1) + h.py; 342 | h.div(:,2:h.D(2)) = h.div(:,2:h.D(2)) - h.py; 343 | 344 | end 345 | 346 | end 347 | 348 | 349 | end 350 | 351 | end -------------------------------------------------------------------------------- /maxflow/asetsDAGMF3D.m: -------------------------------------------------------------------------------- 1 | classdef asetsDAGMF3D < handle 2 | % John SH Baxter, Robarts Research Institute, 2015 3 | % 4 | % Full implementation with of [1] in 3D and DAGMF implementation 5 | % of [2] 6 | % 7 | % [1] Baxter, JSH.; Rajchl, M.; Yuan, J.; Peters, TM. (2014) 8 | % A Continuous Max-Flow Approach to Multi-Labeling Problems Under 9 | % Arbitrary Region Regularization 10 | % arXiv preprint arXiv:1404.0336 11 | % 12 | % [2] Baxter, JSH.; Yuan, J.; Peters, TM. 13 | % Shape Complexes in Continuous Max-Flow Hierarchical Multi- 14 | % Labeling Problems 15 | % arXiv preprint arXiv:1510.04706 16 | 17 | properties 18 | Name 19 | Ct 20 | alpha 21 | D 22 | 23 | u 24 | 25 | pn 26 | pt 27 | px 28 | py 29 | pz 30 | lx 31 | ly 32 | lz 33 | div 34 | g 35 | 36 | wC 37 | P 38 | C 39 | end 40 | 41 | methods 42 | 43 | %constructor 44 | function h = asetsDAGMF3D(children,alpha,Ct) 45 | %add in children 46 | h.C = children; 47 | for i = 1:length(children) 48 | h.C{i}.P{end+1} = h; 49 | end 50 | 51 | %find dimensions 52 | if isempty(children) 53 | h.D = size(Ct); 54 | h.Ct = Ct; 55 | else 56 | h.D = h.C{1}.D; 57 | h.Ct = zeros(0,'like',h.C{1}.Ct); 58 | end 59 | 60 | %create buffers 61 | h.alpha = alpha; 62 | h.u = zeros(h.D,'like',h.Ct); 63 | 64 | end 65 | 66 | %run full-flow algorithm with set number of iterations 67 | function MaxFullFlow(h,numIts,steps,cc) 68 | h.InitializeFullFlow(); 69 | 70 | %find forward and backward orderings using a topological sort 71 | S = {h}; 72 | tdList = {}; 73 | while ~isempty(S) 74 | q = S{end}; 75 | tdList{end+1} = q; 76 | S(end) = []; 77 | 78 | for j = 1:length(q.C) 79 | r = q.C{j}; 80 | putInList = true; 81 | for k = 1:length(r.P) 82 | s = r.P{k}; 83 | inList = false; 84 | for l = 1:length(tdList) 85 | if s == tdList{l} 86 | inList = true; 87 | break 88 | end 89 | end 90 | if inList == false 91 | putInList = false; 92 | break; 93 | end 94 | end 95 | if putInList 96 | S{end+1} = r; 97 | end 98 | end 99 | end 100 | clear S; 101 | buList = flip(tdList); 102 | 103 | %run max-flow algorithm 104 | for i = 1:numIts 105 | 106 | %update spatial flows then clear input flow 107 | for j = 1:length(tdList) 108 | tdList{j}.UpdateSpatialFlows(steps,cc); 109 | if( ~isempty(tdList{j}.pn) ) 110 | tdList{j}.pn = 0; 111 | end 112 | end 113 | 114 | %push down sink (output) flows 115 | for j = 1:length(tdList) 116 | tdList{j}.PushDownFlows(cc); 117 | end 118 | 119 | %push up flow expectations/capacities 120 | for j = 1:length(buList) 121 | buList{j}.PushCapacityUp(cc); 122 | end 123 | 124 | %update labels 125 | for j = 1:length(tdList) 126 | tdList{j}.UpdateLabels(cc); 127 | end 128 | drawnow 129 | end 130 | 131 | h.DeInitializeFullFlow(); 132 | end 133 | 134 | %initialization procedure for full flow including 135 | %default parameterization, normalization and buffers 136 | function InitializeFullFlow(h) 137 | 138 | %give default weights 139 | if isempty(h.wC) 140 | for i = 1:length(h.C) 141 | h.wC(i) = 1/length(h.C{i}.P); 142 | end 143 | end 144 | 145 | %initialize buffers for full flow 146 | for i = 1:length(h.C) 147 | h.C{i}.InitializeFullFlow(); 148 | end 149 | h.g = zeros(h.D,'like',h.Ct); 150 | if( length(h.C) > 1 ) 151 | h.pt = zeros(h.D,'like',h.Ct); 152 | for i = 1:length(h.C) 153 | h.pt = max( h.pt, h.C{1}.pt ); 154 | end 155 | else 156 | h.pt = h.Ct; 157 | end 158 | if( length(h.P) > 1 ) 159 | h.pn = zeros(h.D,'like',h.Ct); 160 | end 161 | h.div = zeros(h.D,'like',h.Ct); 162 | h.u = zeros(h.D,'like',h.Ct); 163 | h.px = zeros([h.D(1)-1 h.D(2) h.D(3)],'like',h.Ct); 164 | h.py = zeros([h.D(1) h.D(2)-1 h.D(3)],'like',h.Ct); 165 | h.pz = zeros([h.D(1) h.D(2) h.D(3)-1],'like',h.Ct); 166 | 167 | %normalize lengths for geodesic shape constraint 168 | if numel(h.lx) > 0 && numel(h.ly) > 0 && numel(h.lz) > 0 169 | denom = (h.lx.^2+h.ly.^2+h.lz.^2).^0.5; 170 | mask = (denom > 0.001); 171 | h.lx(mask) = h.lx(mask) ./ denom(mask); 172 | h.lx(~mask) = 0; 173 | h.ly(mask) = h.ly(mask) ./ denom(mask); 174 | h.ly(~mask) = 0; 175 | h.lz(mask) = h.lz(mask) ./ denom(mask); 176 | h.lz(~mask) = 0; 177 | end 178 | 179 | end 180 | 181 | %deinitialize buffers for full flow 182 | function DeInitializeFullFlow(h) 183 | for i = 1:length(h.C) 184 | h.C{i}.DeInitializeFullFlow(); 185 | end 186 | clear h.g; 187 | clear h.pt; 188 | clear h.pn; 189 | clear h.px; 190 | clear h.py; 191 | clear h.div; 192 | end 193 | 194 | 195 | %update labels (top-down) (not recursive) 196 | function UpdateLabels(h,cc) 197 | if length(h.P)>1 198 | h.u = h.u + cc*(h.pn - h.div - h.pt); 199 | elseif length(h.P)==1 200 | h.u = h.u + cc*(h.P{1}.pt - h.div - h.pt); 201 | end 202 | 203 | if ~isempty(h.Name) 204 | PlotResults(h.Name,h.px,h.py,h.pt,h.div,h.g,h.u) 205 | end 206 | end 207 | 208 | %push down sink flow step (not recursive) 209 | function PushDownFlows(h,cc) 210 | %push down flow 211 | for i = 1:length(h.C) 212 | if length(h.C{i}.P)>1 213 | h.C{i}.pn = h.C{i}.pn + h.wC(i)*h.pt; 214 | end 215 | end 216 | 217 | %figure out sink desired capacity 218 | if ~isempty(h.P) && ~isempty(h.C) 219 | if length(h.P)>1 220 | h.g = h.pn - h.div + h.u/cc; 221 | else 222 | h.g = h.P{1}.pt - h.div + h.u/cc; 223 | end 224 | elseif isempty(h.P) 225 | h.g = 1/cc; 226 | end 227 | end 228 | 229 | %push sink capacities up and set sink flows 230 | function PushCapacityUp(h,cc) 231 | %leaf node, so constrain output flow 232 | if isempty(h.C) 233 | if length(h.P) > 1 234 | h.pt = min(h.Ct, h.pn-h.div+h.u/cc); 235 | else 236 | h.pt = min(h.Ct, h.P{1}.pt-h.div+h.u/cc); 237 | end 238 | 239 | %source node, so accumulate desired in-flow from children and set 240 | %source flow accordingly 241 | elseif isempty(h.P) 242 | for i = 1:length(h.C) 243 | if length(h.C{i}.P)>1 244 | h.g = h.g + h.wC(i)*(h.C{i}.div + h.C{i}.pt + h.wC(i)*h.pt - h.C{i}.pn - h.C{i}.u/cc); 245 | else 246 | h.g = h.g + h.C{i}.div + h.C{i}.pt - h.C{i}.u/cc; 247 | end 248 | end 249 | h.pt = h.g / sum(h.wC .^2); 250 | 251 | %branch node, so accumulate desired in-flow from children and set 252 | %branch output flow accordingly 253 | else 254 | for i = 1:length(h.C) 255 | if length(h.C{i}.P)>1 256 | h.g = h.g + h.wC(i)*(h.C{i}.div + h.C{i}.pt + h.wC(i)*h.pt - h.C{i}.pn - h.C{i}.u/cc); 257 | else 258 | h.g = h.g + h.C{i}.div + h.C{i}.pt - h.C{i}.u/cc; 259 | end 260 | end 261 | h.pt = h.g / (1+sum(h.wC .^2)); 262 | end 263 | 264 | end 265 | 266 | %update spatial flows (not recursive) 267 | function UpdateSpatialFlows(h,steps,cc) 268 | if ~isempty(h.P) 269 | 270 | %gradient descent on flows 271 | if length(h.P) > 1 272 | h.g = steps*( h.div + h.pt - h.pn - h.u/cc ); 273 | else 274 | h.g = steps*( h.div + h.pt - h.P{1}.pt - h.u/cc ); 275 | end 276 | h.px = h.px + h.g(2:h.D(1),:,:)-h.g(1:h.D(1)-1,:,:); 277 | h.py = h.py + h.g(:,2:h.D(2),:)-h.g(:,1:h.D(2)-1,:); 278 | h.pz = h.pz + h.g(:,:,2:h.D(3))-h.g(:,:,1:h.D(3)-1); 279 | 280 | %find flow mag, exemption amounts, and correction 281 | if numel(h.lx) > 0 282 | 283 | %find exemption amount 284 | h.g(2:h.D(1),:,:) = max((h.px<0).*h.px.*h.lx(2:h.D(1),:,:) ,0); 285 | h.g(1,:,:) = 0; 286 | h.g(1:h.D(1)-1,:,:) = h.g(1:h.D(1)-1,:,:)+ max((h.px>0).*h.px.*h.lx(1:h.D(1)-1,:,:),0); 287 | h.g(:,1:h.D(2)-1,:) = h.g(:,1:h.D(2)-1,:)+ max((h.py>0).*h.py.*h.ly(:,1:h.D(2)-1,:),0); 288 | h.g(:,2:h.D(2),:) = h.g(:,2:h.D(2),:) + max((h.py<0).*h.py.*h.ly(:,2:h.D(2),:) ,0); 289 | h.g(:,:,1:h.D(3)-1) = h.g(:,:,1:h.D(3)-1)+ max((h.pz>0).*h.pz.*h.lz(:,:,1:h.D(3)-1),0); 290 | h.g(:,:,2:h.D(3)) = h.g(:,:,2:h.D(3)) + max((h.pz<0).*h.pz.*h.lz(:,:,2:h.D(3)) ,0); 291 | 292 | %find exemption amount 293 | ex = (h.px>0 & h.lx(1:h.D(1)-1,:,:)>0).*h.lx(1:h.D(1)-1,:,:).*h.g(1:h.D(1)-1,:,:); 294 | ex = ex + (h.px<0 & h.lx(2:h.D(1),:,:)<0).*h.lx(2:h.D(1),:,:).*h.g(2:h.D(1),:,:); 295 | 296 | ey = (h.py>0 & h.ly(:,1:h.D(2)-1,:)>0).*h.ly(:,1:h.D(2)-1,:).*h.g(:,1:h.D(2)-1,:); 297 | ey = ey + (h.py<0 & h.ly(:,2:h.D(2),:)<0).*h.ly(:,2:h.D(2),:).*h.g(:,2:h.D(2),:); 298 | 299 | ez = (h.pz>0 & h.lz(:,:,1:h.D(3)-1)>0).*h.lz(:,:,1:h.D(3)-1).*h.g(:,:,1:h.D(3)-1); 300 | ez = ez + (h.pz<0 & h.lz(:,:,2:h.D(3))<0).*h.lz(:,:,2:h.D(3)).*h.g(:,:,2:h.D(3)); 301 | 302 | %apply exemption 303 | h.px = h.px - ex; 304 | h.py = h.py - ey; 305 | h.pz = h.pz - ez; 306 | 307 | %find flow mag 308 | h.g(2:h.D(1),:,:) = h.px.^2; 309 | h.g(1,:,:) = 0; 310 | h.g(1:h.D(1)-1,:,:) = h.g(1:h.D(1)-1,:,:) + h.px.^2; 311 | h.g(:,1:h.D(2)-1,:) = h.g(:,1:h.D(2)-1,:) + h.py.^2; 312 | h.g(:,2:h.D(2),:) = h.g(:,2:h.D(2),:) + h.py.^2; 313 | h.g(:,:,1:h.D(3)-1) = h.g(:,:,1:h.D(3)-1) + h.pz.^2; 314 | h.g(:,:,2:h.D(3)) = h.g(:,:,2:h.D(3)) + h.pz.^2; 315 | h.g = h.g .^ 0.5; 316 | 317 | %correct for flow mag 318 | mask = (h.g <= h.alpha); 319 | if numel(h.alpha) == 1 320 | h.g(~mask) = h.alpha ./ h.g(~mask); 321 | else 322 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 323 | end 324 | h.g(mask) = 1; 325 | h.px = ex + 0.5 * h.px .* (h.g(2:h.D(1),:,:)+h.g(1:h.D(1)-1,:,:)); 326 | h.py = ey + 0.5 * h.py .* (h.g(:,2:h.D(2),:)+h.g(:,1:h.D(2)-1,:)); 327 | h.pz = ez + 0.5 * h.pz .* (h.g(:,:,2:h.D(3))+h.g(:,:,1:h.D(3)-1)); 328 | 329 | %no exemption vector, so a=0 330 | else 331 | 332 | %find flow mag 333 | h.g(2:h.D(1),:,:) = h.px.^2; 334 | h.g(1,:,:) = 0; 335 | h.g(1:h.D(1)-1,:,:) = h.g(1:h.D(1)-1,:,:) + h.px.^2; 336 | h.g(:,1:h.D(2)-1,:) = h.g(:,1:h.D(2)-1,:) + h.py.^2; 337 | h.g(:,2:h.D(2),:) = h.g(:,2:h.D(2),:) + h.py.^2; 338 | h.g(:,:,1:h.D(3)-1) = h.g(:,:,1:h.D(3)-1) + h.pz.^2; 339 | h.g(:,:,2:h.D(3)) = h.g(:,:,2:h.D(3)) + h.pz.^2; 340 | h.g = h.g .^ 0.5; 341 | 342 | %correct for flow mag 343 | mask = (h.g <= h.alpha); 344 | if numel(h.alpha) == 1 345 | h.g(~mask) = h.alpha ./ h.g(~mask); 346 | else 347 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 348 | end 349 | h.g(mask) = 1; 350 | h.px = 0.5 * h.px .* (h.g(2:h.D(1),:,:)+h.g(1:h.D(1)-1,:,:)); 351 | h.py = 0.5 * h.py .* (h.g(:,2:h.D(2),:)+h.g(:,1:h.D(2)-1,:)); 352 | h.pz = 0.5 * h.pz .* (h.g(:,:,2:h.D(3))+h.g(:,:,1:h.D(3)-1)); 353 | end 354 | 355 | %calculate divergence 356 | h.div(1:h.D(1)-1,:,:) = h.px; 357 | h.div(h.D(1),:,:) = 0; 358 | h.div(2:h.D(1),:,:) = h.div(2:h.D(1),:,:) - h.px; 359 | h.div(:,1:h.D(2)-1,:) = h.div(:,1:h.D(2)-1,:) + h.py; 360 | h.div(:,2:h.D(2),:) = h.div(:,2:h.D(2),:) - h.py; 361 | h.div(:,:,1:h.D(3)-1) = h.div(:,:,1:h.D(3)-1) + h.pz; 362 | h.div(:,:,2:h.D(3)) = h.div(:,:,2:h.D(3)) - h.pz; 363 | 364 | end 365 | 366 | end 367 | 368 | 369 | end 370 | end -------------------------------------------------------------------------------- /maxflow/asetsHMF2D.m: -------------------------------------------------------------------------------- 1 | classdef asetsHMF2D < handle 2 | % John SH Baxter, Robarts Research Institute, 2015 3 | % 4 | % Full implementation with of [1,2] in 2D and HMF implementation 5 | % of [3]. Optional geodesic shape constraints (via lx & ly) are 6 | % implemented as described in [4]. 7 | % 8 | % [1] Baxter, JSH.; Rajchl, M.; Yuan, J.; Peters, TM. 9 | % A Continuous Max-Flow Approach to General 10 | % Hierarchical Multi-Labelling Problems 11 | % arXiv preprint arXiv:1404.0336 12 | % 13 | % [2] Rajchl M., J. Baxter, A. McLeod, J. Yuan, W. Qiu, 14 | % T. Peters, and A. Khan (2015). 15 | % Hierarchical Max-Flow Segmentation Framework For Multi-Atlas 16 | % Segmentation with Kohonen Self-Organizing Map Based Gaussian 17 | % Mixture Modeling. Medical Image Analysis 18 | % 19 | % [3] Baxter, JSH.; Rajchl, M.; Yuan, J.; Peters, TM. 20 | % A Proximal Bregman Projection Approach to Continuous 21 | % Max-Flow Problems Using Entropic Distances 22 | % arXiv preprint arXiv:1501.07844 23 | % 24 | % [4] Baxter, JSH.; Yuan, J.; Peters, TM. 25 | % Shape Complexes in Continuous Max-Flow Hierarchical Multi- 26 | % Labeling Problems 27 | % arXiv preprint arXiv:1510.04706 28 | 29 | properties 30 | Name 31 | Ct 32 | alpha 33 | D 34 | 35 | dataCost 36 | reguCost 37 | 38 | u 39 | 40 | pt 41 | px 42 | py 43 | lx 44 | ly 45 | div 46 | g 47 | 48 | P 49 | C 50 | end 51 | 52 | methods 53 | 54 | %constructor 55 | function h = asetsHMF2D(children,alpha,Ct) 56 | %add in children 57 | h.C = children; 58 | for i = 1:length(children) 59 | h.C{i}.P = h; 60 | end 61 | 62 | %find dimensions 63 | if isempty(children) 64 | h.D = size(Ct); 65 | h.Ct = Ct; 66 | else 67 | h.D = h.C{1}.D; 68 | h.Ct = zeros(0,'like',h.C{1}.Ct); 69 | end 70 | 71 | %create buffers 72 | h.alpha = alpha; 73 | h.u = zeros(h.D,'like',h.Ct); 74 | 75 | end 76 | 77 | %run full-flow algorithm with set number of iterations 78 | function MaxFullFlow(h,numIts,steps,cc) 79 | h.InitializeFullFlow(); 80 | for i = 1:numIts 81 | h.UpdateSinkFlows(cc); 82 | h.UpdateLabels(cc); 83 | h.UpdateSpatialFlows(steps,cc); 84 | drawnow 85 | end 86 | h.CalculateCostsFullFlow(); 87 | h.DeInitializeFullFlow(); 88 | end 89 | 90 | %run pseudo-flow algorithm with set number of iterations 91 | function MaxPseudoFlow(h,numIts,steps,cc) 92 | h.InitializePseudoFlow(); 93 | for i = 1:numIts 94 | h.ResetPseudoFlowCapacity(); 95 | h.PushDownPseudoFlows(); 96 | h.g = zeros(h.D); 97 | h.UpdatePseudoFlowLabels(cc,h); 98 | h.NormalizePseudoFlowLabels(h); 99 | h.PushUpCapacityReqs(h); 100 | h.UpdateSpatialPseudoFlows(steps,cc); 101 | end 102 | h.DeInitializePseudoFlow(); 103 | end 104 | 105 | 106 | %initialize buffers for full flow 107 | function InitializeFullFlow(h) 108 | for i = 1:length(h.C) 109 | h.C{i}.InitializeFullFlow(); 110 | end 111 | h.g = zeros(h.D, 'like', h.Ct); 112 | h.pt = zeros(h.D, 'like', h.Ct); 113 | h.u = zeros(h.D, 'like', h.Ct); 114 | h.px = zeros([h.D(1)-1 h.D(2)], 'like', h.Ct); 115 | h.py = zeros([h.D(1) h.D(2)-1], 'like', h.Ct); 116 | h.div = zeros(h.D, 'like', h.px); 117 | 118 | %normalize lengths for geodesic shape constraint 119 | if numel(h.lx) > 0 && numel(h.ly) > 0 120 | denom = (h.lx.^2+h.ly.^2).^0.5; 121 | mask = (denom > 0.001); 122 | h.lx(mask) = h.lx(mask) ./ denom(mask); 123 | h.lx(~mask) = 0; 124 | h.ly(mask) = h.ly(mask) ./ denom(mask); 125 | h.ly(~mask) = 0; 126 | end 127 | 128 | end 129 | 130 | %deinitialize buffers for full flow 131 | function DeInitializeFullFlow(h) 132 | for i = 1:length(h.C) 133 | h.C{i}.DeInitializeFullFlow(); 134 | end 135 | clear h.g; 136 | clear h.pt; 137 | clear h.px; 138 | clear h.py; 139 | clear h.div; 140 | end 141 | 142 | %initialize buffers for pseudo flow 143 | function InitializePseudoFlow(h) 144 | for i = 1:length(h.C) 145 | h.C{i}.InitializePseudoFlow(); 146 | end 147 | h.g = zeros(h.D, 'like', h.Ct); 148 | clear h.pt; 149 | h.px = zeros([h.D(1)-1 h.D(2)], 'like', h.Ct); 150 | h.py = zeros([h.D(1) h.D(2)-1], 'like', h.Ct); 151 | h.div = zeros(h.D, 'like', h.Ct); 152 | if ~isempty(h.C) 153 | h.u = []; 154 | end 155 | 156 | %normalize lengths for geodesic shape constraint 157 | if numel(h.lx) > 0 && numel(h.ly) > 0 158 | denom = (h.lx.^2+h.ly.^2).^0.5; 159 | mask = (denom > 0.001); 160 | h.lx(mask) = h.lx(mask) ./ denom(mask); 161 | h.lx(~mask) = 0; 162 | h.ly(mask) = h.ly(mask) ./ denom(mask); 163 | h.ly(~mask) = 0; 164 | end 165 | end 166 | 167 | %deinitialize buffers for pseudo flow 168 | function DeInitializePseudoFlow(h) 169 | for i = 1:length(h.C) 170 | h.C{i}.DeInitializePseudoFlow(); 171 | if i == 1 172 | h.u = h.C{i}.u; 173 | else 174 | h.u = h.u + h.C{i}.u; 175 | end 176 | end 177 | clear h.g; 178 | clear h.px; 179 | clear h.py; 180 | clear h.div; 181 | end 182 | 183 | %calculate and propogate costs 184 | function CalculateCostsFullFlow(h) 185 | 186 | if isempty(h.C) 187 | h.dataCost = sum( h.u(:) .* h.Ct(:) ); 188 | else 189 | h.dataCost = 0; 190 | end 191 | if ~isempty(h.P) 192 | h.reguCost = sum( h.u(:) .* h.div(:) ); 193 | else 194 | h.reguCost = 0; 195 | end 196 | 197 | for i = 1:length(h.C) 198 | h.C{i}.CalculateCostsFullFlow(); 199 | h.dataCost = h.dataCost + h.C{i}.dataCost; 200 | h.reguCost = h.reguCost + h.C{i}.reguCost; 201 | end 202 | end 203 | 204 | %update labels (top-down) 205 | function UpdateLabels(h,cc) 206 | if ~isempty(h.P) 207 | h.u = h.u + cc*(h.P.pt - h.div - h.pt); 208 | end 209 | for i = 1:length(h.C) 210 | h.C{i}.UpdateLabels(cc); 211 | end 212 | 213 | if length(h.Name)>0 214 | PlotResults(h.Name,h.px,h.py,h.pt,h.div,h.g,h.u) 215 | end 216 | end 217 | 218 | %update spatial flows (top-down) 219 | function UpdateSpatialFlows(h,steps,cc) 220 | if ~isempty(h.P) 221 | 222 | %gradient descent on flows 223 | h.g = steps*( h.div + h.pt - h.P.pt - h.u/cc ); 224 | h.px = h.px + (h.g(2:h.D(1),:)-h.g(1:h.D(1)-1,:)); 225 | h.py = h.py + (h.g(:,2:h.D(2))-h.g(:,1:h.D(2)-1)); 226 | 227 | %find flow mag, exemption amounts, and correction 228 | if numel(h.lx) > 0 229 | 230 | %find exemption amount 231 | a = zeros(h.D,'like',h.Ct); 232 | a(2:h.D(1),:) = max((h.px<0).*h.px.*h.lx(2:h.D(1),:) ,0); 233 | a(1,:) = 0; 234 | a(1:h.D(1)-1,:) = a(1:h.D(1)-1,:)+ max((h.px>0).*h.px.*h.lx(1:h.D(1)-1,:),0); 235 | a(:,1:h.D(2)-1) = a(:,1:h.D(2)-1)+ max((h.py>0).*h.py.*h.ly(:,1:h.D(2)-1),0); 236 | a(:,2:h.D(2)) = a(:,2:h.D(2)) + max((h.py<0).*h.py.*h.ly(:,2:h.D(2)) ,0); 237 | 238 | %find exemption amount 239 | ex = (h.px>0 & h.lx(1:h.D(1)-1,:,:)>0).*h.lx(1:h.D(1)-1,:,:).*a(1:h.D(1)-1,:,:); 240 | ex = ex + (h.px<0 & h.lx(2:h.D(1),:,:)<0).*h.lx(2:h.D(1),:,:).*a(2:h.D(1),:,:); 241 | 242 | ey = (h.py>0 & h.ly(:,1:h.D(2)-1,:)>0).*h.ly(:,1:h.D(2)-1,:).*a(:,1:h.D(2)-1,:); 243 | ey = ey + (h.py<0 & h.ly(:,2:h.D(2),:)<0).*h.ly(:,2:h.D(2),:).*a(:,2:h.D(2),:); 244 | 245 | %apply exemption 246 | h.px = h.px - ex; 247 | h.py = h.py - ey; 248 | 249 | %find flow mag 250 | h.g(1:h.D(1)-1,:) = h.px.^2; 251 | h.g(h.D(1),:) = 0; 252 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 253 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 254 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 255 | h.g = h.g .^ 0.5; 256 | 257 | %correct for flow mag 258 | mask = (h.g <= h.alpha); 259 | if numel(h.alpha) == 1 260 | h.g(~mask) = h.alpha ./ h.g(~mask); 261 | else 262 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 263 | end 264 | h.g(mask) = 1; 265 | h.px = ex + 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 266 | h.py = ey + 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 267 | 268 | %no exemption vector, so a=0 269 | else 270 | 271 | %find flow mag 272 | h.g(1:h.D(1)-1,:) = h.px.^2; 273 | h.g(h.D(1),:) = 0; 274 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 275 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 276 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 277 | h.g = h.g .^ 0.5; 278 | 279 | 280 | %correct for flow mag 281 | mask = (h.g <= h.alpha); 282 | if numel(h.alpha) == 1 283 | h.g(~mask) = h.alpha ./ h.g(~mask); 284 | else 285 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 286 | end 287 | h.g(mask) = 1; 288 | h.px = 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 289 | h.py = 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 290 | end 291 | 292 | %calculate divergence 293 | h.div(1:h.D(1)-1,:) = h.px; 294 | h.div(h.D(1),:) = 0; 295 | h.div(2:h.D(1),:) = h.div(2:h.D(1),:) - h.px; 296 | h.div(:,1:h.D(2)-1) = h.div(:,1:h.D(2)-1) + h.py; 297 | h.div(:,2:h.D(2)) = h.div(:,2:h.D(2)) - h.py; 298 | 299 | end 300 | 301 | %pass on to children 302 | for i = 1:length(h.C) 303 | h.C{i}.UpdateSpatialFlows(steps,cc); 304 | end 305 | end 306 | 307 | %update sink flows bottom up 308 | function UpdateSinkFlows(h,cc) 309 | 310 | %pass call down to children first 311 | for i = 1:length(h.C) 312 | h.C{i}.UpdateSinkFlows(cc); 313 | end 314 | 315 | %if leaf, constrain maximum possible flow by the data cost 316 | if isempty(h.C) 317 | h.pt = min(h.Ct,h.P.pt-h.div+h.u/cc); 318 | 319 | %if source, get flow requests from children and average them 320 | elseif isempty(h.P) 321 | h.pt = 1/cc .* ones(h.D); 322 | for i = 1:length(h.C) 323 | h.pt = h.pt+h.C{i}.pt+h.C{i}.div-h.C{i}.u/cc; 324 | end 325 | h.pt = h.pt / length(h.C); 326 | 327 | %if branch, get flow requests from children as well as desired 328 | %sink flow and average them 329 | else 330 | h.pt = h.P.pt-h.div+h.u/cc; 331 | for i = 1:length(h.C) 332 | h.pt = h.pt+h.C{i}.pt+h.C{i}.div-h.C{i}.u/cc; 333 | end 334 | h.pt = h.pt / (1+length(h.C)); 335 | 336 | end 337 | end 338 | 339 | %prepare for the push-down step 340 | function ResetPseudoFlowCapacity(h) 341 | 342 | %initialize capacity 343 | if isempty(h.C) 344 | h.g = h.div + h.Ct; 345 | elseif isempty(h.C) 346 | h.g = h.div; 347 | else 348 | h.g(:,:) = 0; 349 | end 350 | 351 | %push down call to children 352 | for i = 1:length(h.C) 353 | h.C{i}.ResetPseudoFlowCapacity(); 354 | end 355 | 356 | end 357 | 358 | %do the push-down flow capacities step 359 | function PushDownPseudoFlows(h) 360 | for i = 1:length(h.C) 361 | h.C{i}.g = h.C{i}.g + h.g; 362 | h.C{i}.PushDownPseudoFlows(); 363 | end 364 | end 365 | 366 | %update labels only at leaves 367 | function UpdatePseudoFlowLabels(h,cc,a) 368 | for i = 1:length(h.C) 369 | h.C{i}.UpdatePseudoFlowLabels(cc,a); 370 | end 371 | if isempty(h.C) 372 | h.u = h.u .* exp( - h.g / cc ); 373 | h.g = h.u; 374 | a.g = a.g + h.u; 375 | end 376 | end 377 | 378 | %normalize labels only at leaves 379 | function NormalizePseudoFlowLabels(h,a) 380 | for i = 1:length(h.C) 381 | h.C{i}.NormalizePseudoFlowLabels(a); 382 | end 383 | if isempty(h.C) 384 | h.u = h.u ./ a.g; 385 | h.u(h.u>1) = 1; 386 | h.u(h.u<0) = 0; 387 | %h.g = h.u; 388 | end 389 | end 390 | 391 | %normalize labels at the leaves and push up 392 | function PushUpCapacityReqs(h,a) 393 | %normalize and clear 394 | if ~isempty(h.C) 395 | h.g = zeros(h.D); 396 | end 397 | for i = 1:length(h.C) 398 | h.C{i}.PushUpCapacityReqs(a); 399 | end 400 | if ~isempty(h.P) && ~isempty(h.P.P) 401 | h.P.g = h.P.g + h.g; 402 | end 403 | end 404 | 405 | %update spatial flows (top-down) 406 | function UpdateSpatialPseudoFlows(h,steps,cc) 407 | if ~isempty(h.P) 408 | 409 | %gradient descent on flows 410 | h.g = steps*h.g; 411 | h.px = h.px + h.g(2:h.D(1),:)-h.g(1:h.D(1)-1,:); 412 | h.py = h.py + h.g(:,2:h.D(2))-h.g(:,1:h.D(2)-1); 413 | 414 | %find flow mag, exemption amounts, and correction 415 | if numel(h.lx) > 0 416 | 417 | %find exemption amount 418 | a(1:h.D(1)-1,:) = max((h.px>0).*h.px.*h.lx(1:h.D(1)-1,:),0); 419 | a(h.D(1),:) = 0; 420 | a(2:h.D(1),:) = a(2:h.D(1),:) + max((h.px<0).*h.px.*h.lx(2:h.D(1),:) ,0); 421 | a(:,1:h.D(2)-1) = a(:,1:h.D(2)-1)+ max((h.py>0).*h.py.*h.ly(:,1:h.D(2)-1),0); 422 | a(:,2:h.D(2)) = a(:,2:h.D(2)) + max((h.py<0).*h.py.*h.ly(:,2:h.D(2)) ,0); 423 | 424 | %find exemption amount 425 | ex = (h.px>0 & h.lx(1:h.D(1)-1,:,:)>0).*h.lx(1:h.D(1)-1,:,:).*a(1:h.D(1)-1,:,:); 426 | ex = ex + (h.px<0 & h.lx(2:h.D(1),:,:)<0).*h.lx(2:h.D(1),:,:).*a(2:h.D(1),:,:); 427 | 428 | ey = (h.py>0 & h.ly(:,1:h.D(2)-1,:)>0).*h.ly(:,1:h.D(2)-1,:).*a(:,1:h.D(2)-1,:); 429 | ey = ey + (h.py<0 & h.ly(:,2:h.D(2),:)<0).*h.ly(:,2:h.D(2),:).*a(:,2:h.D(2),:); 430 | 431 | %apply exemption 432 | h.px = h.px - ex; 433 | h.py = h.py - ey; 434 | 435 | %find flow mag 436 | h.g(1:h.D(1)-1,:) = h.px.^2; 437 | h.g(h.D(1),:) = 0; 438 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 439 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 440 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 441 | h.g = h.g .^ 0.5; 442 | 443 | %correct for flow mag 444 | mask = (h.g <= h.alpha); 445 | if numel(h.alpha) == 1 446 | h.g(~mask) = h.alpha ./ h.g(~mask); 447 | else 448 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 449 | end 450 | h.g(mask) = 1; 451 | h.px = ex + 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 452 | h.py = ey + 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 453 | 454 | %no exemption vector, so a=0 455 | else 456 | 457 | %find flow mag 458 | h.g(1:h.D(1)-1,:) = h.px.^2; 459 | h.g(h.D(1),:) = 0; 460 | h.g(2:h.D(1),:) = h.g(2:h.D(1),:) + h.px.^2; 461 | h.g(:,1:h.D(2)-1) = h.g(:,1:h.D(2)-1) + h.py.^2; 462 | h.g(:,2:h.D(2)) = h.g(:,2:h.D(2)) + h.py.^2; 463 | h.g = h.g .^ 0.5; 464 | 465 | 466 | %correct for flow mag 467 | mask = (h.g <= h.alpha); 468 | if numel(h.alpha) == 1 469 | h.g(~mask) = h.alpha ./ h.g(~mask); 470 | else 471 | h.g(~mask) = h.alpha(~mask) ./ h.g(~mask); 472 | end 473 | h.g(mask) = 1; 474 | h.px = 0.5 * h.px .* (h.g(2:h.D(1),:)+h.g(1:h.D(1)-1,:)); 475 | h.py = 0.5 * h.py .* (h.g(:,2:h.D(2))+h.g(:,1:h.D(2)-1)); 476 | end 477 | 478 | %calculate divergence 479 | h.div(1:h.D(1)-1,:) = h.px; 480 | h.div(h.D(1),:) = 0; 481 | h.div(2:h.D(1),:) = h.div(2:h.D(1),:) - h.px; 482 | h.div(:,1:h.D(2)-1) = h.div(:,1:h.D(2)-1) + h.py; 483 | h.div(:,2:h.D(2)) = h.div(:,2:h.D(2)) - h.py; 484 | 485 | end 486 | 487 | %pass on to children 488 | for i = 1:length(h.C) 489 | h.C{i}.UpdateSpatialPseudoFlows(steps,cc); 490 | end 491 | end 492 | 493 | end 494 | 495 | end -------------------------------------------------------------------------------- /maxflow/asetsIshikawa2D.m: -------------------------------------------------------------------------------- 1 | function [u, erriter, i, timet] = asetsIshikawa2D(Ct, alpha, pars) 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % 5 | % Re-implementation with of [1] 6 | % 7 | % [1] Rajchl M., J. Yuan, E. Ukwatta, and T. Peters (2012). 8 | % Fast Interactive Multi-Region Cardiac Segmentation With 9 | % Linearly Ordered Labels. 10 | % ISBI, 2012. pp.1409–1412. 11 | 12 | 13 | if(nargin < 3) 14 | error('Not enough args. Exiting...'); 15 | end 16 | 17 | % setup 18 | rows = pars(1); 19 | cols = pars(2); 20 | nlab = pars(3); 21 | iterNum= pars(4); 22 | beta = pars(5); 23 | cc = pars(6); 24 | steps = pars(7); 25 | 26 | vol = rows*cols*nlab-1; 27 | 28 | % alloc the max flow buffers 29 | pt = zeros(rows, cols, nlab, 'like', Ct); 30 | u = ones(rows, cols, nlab-1, 'like', Ct); 31 | pp1 = zeros(rows, cols+1, nlab-1, 'like', Ct); 32 | pp2 = zeros(rows+1, cols, nlab-1, 'like', Ct); 33 | erriter = zeros(iterNum,1, 'like', Ct); 34 | divp = zeros(rows,cols,nlab-1, 'like', Ct); 35 | 36 | % initialize the flow buffers for faster convergence 37 | [um,init] = min(Ct,[],3); 38 | 39 | for k=1:nlab 40 | pt(:,:,k) = um; 41 | end 42 | 43 | for k=1:nlab-1 44 | tmp = init > k; 45 | u(:,:,k) = tmp; 46 | end 47 | 48 | tic 49 | for i = 1:iterNum 50 | 51 | % update flow within each layer 52 | for k= 1:nlab-1 53 | 54 | ud = divp(:,:,k) - (pt(:,:,k) - pt(:,:,k+1) + u(:,:,k)/cc); 55 | 56 | % the following steps are the gradient descent step with steps as the 57 | % step-size. 58 | pp1(:,2:cols,k) = steps*(ud(:,2:cols) - ud(:,1:cols-1)) + pp1(:,2:cols,k); 59 | pp2(2:rows,:,k) = steps*(ud(2:rows,:) - ud(1:rows-1,:)) + pp2(2:rows,:,k); 60 | 61 | % the following steps are the projection to make |p(x,i)| <= alpha(x) 62 | gk = sqrt((pp1(:,1:cols,k).^2 + pp1(:,2:cols+1,k).^2 +... 63 | pp2(1:rows,:,k).^2 + pp2(2:rows+1,:,k).^2)*0.5); 64 | 65 | gk = double(gk <= alpha(:,:,k)) + double(~(gk <= alpha(:,:,k))).*(gk ./ alpha(:,:,k)); 66 | gk = 1 ./ gk; 67 | 68 | pp1(:,2:cols,k) = (0.5*(gk(:,2:cols) + gk(:,1:cols-1))).*pp1(:,2:cols,k); 69 | pp2(2:rows,:,k) = (0.5*(gk(2:rows,:) + gk(1:rows-1,:))).*pp2(2:rows,:,k); 70 | 71 | divp(:,:,k) = pp1(:,2:cols+1,k)-pp1(:,1:cols,k)+pp2(2:rows+1,:,k)-pp2(1:rows,:,k); 72 | end 73 | for k =1:nlab 74 | if (k == 1) 75 | ud = divp(:,:,k) + pt(:,:,2) - u(:,:,1)/cc + 1/cc; 76 | pt(:,:,1) = min(ud, Ct(:,:,1)); 77 | end 78 | 79 | if (k >= 2) && (k < nlab) 80 | ud = - divp(:,:,k-1) + pt(:,:,k-1) + u(:,:,k-1)/cc; 81 | ud = ud + divp(:,:,k) + pt(:,:,k+1) - u(:,:,k)/cc; 82 | ud = ud/2.0; 83 | 84 | pt(:,:,k) = min(ud,Ct(:,:,k)); 85 | end 86 | 87 | if (k==nlab) 88 | ud = - divp(:,:,nlab-1) + pt(:,:,nlab-1) + u(:,:,nlab-1)/cc; 89 | pt(:,:,nlab) = min(ud, Ct(:,:,nlab)); 90 | end 91 | end 92 | 93 | % update the multiplier u 94 | erru_sum = 0; 95 | for k = 1:nlab-1 96 | erru = cc*(divp(:,:,k) + pt(:,:,k+1) - pt(:,:,k)); 97 | u(:,:,k) = u(:,:,k) - erru; 98 | erru_sum = erru_sum + sum(sum(abs(erru))); 99 | end 100 | 101 | % evaluate the average error 102 | erriter(i) = erru_sum/vol; 103 | if erriter(i) < beta 104 | break; 105 | end 106 | end 107 | 108 | if(strcmp(class(u), 'gpuArray')) 109 | u = gather(u); 110 | erriter = gather(erriter); 111 | end 112 | 113 | timet = toc; 114 | msg = sprintf('number of iterations = %u; time = %f \n', i, timet); 115 | disp(msg); 116 | 117 | 118 | end 119 | 120 | 121 | -------------------------------------------------------------------------------- /maxflow/asetsIshikawa2D_mex.c: -------------------------------------------------------------------------------- 1 | /* Martin Rajchl, Imperial College London, 2015 2 | * 3 | * Re-implementation with of [1] 4 | * 5 | * [1] Rajchl M., J. Yuan, E. Ukwatta, and T. Peters (2012). 6 | * Fast Interactive Multi-Region Cardiac Segmentation With 7 | * Linearly Ordered Labels. 8 | * ISBI, 2012. pp.1409–1412. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MIN(a,b) (((a)<(b))?(a):(b)) 18 | #define MAX(a,b) (((a)>(b))?(a):(b)) 19 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 20 | 21 | void runMaxFlow( float *alpha, float *Ct, 22 | int Nx, int Ny, int nLab, int maxIt, 23 | float errbound, float cc, float steps, 24 | float *u, float *cvg, int *itNum); 25 | 26 | void init(float *Ct, float *pt, float *u, int Nx, int Ny, int nLab); 27 | 28 | void updateP1(float *gk, float *dv, float *pt, float *u, int Nx, int Ny, float cc, int lbl_id); 29 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps, int lbl_id); 30 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps, int lbl_id); 31 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny, int lbl_id); 32 | void updateBX(float *bx, float *gk, int Nx, int Ny, int lbl_id); 33 | void updateBY(float *by, float *gk, int Nx, int Ny, int lbl_id); 34 | void updateDIV(float *dv, float *bx, float *by, int Nx, int Ny, float cc, int lbl_id); 35 | void updateTopLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, float cc, int lbl_id); 36 | void updateMidLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, float cc, int lbl_id); 37 | void updateBottomLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, float cc, int lbl_id); 38 | float updateU(float *dv, float *pt, float *u, int Nx, int Ny, int nLab, float cc); 39 | 40 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 41 | int iNbIn, const mxArray *pmxIn[]) 42 | { 43 | /* vars in */ 44 | float *alpha, *Ct, *pars; 45 | int Nx, Ny, nLab, maxIt; 46 | float errBound, cc, steps; 47 | 48 | /* vars out */ 49 | float *u, *cvg; 50 | int *itNum; 51 | double *runTime; 52 | int nDim; 53 | int dim[4]; 54 | 55 | /* others */ 56 | time_t start_time, end_time; 57 | 58 | /* compute Max-Flow */ 59 | start_time = clock(); 60 | 61 | /* Inputs */ 62 | Ct = mxGetData(pmxIn[0]); /* bound of sink flows */ 63 | alpha = mxGetData(pmxIn[1]); /* penalty parameters */ 64 | pars = mxGetData(pmxIn[2]); /* Vector of parameters */ 65 | 66 | /* 67 | *pfVecParameters Setting 68 | * [0] : number of columns 69 | * [1] : number of rows 70 | * [2] : number of labels 71 | * [3] : the maximum iteration number 72 | * [4] : error criterion 73 | * [5] : cc for the step-size of ALM 74 | * [6] : steps for the step-size of projected-gradient of p 75 | */ 76 | 77 | /* pars */ 78 | Ny = (int) pars[0]; 79 | Nx = (int) pars[1]; 80 | nLab = (int) pars[2]; 81 | maxIt = (int) pars[3]; 82 | errBound = (float) pars[4]; 83 | cc = (float) pars[5]; 84 | steps = (float) pars[6]; 85 | 86 | /* Outputs */ 87 | /* outputs the computed u(x) */ 88 | dim[0] = Ny; 89 | dim[1] = Nx; 90 | dim[2] = nLab-1; 91 | nDim = 3; 92 | 93 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 94 | u = mxGetData(pmxOut[0]); 95 | 96 | /* outputs the convergence rate */ 97 | nDim = 2; 98 | dim[0] = maxIt; 99 | dim[1] = 1; 100 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 101 | cvg = mxGetData(pmxOut[1]); 102 | 103 | /* outputs the iteration number */ 104 | nDim = 2; 105 | dim[0] = 1; 106 | dim[1] = 1; 107 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 108 | itNum = mxGetData(pmxOut[2]); 109 | 110 | /* outputs the computation time */ 111 | nDim = 2; 112 | dim[0] = 1; 113 | dim[1] = 1; 114 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 115 | runTime = mxGetData(pmxOut[3]); 116 | 117 | 118 | /* compute Max-Flow */ 119 | start_time = clock(); 120 | 121 | 122 | runMaxFlow(alpha, Ct, 123 | Nx, Ny, nLab, maxIt, errBound, cc, steps, 124 | u, cvg, itNum); 125 | 126 | 127 | end_time = clock(); 128 | 129 | runTime[0] = difftime(end_time, start_time)/1000000; 130 | 131 | mexPrintf("ishikawa model max flow 2D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 132 | 133 | } 134 | 135 | 136 | void runMaxFlow( float *alpha, float *Ct, 137 | int Nx, int Ny, int nLab, int maxIt, 138 | float errBound, float cc, float steps, 139 | float *u, float *cvg, int *itNum){ 140 | 141 | 142 | float *bx, *by, *dv, *gk, *pt; 143 | int i; 144 | float total_err; 145 | 146 | /* alloc buffers */ 147 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny*(nLab-1)), sizeof(float) ); 148 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)*(nLab-1)), sizeof(float) ); 149 | dv = (float *) calloc( (unsigned)(Nx*Ny*(nLab-1)), sizeof(float) ); 150 | gk = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 151 | pt = (float *) calloc( (unsigned)(Nx*Ny*nLab), sizeof(float) ); 152 | if (!(bx || by || dv || gk || pt)) 153 | mexPrintf("malloc error.\n"); 154 | 155 | init(Ct, pt, u, Nx, Ny, nLab); 156 | 157 | /* iterate */ 158 | i = 0; 159 | for (i = 0; i < maxIt; i++){ 160 | 161 | int k = 0; 162 | for (k = 0; k < (nLab-1); k++){ 163 | 164 | /* update the spatial flow field p(x,lbl) = (bx(x,lbl),by(x,lbl)) */ 165 | updateP1(gk, dv, pt, u, Nx, Ny, cc, k); 166 | updatePX(gk, bx, Nx, Ny, steps, k); 167 | updatePY(gk, by, Nx, Ny, steps, k); 168 | 169 | /* projection step to make |p(x,i)| <= alpha(x,lbl)*/ 170 | projStep(bx, by, alpha, gk, Nx, Ny, k); 171 | 172 | 173 | /* update the component bx, by */ 174 | updateBX(bx, gk, Nx, Ny, k); 175 | updateBY(by, gk, Nx, Ny, k); 176 | 177 | 178 | /* div(x,lbl) */ 179 | updateDIV(dv, bx, by, Nx, Ny, cc, k); 180 | } 181 | 182 | for (k = 0; k < nLab; k++){ 183 | 184 | if (k == 0){ 185 | updateBottomLayerPT(gk, dv, pt, u, Ct, Nx, Ny, cc, k); 186 | } 187 | else if ((k >= 1) && ( k < (nLab-1) )){ 188 | updateMidLayerPT(gk, dv, pt, u, Ct, Nx, Ny, cc, k); 189 | } 190 | else{ 191 | updateTopLayerPT(gk, dv, pt, u, Ct, Nx, Ny, cc, k); 192 | } 193 | 194 | } 195 | 196 | 197 | /* multiplier/labeling functions u(x,lbl) */ 198 | total_err = updateU(dv, pt, u, Nx, Ny, nLab, cc); 199 | 200 | /* evaluate the convergence error */ 201 | cvg[i] = total_err / (float)(Nx*Ny*(nLab-1)); 202 | /*mexPrintf("it= %d, cvg = %f\n", i,cvg[i] ); */ 203 | 204 | /* check if converged */ 205 | if (cvg[i] < errBound) 206 | break; 207 | 208 | } 209 | /* update iteration number */ 210 | itNum[0] = i; 211 | 212 | /* free mem */ 213 | free( (float *) bx ); 214 | free( (float *) by ); 215 | free( (float *) dv ); 216 | free( (float *) gk ); 217 | free( (float *) pt ); 218 | 219 | } 220 | 221 | void init(float *Ct, float *pt, float *u, int Nx, int Ny, int nLab){ 222 | /* init */ 223 | int x, y; 224 | 225 | for (x=0; x < Nx; x++){ 226 | for (y=0; y < Ny; y++){ 227 | 228 | int g_idx = x*Ny + y; 229 | 230 | float minVal = 1e30; 231 | int minId = 1e9; 232 | 233 | /* find the minimum Ct(x,l) */ 234 | int l; 235 | for (l = 0; l < nLab; l++){ 236 | int l_idx = g_idx + l*Nx*Ny; 237 | 238 | if ( minVal > Ct[l_idx] ){ 239 | minVal = Ct[l_idx]; 240 | minId = l; 241 | } 242 | } 243 | 244 | /* init pt, u */ 245 | for (l = 0; l < nLab-1; l++){ 246 | int l_idx = g_idx + l*Nx*Ny; 247 | 248 | pt[l_idx] = minVal; 249 | 250 | if (l >= minId) 251 | u[l_idx] = 0.0f; 252 | else 253 | u[l_idx] = 1.0f; 254 | } 255 | 256 | } 257 | 258 | } 259 | } 260 | 261 | void updateP1(float *gk, float *dv, float *pt, float *u, int Nx, int Ny, float cc, int lbl_id){ 262 | 263 | int x = 0; 264 | int y = 0; 265 | for (x=0; x < Nx; x++){ 266 | for (y=0; y < Ny; y++){ 267 | 268 | int g_idx = x*Ny + y; 269 | int l_idx = g_idx + lbl_id*Nx*Ny; 270 | 271 | gk[g_idx] = dv[l_idx] - (pt[l_idx] 272 | - pt[l_idx+(Nx*Ny)] + u[l_idx]/cc); 273 | } 274 | } 275 | } 276 | 277 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps, int lbl_id){ 278 | 279 | int x = 0; 280 | int y = 0; 281 | for (x=1; x < Nx; x++){ 282 | for (y=0; y < Ny; y++){ 283 | 284 | int g_idx = x*Ny + y; 285 | int l_idx = g_idx + lbl_id*Nx*Ny; 286 | 287 | bx[l_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[l_idx]; 288 | } 289 | } 290 | } 291 | 292 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps, int lbl_id){ 293 | 294 | int x = 0; 295 | int y = 0; 296 | 297 | for(x = 0; x < Nx; x ++){ 298 | for(y = 1; y < Ny; y++){ 299 | 300 | int g_idx = x*Ny + y; 301 | int l_idx = g_idx + lbl_id*Nx*Ny; 302 | 303 | by[l_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[l_idx]; 304 | } 305 | } 306 | } 307 | 308 | 309 | 310 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny, int lbl_id){ 311 | 312 | float fpt; 313 | int x = 0; 314 | int y = 0; 315 | for (x=0; x< Nx; x++){ 316 | for (y=0; y< Ny; y++){ 317 | 318 | int g_idx = x*Ny + y; 319 | int l_idx = g_idx + lbl_id*Nx*Ny; 320 | 321 | if( alpha[l_idx] <= 0 ){ 322 | mexErrMsgTxt("alpha(x,l) must be positive. Exiting..."); 323 | } 324 | 325 | fpt = sqrt((pow(bx[l_idx+Ny],2) + pow(bx[l_idx],2) + 326 | pow(by[l_idx+1],2) + pow(by[l_idx],2))*0.5); 327 | 328 | if (fpt > alpha[l_idx]) 329 | fpt = fpt / alpha[l_idx]; 330 | else 331 | fpt = 1; 332 | 333 | gk[g_idx] = 1/fpt; 334 | } 335 | } 336 | } 337 | 338 | void updateBX(float *bx, float *gk, int Nx, int Ny, int lbl_id){ 339 | 340 | int x = 0; 341 | int y = 0; 342 | 343 | for (x=1; x< Nx; x++){ 344 | for (y=0; y< Ny; y++){ 345 | 346 | int g_idx = x*Ny + y; 347 | int l_idx = g_idx + lbl_id*Nx*Ny; 348 | 349 | bx[l_idx] = (gk[g_idx] + gk[g_idx-Ny]) 350 | *0.5*bx[l_idx]; 351 | } 352 | } 353 | } 354 | 355 | void updateBY(float *by, float *gk, int Nx, int Ny, int lbl_id){ 356 | 357 | int x = 0; 358 | int y = 0; 359 | 360 | for (x=0; x k; 47 | u(:,:,:,k) = tmp; 48 | end 49 | 50 | tic 51 | for i = 1:iterNum 52 | 53 | % update flow within each layer 54 | for k= 1:nlab-1 55 | 56 | ud = divp(:,:,:,k) - (pt(:,:,:,k) - pt(:,:,:,k+1) + u(:,:,:,k)/cc); 57 | 58 | % the following steps are the gradient descent step with steps as the 59 | % step-size. 60 | pp1(:,2:cols,:,k) = steps*(ud(:,2:cols,:) - ud(:,1:cols-1,:)) + pp1(:,2:cols,:,k); 61 | pp2(2:rows,:,:,k) = steps*(ud(2:rows,:,:) - ud(1:rows-1,:,:)) + pp2(2:rows,:,:,k); 62 | pp3(:,:,2:slices,k) = steps*(ud(:,:,2:slices) - ud(:,:,1:slices-1)) + pp3(:,:,2:slices,k); 63 | 64 | % the following steps are the projection to make |p(x,i)| <= alpha(x) 65 | 66 | gk = sqrt((pp1(:,1:cols,:,k).^2 + pp1(:,2:cols+1,:,k).^2 +... 67 | pp2(1:rows,:,:,k).^2 + pp2(2:rows+1,:,:,k).^2 + ... 68 | pp3(:,:,1:slices,k).^2 + pp3(:,:,2:slices+1,k).^2)*0.5); 69 | 70 | gk = double(gk <= alpha(:,:,:,k)) + double(~(gk <= alpha(:,:,:,k))).*(gk ./ alpha(:,:,:,k)); 71 | gk = 1 ./ gk; 72 | 73 | pp1(:,2:cols,:,k) = (0.5*(gk(:,2:cols,:) + gk(:,1:cols-1,:))).*pp1(:,2:cols,:,k); 74 | pp2(2:rows,:,:,k) = (0.5*(gk(2:rows,:,:) + gk(1:rows-1,:,:))).*pp2(2:rows,:,:,k); 75 | pp3(:,:,2:slices,k) = (0.5*(gk(:,:,2:slices) + gk(:,:,1:slices-1))).*pp3(:,:,2:slices,k); 76 | 77 | divp(:,:,:,k) = pp1(:,2:cols+1,:,k)-pp1(:,1:cols,:,k) + ... 78 | pp2(2:rows+1,:,:,k)-pp2(1:rows,:,:,k) + ... 79 | pp3(:,:,2:slices+1,k)-pp3(:,:,1:slices,k); 80 | end 81 | for k =1:nlab 82 | if (k == 1) 83 | ud = divp(:,:,:,k) + pt(:,:,:,2) - u(:,:,:,1)/cc + 1/cc; 84 | pt(:,:,:,1) = min(ud, Ct(:,:,:,1)); 85 | end 86 | 87 | if (k >= 2) & (k < nlab) 88 | ud = - divp(:,:,:,k-1) + pt(:,:,:,k-1) + u(:,:,:,k-1)/cc; 89 | ud = ud + divp(:,:,:,k) + pt(:,:,:,k+1) - u(:,:,:,k)/cc; 90 | ud = ud/2; 91 | 92 | pt(:,:,:,k) = min(ud,Ct(:,:,:,k)); 93 | end 94 | 95 | if (k==nlab) 96 | ud = - divp(:,:,:,nlab-1) + pt(:,:,:,nlab-1) + u(:,:,:,nlab-1)/cc; 97 | pt(:,:,:,nlab) = min(ud, Ct(:,:,:,nlab)); 98 | end 99 | end 100 | 101 | % update the multiplier u 102 | erru_sum = 0; 103 | for k = 1:nlab-1 104 | erru = cc*(divp(:,:,:,k) + pt(:,:,:,k+1) - pt(:,:,:,k)); 105 | u(:,:,:,k) = u(:,:,:,k) - erru; 106 | erru_sum = erru_sum + sum(sum(sum(abs(erru)))); 107 | end 108 | 109 | % evaluate the average error 110 | erriter(i) = erru_sum/vol; 111 | if erriter(i) < beta 112 | break; 113 | end 114 | end 115 | 116 | if(strcmp(class(u), 'gpuArray')) 117 | u = gather(u); 118 | erriter = gather(erriter); 119 | end 120 | 121 | timet = toc; 122 | msg = sprintf('number of iterations = %u; time = %f \n', i, timet); 123 | disp(msg); 124 | 125 | 126 | end 127 | 128 | 129 | -------------------------------------------------------------------------------- /maxflow/asetsIshikawa3D_mex.c: -------------------------------------------------------------------------------- 1 | /* Martin Rajchl, Imperial College London, 2015 2 | * 3 | * Re-implementation with of [1] 4 | * 5 | * [1] Rajchl M., J. Yuan, E. Ukwatta, and T. Peters (2012). 6 | * Fast Interactive Multi-Region Cardiac Segmentation With 7 | * Linearly Ordered Labels. 8 | * ISBI, 2012. pp.1409–1412. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MIN(a,b) (((a)<(b))?(a):(b)) 18 | #define MAX(a,b) (((a)>(b))?(a):(b)) 19 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 20 | 21 | void runMaxFlow( float *alpha, float *Ct, 22 | int Nx, int Ny, int Nz, int nLab, int maxIt, 23 | float errbound, float cc, float steps, 24 | float *u, float *cvg, int *itNum); 25 | 26 | void init(float *Ct, float *pt, float *u, int Nx, int Ny, int Nz, int nLab); 27 | 28 | void updateP1(float *gk, float *dv, float *pt, float *u, int Nx, int Ny, int Nz, float cc, int lbl_id); 29 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps, int lbl_id); 30 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps, int lbl_id); 31 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps, int lbl_id); 32 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz, int lbl_id); 33 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz, int lbl_id); 34 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz, int lbl_id); 35 | void updateBZ(float *bz, float *gk, int Nx, int Ny, int Nz, int lbl_id); 36 | void updateDIV(float *dv, float *bx, float *by, float *bz, int Nx, int Ny, int Nz, float cc, int lbl_id); 37 | void updateTopLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, int Nz, float cc, int lbl_id); 38 | void updateMidLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, int Nz, float cc, int lbl_id); 39 | void updateBottomLayerPT(float *gk, float *dv, float *pt, float *u, float *Ct, int Nx, int Ny, int Nz, float cc, int lbl_id); 40 | float updateU(float *dv, float *pt, float *u, int Nx, int Ny, int Nz, int nLab, float cc); 41 | 42 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 43 | int iNbIn, const mxArray *pmxIn[]) 44 | { 45 | /* vars in */ 46 | float *alpha, *Ct, *pars; 47 | int Nx, Ny, Nz, nLab, maxIt; 48 | float errBound, cc, steps; 49 | 50 | /* vars out */ 51 | float *u, *cvg; 52 | int *itNum; 53 | double *runTime; 54 | int nDim; 55 | int dim[4]; 56 | 57 | /* others */ 58 | time_t start_time, end_time; 59 | 60 | /* compute Max-Flow */ 61 | start_time = clock(); 62 | 63 | /* Inputs */ 64 | Ct = mxGetData(pmxIn[0]); /* bound of sink flows */ 65 | alpha = mxGetData(pmxIn[1]); /* penalty parameters */ 66 | pars = mxGetData(pmxIn[2]); /* Vector of parameters */ 67 | 68 | /* 69 | *pfVecParameters Setting 70 | * [0] : number of columns 71 | * [1] : number of rows 72 | * [2] : number of rows 73 | * [3] : number of labels 74 | * [4] : the maximum iteration number 75 | * [5] : error criterion 76 | * [6] : cc for the step-size of ALM 77 | * [7] : steps for the step-size of projected-gradient of p 78 | */ 79 | 80 | /* pars */ 81 | Ny = (int) pars[0]; 82 | Nx = (int) pars[1]; 83 | Nz = (int) pars[2]; 84 | nLab = (int) pars[3]; 85 | maxIt = (int) pars[4]; 86 | errBound = (float) pars[5]; 87 | cc = (float) pars[6]; 88 | steps = (float) pars[7]; 89 | 90 | /* Outputs */ 91 | /* outputs the computed u(x) */ 92 | dim[0] = Ny; 93 | dim[1] = Nx; 94 | dim[2] = Nz; 95 | dim[3] = nLab-1; 96 | nDim = 4; 97 | 98 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 99 | u = mxGetData(pmxOut[0]); 100 | 101 | /* outputs the convergence rate */ 102 | nDim = 2; 103 | dim[0] = maxIt; 104 | dim[1] = 1; 105 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 106 | cvg = mxGetData(pmxOut[1]); 107 | 108 | /* outputs the iteration number */ 109 | nDim = 2; 110 | dim[0] = 1; 111 | dim[1] = 1; 112 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 113 | itNum = mxGetData(pmxOut[2]); 114 | 115 | /* outputs the computation time */ 116 | nDim = 2; 117 | dim[0] = 1; 118 | dim[1] = 1; 119 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 120 | runTime = mxGetData(pmxOut[3]); 121 | 122 | 123 | /* compute Max-Flow */ 124 | start_time = clock(); 125 | 126 | 127 | runMaxFlow(alpha, Ct, 128 | Nx, Ny, Nz, nLab, maxIt, errBound, cc, steps, 129 | u, cvg, itNum); 130 | 131 | 132 | end_time = clock(); 133 | 134 | runTime[0] = difftime(end_time, start_time)/1000000; 135 | 136 | mexPrintf("ishikawa model max flow 3D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 137 | 138 | } 139 | 140 | 141 | void runMaxFlow( float *alpha, float *Ct, 142 | int Nx, int Ny, int Nz, int nLab, int maxIt, 143 | float errBound, float cc, float steps, 144 | float *u, float *cvg, int *itNum){ 145 | 146 | 147 | float *bx, *by, *bz, *dv, *gk, *pt; 148 | int i; 149 | float total_err; 150 | 151 | /* alloc buffers */ 152 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny*Nz*(nLab-1)), sizeof(float) ); 153 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)*Nz*(nLab-1)), sizeof(float) ); 154 | bz = (float *) calloc( (unsigned)(Nx*Ny*(Nz+1)*(nLab-1)), sizeof(float) ); 155 | dv = (float *) calloc( (unsigned)(Nx*Ny*Nz*(nLab-1)), sizeof(float) ); 156 | gk = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 157 | pt = (float *) calloc( (unsigned)(Nx*Ny*Nz*nLab), sizeof(float) ); 158 | if (!(bx || by || bz || dv || gk || pt)) 159 | mexPrintf("malloc error.\n"); 160 | 161 | init(Ct, pt, u, Nx, Ny, Nz, nLab); 162 | 163 | /* iterate */ 164 | i = 0; 165 | for (i = 0; i < maxIt; i++){ 166 | 167 | int k = 0; 168 | for (k = 0; k < (nLab-1); k++){ 169 | 170 | /* update the spatial flow field p(x,lbl) = (bx(x,lbl),by(x,lbl)) */ 171 | updateP1(gk, dv, pt, u, Nx, Ny, Nz, cc, k); 172 | updatePX(gk, bx, Nx, Ny, Nz, steps, k); 173 | updatePY(gk, by, Nx, Ny, Nz, steps, k); 174 | updatePZ(gk, bz, Nx, Ny, Nz, steps, k); 175 | 176 | /* projection step to make |p(x,i)| <= alpha(x,lbl)*/ 177 | projStep(bx, by, bz, alpha, gk, Nx, Ny, Nz, k); 178 | 179 | 180 | /* update the component bx, by */ 181 | updateBX(bx, gk, Nx, Ny, Nz, k); 182 | updateBY(by, gk, Nx, Ny, Nz, k); 183 | updateBZ(bz, gk, Nx, Ny, Nz, k); 184 | 185 | /* div(x,lbl) */ 186 | updateDIV(dv, bx, by, bz, Nx, Ny, Nz, cc, k); 187 | } 188 | 189 | for (k = 0; k < nLab; k++){ 190 | 191 | if (k == 0){ 192 | updateBottomLayerPT(gk, dv, pt, u, Ct, Nx, Ny, Nz, cc, k); 193 | } 194 | else if ((k >= 1) && ( k < (nLab-1) )){ 195 | updateMidLayerPT(gk, dv, pt, u, Ct, Nx, Ny, Nz, cc, k); 196 | } 197 | else{ 198 | updateTopLayerPT(gk, dv, pt, u, Ct, Nx, Ny, Nz, cc, k); 199 | } 200 | 201 | } 202 | 203 | 204 | /* multiplier/labeling functions u(x,lbl) */ 205 | total_err = updateU(dv, pt, u, Nx, Ny, Nz, nLab, cc); 206 | 207 | 208 | /* evaluate the convergence error */ 209 | cvg[i] = total_err / (float)(Nx*Ny*Nz*(nLab-1)); 210 | /* mexPrintf("it= %d, cvg = %f\n", i,cvg[i] ); */ 211 | 212 | /* check if converged */ 213 | if (cvg[i] <= errBound) 214 | break; 215 | 216 | } 217 | /* update iteration number */ 218 | itNum[0] = i; 219 | 220 | /* free mem */ 221 | free( (float *) bx ); 222 | free( (float *) by ); 223 | free( (float *) bz ); 224 | free( (float *) dv ); 225 | free( (float *) gk ); 226 | free( (float *) pt ); 227 | 228 | } 229 | 230 | void init(float *Ct, float *pt, float *u, int Nx, int Ny, int Nz, int nLab){ 231 | /* init */ 232 | int x, y, z; 233 | 234 | for (z=0; z < Nz; z++){ 235 | for (x=0; x < Nx; x++){ 236 | for (y=0; y < Ny; y++){ 237 | 238 | int g_idx = z*Nx*Ny + x*Ny + y; 239 | 240 | float minVal = 1e30; 241 | int minId = 1e9; 242 | 243 | /* find the minimum Ct(x,l) */ 244 | int l; 245 | for (l = 0; l < nLab; l++){ 246 | int l_idx = g_idx + l*Nx*Ny*Nz; 247 | 248 | if ( minVal > Ct[l_idx] ){ 249 | minVal = Ct[l_idx]; 250 | minId = l; 251 | } 252 | } 253 | 254 | /* init pt, u */ 255 | for (l = 0; l < nLab-1; l++){ 256 | int l_idx = g_idx + l*Nx*Ny*Nz; 257 | 258 | pt[l_idx] = minVal; 259 | 260 | if (l >= minId) 261 | u[l_idx] = 0.0f; 262 | else 263 | u[l_idx] = 1.0f; 264 | } 265 | 266 | } 267 | } 268 | 269 | } 270 | } 271 | 272 | void updateP1(float *gk, float *dv, float *pt, float *u, int Nx, int Ny, int Nz, float cc, int lbl_id){ 273 | 274 | int x = 0; 275 | int y = 0; 276 | int z = 0; 277 | 278 | int graphSize = Nx*Ny*Nz; 279 | 280 | for (z=0; z < Nz; z++){ 281 | for (x=0; x < Nx; x++){ 282 | for (y=0; y < Ny; y++){ 283 | 284 | int g_idx = z*Nx*Ny + x*Ny + y; 285 | int l_idx = g_idx + lbl_id*graphSize; 286 | 287 | gk[g_idx] = dv[l_idx] - (pt[l_idx] 288 | - pt[l_idx+graphSize] + u[l_idx]/cc); 289 | 290 | } 291 | } 292 | } 293 | } 294 | 295 | 296 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps, int lbl_id){ 297 | 298 | int x = 0; 299 | int y = 0; 300 | int z = 0; 301 | int graphSize = Nx*Ny*Nz; 302 | 303 | for (z=0; z < Nz; z++){ 304 | for (x=1; x < Nx; x++){ 305 | for (y=0; y < Ny; y++){ 306 | 307 | 308 | int g_idx = z*Nx*Ny + x*Ny + y; 309 | int l_idx = g_idx + lbl_id*graphSize; 310 | 311 | bx[l_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[l_idx]; 312 | 313 | } 314 | } 315 | } 316 | 317 | } 318 | 319 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps, int lbl_id){ 320 | 321 | int x = 0; 322 | int y = 0; 323 | int z = 0; 324 | int graphSize = Nx*Ny*Nz; 325 | 326 | for (z=0; z < Nz; z++){ 327 | for(x = 0; x < Nx; x ++){ 328 | for(y = 1; y < Ny; y++){ 329 | 330 | 331 | int g_idx = z*Nx*Ny + x*Ny + y; 332 | int l_idx = g_idx + lbl_id*graphSize; 333 | 334 | by[l_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[l_idx]; 335 | 336 | } 337 | } 338 | } 339 | } 340 | 341 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps, int lbl_id){ 342 | 343 | int x = 0; 344 | int y = 0; 345 | int z = 0; 346 | int graphSize = Nx*Ny*Nz; 347 | 348 | for (z=1; z < Nz; z++){ 349 | for(x = 0; x < Nx; x ++){ 350 | for(y = 0; y < Ny; y++){ 351 | 352 | 353 | int g_idx = z*Nx*Ny + x*Ny + y; 354 | int l_idx = g_idx + lbl_id*graphSize; 355 | 356 | bz[l_idx] = steps*(gk[g_idx] - gk[g_idx-(Nx*Ny)]) + bz[l_idx]; 357 | } 358 | } 359 | } 360 | } 361 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 362 | 363 | float fpt; 364 | int x = 0; 365 | int y = 0; 366 | int z = 0; 367 | int graphSize = Nx*Ny*Nz; 368 | 369 | for (z=0; z < Nz; z++){ 370 | for (x=0; x< Nx; x++){ 371 | for (y=0; y< Ny; y++){ 372 | 373 | 374 | int g_idx = z*Nx*Ny + x*Ny + y; 375 | int l_idx = g_idx + lbl_id*graphSize; 376 | 377 | if( alpha[l_idx] <= 0 ){ 378 | mexErrMsgTxt("alpha(x,l) must be positive. Exiting..."); 379 | } 380 | 381 | fpt = sqrt((pow(bx[l_idx+Ny],2) + pow(bx[l_idx],2) + 382 | pow(by[l_idx+1],2) + pow(by[l_idx],2) + 383 | pow(bz[l_idx+(Nx*Ny)],2) + pow(bz[l_idx],2) 384 | )*0.5); 385 | 386 | if (fpt > alpha[l_idx]) 387 | fpt = fpt / alpha[l_idx]; 388 | else 389 | fpt = 1; 390 | 391 | gk[g_idx] = 1/fpt; 392 | 393 | } 394 | } 395 | } 396 | } 397 | 398 | 399 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 400 | 401 | int g_idx; 402 | int l_idx; 403 | int x = 0; 404 | int y = 0; 405 | int z = 0; 406 | int graphSize = Nx*Ny*Nz; 407 | 408 | for (z=0; z < Nz; z++){ 409 | for (x=1; x< Nx; x++){ 410 | for (y=0; y< Ny; y++){ 411 | 412 | 413 | g_idx = z*Nx*Ny + x*Ny + y; 414 | l_idx = g_idx + lbl_id*graphSize; 415 | 416 | bx[l_idx] = (gk[g_idx] + gk[g_idx-Ny]) 417 | *0.5*bx[l_idx]; 418 | } 419 | } 420 | } 421 | } 422 | 423 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 424 | 425 | int g_idx; 426 | int l_idx; 427 | int x = 0; 428 | int y = 0; 429 | int z = 0; 430 | int graphSize = Nx*Ny*Nz; 431 | 432 | for (z=0; z < Nz; z++){ 433 | for (x=0; x 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define MIN(a,b) (((a)<(b))?(a):(b)) 23 | #define MAX(a,b) (((a)>(b))?(a):(b)) 24 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 25 | 26 | void runMaxFlow( float *alpha, float *Ct, 27 | int Nx, int Ny, int nLab, int maxIt, 28 | float errbound, float cc, float steps, 29 | float *u, float *cvg, int *itNum); 30 | 31 | void init(float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int nLab); 32 | 33 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, float cc, int lbl_id); 34 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps, int lbl_id); 35 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps, int lbl_id); 36 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny, int lbl_id); 37 | void updateBX(float *bx, float *gk, int Nx, int Ny, int lbl_id); 38 | void updateBY(float *by, float *gk, int Nx, int Ny, int lbl_id); 39 | void updatePTDIV(float *dv, float *bx, float *by, float *ps, float *u, float *pt, float *Ct, int Nx, int Ny, float cc, int lbl_id); 40 | float updatePSU(float *dv, float *pt, float *u, float *ps, int Nx, int Ny, int nLab, float cc); 41 | 42 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 43 | int iNbIn, const mxArray *pmxIn[]) 44 | { 45 | /* vars in */ 46 | float *alpha, *Ct, *pars; 47 | int Nx, Ny, nLab, maxIt; 48 | float errBound, cc, steps; 49 | 50 | /* vars out */ 51 | float *u, *cvg; 52 | int *itNum; 53 | double *runTime; 54 | int nDim; 55 | int dim[4]; 56 | 57 | /* others */ 58 | time_t start_time, end_time; 59 | 60 | /* compute Max-Flow */ 61 | start_time = clock(); 62 | 63 | /* Inputs */ 64 | Ct = mxGetData(pmxIn[0]); /* bound of sink flows */ 65 | alpha = mxGetData(pmxIn[1]); /* penalty parameters */ 66 | pars = mxGetData(pmxIn[2]); /* Vector of parameters */ 67 | 68 | /* 69 | *pfVecParameters Setting 70 | * [0] : number of columns 71 | * [1] : number of rows 72 | * [2] : number of labels 73 | * [3] : the maximum iteration number 74 | * [4] : error criterion 75 | * [5] : cc for the step-size of ALM 76 | * [6] : steps for the step-size of projected-gradient of p 77 | */ 78 | 79 | /* pars */ 80 | Ny = (int) pars[0]; 81 | Nx = (int) pars[1]; 82 | nLab = (int) pars[2]; 83 | maxIt = (int) pars[3]; 84 | errBound = (float) pars[4]; 85 | cc = (float) pars[5]; 86 | steps = (float) pars[6]; 87 | 88 | /* Outputs */ 89 | /* outputs the computed u(x) */ 90 | dim[0] = Ny; 91 | dim[1] = Nx; 92 | dim[2] = nLab; 93 | nDim = 3; 94 | 95 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 96 | u = mxGetData(pmxOut[0]); 97 | 98 | /* outputs the convergence rate */ 99 | nDim = 2; 100 | dim[0] = maxIt; 101 | dim[1] = 1; 102 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 103 | cvg = mxGetData(pmxOut[1]); 104 | 105 | /* outputs the iteration number */ 106 | nDim = 2; 107 | dim[0] = 1; 108 | dim[1] = 1; 109 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 110 | itNum = mxGetData(pmxOut[2]); 111 | 112 | /* outputs the computation time */ 113 | nDim = 2; 114 | dim[0] = 1; 115 | dim[1] = 1; 116 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 117 | runTime = mxGetData(pmxOut[3]); 118 | 119 | 120 | /* compute Max-Flow */ 121 | start_time = clock(); 122 | 123 | 124 | runMaxFlow(alpha, Ct, 125 | Nx, Ny, nLab, maxIt, errBound, cc, steps, 126 | u, cvg, itNum); 127 | 128 | 129 | end_time = clock(); 130 | 131 | runTime[0] = difftime(end_time, start_time)/1000000; 132 | 133 | mexPrintf("potts model max flow 2D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 134 | 135 | } 136 | 137 | 138 | void runMaxFlow( float *alpha, float *Ct, 139 | int Nx, int Ny, int nLab, int maxIt, 140 | float errBound, float cc, float steps, 141 | float *u, float *cvg, int *itNum){ 142 | 143 | 144 | float *bx, *by, *dv, *gk, *ps, *pt; 145 | int i; 146 | float total_err; 147 | 148 | /* alloc buffers */ 149 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny*nLab), sizeof(float) ); 150 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)*nLab), sizeof(float) ); 151 | dv = (float *) calloc( (unsigned)(Nx*Ny*nLab), sizeof(float) ); 152 | gk = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 153 | ps = (float *) calloc( (unsigned)(Nx*Ny), sizeof(float) ); 154 | pt = (float *) calloc( (unsigned)(Nx*Ny*nLab), sizeof(float) ); 155 | if (!(bx || by || dv || gk || ps || pt)) 156 | mexPrintf("malloc error.\n"); 157 | 158 | init(Ct, ps, pt, u, Nx, Ny, nLab); 159 | 160 | 161 | /* iterate */ 162 | i = 0; 163 | for (i = 0; i < maxIt; i++){ 164 | 165 | int k = 0; 166 | for (k = 0; k < nLab; k++){ 167 | 168 | /* update the spatial flow field p(x,lbl) = (bx(x,lbl),by(x,lbl)) */ 169 | updateP1(gk, dv, ps, pt, u, Nx, Ny, cc, k); 170 | updatePX(gk, bx, Nx, Ny, steps, k); 171 | updatePY(gk, by, Nx, Ny, steps, k); 172 | 173 | /* projection step to make |p(x,i)| <= alpha(x,lbl)*/ 174 | projStep(bx, by, alpha, gk, Nx, Ny, k); 175 | 176 | 177 | /* update the component bx, by */ 178 | updateBX(bx, gk, Nx, Ny, k); 179 | updateBY(by, gk, Nx, Ny, k); 180 | 181 | 182 | /* update the sink flow field pt(x,lbl) pd and div(x,lbl) */ 183 | updatePTDIV(dv, bx, by, ps, u, pt, Ct, Nx, Ny, cc, k); 184 | } 185 | 186 | /* update ps(x) and the multiplier/labeling functions u(x,lbl) */ 187 | total_err = updatePSU(dv, pt, u, ps, Nx, Ny, nLab, cc); 188 | 189 | /* to be implemented */ 190 | /* evaluate the convergence error */ 191 | cvg[i] = total_err / (float)(Nx*Ny*nLab); 192 | /*mexPrintf("it= %d, cvg = %f\n", i,cvg[i] ); */ 193 | 194 | /* check if converged */ 195 | if (cvg[i] <= errBound) 196 | break; 197 | 198 | } 199 | /* update iteration number */ 200 | itNum[0] = i; 201 | 202 | /* free mem */ 203 | free( (float *) bx ); 204 | free( (float *) by ); 205 | free( (float *) dv ); 206 | free( (float *) gk ); 207 | free( (float *) ps ); 208 | free( (float *) pt ); 209 | 210 | } 211 | 212 | void init(float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int nLab){ 213 | /* init */ 214 | int x, y; 215 | 216 | for (x=0; x < Nx; x++){ 217 | for (y=0; y < Ny; y++){ 218 | 219 | int g_idx = x*Ny + y; 220 | 221 | float minVal = 1e30; 222 | int minId = 1e9; 223 | 224 | /* find the minimum Ct(x,l) */ 225 | int l; 226 | for (l = 0; l < nLab; l++){ 227 | int l_idx = g_idx + l*Nx*Ny; 228 | 229 | if ( minVal > Ct[l_idx] ){ 230 | minVal = Ct[l_idx]; 231 | minId = l; 232 | } 233 | } 234 | 235 | /* init ps, pt, u */ 236 | ps[g_idx] = minVal; 237 | 238 | for (l = 0; l < nLab; l++){ 239 | int l_idx = g_idx + l*Nx*Ny; 240 | 241 | pt[l_idx] = ps[g_idx]; 242 | 243 | if (l == minId) 244 | u[l_idx] = 1.0f; 245 | } 246 | 247 | } 248 | 249 | } 250 | } 251 | 252 | 253 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, float cc, int lbl_id){ 254 | 255 | int x = 0; 256 | int y = 0; 257 | for (x=0; x < Nx; x++){ 258 | for (y=0; y < Ny; y++){ 259 | 260 | int g_idx = x*Ny + y; 261 | int l_idx = g_idx + lbl_id*Nx*Ny; 262 | 263 | gk[g_idx] = dv[l_idx] - (ps[g_idx] 264 | - pt[l_idx] + u[l_idx]/cc); 265 | 266 | } 267 | } 268 | } 269 | 270 | void updatePX(float *gk, float *bx, int Nx, int Ny, float steps, int lbl_id){ 271 | 272 | int x = 0; 273 | int y = 0; 274 | for (x=1; x < Nx; x++){ 275 | for (y=0; y < Ny; y++){ 276 | 277 | int g_idx = x*Ny + y; 278 | int l_idx = g_idx + lbl_id*Nx*Ny; 279 | 280 | bx[l_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[l_idx]; 281 | } 282 | } 283 | } 284 | 285 | void updatePY(float *gk, float *by, int Nx, int Ny, float steps, int lbl_id){ 286 | 287 | int x = 0; 288 | int y = 0; 289 | 290 | for(x = 0; x < Nx; x ++){ 291 | for(y = 1; y < Ny; y++){ 292 | 293 | int g_idx = x*Ny + y; 294 | int l_idx = g_idx + lbl_id*Nx*Ny; 295 | 296 | by[l_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[l_idx]; 297 | } 298 | } 299 | } 300 | 301 | 302 | 303 | void projStep(float *bx, float *by, float *alpha, float *gk, int Nx, int Ny, int lbl_id){ 304 | 305 | float fpt; 306 | int x = 0; 307 | int y = 0; 308 | for (x=0; x< Nx; x++){ 309 | for (y=0; y< Ny; y++){ 310 | 311 | int g_idx = x*Ny + y; 312 | int l_idx = g_idx + lbl_id*Nx*Ny; 313 | 314 | if( alpha[l_idx] <= 0 ){ 315 | mexErrMsgTxt("alpha(x,l) must be positive. Exiting..."); 316 | } 317 | 318 | fpt = sqrt((pow(bx[l_idx+Ny],2) + pow(bx[l_idx],2) + 319 | pow(by[l_idx+1],2) + pow(by[l_idx],2))*0.5); 320 | 321 | if (fpt > alpha[l_idx]) 322 | fpt = fpt / alpha[l_idx]; 323 | else 324 | fpt = 1; 325 | 326 | gk[g_idx] = 1/fpt; 327 | } 328 | } 329 | } 330 | 331 | void updateBX(float *bx, float *gk, int Nx, int Ny, int lbl_id){ 332 | 333 | int x = 0; 334 | int y = 0; 335 | 336 | for (x=1; x< Nx; x++){ 337 | for (y=0; y< Ny; y++){ 338 | 339 | int g_idx = x*Ny + y; 340 | int l_idx = g_idx + lbl_id*Nx*Ny; 341 | 342 | bx[l_idx] = (gk[g_idx] + gk[g_idx-Ny]) 343 | *0.5*bx[l_idx]; 344 | } 345 | } 346 | } 347 | 348 | void updateBY(float *by, float *gk, int Nx, int Ny, int lbl_id){ 349 | 350 | int x = 0; 351 | int y = 0; 352 | 353 | for (x=0; x 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define MIN(a,b) (((a)<(b))?(a):(b)) 23 | #define MAX(a,b) (((a)>(b))?(a):(b)) 24 | #define ABS(x) ( (x) > 0.0 ? x : -(x) ) 25 | 26 | void runMaxFlow( float *alpha, float *Ct, 27 | int Nx, int Ny, int Nz, int nLab, int maxIt, 28 | float errbound, float cc, float steps, 29 | float *u, float *cvg, int *itNum); 30 | 31 | void init(float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, int nLab); 32 | 33 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, float cc, int lbl_id); 34 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps, int lbl_id); 35 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps, int lbl_id); 36 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps, int lbl_id); 37 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz, int lbl_id); 38 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz, int lbl_id); 39 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz, int lbl_id); 40 | void updateBZ(float *bz, float *gk, int Nx, int Ny, int Nz, int lbl_id); 41 | void updatePTDIV(float *dv, float *bx, float *by, float *bz, float *ps, float *u, float *pt, float *Ct, int Nx, int Ny, int Nz, float cc, int lbl_id); 42 | float updatePSU(float *dv, float *pt, float *u, float *ps, int Nx, int Ny, int Nz, int nLab, float cc); 43 | 44 | extern void mexFunction(int iNbOut, mxArray *pmxOut[], 45 | int iNbIn, const mxArray *pmxIn[]) 46 | { 47 | /* vars in */ 48 | float *alpha, *Ct, *pars; 49 | int Nx, Ny, Nz, nLab, maxIt; 50 | float errBound, cc, steps; 51 | 52 | /* vars out */ 53 | float *u, *cvg; 54 | int *itNum; 55 | double *runTime; 56 | 57 | int dim[4]; 58 | int nDim; 59 | 60 | /* others */ 61 | time_t start_time, end_time; 62 | 63 | /* compute Max-Flow */ 64 | start_time = clock(); 65 | 66 | /* Inputs */ 67 | Ct = mxGetData(pmxIn[0]); /* bound of sink flows */ 68 | alpha = mxGetData(pmxIn[1]); /* penalty parameters */ 69 | pars = mxGetData(pmxIn[2]); /* Vector of parameters */ 70 | 71 | /* 72 | *pfVecParameters Setting 73 | * [0] : number of columns 74 | * [1] : number of rows 75 | * [2] : number of slices 76 | * [3] : number of labels 77 | * [4] : the maximum iteration number 78 | * [5] : error criterion 79 | * [6] : cc for the step-size of ALM 80 | * [7] : steps for the step-size of projected-gradient of p 81 | */ 82 | 83 | /* pars */ 84 | Ny = (int) pars[0]; 85 | Nx = (int) pars[1]; 86 | Nz = (int) pars[2]; 87 | nLab = (int) pars[3]; 88 | maxIt = (int) pars[4]; 89 | errBound = (float) pars[5]; 90 | cc = (float) pars[6]; 91 | steps = (float) pars[7]; 92 | 93 | /* Outputs */ 94 | /* outputs the computed u(x) */ 95 | dim[0] = Ny; 96 | dim[1] = Nx; 97 | dim[2] = Nz; 98 | dim[3] = nLab; 99 | nDim = 4; 100 | 101 | pmxOut[0] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 102 | u = mxGetData(pmxOut[0]); 103 | 104 | /* outputs the convergence rate */ 105 | nDim = 2; 106 | dim[0] = maxIt; 107 | dim[1] = 1; 108 | pmxOut[1] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 109 | cvg = mxGetData(pmxOut[1]); 110 | 111 | /* outputs the iteration number */ 112 | nDim = 2; 113 | dim[0] = 1; 114 | dim[1] = 1; 115 | pmxOut[2] = mxCreateNumericArray(nDim,(const int*)dim,mxUINT16_CLASS,mxREAL); 116 | itNum = mxGetData(pmxOut[2]); 117 | 118 | /* outputs the computation time */ 119 | nDim = 2; 120 | dim[0] = 1; 121 | dim[1] = 1; 122 | pmxOut[3] = mxCreateNumericArray(nDim,(const int*)dim,mxSINGLE_CLASS,mxREAL); 123 | runTime = mxGetData(pmxOut[3]); 124 | 125 | 126 | /* compute Max-Flow */ 127 | start_time = clock(); 128 | 129 | 130 | runMaxFlow(alpha, Ct, 131 | Nx, Ny, Nz, nLab, maxIt, errBound, cc, steps, 132 | u, cvg, itNum); 133 | 134 | 135 | end_time = clock(); 136 | 137 | runTime[0] = difftime(end_time, start_time)/1000000; 138 | 139 | mexPrintf("potts model max flow 3D: number of iterations = %i; time = %.4f sec\n",itNum[0],runTime[0]); 140 | 141 | } 142 | 143 | 144 | void runMaxFlow( float *alpha, float *Ct, 145 | int Nx, int Ny, int Nz, int nLab, int maxIt, 146 | float errBound, float cc, float steps, 147 | float *u, float *cvg, int *itNum){ 148 | 149 | 150 | float *bx, *by, *bz, *dv, *gk, *ps, *pt; 151 | int i; 152 | float total_err; 153 | 154 | /* alloc buffers */ 155 | bx = (float *) calloc( (unsigned)((Nx+1)*Ny*Nz*nLab), sizeof(float) ); 156 | by = (float *) calloc( (unsigned)(Nx*(Ny+1)*Nz*nLab), sizeof(float) ); 157 | bz = (float *) calloc( (unsigned)(Nx*Ny*(Nz+1)*nLab), sizeof(float) ); 158 | dv = (float *) calloc( (unsigned)(Nx*Ny*Nz*nLab), sizeof(float) ); 159 | gk = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 160 | ps = (float *) calloc( (unsigned)(Nx*Ny*Nz), sizeof(float) ); 161 | pt = (float *) calloc( (unsigned)(Nx*Ny*Nz*nLab), sizeof(float) ); 162 | if (!(bx || by || bz || dv || gk || ps || pt)) 163 | mexPrintf("malloc error.\n"); 164 | 165 | 166 | init(Ct, ps, pt, u, Nx, Ny, Nz, nLab); 167 | 168 | /* iterate */ 169 | i = 0; 170 | for (i = 0; i < maxIt; i++){ 171 | 172 | int k = 0; 173 | for (k = 0; k < nLab; k++){ 174 | 175 | /* update the spatial flow field p(x,lbl) = (bx(x,lbl),by(x,lbl)) */ 176 | updateP1(gk, dv, ps, pt, u, Nx, Ny, Nz, cc, k); 177 | updatePX(gk, bx, Nx, Ny, Nz, steps, k); 178 | updatePY(gk, by, Nx, Ny, Nz, steps, k); 179 | updatePZ(gk, bz, Nx, Ny, Nz, steps, k); 180 | 181 | /* projection step to make |p(x,i)| <= alpha(x,lbl)*/ 182 | projStep(bx, by, bz, alpha, gk, Nx, Ny, Nz, k); 183 | 184 | /* update the component bx, by */ 185 | updateBX(bx, gk, Nx, Ny, Nz, k); 186 | updateBY(by, gk, Nx, Ny, Nz, k); 187 | updateBZ(bz, gk, Nx, Ny, Nz, k); 188 | 189 | /* update the sink flow field pt(x,lbl) pd and div(x,lbl) */ 190 | updatePTDIV(dv, bx, by, bz, ps, u, pt, Ct, Nx, Ny, Nz, cc, k); 191 | } 192 | 193 | /* update ps(x) and the multiplier/labeling functions u(x,lbl) */ 194 | total_err = updatePSU(dv, pt, u, ps, Nx, Ny, Nz, nLab, cc); 195 | 196 | /* evaluate the convergence error */ 197 | cvg[i] = total_err / (float)(Nx*Ny*Nz*nLab); 198 | /*mexPrintf("it= %d, cvg = %f\n", i,cvg[i] ); */ 199 | 200 | /* check if converged */ 201 | if (cvg[i] <= errBound) 202 | break; 203 | 204 | } 205 | /* update iteration number */ 206 | itNum[0] = i; 207 | 208 | /* free mem */ 209 | free( (float *) bx ); 210 | free( (float *) by ); 211 | free( (float *) bz ); 212 | free( (float *) dv ); 213 | free( (float *) gk ); 214 | free( (float *) ps ); 215 | free( (float *) pt ); 216 | 217 | } 218 | 219 | void init(float *Ct, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, int nLab){ 220 | /* init */ 221 | int x, y, z; 222 | 223 | for (z=0; z < Nz; z++){ 224 | for (x=0; x < Nx; x++){ 225 | for (y=0; y < Ny; y++){ 226 | 227 | int g_idx = z*Nx*Ny + x*Ny + y; 228 | 229 | float minVal = 1e30; 230 | int minId = 1e9; 231 | 232 | /* find the minimum Ct(x,l) */ 233 | int l; 234 | for (l = 0; l < nLab; l++){ 235 | int l_idx = g_idx + l*Nx*Ny*Nz; 236 | 237 | if ( minVal > Ct[l_idx] ){ 238 | minVal = Ct[l_idx]; 239 | minId = l; 240 | } 241 | } 242 | 243 | /* init ps, pt, u */ 244 | ps[g_idx] = minVal; 245 | 246 | for (l = 0; l < nLab; l++){ 247 | int l_idx = g_idx + l*Nx*Ny*Nz; 248 | 249 | pt[l_idx] = ps[g_idx]; 250 | 251 | if (l == minId) 252 | u[l_idx] = 1.0f; 253 | } 254 | 255 | } 256 | } 257 | } 258 | } 259 | 260 | void updateP1(float *gk, float *dv, float *ps, float *pt, float *u, int Nx, int Ny, int Nz, float cc, int lbl_id){ 261 | 262 | int x = 0; 263 | int y = 0; 264 | int z = 0; 265 | 266 | for (z=0; z < Nz; z++){ 267 | for (x=0; x < Nx; x++){ 268 | for (y=0; y < Ny; y++){ 269 | 270 | int g_idx = z*Nx*Ny + x*Ny + y; 271 | int l_idx = g_idx + lbl_id*Nx*Ny*Nz; 272 | 273 | gk[g_idx] = dv[l_idx] - (ps[g_idx] 274 | - pt[l_idx] + u[l_idx]/cc); 275 | 276 | } 277 | } 278 | } 279 | } 280 | 281 | void updatePX(float *gk, float *bx, int Nx, int Ny, int Nz, float steps, int lbl_id){ 282 | 283 | int x = 0; 284 | int y = 0; 285 | int z = 0; 286 | for (z=0; z < Nz; z++){ 287 | for (x=1; x < Nx; x++){ 288 | for (y=0; y < Ny; y++){ 289 | 290 | 291 | int g_idx = z*Nx*Ny + x*Ny + y; 292 | int l_idx = g_idx + lbl_id*Nx*Ny*Nz; 293 | 294 | bx[l_idx] = steps*(gk[g_idx] - gk[g_idx-Ny]) + bx[l_idx]; 295 | 296 | } 297 | } 298 | } 299 | 300 | } 301 | 302 | void updatePY(float *gk, float *by, int Nx, int Ny, int Nz, float steps, int lbl_id){ 303 | 304 | int x = 0; 305 | int y = 0; 306 | int z = 0; 307 | for (z=0; z < Nz; z++){ 308 | for(x = 0; x < Nx; x ++){ 309 | for(y = 1; y < Ny; y++){ 310 | 311 | 312 | int g_idx = z*Nx*Ny + x*Ny + y; 313 | int l_idx = g_idx + lbl_id*Nx*Ny*Nz; 314 | 315 | by[l_idx] = steps*(gk[g_idx] - gk[g_idx-1]) + by[l_idx]; 316 | 317 | } 318 | } 319 | } 320 | } 321 | 322 | void updatePZ(float *gk, float *bz, int Nx, int Ny, int Nz, float steps, int lbl_id){ 323 | 324 | int x = 0; 325 | int y = 0; 326 | int z = 0; 327 | for (z=1; z < Nz; z++){ 328 | for(x = 0; x < Nx; x ++){ 329 | for(y = 0; y < Ny; y++){ 330 | 331 | 332 | int g_idx = z*Nx*Ny + x*Ny + y; 333 | int l_idx = g_idx + lbl_id*Nx*Ny*Nz; 334 | 335 | bz[l_idx] = steps*(gk[g_idx] - gk[g_idx-(Nx*Ny)]) + bz[l_idx]; 336 | } 337 | } 338 | } 339 | } 340 | 341 | 342 | void projStep(float *bx, float *by, float *bz, float *alpha, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 343 | 344 | float fpt; 345 | int x = 0; 346 | int y = 0; 347 | int z = 0; 348 | for (z=0; z < Nz; z++){ 349 | for (x=0; x< Nx; x++){ 350 | for (y=0; y< Ny; y++){ 351 | 352 | 353 | int g_idx = z*Nx*Ny + x*Ny + y; 354 | int l_idx = g_idx + lbl_id*Nx*Ny*Nz; 355 | 356 | if( alpha[l_idx] <= 0 ){ 357 | mexErrMsgTxt("alpha(x,l) must be positive. Exiting..."); 358 | } 359 | 360 | fpt = sqrt((pow(bx[l_idx+Ny],2) + pow(bx[l_idx],2) + 361 | pow(by[l_idx+1],2) + pow(by[l_idx],2) + 362 | pow(bz[l_idx+(Nx*Ny)],2) + pow(bz[l_idx],2) 363 | )*0.5); 364 | 365 | if (fpt > alpha[l_idx]) 366 | fpt = fpt / alpha[l_idx]; 367 | else 368 | fpt = 1; 369 | 370 | gk[g_idx] = 1/fpt; 371 | } 372 | } 373 | } 374 | } 375 | 376 | void updateBX(float *bx, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 377 | 378 | int g_idx; 379 | int l_idx; 380 | int x = 0; 381 | int y = 0; 382 | int z = 0; 383 | for (z=0; z < Nz; z++){ 384 | for (x=1; x< Nx; x++){ 385 | for (y=0; y< Ny; y++){ 386 | 387 | 388 | g_idx = z*Nx*Ny + x*Ny + y; 389 | l_idx = g_idx + lbl_id*Nx*Ny*Nz; 390 | 391 | bx[l_idx] = (gk[g_idx] + gk[g_idx-Ny]) 392 | *0.5*bx[l_idx]; 393 | } 394 | } 395 | } 396 | } 397 | 398 | void updateBY(float *by, float *gk, int Nx, int Ny, int Nz, int lbl_id){ 399 | 400 | int g_idx; 401 | int l_idx; 402 | int x = 0; 403 | int y = 0; 404 | int z = 0; 405 | for (z=0; z < Nz; z++){ 406 | for (x=0; x 0.5; 56 | I2 = u2 > 0.5; 57 | 58 | % visualize 59 | if (visualizationFLAG) 60 | 61 | figure(); 62 | 63 | subplot(2,2,1); imshow(Cs,[]); title('C_s') 64 | subplot(2,2,2); imshow(Ct,[]); title('C_t') 65 | 66 | % view resulting labeling functions from each implementation 67 | subplot(2,2,3); imshow(I,[0 1]); title('u_{mex}') 68 | subplot(2,2,4); imshow(I2,[0 1]); title('u_{Matlab}') 69 | 70 | disp(['Labelling error between implementations = ', num2str(sum(sum(abs(I-I2))))]); 71 | 72 | % convergence plots 73 | figure(); 74 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 75 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 76 | 77 | end 78 | colormap('gray'); 79 | 80 | end 81 | 82 | 83 | 84 | if (run3DBinaryMFTestFLAG) 85 | 86 | % alloc the source and sink capacities Cs and Ct 87 | Cs = zeros(r,c,s); 88 | Ct = zeros(r,c,s); 89 | 90 | % create random cost functions 91 | rng shuffle; 92 | Cs = rand(r,c,s); 93 | Ct = rand(r,c,s); 94 | 95 | h = fspecial('gaussian', [17 17], 1); 96 | Cs = imfilter(Cs,h); 97 | Ct = imfilter(Ct,h); 98 | 99 | % create constant pairwise costs 100 | alpha = 0.1.*ones(r,c,s); 101 | 102 | % call 3D max-flow optimizer 103 | 104 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 105 | pars = [r; c; s; maxIter; convErrBound3D; 0.25; 0.11]; 106 | 107 | % run both 3D matlab and mex implementations 108 | [u, erriter, i, timet] = asetsBinaryMF3D_mex(single(Cs), single(Ct), single(alpha), single(pars)); 109 | [u2, erriter2, i2, timet2] = asetsBinaryMF3D(Cs, Ct, alpha, pars); 110 | 111 | 112 | % threshold to discretize continuous labels 113 | I = u > 0.5; 114 | I2 = u2 > 0.5; 115 | 116 | % visualize 117 | if (visualizationFLAG) 118 | 119 | % compute mid slices in each direction 120 | vis_r = idivide(r,uint8(2)); 121 | vis_c = idivide(c,uint8(2)); 122 | vis_s = idivide(s,uint8(2)); 123 | 124 | figure(); 125 | subplot(3,4,1); imshow(squeeze(Cs(vis_r,:,:)),[]); title('C_s') 126 | subplot(3,4,2); imshow(squeeze(Ct(vis_r,:,:)),[]); title('C_t') 127 | subplot(3,4,3); imshow(squeeze(I(vis_r,:,:)),[]); title('u_{mex}') 128 | subplot(3,4,4); imshow(squeeze(I2(vis_r,:,:)),[]); title('u_{Matlab}') 129 | 130 | subplot(3,4,5); imshow(squeeze(Cs(:,vis_c,:)),[]); 131 | subplot(3,4,6); imshow(squeeze(Ct(:,vis_c,:)),[]); 132 | subplot(3,4,7); imshow(squeeze(I(:,vis_c,:)),[]); 133 | subplot(3,4,8); imshow(squeeze(I2(:,vis_c,:)),[]); 134 | 135 | subplot(3,4,9); imshow(squeeze(Cs(:,:,vis_s)),[]); 136 | subplot(3,4,10); imshow(squeeze(Ct(:,:,vis_s)),[]); 137 | subplot(3,4,11); imshow(squeeze(I(:,:,vis_s)),[]); 138 | subplot(3,4,12); imshow(squeeze(I2(:,:,vis_s)),[]); 139 | 140 | disp(['Labelling error between implementations = ', num2str(sum(sum(sum(abs(I-I2)))))]); 141 | 142 | % convergence plots 143 | figure(); 144 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 145 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 146 | 147 | end 148 | colormap('gray'); 149 | end 150 | 151 | 152 | 153 | if(run2DBinaryMF_starShapeTestFLAG) 154 | 155 | % alloc a cost function Ct for each label i, int lId 156 | Ct = zeros(r,c, numberOfLabels); 157 | alpha = zeros(r,c, numberOfLabels); 158 | 159 | % for each label assign a point the star shape is enforced towards 160 | for i=1:numberOfLabels 161 | ss_initPoints(i,:) = [randi([1 c]), randi([1 r])]; 162 | end 163 | 164 | % for each label assign a random data cost 165 | for i=1:numberOfLabels 166 | rng shuffle; 167 | Ct(:,:,i) = rand(r,c); 168 | h = fspecial('gaussian', [1 17], 1); 169 | Ct(:,:,i) = imfilter(Ct(:,:,i),h); 170 | 171 | end 172 | 173 | % for each label assign a constant regularization weight 174 | for i=1:numberOfLabels 175 | alpha(:,:,i) = (0.25).*ones(r,c); 176 | end 177 | 178 | % call max-flow optimizer 179 | % pars = [rows; columns; numberOfLabels; maxIter; convRate; cc; stepSize_s, stepSize_v]; 180 | pars = [r; c; numberOfLabels; 1000; 1e-11; 0.2; 0.16; 0.7]; 181 | 182 | % run both 2D matlab and mex implementations 183 | [u, erriter, i, timet] = asetsBinaryMF2D_starShape(Ct, alpha, pars, ss_initPoints); 184 | 185 | % maj vote to discretize continuous labels 186 | [um,I] = max(u, [], 3); 187 | 188 | % visualize 189 | if (visualizationFLAG) 190 | 191 | figure(); 192 | for i=1:(numberOfLabels) 193 | subplot(2,numberOfLabels,i); imshow(Ct(:,:,i),[]); 194 | subplot(2,numberOfLabels,i+numberOfLabels); imshow(u(:,:,i),[0 1]); 195 | end 196 | 197 | % view resulting labeling functions and init points 198 | figure(); 199 | subplot(1,2,1); imshow(I,[1 numberOfLabels]); hold on; 200 | colormap('jet'); 201 | for i=1:numberOfLabels 202 | plot(ss_initPoints(i,1),ss_initPoints(i,2),'ob', 'MarkerSize', 10, 'MarkerFaceColor','w'); 203 | text(ss_initPoints(i,1),ss_initPoints(i,2),num2str(i),... 204 | 'FontSize',8,... 205 | 'HorizontalAlignment','center'); 206 | end 207 | colorbar(); 208 | % plot convergence rate 209 | subplot(1,2,2); loglog(erriter); 210 | 211 | drawnow(); 212 | end 213 | keyboard; 214 | 215 | end 216 | 217 | 218 | end 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /tests/runTestHMFLinearModel.m: -------------------------------------------------------------------------------- 1 | function [] = runTestHMFLinearModel() 2 | 3 | % John Baxter, Robarts Research Institute, London, Ontario, 2015 4 | % Testing scripts for hierarchical max flow regularization 5 | % Test script comprises of a linear (Ishikawa) image reconstruction model 6 | 7 | % include max-flow solver 8 | addpath(['..', filesep, 'maxflow']); 9 | addpath(['..', filesep, 'lib']); 10 | 11 | alpha = 0.15; 12 | noise = 0.15; 13 | showVis = 0; 14 | 15 | %create image to denoise 16 | image = mat2gray(imread('cell.tif')); 17 | imageDenoised = image; 18 | image = image + noise*randn(size(image)); 19 | 20 | %create max-flow model 21 | numLevels = 40; 22 | endlabel = cell(1,numLevels); 23 | for i = 1:numLevels 24 | endlabel{i} = asetsHMF2D({},alpha,sqrt(abs(image-(i-1)/(numLevels-1)))); 25 | end 26 | s = asetsHMF2D(endlabel,0); 27 | tic; s.MaxFullFlow(200,0.1,1/numLevels); toc 28 | 29 | %reconstruct image 30 | accum = zeros(size(image)); 31 | for i = 1:numLevels 32 | accum = accum + endlabel{i}.u; 33 | end 34 | recon = zeros(size(image)); 35 | for i = 1:numLevels 36 | recon = recon + endlabel{i}.u .* ((i-1)/(numLevels-1)); 37 | end 38 | recon = recon ./ accum; 39 | 40 | %get norms for display 41 | maxTFlow = max(s.pt(:)); 42 | maxCFlow = 0; 43 | maxSFlow = 0; 44 | for i = 1:numLevels 45 | maxCFlow = max(maxTFlow,max(endlabel{i}.Ct(:))); 46 | maxTFlow = max(maxTFlow,max(endlabel{i}.pt(:))); 47 | maxSFlow = max( maxSFlow, ... 48 | max(max(endlabel{i}.px(:)), -min(endlabel{i}.px(:))) ); 49 | end 50 | 51 | figure(2) 52 | subplot(4,1,1); 53 | imshow(image(:,:,1),[0,1]); title('Noisy image'); 54 | subplot(4,1,2); 55 | imshow(recon(:,:,1),[0,1]); title('Reconstructed image'); 56 | subplot(4,1,3); 57 | imshow(abs(recon(:,:,1)-imageDenoised(:,:,1)),[0 1]); 58 | subplot(4,1,4); 59 | imshow(abs(recon(:,:,1)-image(:,:,1)),[0 1]); 60 | 61 | if showVis 62 | figure(1); clf; 63 | subplot(7,1+numLevels,2*(numLevels+1)+1); 64 | imshow(s.pt,[0,maxTFlow]); 65 | for i = 1:numLevels 66 | subplot(7,1+numLevels,0*(numLevels+1)+1+i); 67 | imshow(endlabel{i}.u,[0,1]); 68 | subplot(7,1+numLevels,1*(numLevels+1)+1+i); 69 | imshow(endlabel{i}.Ct,[0,maxCFlow]); 70 | subplot(7,1+numLevels,2*(numLevels+1)+1+i); 71 | imshow(endlabel{i}.pt,[0,maxTFlow]); 72 | subplot(7,1+numLevels,3*(numLevels+1)+1+i); 73 | imshow(endlabel{i}.div,[-maxSFlow,maxSFlow]); 74 | subplot(7,1+numLevels,4*(numLevels+1)+1+i); 75 | imshow(endlabel{i}.px,[-maxSFlow,maxSFlow]); 76 | subplot(7,1+numLevels,5*(numLevels+1)+1+i); 77 | imshow(endlabel{i}.py,[-maxSFlow,maxSFlow]); 78 | subplot(7,1+numLevels,6*(numLevels+1)+1+i); 79 | imshow(endlabel{i}.g,[0 1]); 80 | end 81 | end 82 | 83 | end -------------------------------------------------------------------------------- /tests/runTestIshikawa.m: -------------------------------------------------------------------------------- 1 | function [] = runTestIshikawa() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Testing scripts for Ishikawa model regularization 5 | 6 | close all; 7 | clear all; 8 | 9 | addpath(['..', filesep, 'maxflow']); 10 | addpath(['..', filesep, 'lib']); 11 | 12 | % flags 13 | run2DIshikawaTestFLAG = 1; 14 | run3DIshikawaTestFLAG = 1; 15 | 16 | visualizationFLAG = 1; 17 | 18 | % parameters 19 | numberOfLabels = 6; 20 | r = 64; % number of rows 21 | c = 64; % number of columns 22 | s = 10; % number of slices 23 | 24 | maxIter = 500; % maximum number of max flow iterations 25 | convErrBound2D = 1e-11; % bound at which the max flow is considered converged 26 | convErrBound3D = 1e-11; % bound at which the max flow is considered converged 27 | 28 | if (run2DIshikawaTestFLAG) 29 | 30 | % alloc a cost function Ct for each label i, int lId 31 | Ct = zeros(r,c, numberOfLabels); 32 | alpha = zeros(r,c, numberOfLabels-1); 33 | 34 | % for each label assign a random data cost 35 | for i=1:numberOfLabels 36 | rng shuffle; 37 | Ct(:,:,i) = rand(r,c); 38 | h = fspecial('gaussian', [1 17], 1); 39 | Ct(:,:,i) = imfilter(Ct(:,:,i),h); 40 | end 41 | 42 | % for each label assign a constant regularization weight 43 | for i=1:(numberOfLabels-1) 44 | alpha(:,:,i) = (0.8/i).*ones(r,c); 45 | end 46 | 47 | % call max-flow optimizer 48 | % pars = [rows; columns; numberOfLabels; maxIter; convRate; cc; stepSize]; 49 | pars = [r; c; numberOfLabels; maxIter; convErrBound2D; 0.75; 0.16]; 50 | 51 | % run both 2D matlab and mex implementations 52 | [u, erriter, i, timet] = asetsIshikawa2D_mex(single(Ct), single(alpha), single(pars)); 53 | [u2, erriter2, i2, timet2] = asetsIshikawa2D(single(Ct), single(alpha), single(pars)); 54 | 55 | 56 | % threshold discretize continuous labels 57 | ut = zeros(r,c); 58 | for k=1:numberOfLabels-1 59 | ut = ut + (u(:,:,k) > 0.5); 60 | end 61 | 62 | u2t = zeros(r,c); 63 | for k=1:numberOfLabels-1 64 | u2t = u2t + (u2(:,:,k) > 0.5); 65 | end 66 | 67 | 68 | % visualize 69 | if (visualizationFLAG) 70 | 71 | figure(); 72 | for i=1:(numberOfLabels-1) 73 | subplot(3,numberOfLabels,i); imshow(Ct(:,:,i),[]); title(['Ct_',num2str(i)]); 74 | subplot(3,numberOfLabels,i+numberOfLabels); imshow(u(:,:,i),[0 1]); title(['mex/C: u_',num2str(i)]); 75 | subplot(3,numberOfLabels,i+2*numberOfLabels); imshow(u2(:,:,i),[0 1]); title(['Matlab/CUDA: u_',num2str(i)]); 76 | end 77 | 78 | % view resulting labeling functions from each implementation 79 | figure(); 80 | subplot(1,2,1); imshow(ut,[1 numberOfLabels]); title('mex/C: u_{discrete}'); 81 | subplot(1,2,2); imshow(u2t,[1 numberOfLabels]); title('Matlab/CUDA: u_{discrete}'); 82 | 83 | implErr = 0; 84 | for i = 1:(numberOfLabels-1) 85 | implErr = implErr + sum(sum(abs((ut == i) - (u2t == i)))); 86 | end 87 | disp(['Labeling error between implementations = ', num2str(implErr)]); 88 | 89 | % convergence plots 90 | figure(); 91 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 92 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 93 | 94 | 95 | end 96 | colormap('jet'); 97 | 98 | end 99 | 100 | 101 | 102 | if (run3DIshikawaTestFLAG) 103 | 104 | % alloc a cost function Ct for each label i, int lId 105 | Ct = zeros(r,c,s, numberOfLabels); 106 | alpha = zeros(r,c,s, numberOfLabels); 107 | 108 | % for each label assign a random data cost 109 | for i=1:numberOfLabels 110 | rng shuffle; 111 | Ct(:,:,:,i) = rand(r,c,s); 112 | h = fspecial('gaussian', [1 17], 2); 113 | for j=1:s 114 | Ct(:,:,j,i) = imfilter(Ct(:,:,j,i),h); 115 | end 116 | end 117 | 118 | % for each label assign a constant regularization weight 119 | for i=1:numberOfLabels 120 | alpha(:,:,:,i) = (0.3/i).*ones(r,c,s); 121 | end 122 | 123 | 124 | % call 3D max-flow optimizer 125 | 126 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 127 | pars = [r; c; s; numberOfLabels; maxIter; convErrBound3D; 0.75; 0.1]; 128 | 129 | % run both 3D matlab and mex implementations 130 | [u, erriter, i, timet] = asetsIshikawa3D_mex(single(Ct), single(alpha), single(pars)); 131 | [u2, erriter2, i2, timet2] = asetsIshikawa3D(single(Ct), single(alpha), single(pars)); 132 | 133 | 134 | % threshold discretize continuous labels 135 | ut = zeros(r,c,s); 136 | for k=1:numberOfLabels-1 137 | ut = ut + (u(:,:,:,k) > 0.5); 138 | end 139 | 140 | u2t = zeros(r,c,s); 141 | for k=1:numberOfLabels-1 142 | u2t = u2t + (u2(:,:,:,k) > 0.5); 143 | end 144 | 145 | % visualize 146 | if(visualizationFLAG) 147 | 148 | % compute mid slices in each direction 149 | vis_r = idivide(r,uint8(2)); 150 | vis_c = idivide(c,uint8(2)); 151 | vis_s = idivide(s,uint8(2)); 152 | 153 | figure(); 154 | for i=1:(numberOfLabels-1) 155 | subplot(4,numberOfLabels,i); imshow(Ct(:,:,vis_s,i),[]); title(['Ct_',num2str(i)]); 156 | subplot(4,numberOfLabels,i+numberOfLabels); imshow(squeeze(u(:,:,vis_s,i)),[0 1]); title(['mex/C: u_',num2str(i)]); 157 | subplot(4,numberOfLabels,i+2*numberOfLabels); imshow(squeeze(u2(:,:,vis_s,i)),[0 1]); title(['Matlab/CUDA: u_',num2str(i)]); 158 | end 159 | 160 | % view resulting labeling functions from each implementation 161 | figure(); 162 | subplot(2,3,1); imshow(squeeze(ut(vis_r,:,:)),[1 numberOfLabels]); title('mex/C: u_{discrete}'); 163 | subplot(2,3,2); imshow(squeeze(ut(:,vis_c,:)),[1 numberOfLabels]); 164 | subplot(2,3,3); imshow(squeeze(ut(:,:,vis_s)),[1 numberOfLabels]); 165 | subplot(2,3,4); imshow(squeeze(u2t(vis_r,:,:)),[1 numberOfLabels]); title('Matlab/CUDA: u_{discrete}'); 166 | subplot(2,3,5); imshow(squeeze(u2t(:,vis_c,:)),[1 numberOfLabels]); 167 | subplot(2,3,6); imshow(squeeze(u2t(:,:,vis_s)),[1 numberOfLabels]); 168 | 169 | colormap('jet'); 170 | implErr = 0; 171 | for i = 1:(numberOfLabels-1) 172 | implErr = implErr + sum(sum(sum(abs((ut == i) - (u2t == i))))); 173 | end 174 | disp(['Labeling error between implementations = ', num2str(implErr)]); 175 | 176 | % convergence plots 177 | figure(); 178 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 179 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 180 | 181 | end 182 | end 183 | 184 | 185 | end 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /tests/runTestPotts.m: -------------------------------------------------------------------------------- 1 | function [] = runTestPotts() 2 | 3 | % Martin Rajchl, Imperial College London, 2015 4 | % Testing scripts for Potts model regularization 5 | 6 | close all; 7 | clear all; 8 | 9 | addpath(['..', filesep, 'maxflow']); 10 | addpath(['..', filesep, 'lib']); 11 | 12 | % flags 13 | run2DPottsTestFLAG = 1; 14 | run3DPottsTestFLAG = 1; 15 | run2DPotts_starShapeTestFLAG = 0; 16 | 17 | visualizationFLAG = 1; 18 | 19 | % parameters 20 | numberOfLabels = 10; 21 | r = 64; % number of rows 22 | c = 64; % number of columns 23 | s = 32; % number of slices 24 | 25 | maxIter = 300; % maximum number of iterations 26 | convErrBound2D = 1e-5; % bound at which the max flow is considered converged 27 | convErrBound3D = 1e-5; % bound at which the max flow is considered converged 28 | 29 | if (run2DPottsTestFLAG) 30 | 31 | % alloc a cost function Ct for each label i, int lId 32 | Ct = zeros(r,c, numberOfLabels); 33 | alpha = zeros(r,c, numberOfLabels); 34 | 35 | % for each label assign a random data cost 36 | for i=1:numberOfLabels 37 | rng shuffle; 38 | Ct(:,:,i) = rand(r,c); 39 | h = fspecial('gaussian', [1 17], 1); 40 | Ct(:,:,i) = imfilter(Ct(:,:,i),h); 41 | end 42 | 43 | % for each label assign a constant regularization weight 44 | for i=1:numberOfLabels 45 | alpha(:,:,i) = (0.05*i).*ones(r,c); 46 | end 47 | 48 | % call max-flow optimizer 49 | % pars = [rows; columns; numberOfLabels; maxIter; convRate; cc; stepSize]; 50 | pars = [r; c; numberOfLabels; maxIter; convErrBound2D; 0.2; 0.16]; 51 | 52 | % run both 2D matlab and mex implementations 53 | [u, erriter, i, timet] = asetsPotts2D_mex(single(Ct), single(alpha), single(pars)); 54 | [u2, erriter2, i2, timet2] = asetsPotts2D(Ct, alpha, pars); 55 | 56 | 57 | % maj vote to discretize continuous labels 58 | [um,I] = max(u, [], 3); 59 | [um,I2] = max(u2, [], 3); 60 | 61 | % visualize 62 | if (visualizationFLAG) 63 | 64 | figure(); 65 | for i=1:(numberOfLabels) 66 | subplot(2,numberOfLabels,i); imshow(Ct(:,:,i),[]); title(['Ct_{',num2str(i),'}']); 67 | subplot(2,numberOfLabels,i+numberOfLabels); imshow(u(:,:,i),[0 max(u(:))]); title(['u_{',num2str(i),'}']); 68 | end 69 | 70 | % view resulting labeling functions from each implementation 71 | figure(); 72 | subplot(1,2,1); imshow(I,[1 numberOfLabels]); title('u_{C}'); 73 | subplot(1,2,2); imshow(I2,[1 numberOfLabels]); title('u_{Matlab}'); 74 | 75 | implErr = 0; 76 | for i = 1:numberOfLabels 77 | implErr = implErr + sum(sum(abs((I == i) - (I2 == i)))); 78 | end 79 | disp(['Labeling error between implementations = ', num2str(implErr)]); 80 | 81 | % convergence plots 82 | figure(); 83 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 84 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 85 | 86 | 87 | end 88 | colormap('jet'); 89 | 90 | end 91 | 92 | 93 | 94 | if (run3DPottsTestFLAG) 95 | 96 | % alloc a cost function Ct for each label i, int lId 97 | Ct = zeros(r,c,s, numberOfLabels); 98 | alpha = zeros(r,c,s, numberOfLabels); 99 | 100 | % for each label assign a random data cost 101 | for i=1:numberOfLabels 102 | rng shuffle; 103 | Ct(:,:,:,i) = rand(r,c,s); 104 | h = fspecial('gaussian', [1 17], 2); 105 | for j=1:s 106 | Ct(:,:,j,i) = imfilter(Ct(:,:,j,i),h); 107 | end 108 | end 109 | 110 | % for each label assign a constant regularization weight 111 | for i=1:numberOfLabels 112 | alpha(:,:,:,i) = (0.025*i).*ones(r,c,s); 113 | end 114 | 115 | % call 3D max-flow optimizer 116 | 117 | % pars = [rows; columns; slices; numberOfLabels; maxIter; convRate; cc; stepSize]; 118 | pars = [r; c; s; numberOfLabels; maxIter; convErrBound3D; 0.25; 0.11]; 119 | 120 | % run both 3D matlab and mex implementations 121 | [u, erriter, i, timet] = asetsPotts3D_mex(single(Ct), single(alpha), single(pars)); 122 | [u2, erriter2, i2, timet2] = asetsPotts3D(single(Ct), single(alpha), single(pars)); 123 | 124 | 125 | % maj vote to discretize continuous labels 126 | [um,I] = max(u, [], 4); 127 | [um,I2] = max(u2, [], 4); 128 | 129 | % visualize 130 | if(visualizationFLAG) 131 | 132 | % compute mid slices in each direction 133 | vis_r = idivide(r,uint8(2)); 134 | vis_c = idivide(c,uint8(2)); 135 | vis_s = idivide(s,uint8(2)); 136 | 137 | figure(); 138 | for i=1:(numberOfLabels) 139 | subplot(4,numberOfLabels,i); imshow(Ct(:,:,vis_s,i),[]); title(['Ct_{',num2str(i),'}']); 140 | subplot(4,numberOfLabels,i+numberOfLabels); imshow(squeeze(u(vis_r,:,:,i)),[0 max(u(:))]); title(['u_{',num2str(i),'}']); 141 | subplot(4,numberOfLabels,i+2*numberOfLabels); imshow(squeeze(u(:,vis_c,:,i)),[0 max(u(:))]); 142 | subplot(4,numberOfLabels,i+3*numberOfLabels); imshow(squeeze(u(:,:,vis_s,i)),[0 max(u(:)) ]); 143 | end 144 | 145 | % view resulting labeling functions from each implementation 146 | figure(); 147 | subplot(2,3,1); imshow(squeeze(I(vis_r,:,:)),[1 numberOfLabels]); title('u_{C}'); 148 | subplot(2,3,2); imshow(squeeze(I(:,vis_c,:)),[1 numberOfLabels]); 149 | subplot(2,3,3); imshow(squeeze(I(:,:,vis_s)),[1 numberOfLabels]); 150 | subplot(2,3,4); imshow(squeeze(I2(vis_r,:,:)),[1 numberOfLabels]); title('u_{Matlab}'); 151 | subplot(2,3,5); imshow(squeeze(I2(:,vis_c,:)),[1 numberOfLabels]); 152 | subplot(2,3,6); imshow(squeeze(I2(:,:,vis_s)),[1 numberOfLabels]); 153 | 154 | colormap('jet'); 155 | implErr = 0; 156 | for i = 1:numberOfLabels 157 | implErr = implErr + sum(sum(sum(abs((I == i) - (I2 == i))))); 158 | end 159 | disp(['Labeling error between implementations = ', num2str(implErr)]); 160 | 161 | % convergence plots 162 | figure(); 163 | subplot(1,2,1); loglog(erriter); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence mex/C'); 164 | subplot(1,2,2); loglog(erriter2); xlim([1 maxIter]); ylim([min([erriter; erriter2]), max([erriter; erriter2])]); title('convergence Matlab/CUDA'); 165 | 166 | end 167 | end 168 | 169 | if(run2DPotts_starShapeTestFLAG) 170 | 171 | % alloc a cost function Ct for each label i, int lId 172 | Ct = zeros(r,c, numberOfLabels); 173 | alpha = zeros(r,c, numberOfLabels); 174 | 175 | % for each label assign a point the star shape is enforced towards 176 | for i=1:numberOfLabels 177 | ss_initPoints(i,:) = [randi([1 c]), randi([1 r])]; 178 | end 179 | 180 | % for each label assign a random data cost 181 | for i=1:numberOfLabels 182 | rng shuffle; 183 | Ct(:,:,i) = rand(r,c); 184 | h = fspecial('gaussian', [1 17], 1); 185 | Ct(:,:,i) = imfilter(Ct(:,:,i),h); 186 | 187 | end 188 | 189 | % for each label assign a constant regularization weight 190 | for i=1:numberOfLabels 191 | alpha(:,:,i) = (0.25).*ones(r,c); 192 | end 193 | 194 | % call max-flow optimizer 195 | % pars = [rows; columns; numberOfLabels; maxIter; convRate; cc; stepSize_s, stepSize_v]; 196 | pars = [r; c; numberOfLabels; 1000; 1e-11; 0.2; 0.16; 0.7]; 197 | 198 | % run both 2D matlab and mex implementations 199 | [u, erriter, i, timet] = asetsPotts2D_starShape(Ct, alpha, pars, ss_initPoints); 200 | 201 | % maj vote to discretize continuous labels 202 | [um,I] = max(u, [], 3); 203 | 204 | % visualize 205 | if (visualizationFLAG) 206 | 207 | figure(); 208 | for i=1:(numberOfLabels) 209 | subplot(2,numberOfLabels,i); imshow(Ct(:,:,i),[]); 210 | subplot(2,numberOfLabels,i+numberOfLabels); imshow(u(:,:,i),[0 1]); 211 | end 212 | 213 | % view resulting labeling functions and init points 214 | figure(); 215 | subplot(1,2,1); imshow(I,[1 numberOfLabels]); hold on; 216 | colormap('jet'); 217 | for i=1:numberOfLabels 218 | plot(ss_initPoints(i,1),ss_initPoints(i,2),'ob', 'MarkerSize', 10, 'MarkerFaceColor','w'); 219 | text(ss_initPoints(i,1),ss_initPoints(i,2),num2str(i),... 220 | 'FontSize',8,... 221 | 'HorizontalAlignment','center'); 222 | end 223 | colorbar(); 224 | % plot convergence rate 225 | subplot(1,2,2); loglog(erriter); 226 | 227 | drawnow(); 228 | end 229 | 230 | end 231 | 232 | end 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | Website: 2 | [ ] - Update to include examples and applications 3 | 4 | Applications: 5 | [x] - 3D interactive segmentation 6 | [x] - Probabilistic label map regularization 7 | [x] - Regularization of manual segmentations (Gaussian smooth, max flow) 8 | [ ] - Refactor visualization 9 | [ ] - Star-shape prior application 10 | 11 | Optimization methods: 12 | [x] - Binary Max Flow solver 13 | [x] - HMF optimizer 14 | [x] - Pseudo-Flow optimizer 15 | [ ] - Co-segmentation continuous max flow solver 16 | [x] - Ishikawa model solver 17 | [x] - CUDA optimizer for Potts 18 | [x] - CUDA optimizer for Binary MF 19 | [x] - CUDA optimizer for Ishikawa 20 | [ ] - CUDA optimizer for HMF 21 | 22 | Tests: 23 | [x] - binary max flow matlab vs. mex 24 | [x] - Potts matlab vs. mex 25 | [ ] - Full flow vs pseudo-flow 26 | [ ] - Ishikawa vs. Potts vs. HMF 27 | 28 | Tutorials: 29 | [x] - Binary image segmentation 30 | [x] - Color image segmentation 31 | [x] - Using CUDA solvers 32 | [ ] - Comparison of multi-region solvers 33 | [ ] - Image restoration 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tutorials/t01_graphCutSegmentation_binary.m: -------------------------------------------------------------------------------- 1 | %% Tutorial 01: Binary Graph cut segmentation 2 | % Martin Rajchl, Imperial College London, 2015 3 | % 4 | % [1] Yuan, J.; Bae, E.; Tai, X,-C.; 5 | % A study on continuous max-flow and min-cut approaches 6 | % IEEE CVPR, 2010 7 | % 8 | % [2] Rajchl, M.; Yuan, J.; Peters, TM. 9 | % Real-time segmentation in 4D ultrasound with 10 | % continuous max-flow 11 | % SPIE Medical Imaging 2012, 83141F-83141F-8 12 | 13 | 14 | clear all; close all; 15 | 16 | % include max-flow solver 17 | addpath(['..', filesep, 'maxflow']); 18 | addpath(['..', filesep, 'lib']); 19 | 20 | % 1. Load an 8-bit greyscale image: 21 | load('../data/natural_imgs.mat','cameraman'); 22 | img = cameraman; 23 | 24 | % 2. Normalize the image intensity to [0,1]: 25 | img = single(img); 26 | img_n = (img - min(img(:))) / (max(img(:)) - min(img(:))); 27 | 28 | % 3. Create two cost functions to model foreground (fg) and background (bg): 29 | 30 | % The costs are defined as L1 distances of the image to two intensity 31 | % values we consider descriptive of fg and bg, respectively. 32 | val_fg = 0.75; val_bg = 0.25; 33 | 34 | % compute the L1 cost terms 35 | cost_fg = abs(img_n - val_fg); 36 | cost_bg = abs(img_n - val_bg); 37 | 38 | % visualize them 39 | figure(); 40 | subplot(1,2,1); imshow(cost_fg,[]); title('cost_{fg}'); 41 | subplot(1,2,2); imshow(cost_bg,[]); title('cost_{bg}'); 42 | 43 | % 4. Construct an s-t graph: 44 | [sx, sy] = size(cost_fg); 45 | 46 | % allocate s and t links 47 | Cs = zeros(sx,sy); 48 | Ct = zeros(sx,sy); 49 | 50 | % allocate alpha(x), the regularization weight at each node x 51 | alpha = zeros(sx,sy); 52 | 53 | % 5. Assign capacities to the graph: 54 | % Assign the costs from 3. as capacities for the s-t links as data 55 | % consistency terms 56 | Cs = cost_bg; 57 | Ct = cost_fg; 58 | 59 | % Assign a regularization weight (equivalent to pairwise terms) for each 60 | % node x. Here we employ a constant regularization weight alpha. The higher 61 | % alpha is, the more smoothness penalty is assigned. 62 | alpha = 0.25.*ones(sx,sy); 63 | 64 | % 6. Set up the parameters for the max flow optimizer: 65 | % [1] graph dimension 1 66 | % [2] graph dimension 2 67 | % [3] number of maximum iterations for the optimizer (default 200) 68 | % [4] an error bound at which we consider the solver converged (default 69 | % 1e-5) 70 | % [5] c parameter of the multiplier (default 0.2) 71 | % [6] step size for the gradient descent step when calulating the spatial 72 | % flows p(x) (default 0.16) 73 | pars = [sx; sy; 200; 1e-5; 0.2; 0.16]; 74 | 75 | % 7. Call the binary max flow optimizer with Cs, Ct, alpha and pars to obtain 76 | % the continuous labelling function u, the convergence over iterations 77 | % (conv), the number of iterations (numIt) and the run time (time); 78 | [u, conv, numIt, time] = asetsBinaryMF2D(Cs, Ct, alpha, pars); 79 | 80 | % 8. Threshold the continuous labelling function u to obtain a discrete 81 | % segmentation result 82 | ut = u > 0.5; 83 | 84 | % 9. Visualize the orignial image and the segmentation 85 | figure(); 86 | subplot(1,3,1); imshow(img,[]); title('Original image'); 87 | subplot(1,3,2); imshow(u,[]); title('Segmentation: u_{continuous}'); 88 | subplot(1,3,3); imshow(ut,[]); title('Segmentation u_{discrete}'); 89 | 90 | -------------------------------------------------------------------------------- /tutorials/t02_graphCutSegmentation_potts.m: -------------------------------------------------------------------------------- 1 | %% Tutorial 02: Multi-region graph cut color segmentation with the Potts model 2 | % Martin Rajchl, Imperial College London, 2015 3 | % 4 | % [1] Yuan, J.; Bae, E.; Tai, X.-C.; Boykov, Y. 5 | % A Continuous Max-Flow Approach to Potts Model 6 | % ECCV, 2010 7 | % 8 | % [2] Baxter, JSH.; Rajchl, M.; Yuan, J.; Peters, TM. 9 | % A Continuous Max-Flow Approach to General 10 | % Hierarchical Multi-Labelling Problems 11 | % arXiv preprint arXiv:1404.0336 12 | 13 | 14 | clear all; close all; 15 | 16 | % include max-flow solver 17 | addpath(['..', filesep, 'maxflow']); 18 | addpath(['..', filesep, 'lib']); 19 | 20 | % 1. Load a color image and cast to single: 21 | load('../data/natural_imgs.mat','berkeley_color_124084'); 22 | img = berkeley_color_124084; 23 | imgs = single(img); 24 | 25 | % 2. Create N = 4 cost functions to model each of the regions: 26 | numberOfLabels = 4; 27 | [sx, sy, rgb] = size(img); 28 | 29 | % The costs are defined as L1 distances of the rgb image to N color models: 30 | model(1,:) = [225,0,0]; % red 31 | model(2,:) = [0,150,50]; % green 32 | model(3,:) = [255,225,0]; % yellow 33 | model(4,:) = [0,0,0]; % black 34 | 35 | % compute the L1 cost term for each of the N = 4 regions: 36 | cost = zeros(sx, sy, numberOfLabels,class(imgs)); 37 | for i=1:numberOfLabels 38 | cost(:,:,i) = (abs(imgs(:,:,1) - model(i,1)) + abs(imgs(:,:,2) - model(i,2)) + abs(imgs(:,:,3) - model(i,3))); 39 | end 40 | 41 | % visualize them 42 | figure(); 43 | subplot(2,3,1); imshow(img,[]); title('image'); 44 | subplot(2,3,2); imshow(cost(:,:,1),[]); title('cost_{red}'); 45 | subplot(2,3,3); imshow(cost(:,:,2),[]); title('cost_{green}'); 46 | subplot(2,3,5); imshow(cost(:,:,3),[]); title('cost_{yellow}'); 47 | subplot(2,3,6); imshow(cost(:,:,4),[]); title('cost_{black}'); 48 | 49 | % 4. Construct the multi-label graph: 50 | % allocate the sink links Ct(x) 51 | Ct = zeros(sx,sy, numberOfLabels,class(imgs)); 52 | 53 | % allocate alpha(x), the regularization weight at each node x 54 | alpha = zeros(sx,sy, numberOfLabels,class(imgs)); 55 | 56 | % 5. Assign capacities to the graph: 57 | % Since this is a multi-label graph, where the source flows ps(x) are unconstrained, 58 | % we define our sink capacities Ct(x,l) for each label l: 59 | 60 | for i=1:numberOfLabels 61 | Ct(:,:,i) = cost(:,:,i); 62 | end 63 | 64 | % Assign a regularization weight (equivalent to pairwise terms) for each 65 | % node x. Here we employ a constant regularization weight alpha(x,l). 66 | % Note, that the original Potts model does not have alpha indexed by label. 67 | % This implementation is more flexible and represents a special case of 68 | % the hierarchical max flow, the horizontal model. However, in this case 69 | % we employ the orignial Potts model to multi-region segmentation 70 | % with constant regularization. Further, the regularization weight needs to 71 | % be in a similar order of magnitude as Ct(x,l). 72 | alpha = 0.025*max(cost(:)).*ones(sx,sy,numberOfLabels,class(imgs)); 73 | 74 | % 6. Set up the parameters for the max flow optimizer: 75 | % [1] graph dimension 1 76 | % [2] graph dimension 2 77 | % [3] number of labels 78 | % [4] number of maximum iterations for the optimizer (default 300) 79 | % [5] an error bound at which we consider the solver converged (default 80 | % 1e-5) 81 | % [6] c parameter of the multiplier (default 0.2) 82 | % [7] step size for the gradient descent step when calulating the spatial 83 | % flows p(x) (default 0.16) 84 | pars = [sx; sy; numberOfLabels; 200; 1e-5; 0.2; 0.16]; 85 | 86 | % 7. Call the Potts model max flow optimizer Ct(x,l), alpha(x) and pars to obtain 87 | % the continuous labelling function u(x,l), the convergence over iterations 88 | % (conv), the number of iterations (numIt) and the run time (time); 89 | [u, conv, numIt, time] = asetsPotts2D(Ct, alpha, pars); 90 | 91 | % 8. To discretize the continuous labelling function u(x,l) we employ a 92 | % majorty vote over l to obtain a final segmentation: 93 | [tmp, idx] = max(u, [], 3); 94 | 95 | % 9. Visualize the orignial image and the segmentation with the original 96 | % color models: 97 | seg = zeros(sx, sy, rgb, 'uint8'); 98 | for x=1:sx 99 | for y=1:sy 100 | seg(x,y,:) = model(idx(x,y),:); 101 | end 102 | end 103 | figure(); 104 | subplot(1,2,1); imshow(img,[]); title('Original image'); 105 | subplot(1,2,2); imshow(seg,[]); title('Segmentation u_{discrete}'); 106 | 107 | -------------------------------------------------------------------------------- /tutorials/t03_usingCUDA.m: -------------------------------------------------------------------------------- 1 | %% Tutorial 03: Using different implentation of solvers (Matlab/C/CUDA) 2 | % Martin Rajchl, Imperial College London, 2015 3 | % 4 | % [1] Baxter, JSH.; Rajchl, M.; Yuan, J.; Peters, TM. 5 | % A Continuous Max-Flow Approach to General 6 | % Hierarchical Multi-Labelling Problems 7 | % arXiv preprint arXiv:1404.0336 8 | % 9 | % [2] Rajchl, M.; Yuan, J.; Peters, TM. 10 | % Real-time segmentation in 4D ultrasound with 11 | % continuous max-flow 12 | % SPIE Medical Imaging 2012, 83141F-83141F-8 13 | 14 | clear all; close all; 15 | 16 | % include max-flow solver 17 | addpath(['..', filesep, 'maxflow']); 18 | addpath(['..', filesep, 'lib']); 19 | 20 | % 1. Load a volume image 21 | load(['..', filesep, 'data', filesep, 'brain_1125.mat'], 'img'); 22 | 23 | [r,c,s] = size(img); 24 | 25 | % 2. Normalize the image intensity to [0,1]: 26 | img = single(img); 27 | img_n = (img - min(img(:))) / (max(img(:)) - min(img(:))); 28 | 29 | % 3. Create two cost functions as in Tutorial 01 30 | val_fg = 0.75; val_bg = 0.25; 31 | 32 | Cs = abs(img_n - val_fg); 33 | Ct = abs(img_n - val_bg); 34 | 35 | alpha = 0.05.*ones(r,c,s); 36 | 37 | % 4. Setup execution parameters: 38 | pars = [r; c; s; 200; 1e-11; 0.25; 0.11]; 39 | 40 | % 5. Run different implementations of the optimizer on the problem: 41 | 42 | % Matlab with double precision 43 | [u1, conv1, i1, time1] = asetsBinaryMF3D(double(Cs), double(Ct), double(alpha), pars); 44 | 45 | % Matlab with single precision 46 | [u2, conv2, i2, time2] = asetsBinaryMF3D(single(Cs), single(Ct), single(alpha), pars); 47 | 48 | % MEX/C implementation with single precision 49 | if(exist('../lib/asetsBinaryMF3D_mex','file')) 50 | [u3, conv3, i3, time3] = asetsBinaryMF3D_mex(single(Cs), single(Ct), single(alpha), single(pars)); 51 | else 52 | error('No compiled solvers found. Skipping computation. Please run the ./compile script and retry.'); 53 | end 54 | 55 | % Matlab-interal CUDA implementation with single precision 56 | if(gpuDeviceCount) 57 | [u4, conv4, i4, time4] = asetsBinaryMF3D(gpuArray(single(Cs)), gpuArray(single(Ct)), gpuArray(single(alpha)), pars); 58 | else 59 | error('No CUDA devices detected. Skipping computation.'); 60 | end 61 | 62 | % threshold discretize continuous labels 63 | ut1 = u1 > 0.5; 64 | ut2 = u2 > 0.5; 65 | ut3 = u3 > 0.5; 66 | ut4 = u4 > 0.5; 67 | 68 | % visualize the results 69 | figure(); slice = 45; 70 | subplot(2,5,1); imshow(img(:,:,slice),[]); title('Original image'); 71 | subplot(2,5,2); imshow(ut1(:,:,slice),[]); title(['Seg Matlab (single) in ', num2str(time1),'s.']); 72 | subplot(2,5,3); imshow(ut2(:,:,slice),[]); title(['Seg Matlab (double) in ', num2str(time2),'s.']); 73 | subplot(2,5,4); imshow(ut3(:,:,slice),[]); title(['Seg MEX/C (single) in ', num2str(time3),'s.']); 74 | subplot(2,5,5); imshow(ut4(:,:,slice),[]); title(['Seg CUDA (single) in ', num2str(time4),'s.']); 75 | 76 | subplot(2,5,7); loglog(conv1); title('Convergence Matlab (single)'); 77 | subplot(2,5,8); loglog(conv2); title('Convergence Matlab (double)'); 78 | subplot(2,5,9); loglog(conv3); title('Convergence MEX/C (single)'); 79 | subplot(2,5,10); loglog(conv4); title('Convergence CUDA (single)'); 80 | --------------------------------------------------------------------------------