diff --git a/matlab/README.md b/matlab/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b2135fe6927006832f3ce00b91d7c7d69d703067 --- /dev/null +++ b/matlab/README.md @@ -0,0 +1,69 @@ +# Maximum Curvature and Miura Matching (Cross-correlation) + +This directory contains scripts to generate references from the Matlab library +for Repeated Line Tracking (RLT) and Maximum Curvature extraction from B.Ton. +The original source code download link is here: http://ch.mathworks.com/matlabcentral/fileexchange/35716-miura-et-al-vein-extraction-methods + + +## Usage Instructions + +Make sure you have the `matlab` binary available on your path. At Idiap, this +can be done by executing: + +```sh +$ SETSHELL matlab +$ which matlab +/idiap/resource/software/matlab/stable/bin/matlab +``` + +Call `run.sh`, that will perform the maximum curvature on the provided image, +and output various control variables in various HDF5 files: + +```sh +$ run.sh ../bob/bio/vein/tests/extractors/image.hdf5 ../bob/bio/vein/tests/extractors/mask.hdf5 mc +... +``` + +Or, a quicker way, without contaminating your environment: + +```sh +$ setshell.py matlab ./run.sh ../bob/bio/vein/tests/extractors/image.hdf5 ../bob/bio/vein/tests/extractors/mask.hdf5 mc +... +``` + +The program `run.sh` calls the function `lib/maximum_curvature.m`, which +processes and dumps results to output files. + +After running, this program produces 5 files: + +* `*_kappa_matlab.hdf5`: contains the kappa variable +* `*_v_matlab.hdf5`: contains the V variable +* `*_vt_matlab.hdf5`: contains the accumulated Vt variable +* `*_cd_matlab.hdf5`: contains the Cd variable +* `*_g_matlab.hdf5`: contains the accumulated Cd variable called G (paper) +* `*_bin_matlab.hdf5:`: the final binarised results (G thresholded) + +Once you have generated the references, you can compare the Maximum Curvature +algorithm implemented in Python against the one in Matlab with the following +command: + +```sh +$ ../bin/python compare.py +Comparing kappa[0]: 2.51624726501e-14 +Comparing kappa[1]: 2.10662186414e-13 +Comparing kappa[2]: 1.09901515494e-13 +Comparing kappa[3]: 1.0902340027e-13 +Comparing V[0]: 1.04325752096e-14 +Comparing V[1]: 8.5519523202e-14 +Comparing V[2]: 9.20009110336e-05 +Comparing V[3]: 4.02339072347e-14 +Comparing Vt: 9.20009111675e-05 +Comparing Cd[0]: 1.08636347658e-13 +Comparing Cd[1]: 2.8698038577e-14 +Comparing Cd[2]: 3.40774680626e-14 +Comparing Cd[3]: 3.2892922132e-14 +Comparing G: 1.57966982699e-13 +``` + +The program displays the sum of absolute differences between the Matlab +reference and Python's. diff --git a/matlab/compare.py b/matlab/compare.py new file mode 100644 index 0000000000000000000000000000000000000000..f78bfad4e513da2d96309c49c13238c235b0f63c --- /dev/null +++ b/matlab/compare.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + +# Run this script to output a debugging comparison of the Python implementation +# against matlab references you just extracted + +import numpy +import bob.io.base +import bob.io.matlab +from bob.bio.vein.extractor import MaximumCurvature + +# Load inputs +image = bob.io.base.load('../bob/bio/vein/tests/extractors/image.hdf5') +image = image.T.astype('float64')/255. +region = bob.io.base.load('../bob/bio/vein/tests/extractors/mask.hdf5') +region = region.T.astype('bool') + +# Loads matlab references +kappa_matlab = bob.io.base.load('mc_kappa_matlab.hdf5') +kappa_matlab = kappa_matlab.transpose(2,1,0) +V_matlab = bob.io.base.load('mc_v_matlab.hdf5') +V_matlab = V_matlab.transpose(2,1,0) +Vt_matlab = bob.io.base.load('mc_vt_matlab.hdf5') +Vt_matlab = Vt_matlab.T +Cd_matlab = bob.io.base.load('mc_cd_matlab.hdf5') +Cd_matlab = Cd_matlab.transpose(2,1,0) +G_matlab = bob.io.base.load('mc_g_matlab.hdf5') +G_matlab = G_matlab.T + +# Apply Python implementation +from bob.bio.vein.extractor.MaximumCurvature import MaximumCurvature +MC = MaximumCurvature(3) + +kappa = MC.detect_valleys(image, region) #OK +V = MC.eval_vein_probabilities(kappa) #OK +Vt = V.sum(axis=2) #OK +Cd = MC.connect_centres(Vt) #OK +G = numpy.amax(Cd, axis=2) #OK + +# Compare outputs +for k in range(4): + print('Comparing kappa[%d]: %s' % (k, + numpy.abs(kappa[...,k]-kappa_matlab[...,k]).sum())) + +for k in range(4): + print('Comparing V[%d]: %s' % (k, numpy.abs(V[...,k]-V_matlab[...,k]).sum())) + +print('Comparing Vt: %s' % numpy.abs(Vt-Vt_matlab).sum()) + +for k in range(4): + print('Comparing Cd[%d]: %s' % (k, + numpy.abs(Cd[2:-3,2:-3,k]-Cd_matlab[2:-3,2:-3,k]).sum())) + +print('Comparing G: %s' % numpy.abs(G[2:-3,2:-3]-G_matlab[2:-3,2:-3]).sum()) diff --git a/matlab/lib/license.txt b/matlab/lib/license.txt new file mode 100644 index 0000000000000000000000000000000000000000..0225a2b84323c48b07c435b3fda6abc2b533eba4 --- /dev/null +++ b/matlab/lib/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2012, Bram Ton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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 + +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. diff --git a/matlab/lib/miura_match.m b/matlab/lib/miura_match.m new file mode 100644 index 0000000000000000000000000000000000000000..ccc040659f30e576eff0a3d7a8e8f19558ce9be1 --- /dev/null +++ b/matlab/lib/miura_match.m @@ -0,0 +1,52 @@ +function score = miura_match(I, R, cw, ch) +% This is the matching procedure described by Miura et al. in their paper. +% A small difference is that this matching function calculates the match +% ratio instead of the mismatch ratio. + +% Parameters: +% I - Input image +% R - Registered template image +% cw - Maximum search displacement in x-direction +% ch - Maximum search displacement in y-direction + +% Returns: +% score - Value between 0 and 0.5, larger value is better match + +% Reference: +% Feature extraction of finger vein patterns based on iterative line +% tracking and its application to personal identification +% N. Miura, A. Nagasaka, and T. Miyatake +% Syst. Comput. Japan 35 (7 June 2004), pp. 61--71 +% doi: 10.1002/scj.v35:7 + +% Author: Bram Ton <b.t.ton@alumnus.utwente.nl> +% Date: 20th December 2011 +% License: Simplified BSD License + +[h w] = size(R); % Get height and width of registered data + +% Determine value of match, just cross-correlation, see also xcorr2 +Nm = conv2(I, rot90(R(ch+1:h-ch, cw+1:w-cw),2), 'valid'); + +% Maximum value of match +[Nmm,mi] = max(Nm(:)); % (what about multiple maximum values ?) +[t0,s0] = ind2sub(size(Nm),mi); + +% Normalize +score = Nmm/(sum(sum(R(ch+1:h-ch, cw+1:w-cw))) + sum(sum(I(t0:t0+h-2*ch-1, s0:s0+w-2*cw-1)))); + +% %% Bram Test +% Ipad = zeros(h+2*ch,w+2*cw); +% Ipad(ch+1:ch+h,cw+1:cw+w) = I; +% +% Nm = conv2(Ipad, rot90(R,2), 'valid'); +% +% % Maximum value of match +% [Nmm,mi] = max(Nm(:)); % (what about multiple maximum values ?) +% [t0,s0] = ind2sub(size(Nm),mi); +% +% % Normalize +% score = Nmm/(sum(sum(R(ch+1:h-ch, cw+1:w-cw))) + sum(sum(Ipad(t0:t0+h-2*ch-1, s0:s0+w-2*cw-1)))); +% %score = max(max(normxcorr2(R(ch+1:h-ch, cw+1:w-cw),I))); + + diff --git a/matlab/lib/miura_max_curvature.m b/matlab/lib/miura_max_curvature.m new file mode 100644 index 0000000000000000000000000000000000000000..ee83ee23c59eeb1f54cdae2c0b026ab7a52b6ff3 --- /dev/null +++ b/matlab/lib/miura_max_curvature.m @@ -0,0 +1,311 @@ +function veins = miura_max_curvature(img, fvr, sigma, stem) +% Maximum curvature method + +% Parameters: +% img - Input vascular image +% fvr - Finger vein region +% sigma - Sigma used for determining derivatives +% stem - (optional) name of the stem where to save partial results + +% Returns: +% veins - Vein image + +% Reference: +% Extraction of finger-vein patterns using maximum curvature points in +% image profiles +% N. Miura, A. Nagasaka, and T. Miyatake +% IAPR conference on machine vision applications 9 (2005), pp. 347--350 + +% Author: Bram Ton <b.t.ton@alumnus.utwente.nl> +% Date: 20th December 2011 +% License: Simplified BSD License + +% Changelog: +% 2012/01/10 - Speed enhancement by rewriting diag() functions +% with linear indices. + +% Construct filter kernels +winsize = ceil(4*sigma); +[X,Y] = meshgrid(-winsize:winsize, -winsize:winsize); + +h = (1/(2*pi*sigma^2)).*exp(-(X.^2 + Y.^2)/(2*sigma^2)); +hx = (-X/(sigma^2)).*h; +hxx = ((X.^2 - sigma^2)/(sigma^4)).*h; +hy = hx'; +hyy = hxx'; +hxy = ((X.*Y)/(sigma^4)).*h; + +% Do the actual filtering +fx = imfilter(img, hx, 'replicate', 'conv'); +fxx = imfilter(img, hxx, 'replicate', 'conv'); +fy = imfilter(img, hy, 'replicate', 'conv'); +fyy = imfilter(img, hyy, 'replicate', 'conv'); +fxy = imfilter(img, hxy, 'replicate', 'conv'); +f1 = 0.5*sqrt(2)*(fx + fy); % \ +f2 = 0.5*sqrt(2)*(fx - fy); % / +f11 = 0.5*fxx + fxy + 0.5*fyy; % \\ +f22 = 0.5*fxx - fxy + 0.5*fyy; % // + +[img_h, img_w] = size(img); % Image height and width + +%% Calculate curvatures +k = zeros(img_h, img_w, 4); +k(:,:,1) = (fxx./((1 + fx.^2).^(3/2))).*fvr; % hor +k(:,:,2) = (fyy./((1 + fy.^2).^(3/2))).*fvr; % ver +k(:,:,3) = (f11./((1 + f1.^2).^(3/2))).*fvr; % \ +k(:,:,4) = (f22./((1 + f2.^2).^(3/2))).*fvr; % / + +if nargin >= 4 + hdf5write(strcat(stem, '_kappa_matlab.hdf5'), '/kappa', k); +end + +%% Scores +V = zeros(img_h, img_w, 4); +Vt = zeros(img_h, img_w); +Wr = 0; + +% Horizontal direction +bla = k(:,:,1) > 0; +for y=1:img_h + for x=1:img_w + if(bla(y,x)) + Wr = Wr + 1; + end + + if ( Wr > 0 && (x == img_w || ~bla(y,x)) ) + if (x == img_w) + % Reached edge of image + pos_end = x; + else + pos_end = x - 1; + end + + pos_start = pos_end - Wr + 1; % Start pos of concave + [~, I] = max(k(y, pos_start:pos_end,1)); + pos_max = pos_start + I - 1; + Scr = k(y,pos_max,1)*Wr; + V(y,pos_max,1) = V(y,pos_max,1) + Scr; + Vt(y,pos_max) = Vt(y,pos_max) + Scr; + Wr = 0; + end + end +end + +% Vertical direction +bla = k(:,:,2) > 0; +for x=1:img_w + for y=1:img_h + if(bla(y,x)) + Wr = Wr + 1; + end + + if ( Wr > 0 && (y == img_h || ~bla(y,x)) ) + if (x == img_h) + % Reached edge of image + pos_end = y; + else + pos_end = y - 1; + end + + pos_start = pos_end - Wr + 1; % Start pos of concave + [~, I] = max(k(pos_start:pos_end,x,2)); + pos_max = pos_start + I - 1; + Scr = k(pos_max,x,2)*Wr; + V(pos_max,x,2) = V(pos_max,x,2) + Scr; + Vt(pos_max,x) = Vt(pos_max,x) + Scr; + Wr = 0; + end + end +end + +% Direction: \ +bla = k(:,:,3) > 0; +for start=1:(img_w+img_h-1) + % Initial values + if (start <= img_w) + x = start; + y = 1; + else + x = 1; + y = start - img_w + 1; + end + done = false; + + while ~done + if(bla(y,x)) + Wr = Wr + 1; + end + + if ( Wr > 0 && (y == img_h || x == img_w || ~bla(y,x)) ) + if (y == img_h || x == img_w) + % Reached edge of image + pos_x_end = x; + pos_y_end = y; + else + pos_x_end = x - 1; + pos_y_end = y - 1; + end + pos_x_start = pos_x_end - Wr + 1; + pos_y_start = pos_y_end - Wr + 1; + + %d = diag(k(pos_y_start:pos_y_end, pos_x_start:pos_x_end, 3)); + % More efficient implementation than diag(..) + d = k(((pos_x_start-1)*img_h + pos_y_start + 2*img_w*img_h):(img_h + 1):((pos_x_end-1)*img_h + pos_y_end + 2*img_w*img_h)); + [~, I] = max(d); + pos_x_max = pos_x_start + I - 1; + pos_y_max = pos_y_start + I - 1; + Scr = k(pos_y_max,pos_x_max,3)*Wr; + V(pos_y_max,pos_x_max,3) = V(pos_y_max,pos_x_max,3) + Scr; + Vt(pos_y_max,pos_x_max) = Vt(pos_y_max,pos_x_max) + Scr; + Wr = 0; + end + + if((x == img_w) || (y == img_h)) + done = true; + else + x = x + 1; + y = y + 1; + end + end +end + +% Direction: / +bla = k(:,:,4) > 0; +for start=1:(img_w+img_h-1) + % Initial values + if (start <= img_w) + x = start; + y = img_h; + else + x = 1; + y = img_w+img_h-start; + end + done = false; + + while ~done + if(bla(y,x)) + Wr = Wr + 1; + end + + if ( Wr > 0 && (y == 1 || x == img_w || ~bla(y,x)) ) + if (y == 1 || x == img_w) + % Reached edge of image + pos_x_end = x; + pos_y_end = y; + else + pos_x_end = x - 1; + pos_y_end = y + 1; + end + pos_x_start = pos_x_end - Wr + 1; + pos_y_start = pos_y_end + Wr - 1; + + %d = diag(flipud(k(pos_y_end:pos_y_start, pos_x_start:pos_x_end, 4))); + % More efficient implementation than diag(flipud(..)) + d = k(((pos_x_start-1)*img_h + pos_y_start + 3*img_w*img_h):(img_h - 1):((pos_x_end-1)*img_h + pos_y_end + 3*img_w*img_h)); + [~, I] = max(d); + pos_x_max = pos_x_start + I - 1; + pos_y_max = pos_y_start - I + 1; + Scr = k(pos_y_max,pos_x_max,4)*Wr; + V(pos_y_max,pos_x_max,4) = V(pos_y_max,pos_x_max,4) + Scr; + Vt(pos_y_max,pos_x_max) = Vt(pos_y_max,pos_x_max) + Scr; + Wr = 0; + end + + if((x == img_w) || (y == 1)) + done = true; + else + x = x + 1; + y = y - 1; + end + end +end + + +%Vt = V(:,:,1) + V(:,:,2) + V(:,:,3) + V(:,:,4); + +if nargin >= 4 + hdf5write(strcat(stem, '_v_matlab.hdf5'), '/V', V); +end + +if nargin >= 4 + hdf5write(strcat(stem, '_vt_matlab.hdf5'), '/Vt', Vt); +end + +%% Connection of vein centres +Cd = zeros(img_h, img_w, 4); +for x=3:img_w-3 + for y=3:img_h-3 + Cd(y,x,1) = min(max(Vt(y,x+1), Vt(y,x+2)) ,... + max(Vt(y,x-1), Vt(y,x-2))); % Hor + Cd(y,x,2) = min(max(Vt(y+1,x), Vt(y+2,x)) ,... + max(Vt(y-1,x), Vt(y-2,x))); % Vert + Cd(y,x,3) = min(max(Vt(y-1,x-1),Vt(y-2,x-2)),... + max(Vt(y+1,x+1),Vt(y+2,x+2))); % \ + Cd(y,x,4) = min(max(Vt(y+1,x-1),Vt(y+2,x-2)),... + max(Vt(y-1,x+1),Vt(y-2,x+2))); % / + end +end + +if nargin >= 4 + hdf5write(strcat(stem, '_cd_matlab.hdf5'), '/Cd', Cd); +end + +veins = max(Cd,[],3); + +if nargin >= 4 + hdf5write(strcat(stem, '_g_matlab.hdf5'), '/G', veins); +end + +md = median(veins(veins>0)); +veins_bin = veins > md; + +if nargin >= 4 + hdf5write(strcat(stem, '_bin.hdf5'), '/binarised', uint8(veins_bin)); +end + +% %% Plot results +% figure('Name', 'Second order derivatives'); +% subplot(2,2,1); +% imshow(fxx, []); +% title('Horizontal'); +% subplot(2,2,2); +% imshow(fyy, []); +% title('Vertical'); +% subplot(2,2,3); +% imshow(f11, []); +% title('\'); +% subplot(2,2,4); +% imshow(f22, []); +% title('/'); +% +% figure('Name', 'Curvatures'); +% subplot(2,2,1); +% %imshow(log(k(:,:,1) + 1), []); +% imshow(k(:,:,1) > 0, []); +% title('Horizontal'); +% subplot(2,2,2); +% %imshow(log(k(:,:,2) + 1), []); +% imshow(k(:,:,2) > 0, []); +% title('Vertical'); +% subplot(2,2,3); +% %imshow(log(k(:,:,3) + 1), []); +% imshow(k(:,:,3) > 0, []); +% title('\'); +% subplot(2,2,4); +% %imshow(log(k(:,:,4) + 1), []); +% imshow(k(:,:,4) > 0, []); +% title('/'); +% +% figure('Name', 'Scores'); +% subplot(2,2,1); +% imshow(V(:,:,1)); +% title('Horizontal'); +% subplot(2,2,2); +% imshow(V(:,:,2)); +% title('Vertical'); +% subplot(2,2,3); +% imshow(V(:,:,3)); +% title('\'); +% subplot(2,2,4); +% imshow(V(:,:,3)); +% title('/'); diff --git a/matlab/lib/miura_repeated_line_tracking.m b/matlab/lib/miura_repeated_line_tracking.m new file mode 100644 index 0000000000000000000000000000000000000000..08b3eb55786c9686ef5e4a339fc2c6b16baf12a7 --- /dev/null +++ b/matlab/lib/miura_repeated_line_tracking.m @@ -0,0 +1,186 @@ +function veins = miura_repeated_line_tracking(img, fvr, iterations, r, W) +% Repeated line tracking + +% Parameters: +% img - Input vascular image +% fvr - Binary image of the finger region +% iterations - Maximum number of iterations +% r - Distance between tracking point and cross section of the profile +% W - Width of profile + +% Returns: +% veins - Vein image + +% Reference: +% Feature extraction of finger vein patterns based on repeated line +% tracking and its application to personal identification +% N. Miura, A. Nagasaka, and T. Miyatake +% Machine Vision and Applications, Volume 15, Number 4 (2004), pp. 194--203 +% doi: 10.1007/s00138-004-0149-2 + +% Author: Bram Ton <b.t.ton@alumnus.utwente.nl> +% Date: 20th December 2011 +% License: Simplified BSD License + +p_lr = 0.5; % Probability of goin left or right +p_ud = 0.25; % Probability of going up or down +% writerObj = VideoWriter('peaks.avi'); +% open(writerObj); +Tr = zeros(size(img)); % Locus space +bla = [-1,-1; -1,0; -1,1; 0,-1; 0,0; 0,1; 1,-1; 1,0; 1,1]; + +% Check if W is even +if (mod(W,2) == 0) + disp('Error: W must be odd') +end +ro = round(r*sqrt(2)/2); % r for oblique directions +hW = (W-1)/2; % half width for horz. and vert. directions +hWo = round(hW*sqrt(2)/2); % half width for oblique directions + +% Omit unreachable borders +fvr(1:r+hW,:) = 0; +fvr(end-(r+hW-1):end,:) = 0; +fvr(:,1:r+hW) = 0; +fvr(:,end-(r+hW-1):end) = 0; + +%% Uniformly distributed starting points +indices = find(fvr > 0); +a = randperm(length(indices)); +a = a(1:iterations); % Limit to number of iterations +[ys, xs] = ind2sub(size(img), indices(a)); +%ys = [200;20];% LET OP +%xs= [200;200];% LET OP + +%% Iterate through all starting points +for it = 1:size(ys,1) + xc = xs(it); % Current tracking point, x + yc = ys(it); % Current tracking point, y + + % Determine the moving-direction attributes + % Going left or right ? + if rand() >= 0.5 + Dlr = -1; % Going left + else + Dlr = 1; % Going right + end + + % Going up or down ? + if rand() >= 0.5 + Dud = -1; % Going up + else + Dud = 1; % Going down + end + + % Initialize locus-positition table Tc + Tc = false(size(img)); + + %Dlr = -1; Dud=-1;% LET OP + Vl = 1; + while Vl > 0; + %% Determine the moving candidate point set Nc + Nr = false(3); + Rnd = rand(); + %Rnd = 0.8;% LET OP + if Rnd < p_lr + % Going left or right + Nr(:,2+Dlr) = true; + elseif (Rnd >= p_lr) && (Rnd < p_lr + p_ud) + % Going up or down + Nr(2+Dud,:) = true; + else + % Going any direction + Nr = true(3); + Nr(2,2) = false; + end + + tmp = find( ~Tc(yc-1:yc+1,xc-1:xc+1) & Nr & fvr(yc-1:yc+1,xc-1:xc+1) ); + Nc =[xc + bla(tmp,1), yc + bla(tmp,2)]; + + if size(Nc,1)==0 + Vl=-1; + continue + end + + %% Detect dark line direction near current tracking point + Vdepths = zeros(size(Nc,1),1); % Valley depths + for i = 1:size(Nc,1) + % Horizontal or vertical + if Nc(i,2) == yc + % Horizontal plane + yp = Nc(i,2); + if Nc(i,1) > xc + % Right direction + xp = Nc(i,1) + r; + else + % Left direction + xp = Nc(i,1) - r; + end + + Vdepths(i) = img(yp + hW, xp) - ... + 2*img(yp,xp) + ... + img(yp - hW, xp); + elseif Nc(i,1) == xc + % Vertical plane + xp = Nc(i,1); + if Nc(i,2) > yc + % Down direction + yp = Nc(i,2) + r; + else + % Up direction + yp = Nc(i,2) - r; + end + + Vdepths(i) = img(yp, xp + hW) - ... + 2*img(yp,xp) + ... + img(yp, xp - hW); + end + + % Oblique directions + if ((Nc(i,1) > xc) && (Nc(i,2) < yc)) || ((Nc(i,1) < xc) && (Nc(i,2) > yc)) + % Diagonal, up / + if Nc(i,1) > xc && Nc(i,2) < yc + % Top right + xp = Nc(i,1) + ro; + yp = Nc(i,2) - ro; + else + % Bottom left + xp = Nc(i,1) - ro; + yp = Nc(i,2) + ro; + end + + Vdepths(i) = img(yp - hWo, xp - hWo) - ... + 2*img(yp,xp) + ... + img(yp + hWo, xp + hWo); + else + % Diagonal, down \ + if Nc(i,1) < xc && Nc(i,2) < yc + % Top left + xp = Nc(i,1) - ro; + yp = Nc(i,2) - ro; + else + % Bottom right + xp = Nc(i,1) + ro; + yp = Nc(i,2) + ro; + end + + Vdepths(i) = img(yp + hWo, xp - hWo) - ... + 2*img(yp,xp) + ... + img(yp - hWo, xp + hWo); + end + end % End search of candidates + + [~, index] = max(Vdepths); % Determine best candidate + + % Register tracking information + Tc(yc, xc) = true; + + % Increase value of tracking space + Tr(yc, xc) = Tr(yc, xc) + 1; + %writeVideo(writerObj,Tr); + + % Move tracking point + xc = Nc(index, 1); + yc = Nc(index, 2); + end +end +veins = Tr; \ No newline at end of file diff --git a/matlab/lib/miura_usage.m b/matlab/lib/miura_usage.m new file mode 100644 index 0000000000000000000000000000000000000000..51c815c993dddf069ee587fa24529cceb7fd7f64 --- /dev/null +++ b/matlab/lib/miura_usage.m @@ -0,0 +1,66 @@ +% Howto use the miura_* scripts. + +img = im2double(imread('finger.png')); % Read the image +img = imresize(img,0.5); % Downscale image + +% Get the valid region, this is a binary mask which indicates the region of +% the finger. For quick testing it is possible to use something like: +% fvr = ones(size(img)); +% The lee_region() function can be found here: +% http://www.mathworks.com/matlabcentral/fileexchange/35752-finger-region-localisation +fvr = lee_region(img,4,40); % Get finger region + +%% Extract veins using maximum curvature method +sigma = 3; % Parameter +v_max_curvature = miura_max_curvature(img,fvr,sigma); + +% Binarise the vein image +md = median(v_max_curvature(v_max_curvature>0)); +v_max_curvature_bin = v_max_curvature > md; + +%% Extract veins using repeated line tracking method +max_iterations = 3000; r=1; W=17; % Parameters +v_repeated_line = miura_repeated_line_tracking(img,fvr,max_iterations,r,W); + +% Binarise the vein image +md = median(v_repeated_line(v_repeated_line>0)); +v_repeated_line_bin = v_repeated_line > md; + +%% Match +cw = 80; ch=30; +% Note that the match score is between 0 and 0.5 +score = miura_match(double(v_repeated_line_bin), double(v_max_curvature_bin), cw, ch); +fprintf('Match score: %6.4f %%\n', score); + +%% Visualise +% Overlay the extracted veins on the original image +overlay_max_curvature = zeros([size(img) 3]); +overlay_max_curvature(:,:,1) = img; +overlay_max_curvature(:,:,2) = img + 0.4*v_max_curvature_bin; +overlay_max_curvature(:,:,3) = img; + +% Overlay the extracted veins on the original image +overlay_repeated_line = zeros([size(img) 3]); +overlay_repeated_line(:,:,1) = img; +overlay_repeated_line(:,:,2) = img + 0.4*v_repeated_line_bin; +overlay_repeated_line(:,:,3) = img; + +figure; +subplot(3,2,1) + imshow(img,[]) + title('Original captured image') +subplot(3,2,2) + imshow(fvr) + title('Detected finger region') +subplot(3,2,3) + imshow(v_max_curvature_bin) + title('Binarised veins extracted by maximum curvature method') +subplot(3,2,4) + imshow(overlay_max_curvature) + title('Maximum curvature method') +subplot(3,2,5) + imshow(v_repeated_line_bin) + title('Binarised veins extracted by repeated line tracking method') +subplot(3,2,6) + imshow(overlay_repeated_line) + title('Repeated line tracking method') \ No newline at end of file diff --git a/matlab/run.sh b/matlab/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b95ff2dd8e7cb6b8ce0d8d0883157d0e5d0842bd --- /dev/null +++ b/matlab/run.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ $# == 0 ]; then + echo "usage: $0 input_image.mat input_region.mat output_stem" + exit 1 +fi + +_matlab=`which matlab`; + +if [ -z "${_matlab}" ]; then + echo "I cannot find a matlab binary to execute, please setup first" + exit 1 +fi + +# Does some environment manipulation to get paths right before we start +_basedir="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" +_matlabpath=${_basedir}/lib +unset _basedir; +if [ -n "${MATLABPATH}" ]; then + _matlabpath=${_matlabpath}:${MATLABPATH} +fi +export MATLABPATH=${_matlabpath}; +unset _matlabpath; + +# Calls matlab with our inputs +${_matlab} -nodisplay -nosplash -nodesktop -r "image = im2double(hdf5read('${1}','/array')); region = double(hdf5read('${2}','/array')); miura_max_curvature(image, region, 3, '${3}'); quit;"