From f893206194e5088d1401a518290004a73d23b478 Mon Sep 17 00:00:00 2001 From: qiuting1995 Date: Mon, 10 Apr 2017 10:33:49 +0200 Subject: [PATCH 1/4] code 3.1&3.2 I wrote some codes in part 3.1&3.2, realize the calculation of the lowest cost path. --- inteligent_scissors/fLiveWireCalcP.m | 98 +++++++++++++++++++++++ inteligent_scissors/fLiveWireGetCostFcn.m | 21 +++++ inteligent_scissors/fLiveWireGetPath.m | 32 ++++++++ inteligent_scissors/main.m | 15 ++++ 4 files changed, 166 insertions(+) create mode 100644 inteligent_scissors/fLiveWireCalcP.m create mode 100644 inteligent_scissors/fLiveWireGetCostFcn.m create mode 100644 inteligent_scissors/fLiveWireGetPath.m create mode 100644 inteligent_scissors/main.m diff --git a/inteligent_scissors/fLiveWireCalcP.m b/inteligent_scissors/fLiveWireCalcP.m new file mode 100644 index 0000000..fee7a27 --- /dev/null +++ b/inteligent_scissors/fLiveWireCalcP.m @@ -0,0 +1,98 @@ +function [iPX, iPY] = fLiveWireCalcP(dFg, iXS, iYS, dRadius) +%FLIVEWIRECALCP Calculates the path maps in a live-wire implementation . + +iLISTMAXLENGTH = 10000; + +if nargin < 4, dRadius = 10000; end + +[iNRows, iNCols] = size(dFg); +iNPixelsToProcess = min([int32(pi.*dRadius.^2), iNRows.*iNCols]); +iNPixelsProcessed = int32(0); + +% Initialize live-wire data structures +iLI = zeros(iLISTMAXLENGTH, 3, 'int32'); % The active list coordinates (x, y, lin) +dLG = zeros(iLISTMAXLENGTH, 1); % The active list weights +iLInd = uint16(0); % The active list index: Points to the last element of iLI and dLG + +lE = false(size(dFg)); % Binary image indicating whether a pixel has peen processed or not +iPX = zeros(size(dFg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed +iPY = zeros(size(dFg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed + +% Build neighbourhoor index lookup tables for faster loop execution +[iXX, iYY] = meshgrid(1:iNCols, 1:iNRows); +iXX = int16(iXX); iYY = int16(iYY); +iNX = cat(3, iXX - 1, iXX , iXX + 1, iXX - 1, iXX + 1, iXX - 1, iXX , iXX + 1); +iNY = cat(3, iYY - 1, iYY - 1, iYY - 1, iYY , iYY , iYY + 1, iYY + 1, iYY + 1); +iNX = permute(iNX, [3 2 1]); +iNY = permute(iNY, [3 2 1]); + +iDirX = int16([1; 0; -1; 1; -1; 1; 0; -1]); +iDirY = int16([1; 1; 1; 0; 0; -1; -1; -1]); +%dWeight = [1, 0.7, 1, 0.7, 0.7, 1, 0.7, 1]; + +% Initialize active list with zero cost seed pixel. +iLInd = iLInd + 1; +iLI(iLInd, :) = [iXS, iYS, (iXS - 1).*iNRows + iYS]; +dLG(iLInd) = 0; + +% While there are still objects in the active list and pixel limit not +% reached +while (iLInd > 0) && (iNPixelsProcessed < iNPixelsToProcess) + % Determine pixel q in list with minimal cost and remove from active + % list. Mark q as processed. + [temp, iInd] = min(dLG(1:iLInd)); + iQI = iLI(iInd, :); + dQG = dLG(iInd); + iLI(iInd, :) = iLI(iLInd, :); + dLG(iInd, :) = dLG(iLInd, :); + iLInd = iLInd - 1; + + lE(iQI(2), iQI(1)) = true; + + % Generate neighbourhood of q + iN = zeros(8, 4, 'int16'); + iN(:, 1) = iNX(:, iQI(1), iQI(2)); + iN(:, 2) = iNY(:, iQI(1), iQI(2)); + iN(:, 3) = iDirX; + iN(:, 4) = iDirY; + + % Mask out pixels that are out of bounds (q is a border pixel) + lValid = (iN(:, 1) > 0) & (iN(:, 1) <= iNCols) & ... + (iN(:, 2) > 0) & (iN(:, 2) <= iNRows); + + iN = iN(lValid, :); + %dThisWeight = dWeight(lValid); + + % Loop over the neighbourhood of q + for iI = 1:size(iN, 1); + if lE(iN(iI, 2), iN(iI, 1)), continue, end % Skip if neighbourhodd pixel already processed + iR = iN(iI,:); + + iLinInd = (int32(iR(2)) - 1).*iNRows + int32(iR(1)); + % Compute the new accumulated cost to the neighbourhood pixel + dThisG = dQG + dFg(iR(2), iR(1));%.*dThisWeight(iI); + + % Check whether r is already in active list and if the + % current cost is lower than the previous + iInd = find(iLI(1:iLInd, 3) == iLinInd); + if ~isempty(iInd) + if dThisG < dLG(iInd) + dLG(iInd) = dThisG; + iPX(iR(2), iR(1)) = iR(3); + iPY(iR(2), iR(1)) = iR(4); + end + else + % If r is not in the active list, add it! + iLInd = iLInd + 1; + iLI(iLInd, :) = [int32(iR(1)), int32(iR(2)), iLinInd]; + dLG(iLInd) = dThisG; + + iPX(iR(2), iR(1)) = iR(3); + iPY(iR(2), iR(1)) = iR(4); + end + + end % FOR loop over the neighborhood of q + + iNPixelsProcessed = iNPixelsProcessed + 1; + +end % WHILE diff --git a/inteligent_scissors/fLiveWireGetCostFcn.m b/inteligent_scissors/fLiveWireGetCostFcn.m new file mode 100644 index 0000000..9899e7c --- /dev/null +++ b/inteligent_scissors/fLiveWireGetCostFcn.m @@ -0,0 +1,21 @@ +function dF = fLiveWireGetCostFcn(dImg, dWz, dWg, dWd) + +if nargin < 2, + dWz = 0.2; + dWg = 0.8; + dWd = 0.2; +end + +% Calculat the cost function +% The gradient strength cost Fg +dImg = double(dImg); +[dY, dX] = gradient(dImg); +dFg = sqrt(dX.^2 + dY.^2); +dFg = 1 - dFg./max(dFg(:)); + +% The zero-crossing cost Fz +lFz = ~edge(dImg, 'zerocross'); + +% The gradient direction Fd ?? +% The Sum: +dF = dWz.*double(lFz)+ dWg.*dFg; diff --git a/inteligent_scissors/fLiveWireGetPath.m b/inteligent_scissors/fLiveWireGetPath.m new file mode 100644 index 0000000..1527035 --- /dev/null +++ b/inteligent_scissors/fLiveWireGetPath.m @@ -0,0 +1,32 @@ +function [iX, iY] = fLiveWireGetPath(iPX, iPY, iXS, iYS) +%FLIVEWIREGETPATH Traces the cheapest path from (IXS, IYS)^T through the +%pathmaps IPX, IPY back to the seed . + +iMAXPATH = 1000; + +% Initialize the variables +iPX = int16(iPX); +iPY = int16(iPY); +iXS = int16(iXS); +iYS = int16(iYS); + +iX = zeros(iMAXPATH, 1, 'int16'); +iY = zeros(iMAXPATH, 1, 'int16'); + +iLength = 1; +iX(iLength) = iXS; +iY(iLength) = iYS; + +% While not at the seed point: march back in the direction indicated by the +% path maps iPX (x-direction) and iPY (y-direction). +while (iPX(iYS, iXS) ~= 0) || (iPY(iYS, iXS) ~= 0) % We're not at the seed + iXS = iXS + iPX(iYS, iXS); + iYS = iYS + iPY(iYS, iXS); + iLength = iLength + 1; + iX(iLength) = iXS; + iY(iLength) = iYS; +end + +% revert vectors (to make it a forward path) and don't return the seed point. +iX = iX(iLength - 1:-1:1); +iY = iY(iLength - 1:-1:1); diff --git a/inteligent_scissors/main.m b/inteligent_scissors/main.m new file mode 100644 index 0000000..54133d1 --- /dev/null +++ b/inteligent_scissors/main.m @@ -0,0 +1,15 @@ +clear; +close all; +I = imread('a.png'); +if size(I, 3) == 3, I = rgb2gray(I); end +dImg = double(I); +dF = fLiveWireGetCostFcn(dImg); % The cost function of the live-wire algorithm, see Ref +iPX = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. +iPY = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. +dX = 50; dY = 50; +iXPath = dX; iYPath = dY; +[iPX, iPY] = fLiveWireCalcP(dF, dX, dY); +dX = 100; dY = 150; +[iXPath, iYPath] = fLiveWireGetPath(iPX, iPY, dX, dY); +[iPX, iPY] = fLiveWireCalcP(dF, dX, dY); + From 24fc03152ab6294ca539d67a41204ef78e0af4cc Mon Sep 17 00:00:00 2001 From: qiuting1995 Date: Mon, 10 Apr 2017 11:57:42 +0200 Subject: [PATCH 2/4] Update fLiveWireGetCostFcn.m --- inteligent_scissors/fLiveWireGetCostFcn.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inteligent_scissors/fLiveWireGetCostFcn.m b/inteligent_scissors/fLiveWireGetCostFcn.m index 9899e7c..8812846 100644 --- a/inteligent_scissors/fLiveWireGetCostFcn.m +++ b/inteligent_scissors/fLiveWireGetCostFcn.m @@ -1,9 +1,9 @@ function dF = fLiveWireGetCostFcn(dImg, dWz, dWg, dWd) if nargin < 2, - dWz = 0.2; - dWg = 0.8; - dWd = 0.2; + dWz = 0.43; + dWg = 0.43; + dWd = 0.14; end % Calculat the cost function From 73143b38a5fb51adb28a84f488b0619d9134bd40 Mon Sep 17 00:00:00 2001 From: qiuting1995 Date: Tue, 30 May 2017 11:04:42 +0200 Subject: [PATCH 3/4] add Fd to complete the local cost last time I used Fz and Fg to calculate the l(q). this time I add Fd to calculate the l(p,q). and with l(p,q) I modify some code in other functions. --- inteligent_scissors/fLiveWireCalcP.m | 67 ++++++++++++++++++++--- inteligent_scissors/fLiveWireGetCostFcn.m | 28 +++++++--- inteligent_scissors/main.m | 9 ++- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/inteligent_scissors/fLiveWireCalcP.m b/inteligent_scissors/fLiveWireCalcP.m index fee7a27..1297b65 100644 --- a/inteligent_scissors/fLiveWireCalcP.m +++ b/inteligent_scissors/fLiveWireCalcP.m @@ -1,11 +1,11 @@ -function [iPX, iPY] = fLiveWireCalcP(dFg, iXS, iYS, dRadius) +function [iPX, iPY] = fLiveWireCalcP(dImg, iXS, iYS, dRadius) %FLIVEWIRECALCP Calculates the path maps in a live-wire implementation . iLISTMAXLENGTH = 10000; if nargin < 4, dRadius = 10000; end -[iNRows, iNCols] = size(dFg); +[iNRows, iNCols] = size(dImg); iNPixelsToProcess = min([int32(pi.*dRadius.^2), iNRows.*iNCols]); iNPixelsProcessed = int32(0); @@ -14,9 +14,9 @@ dLG = zeros(iLISTMAXLENGTH, 1); % The active list weights iLInd = uint16(0); % The active list index: Points to the last element of iLI and dLG -lE = false(size(dFg)); % Binary image indicating whether a pixel has peen processed or not -iPX = zeros(size(dFg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed -iPY = zeros(size(dFg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed +lE = false(size(dImg)); % Binary image indicating whether a pixel has peen processed or not +iPX = zeros(size(dImg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed +iPY = zeros(size(dImg), 'int8');% Vector field following the minimum cost path from each processed pixel to the seed % Build neighbourhoor index lookup tables for faster loop execution [iXX, iYY] = meshgrid(1:iNCols, 1:iNRows); @@ -30,10 +30,41 @@ iDirY = int16([1; 1; 1; 0; 0; -1; -1; -1]); %dWeight = [1, 0.7, 1, 0.7, 0.7, 1, 0.7, 1]; +%to calculate the dImgP and dImgQ(8 neighbors) +dImgP=dImg; +dImgNQ = cat(3,dImg,dImg,dImg,dImg,dImg,dImg,dImg,dImg); +%dImgNQ = permute(dImgNQ, [3 1 2]); +id = repmat({':'}, ndims(dImg), 1); +n = size(dImg, 1); +m = size(dImg, 2); +for i = 1:8 + if iDirX(i) > 0 + id{1} = [2:n 1]; + elseif iDirX(i) < 0 + id{1} = [n 1:n - 1]; + else + id{1} = [1:n]; + end + + if iDirY(i) > 0 + id{2} = [2:n 1]; + elseif iDirY(i) < 0 + id{2} = [n 1:n - 1]; + else + id{2} = [1:n]; + end + dImgNQ(:,:,i)= dImg(id{:}); +end +dF = cat(3,dImg,dImg,dImg,dImg,dImg,dImg,dImg,dImg); +%dF = permute(dF, [3 1 2]); +for i = 1:8 + dF(:,:,i) = fLiveWireGetCostFcn(dImgP,dImgNQ(:,:,i)); +end % Initialize active list with zero cost seed pixel. iLInd = iLInd + 1; iLI(iLInd, :) = [iXS, iYS, (iXS - 1).*iNRows + iYS]; dLG(iLInd) = 0; + % While there are still objects in the active list and pixel limit not % reached @@ -65,12 +96,34 @@ % Loop over the neighbourhood of q for iI = 1:size(iN, 1); - if lE(iN(iI, 2), iN(iI, 1)), continue, end % Skip if neighbourhodd pixel already processed + if lE(iN(iI, 2), iN(iI, 1)), continue, end % Skip if neighbourhood pixel already processed iR = iN(iI,:); iLinInd = (int32(iR(2)) - 1).*iNRows + int32(iR(1)); % Compute the new accumulated cost to the neighbourhood pixel - dThisG = dQG + dFg(iR(2), iR(1));%.*dThisWeight(iI); + + %dImgP=dImg; + %id = repmat({':'}, ndims(dImg), 1); + %n = size(dImg, 1); + % if iR(3) > 0 + % id{1} = [2:n 1]; + % elseif iR(3) < 0 + % id{1} = [n 1:n - 1]; + % else + % id{1} = [1:n]; + % end + % n = size(dImg, 2); + % if iR(4) > 0 + % id{2} = [2:n 1]; + % elseif iR(4) < 0 + % id{2} = [n 1:n - 1]; + % else + % id{2} = [1:n]; + % end + + % dImgQ= dImg(id{:}); + % dF = fLiveWireGetCostFcn(dImgP,dImgQ);%此处循环次数太多,时间复杂度太高,换成只计算一遍 + dThisG = dQG + dF(iR(2), iR(1),iI);%.*dThisWeight(iI); % Check whether r is already in active list and if the % current cost is lower than the previous diff --git a/inteligent_scissors/fLiveWireGetCostFcn.m b/inteligent_scissors/fLiveWireGetCostFcn.m index 8812846..d7469c5 100644 --- a/inteligent_scissors/fLiveWireGetCostFcn.m +++ b/inteligent_scissors/fLiveWireGetCostFcn.m @@ -1,21 +1,31 @@ -function dF = fLiveWireGetCostFcn(dImg, dWz, dWg, dWd) +function dF = fLiveWireGetCostFcn(dImgP,dImgQ) +%calculate the local cost of p and q -if nargin < 2, +%if nargin < 2, dWz = 0.43; dWg = 0.43; dWd = 0.14; -end +%end % Calculat the cost function % The gradient strength cost Fg -dImg = double(dImg); -[dY, dX] = gradient(dImg); -dFg = sqrt(dX.^2 + dY.^2); -dFg = 1 - dFg./max(dFg(:)); +%dImg = double(dImg); +%[dY, dX] = gradient(dImg); +%dFg = sqrt(dX.^2 + dY.^2); +%dFg = 1 - dFg./max(dFg(:)); +dImgQ = double(dImgQ); +[GmagQ, GdirQ] = imgradient(dImgQ);%it's to calculate the gradient and direction of image +dFg = 1 - GmagQ./max(GmagQ(:)); % The zero-crossing cost Fz -lFz = ~edge(dImg, 'zerocross'); +lFz = ~edge(dImgQ, 'zerocross');%zero crossing edge detector, 0 stands for "moving fast" % The gradient direction Fd ?? +dImgP = double(dImgP); +[GmagP, GdirP] = imgradient(dImgP); +GdirQ=abs(GdirQ); +GdirP=abs(GdirP); +dFd=(abs(GdirQ-pi)+abs(GdirP-pi))./pi; + % The Sum: -dF = dWz.*double(lFz)+ dWg.*dFg; +dF = dWz.*double(lFz)+ dWg.*dFg +dWd.*dFd; diff --git a/inteligent_scissors/main.m b/inteligent_scissors/main.m index 54133d1..7ac6283 100644 --- a/inteligent_scissors/main.m +++ b/inteligent_scissors/main.m @@ -3,13 +3,12 @@ I = imread('a.png'); if size(I, 3) == 3, I = rgb2gray(I); end dImg = double(I); -dF = fLiveWireGetCostFcn(dImg); % The cost function of the live-wire algorithm, see Ref +%dF = fLiveWireGetCostFcn(dImg,dImg-1); % The cost function of the live-wire algorithm, see Ref iPX = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. iPY = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. dX = 50; dY = 50; iXPath = dX; iYPath = dY; -[iPX, iPY] = fLiveWireCalcP(dF, dX, dY); -dX = 100; dY = 150; +[iPX, iPY] = fLiveWireCalcP(dImg, dX, dY); +dX = 51; dY = 55; [iXPath, iYPath] = fLiveWireGetPath(iPX, iPY, dX, dY); -[iPX, iPY] = fLiveWireCalcP(dF, dX, dY); - +[iPX, iPY] = fLiveWireCalcP(dImg, dX, dY); From 05c7f9b925eb1490bd65256bec49d2ee31201671 Mon Sep 17 00:00:00 2001 From: qiuting1995 Date: Tue, 30 May 2017 15:02:39 +0200 Subject: [PATCH 4/4] add livewire function --- inteligent_scissors/livewire.m | 263 +++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 inteligent_scissors/livewire.m diff --git a/inteligent_scissors/livewire.m b/inteligent_scissors/livewire.m new file mode 100644 index 0000000..15043db --- /dev/null +++ b/inteligent_scissors/livewire.m @@ -0,0 +1,263 @@ +function varargout = livewire(I, dRadius, dCaptureRadius) + +dPointerCross = NaN(16); +dPointerCross(8 ,1:15) = 2; +dPointerCross(1:15, 8) = 2; + +dPointerCrossMag = ... + [NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, 2, NaN, NaN, NaN, 2, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, 2, NaN, NaN, NaN, 2, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, 2, NaN, NaN, NaN, 2, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, 2, NaN, NaN, NaN, 2, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, 2, NaN, NaN, NaN, 2, 2, 2, NaN, NaN; ... + NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]; + +% Check if number of output arguments is acceptable. +if nargout == 2 || nargout > 3 + error('LIVEWIRE expects either 0, 2 or 3 output arguments.'); +end + +% Check input argument and try to get a figure/axes/image handle if no +% input image is supplied (operate on current axes). +if ~nargin + hF = get(0, 'CurrentFigure'); + if isempty(hF), error('Found no figure. Call LIVEWIRE with an image argument!'); end + + hA = get(hF, 'CurrentAxes'); + if isempty(hA), error('Found no axes. Call LIVEWIRE with an image argument!'); end + + hI = findobj(hA, 'Type', 'image'); + if isempty(hF), error('Found no image object. Call LIVEWIRE with an image argument!'); end + + dImg = double(get(hI, 'CData')); +else + if size(I, 3) == 3, I = rgb2gray(I); end + dImg = double(I); + hI = imshow(dImg, []); + hA = gca; + hF = gcf; +end + +% Define optional input parameters if omitted +if nargin < 2 + SOptions.dRadius = 200; + SOptions.dCaptureRadius = 4; +end + +% Bring figure to the front and set callbacks +figure(hF); +try + set(hF, ... + 'WindowButtonDownFcn' , @fButtonDownFcn,... + 'KeyPressFcn' , @fKeyPressFcn, ... + 'KeyReleaseFcn' , @fKeyReleaseFcn, ... + 'Pointer' , 'custom', ... + 'PointerShapeCData' , dPointerCrossMag, ... + 'PointerShapeHotSpot' , [8, 8], ... + 'DoubleBuffer' , 'on'); %'WindowButtonMotionFcn' , @fMotionFcn, ... +catch + set(hF, ... + 'WindowButtonDownFcn' , @fButtonDownFcn,... + 'KeyPressFcn' , @fKeyPressFcn, ... + 'Pointer' , 'custom', ... + 'PointerShapeCData' , dPointerCrossMag, ... + 'PointerShapeHotSpot' , [8, 8], ... + 'DoubleBuffer' , 'on'); %'WindowButtonMotionFcn' , @fMotionFcn, ... +end + +% Create the line objects (use two lines for better visibility) +hLine1 = line(... + 'Parent' , hA, ... + 'XData' , [], ... + 'YData' , [], ... + 'Clipping' , 'off', ... + 'Color' , 'g', ... + 'LineStyle' , '-', ... + 'LineWidth' , 1.5); + +hLine2 = line(... + 'Parent' , hA, ... + 'XData' , [], ... + 'YData' , [], ... + 'Clipping' , 'off', ... + 'Color' , 'r', ... + 'LineStyle' , ':', ... + 'LineWidth' , 1.5); + +% Initialize the global variables +%dF = fLiveWireGetCostFcn(dImg); % The cost function of the live-wire algorithm, see Ref. +iPX = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. +iPY = zeros(size(dImg), 'int8'); % The path map that shows the cheapest path to the sast anchor point. +dXData = []; % The x-coordinates of the path +dYData = []; % The y-coordinates of the path +iAnchorList = zeros(200, 1); % A list of the anchor point indices in dXData and dYData for undo operations +iAnchorInd = 0; % The index of the list +lRegularEnd = false; % Indicates whether path was successfully drawn or the UI was aborted. +lControl = false; % Indicates whether the control key is pressed + +uiwait(hF); + +if ~lRegularEnd + switch nargout + + case 1 + varargout{1} = false(size(dImg)); + + case 3 + varargout{1} = false(size(dImg)); + varargout{2} = []; + varargout{3} = []; + + otherwise + + end + return +end + +% Assign the return values +lMask = poly2mask(dXData, dYData, size(dImg, 1), size(dImg, 2)); +switch nargout + + case 0 + figure, imshow(lMask) + + case 1 + varargout{1} = lMask; + + case 3 + varargout{1} = lMask; + varargout{2} = dXData; + varargout{3} = dYData; + + otherwise +end + + + % Executed when the mouse button is pressed. Used to determine a + % new anchor point or to end the user interaction. + function fButtonDownFcn(hObject, eventdata) %#ok<*INUSD> eventdata is repeatedly unused + [dX, dY] = fGetAxesPos; + if ~dX, return, end + + if isempty(dXData) + % The starting point of the path is selected + dXData = dX; + dYData = dY; + else + % A new anchor point and the cheapest path to the last anchor + % point is appended to the path + [iXPath, iYPath] = fLiveWireGetPath(iPX, iPY, dX, dY); + if isempty(iXPath) + iXPath = dX; + iYPath = dY; + end + dXData = [dXData, double(iXPath(:)')]; + dYData = [dYData, double(iYPath(:)')]; + end + + iAnchorInd = iAnchorInd + 1; + iAnchorList(iAnchorInd) = length(dXData); % Save the previous path length for the undo operation + + % Update the UI and calculate the new path map iP + set([hLine1, hLine2], 'XData', dXData, 'YData', dYData); + drawnow expose + [iPX, iPY] = fLiveWireCalcP(dImg, dX, dY, SOptions.dRadius); + + % If right-click, double-click or shift-click occurred, close path + % and return. + if ~(strcmp(get(hF, 'SelectionType'), 'normal')) && ~(lControl) + [iXPath, iYPath] = fLiveWireGetPath(iPX, iPY, dXData(1), dYData(1)); + if isempty(iXPath) + iXPath = dXData(1); + iYPath = dYData(1); + end + dXData = [dXData, double(iXPath(:)')]; + dYData = [dYData, double(iYPath(:)')]; + set([hLine1, hLine2], 'XData', dXData, 'YData', dYData); + drawnow expose + set(hF, 'WindowButtonMotionFcn', '', 'WindowButtonDownFcn', '', 'KeyPressFcn', ''); + lRegularEnd = true; + uiresume(hF); + end + + end + + function [dX, dY] = fGetAxesPos() + dPos = get(hA, 'CurrentPoint'); + dXLim = get(hA, 'XLim'); + dYLim = get(hA, 'YLim'); + dX = dPos(1, 1); + dY = dPos(1, 2); + if (dX < dXLim(1)) || (dX > dXLim(2)) || ... + (dY < dYLim(1)) || (dY > dYLim(2)) + dX = 0; + dY = 0; + end + end + + function fKeyPressFcn(hObject, eventdata) + + if strcmp(eventdata.Key, 'control') + set(hF, 'PointerShapeCData', dPointerCross); + lControl = true; + end + + switch eventdata.Key + % ENTER: close the path and return + case 'return' + [iXPath, iYPath] = fLiveWireGetPath(iPX, iPY, dXData(1), dYData(1)); + if isempty(iXPath) + iXPath = dXData(1); + iYPath = dYData(1); + end + dXData = [dXData, double(iXPath(:)')]; + dYData = [dYData, double(iYPath(:)')]; + set([hLine1, hLine2], 'XData', dXData, 'YData', dYData); + drawnow expose + set(hF, 'WindowButtonMotionFcn', '', 'WindowButtonDownFcn', '', 'KeyPressFcn', ''); + lRegularEnd = true; + uiresume(hF); + + % DEL or BACKSPACE: delete path to last anchor + case {'delete', 'backspace'} + iAnchorInd = iAnchorInd - 1; + if ~iAnchorInd + return + end + + dXData = dXData(1:iAnchorList(iAnchorInd)); + dYData = dYData(1:iAnchorList(iAnchorInd)); + + set([hLine1, hLine2], 'XData', dXData, 'YData', dYData); + drawnow expose + [iPX, iPY] = fLiveWireCalcP(dImg, dXData(end), dYData(end), SOptions.dRadius); + fMotionFcn(hObject, []); + + otherwise + + end + end + + function fKeyReleaseFcn(hObject, eventdata) + if strcmp(eventdata.Key, 'control') + lControl = false; + set(hF, 'PointerShapeCData', dPointerCrossMag); + end + end + + % Closes the figure if requested. + function fCloseGUI(hObject, eventdata) %#ok <-stupid! + delete(hObject); + end + +end