# from https://www.mathworks.com/matlabcentral/fileexchange/43400-skeleton3d function FillEulerLUT() LUT=(zeros(Int8,255,1)); LUT[1]= 1; LUT[3]= -1; LUT[5]= -1; LUT[7]= 1; LUT[9]= -3; LUT[11] = -1; LUT[13] = -1; LUT[15] = 1; LUT[17] = -1; LUT[19] = 1; LUT[21] = 1; LUT[23] = -1; LUT[25] = 3; LUT[27] = 1; LUT[29] = 1; LUT[31] = -1; LUT[33] = -3; LUT[35] = -1; LUT[37] = 3; LUT[39] = 1; LUT[41] = 1; LUT[43] = -1; LUT[45] = 3; LUT[47] = 1; LUT[49] = -1; LUT[51] = 1; LUT[53] = 1; LUT[55] = -1; LUT[57] = 3; LUT[59] = 1; LUT[61] = 1; LUT[63] = -1; LUT[65] = -3; LUT[67] = 3; LUT[69] = -1; LUT[71] = 1; LUT[73] = 1; LUT[75] = 3; LUT[77] = -1; LUT[79] = 1; LUT[81] = -1; LUT[83] = 1; LUT[85] = 1; LUT[87] = -1; LUT[89] = 3; LUT[91] = 1; LUT[93] = 1; LUT[95] = -1; LUT[97] = 1; LUT[99] = 3; LUT[101] = 3; LUT[103] = 1; LUT[105] = 5; LUT[107] = 3; LUT[109] = 3; LUT[111] = 1; LUT[113] = -1; LUT[115] = 1; LUT[117] = 1; LUT[119] = -1; LUT[121] = 3; LUT[123] = 1; LUT[125] = 1; LUT[127] = -1; LUT[129] = -7; LUT[131] = -1; LUT[133] = -1; LUT[135] = 1; LUT[137] = -3; LUT[139] = -1; LUT[141] = -1; LUT[143] = 1; LUT[145] = -1; LUT[147] = 1; LUT[149] = 1; LUT[151] = -1; LUT[153] = 3; LUT[155] = 1; LUT[157] = 1; LUT[159] = -1; LUT[161] = -3; LUT[163] = -1; LUT[165] = 3; LUT[167] = 1; LUT[169] = 1; LUT[171] = -1; LUT[173] = 3; LUT[175] = 1; LUT[177] = -1; LUT[179] = 1; LUT[181] = 1; LUT[183] = -1; LUT[185] = 3; LUT[187] = 1; LUT[189] = 1; LUT[191] = -1; LUT[193] = -3; LUT[195] = 3; LUT[197] = -1; LUT[199] = 1; LUT[201] = 1; LUT[203] = 3; LUT[205] = -1; LUT[207] = 1; LUT[209] = -1; LUT[211] = 1; LUT[213] = 1; LUT[215] = -1; LUT[217] = 3; LUT[219] = 1; LUT[221] = 1; LUT[223] = -1; LUT[225] = 1; LUT[227] = 3; LUT[229] = 3; LUT[231] = 1; LUT[233] = 5; LUT[235] = 3; LUT[237] = 3; LUT[239] = 1; LUT[241] = -1; LUT[243] = 1; LUT[245] = 1; LUT[247] = -1; LUT[249] = 3; LUT[251] = 1; LUT[253] = 1; LUT[255] = -1; return LUT end function p_EulerInv(img,LUT) #img=logical, LUT=int8, EulerInv=logical if numel(LUT) > 255 display("skeleton3D:p_EulerInv:LUTwithTooManyElems","LUT with 255 elements expected"); end # Calculate Euler characteristic for each octant and sum up eulerChar = int8(zeros(size(img,1),1)); n=ones(Int8,size(img,1),1);#pre-make # Octant SWU bitorTable = uint8[128; 64; 32; 16; 8; 4; 2]; n[:]=1;#restore to ones n[img[:,25]==1] = bitor[n[img[:,25]==1], bitorTable[1]]; n[img[:,26]==1] = bitor[n[img[:,26]==1], bitorTable[2]]; n[img[:,16]==1] = bitor[n[img[:,16]==1], bitorTable[3]]; n[img[:,17]==1] = bitor[n[img[:,17]==1], bitorTable[4]]; n[img[:,22]==1] = bitor[n[img[:,22]==1], bitorTable[5]]; n[img[:,23]==1] = bitor[n[img[:,23]==1], bitorTable[6]]; n[img[:,13]==1] = bitor[n[img[:,13]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant SEU n[:]=1;#restore to ones n[img[:,27]==1] = bitor[n[img[:,27]==1], bitorTable[1]]; n[img[:,24]==1] = bitor[n[img[:,24]==1], bitorTable[2]]; n[img[:,18]==1] = bitor[n[img[:,18]==1], bitorTable[3]]; n[img[:,15]==1] = bitor[n[img[:,15]==1], bitorTable[4]]; n[img[:,26]==1] = bitor[n[img[:,26]==1], bitorTable[5]]; n[img[:,23]==1] = bitor[n[img[:,23]==1], bitorTable[6]]; n[img[:,17]==1] = bitor[n[img[:,17]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant NWU n[:]=1;#restore to ones n[img[:,19]==1] = bitor[n[img[:,19]==1], bitorTable[1]]; n[img[:,22]==1] = bitor[n[img[:,22]==1], bitorTable[2]]; n[img[:,10]==1] = bitor[n[img[:,10]==1], bitorTable[3]]; n[img[:,13]==1] = bitor[n[img[:,13]==1], bitorTable[4]]; n[img[:,20]==1] = bitor[n[img[:,20]==1], bitorTable[5]]; n[img[:,23]==1] = bitor[n[img[:,23]==1], bitorTable[6]]; n[img[:,11]==1] = bitor[n[img[:,11]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant NEU n[:]=1;#restore to ones n[img[:,21]==1] = bitor[n[img[:,21]==1], bitorTable[1]]; n[img[:,24]==1] = bitor[n[img[:,24]==1], bitorTable[2]]; n[img[:,20]==1] = bitor[n[img[:,20]==1], bitorTable[3]]; n[img[:,23]==1] = bitor[n[img[:,23]==1], bitorTable[4]]; n[img[:,12]==1] = bitor[n[img[:,12]==1], bitorTable[5]]; n[img[:,15]==1] = bitor[n[img[:,15]==1], bitorTable[6]]; n[img[:,11]==1] = bitor[n[img[:,11]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant SWB n[:]=1;#restore to ones n[img[:, 7]==1] = bitor[n[img[:, 7]==1], bitorTable[1]]; n[img[:,16]==1] = bitor[n[img[:,16]==1], bitorTable[2]]; n[img[:, 8]==1] = bitor[n[img[:, 8]==1], bitorTable[3]]; n[img[:,17]==1] = bitor[n[img[:,17]==1], bitorTable[4]]; n[img[:, 4]==1] = bitor[n[img[:, 4]==1], bitorTable[5]]; n[img[:,13]==1] = bitor[n[img[:,13]==1], bitorTable[6]]; n[img[:, 5]==1] = bitor[n[img[:, 5]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant SEB n[:]=1;#restore to ones n[img[:, 9]==1] = bitor[n[img[:, 9]==1], bitorTable[1]]; n[img[:, 8]==1] = bitor[n[img[:, 8]==1], bitorTable[2]]; n[img[:,18]==1] = bitor[n[img[:,18]==1], bitorTable[3]]; n[img[:,17]==1] = bitor[n[img[:,17]==1], bitorTable[4]]; n[img[:, 6]==1] = bitor[n[img[:, 6]==1], bitorTable[5]]; n[img[:, 5]==1] = bitor[n[img[:, 5]==1], bitorTable[6]]; n[img[:,15]==1] = bitor[n[img[:,15]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[[n]]; # Octant NWB n[:]=1;#restore to ones n[img[:, 1]==1] = bitor[n[img[:, 1]==1], bitorTable[1]]; n[img[:,10]==1] = bitor[n[img[:,10]==1], bitorTable[2]]; n[img[:, 4]==1] = bitor[n[img[:, 4]==1], bitorTable[3]]; n[img[:,13]==1] = bitor[n[img[:,13]==1], bitorTable[4]]; n[img[:, 2]==1] = bitor[n[img[:, 2]==1], bitorTable[5]]; n[img[:,11]==1] = bitor[n[img[:,11]==1], bitorTable[6]]; n[img[:, 5]==1] = bitor[n[img[:, 5]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; # Octant NEB n[:]=1;#restore to ones n[img[:, 3]==1] = bitor[n[img[:, 3]==1], bitorTable[1]]; n[img[:, 2]==1] = bitor[n[img[:, 2]==1], bitorTable[2]]; n[img[:,12]==1] = bitor[n[img[:,12]==1], bitorTable[3]]; n[img[:,11]==1] = bitor[n[img[:,11]==1], bitorTable[4]]; n[img[:, 6]==1] = bitor[n[img[:, 6]==1], bitorTable[5]]; n[img[:, 5]==1] = bitor[n[img[:, 5]==1], bitorTable[6]]; n[img[:,15]==1] = bitor[n[img[:,15]==1], bitorTable[7]]; eulerChar = eulerChar + LUT[n]; EulerInv= eulerChar==0 ; return EulerInv end function p_is_simple(N) #is_simple=logical, N=logical # copy neighbors for labeling n_p = size(N,1); # is_simple = logical(ones(n_p, 1)); ##ok<LOGL> is_simple=fill(true,n_p, 1) cube = zeros(Int8,n_p, 26); cube[:, 1:13]=N[:, 1:13]; cube[:, 14:26]=N[:,15:27]; label = 2*(ones(Int8,n_p, 1)); # for all points in the neighborhood for i=1:26 idx = cube[:,i]==1 & is_simple; if sum(idx .!=0)>0 # start recursion with any octant that contains the point i if i==1 ||i==2||i==4||i==5||i==10||i==11||i==13 cube[idx,:] = p_oct_label(1, label, cube[idx,:] ); elseif i==3 ||i==6||i==12||i==14 cube[idx,:] = p_oct_label(2, label, cube[idx,:] ); elseif i==7 ||i==8||i==15||i==16 cube[idx,:] = p_oct_label(3, label, cube[idx,:] ); elseif i==9 ||i==17 cube[idx,:] = p_oct_label(4, label, cube[idx,:] ); elseif i==18 ||i==19||i==21||i==22 cube[idx,:] = p_oct_label(5, label, cube[idx,:] ); elseif i==20 ||i==23 cube[idx,:] = p_oct_label(6, label, cube[idx,:] ); elseif i==24 ||i==25 cube[idx,:] = p_oct_label(7, label, cube[idx,:] ); elseif i==26 cube[idx,:] = p_oct_label(8, label, cube[idx,:] ); end label[idx] = label[idx]+1; del_idx = label>=4; if sum(del_idx .!=0)>0 is_simple[del_idx] = false; end end end return is_simple end function p_oct_label(octant, label, cube) #cube=uint8, octant=double integer, label=uint8, cube=uint8 # check if there are points in the octant with value 1 if ( octant==1 ) # set points in this octant to current label # and recurseive labeling of adjacent octants idx = cube[:,1] == 1; if sum(idx .!=0)>0 cube[idx,1] = label[idx]; end idx = cube[:,2] == 1; if sum(idx .!=0)>0 cube[idx,2] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); end idx = cube[:,4] == 1; if sum(idx .!=0)>0 cube[idx,4] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); end idx = cube[:,5] == 1; if sum(idx .!=0)>0 cube[idx,5] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,10] == 1; if sum(idx .!=0)>0 cube[idx,10] = label[idx]; cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); end idx = cube[:,11] == 1; if sum(idx .!=0)>0 cube[idx,11] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,13] == 1; if sum(idx .!=0)>0 cube[idx,13] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end end if ( octant==2 ) idx = cube[:,2] == 1; if sum(idx .!=0)>0 cube[idx,2] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); end idx = cube[:,5] == 1; if sum(idx .!=0)>0 cube[idx,5] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,11] == 1; if sum(idx .!=0)>0 cube[idx,11] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,3] == 1; if sum(idx .!=0)>0 cube[idx,3] = label[idx]; end idx = cube[:,6] == 1; if sum(idx .!=0)>0 cube[idx,6] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,12] == 1; if sum(idx .!=0)>0 cube[idx,12] = label[idx]; cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,14] == 1; if sum(idx .!=0)>0 cube[idx,14] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end end if( octant==3 ) idx = cube[:,4] == 1; if sum(idx .!=0)>0 cube[idx,4] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); end idx = cube[:,5] == 1; if sum(idx .!=0)>0 cube[idx,5] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,13] == 1; if sum(idx .!=0)>0 cube[idx,13] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,7] == 1; if sum(idx .!=0)>0 cube[idx,7] = label[idx]; end idx = cube[:,8] == 1; if sum(idx .!=0)>0 cube[idx,8] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,15] == 1; if sum(idx .!=0)>0 cube[idx,15] = label[idx]; cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,16] == 1; if sum(idx .!=0)>0 cube[idx,16] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end end if( octant==4 ) idx = cube[:,5] == 1; if sum(idx .!=0)>0 cube[idx,5] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); end idx = cube[:,6] == 1; if sum(idx .!=0)>0 cube[idx,6] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); end idx = cube[:,14] == 1; if sum(idx .!=0)>0 cube[idx,14] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end idx = cube[:,8] == 1; if sum(idx .!=0)>0 cube[idx,8] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); end idx = cube[:,16] == 1; if sum(idx .!=0)>0 cube[idx,16] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end idx = cube[:,9] == 1; if sum(idx .!=0)>0 cube[idx,9] = label[idx]; end idx = cube[:,17] == 1; if sum(idx .!=0)>0 cube[idx,17] = label[idx]; cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end end if ( octant==5 ) idx = cube[:,10] == 1; if sum(idx .!=0)>0 cube[idx,10] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); end idx = cube[:,11] == 1; if sum(idx .!=0)>0 cube[idx,11] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,13] == 1; if sum(idx .!=0)>0 cube[idx,13] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,18] == 1; if sum(idx .!=0)>0 cube[idx,18] = label[idx]; end idx = cube[:,19] == 1; if sum(idx .!=0)>0 cube[idx,19] = label[idx]; cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,21] == 1; if sum(idx .!=0)>0 cube[idx,21] = label[idx]; cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,22] == 1; if sum(idx .!=0)>0 cube[idx,22] = label[idx]; cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end end if( octant==6 ) idx = cube[:,11] == 1; if sum(idx .!=0)>0 cube[idx,11] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); end idx = cube[:,12] == 1; if sum(idx .!=0)>0 cube[idx,12] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); end idx = cube[:,14] == 1; if sum(idx .!=0)>0 cube[idx,14] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end idx = cube[:,19] == 1; if sum(idx .!=0)>0 cube[idx,19] = label[idx]; cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); end idx = cube[:,22] == 1; if sum(idx .!=0)>0 cube[idx,22] = label[idx]; cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end idx = cube[:,20] == 1; if sum(idx .!=0)>0 cube[idx,20] = label[idx]; end idx = cube[:,23] == 1; if sum(idx .!=0)>0 cube[idx,23] = label[idx]; cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end end if( octant==7 ) idx = cube[:,13] == 1; if sum(idx .!=0)>0 cube[idx,13] = label[idx]; cube[idx,:] = p_oct_label(1,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); end idx = cube[:,15] == 1; if sum(idx .!=0)>0 cube[idx,15] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); end idx = cube[:,16] == 1; if sum(idx .!=0)>0 cube[idx,16] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(8,label[idx],cube[idx,:]); end idx = cube[:,21] == 1; if sum(idx .!=0)>0 cube[idx,21] = label[idx]; cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); end idx = cube[:,22] == 1; if sum(idx .!=0)>0 cube[idx,22] = label[idx]; cube[idx,:] = p_oct_label([5,label[idx],cube[idx,:]]); cube[idx,:] = p_oct_label([6,label[idx],cube[idx,:]]); cube[idx,:] = p_oct_label([8,label[idx],cube[idx,:]]); end idx = cube[:,24] == 1; if sum(idx .!=0)>0 cube[idx,24] = label[idx]; end idx = cube[:,25] == 1; if sum(idx .!=0)>0 cube[idx,25] = label[idx]; cube[idx,:] = p_oct_label([8,label[idx],cube[idx,:]]); end end if ( octant==8 ) idx = cube[:,14] == 1; if sum(idx .!=0)>0 cube[idx,14] = label[idx]; cube[idx,:] = p_oct_label(2,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,16] == 1; if sum(idx .!=0)>0 cube[idx,16] = label[idx]; cube[idx,:] = p_oct_label(3,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,17] == 1; if sum(idx .!=0)>0 cube[idx,17] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,22] == 1; if sum(idx .!=0)>0 cube[idx,22] = label[idx]; cube[idx,:] = p_oct_label(5,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,17] == 1; if sum(idx .!=0)>0 cube[idx,17] = label[idx]; cube[idx,:] = p_oct_label(4,label[idx],cube[idx,:]); end idx = cube[:,23] == 1; if sum(idx .!=0)>0 cube[idx,23] = label[idx]; cube[idx,:] = p_oct_label(6,label[idx],cube[idx,:]); end idx = cube[:,25] == 1; if sum(idx .!=0)>0 cube[idx,25] = label[idx]; cube[idx,:] = p_oct_label(7,label[idx],cube[idx,:]); end idx = cube[:,26] == 1; if sum(idx .!=0)>0 cube[idx,26] = label[idx]; end end return cube end function pk_get_nh(img,i,x,y,z) #nhood=logical, img=logical, i=double integer sz=size(img); # x,y,z=ind2sub(sz,i); # nhood = logical(zeros(length(i),27)); ##ok<LOGL> nhood=fill(false,length(i),27) for xx=1:3 for yy=1:3 for zz=1:3 w=sub2ind( [3 3 3],xx,yy,zz); idx = sub2ind(sz,x+xx-2,y+yy-2,z+zz-2); nhood[:,w]=img[idx]; end end end return nhood end function Skeleton3D(skel,spare) # # SKELETON3D Calculate the 3D skeleton of an arbitrary binary volume using parallel medial axis thinning. # # # # skel = SKELETON3D(img) returns the skeleton of the binary volume 'img' # # skel = SKELETON3D(img,mask) preserves foreground voxels in 'mask' # # # # MATLAB vectorized implementation of the algorithm by Lee, Kashyap and Chu # # "Building skeleton models via 3-D medial surface/axis thinning algorithms." # # Computer Vision, Graphics, and Image Processing, 56(6):462???478, 1994. # # # # Inspired by the ITK implementation by Hanno Homann # # http://hdl.handle.net/1926/1292 # # and the Fiji/ImageJ plugin by Ignacio Arganda-Carreras # # http://fiji.sc/wiki/index.php/Skeletonize3D # # # # Philip Kollmannsberger (philipk@gmx.net) # # # # For more information, see <a # # href="matlab:web('http://www.mathworks.com/matlabcentral/fileexchange/43400-skeleton3d')">Skeleton3D</a> at the MATLAB File Exchange. # #Edited to not require the IPT (by replacing the padarray function) and # #to make compatible with ML6.5 (by changing all pre-allocation that involve # #the __,'like',p syntax). # #Further edits were made to be more strict with data types. # if ~isa(skel,'logical') || sum(size(skel)>1)~=3 # error('first input must be a 3D logical array') # end # if nargin==2 # if ~isa(spare,'logical') || ~isequal(size(spare),size(skel)) # error('both inputs must be 3D logical arrays of the same size') # end # end # pad volume with zeros to avoid edge effects # tmp=skel; # skel=logical(zeros(size(tmp)+[2 2 2] )); ##ok<LOGL> # skel[2:end-1,2:end-1,2:end-1]=tmp; # if (nargin==2) # tmp=spare; # spare=logical(zeros(size(tmp)+[2 2 2] )); ##ok<LOGL> # spare[2:end-1,2:end-1,2:end-1]=tmp; # end skel=padarray(skel,Pad(1, 1,1)); # if (nargin==2) # spare=padarray(spare,[1 1 1]); # end # if (nargin==2) # spare=padarray(spare,Pad(1, 1,1)); # end # fill lookup table eulerLUT = FillEulerLUT(); width = size(skel,1); height = size(skel,2); depth = size(skel,3); unchangedBorders = 0; while (unchangedBorders < 6 ) # loop until no change for all six border types unchangedBorders = 0; for currentBorder=1:6 # loop over all 6 directions cands=fill(false,width,height,depth); ##ok<LOGL> # cands= false(width,height,depth, "like", skel); if currentBorder==4 x=2:size(skel,1); # identify border voxels as candidates cands[x,:,:]=((skel[x,:,:] - skel[x.-1,:,:]) .!= 0); elseif currentBorder==3 x=1:size(skel,1)-1; cands[x,:,:]=((skel[x,:,:] - skel[x.+1,:,:]) .!= 0); elseif currentBorder==1 y=2:size(skel,2); cands[:,y,:]=((skel[:,y,:] - skel[:,y.-1,:]) .!= 0); elseif currentBorder==2 y=1:(size(skel,2)-1); cands[:,y,:]=((skel[:,y,:] - skel[:,y.+1,:]) .!= 0); elseif currentBorder==6 z=2:size(skel,3); cands[:,:,z]=((skel[:,:,z] - skel[:,:,z.-1]) .!= 0); elseif currentBorder==5 z=1:size(skel,3)-1; cands[:,:,z]=((skel[:,:,z] - skel[:,:,z.+1]) .!= 0); end # if excluded voxels were passed, remove them from candidates # if (nargin==2) # cands = cands & ~spare; # end # # make sure all candidates are indeed foreground voxels # cands = cands[:] & skel[:]; # make sure all candidates are indeed foreground voxels cands[:]=cands[:] .==1 .& skel[:] .==1 cands1=cands[:] .==1 .& skel[:] .==1 noChange = true; if sum(cands1)>0 cands = findall(n -> n != 0, cands) x=map(x->x[1],cands) y=map(x->x[2],cands) z=map(x->x[3],cands) # get 26-neighbourhood of candidates in volume nhood = pk_get_nh(skel,cands,x,y,z); # remove all endpoints (exactly one nb) from list di1 = sum(nhood,dims=2)==2; nhood[di1,:]=[]; cands[di1]=[]; x[di1]=[]; y[di1]=[]; z[di1]=[]; # remove all non-Euler-invariant points from list di2 = p_EulerInv(nhood, eulerLUT); nhood[di2,:]=[]; cands[di2]=[]; x[di2]=[]; y[di2]=[]; z[di2]=[]; # remove all non-simple points from list di3 = p_is_simple(nhood); x[di3]=[]; y[di3]=[]; z[di3]=[]; # if any candidates left: divide into 8 independent subvolumes if (isempty(x)) x1 = logical(mod(x,2)); x2 = ~x1; y1 = logical(mod(y,2)); y2 = ~y1; z1 = logical(mod(z,2)); z2 = ~z1; ilst[1].l = x1 & y1 & z1; ilst[2].l = x2 & y1 & z1; ilst[3].l = x1 & y2 & z1; ilst[4].l = x2 & y2 & z1; ilst[5].l = x1 & y1 & z2; ilst[6].l = x2 & y1 & z2; ilst[7].l = x1 & y2 & z2; ilst[8].l = x2 & y2 & z2; #do parallel re-checking for all points in each subvolume # With euler criteria and simple point --> keep topology for i = 1:8 if any(ilst[i].l) idx = ilst[i].l; li = sub2ind( [width height depth],x[idx],y[idx],z[idx] ); skel[li]=0; # remove points nh = pk_get_nh(skel,li,x,y,z); di_rc_euler = p_EulerInv(nh, eulerLUT); di_rc = p_is_simple(nh); # Check for the simple plint criteria no_single_change = true ; if sum(di_rc)>0 # if topology changed: revert skel[li[di_rc]] = true; no_single_change = false; end # Check for the euler criteria if sum(di_rc_euler)>0 skel[li[di_rc_euler]] = true; no_single_change = false; end if no_single_change noChange = false; # at least one voxel removed end end end end end if (noChange) unchangedBorders = unchangedBorders + 1; end end end # get rid of padded zeros skel = skel[2:end-1,2:end-1,2:end-1]; return skel end