├── .gitignore ├── .DS_Store ├── code ├── .DS_Store ├── toolbox │ ├── jjcao_io │ │ ├── read_noff.m │ │ └── read_off.m │ ├── kdtree │ │ ├── kdtree_build.mexw32 │ │ ├── kdtree_build.mexw64 │ │ ├── kdtree_delete.mexw32 │ │ ├── kdtree_delete.mexw64 │ │ ├── kdtree_ball_query.mexw32 │ │ ├── kdtree_ball_query.mexw64 │ │ ├── kdtree_range_query.mexw32 │ │ ├── kdtree_range_query.mexw64 │ │ ├── kdtree_nearest_neighbor.mexw32 │ │ ├── kdtree_nearest_neighbor.mexw64 │ │ ├── kdtree_k_nearest_neighbors.mexw32 │ │ └── kdtree_k_nearest_neighbors.mexw64 │ ├── zj_fitting │ │ └── pca_fitting.m │ ├── jjcao_common │ │ └── getoptions.m │ ├── jjcao_point │ │ ├── compute_average_radius.m │ │ ├── check_face_vertex.m │ │ ├── compute_normal_mesh.m │ │ ├── compute_normals.m │ │ ├── compute_orientation_normals.m │ │ ├── compute_points_sigms_normals.m │ │ ├── compute_points_sigms_normals_two.m │ │ └── pcd_noise_point.m │ ├── jjcao_plot │ │ └── plot_mesh.m │ └── jjcao_interact │ │ └── view3d.m ├── fitting_threshold_selection.m ├── compute_normal_NWR_EACH2.m ├── compute_normal_NWR_EACH6_ExraFea.m ├── compute_normal_NWR_EACH5_ExraFea.m ├── feature_threshold_selection.m ├── GS.m ├── compute_normal_NWR_EACH4_ExraFea.m ├── normalEstimatePCV.m └── normalEstimatePCVMN.m ├── Multi-Normal_2019.pdf └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | fig/ 3 | 4 | code/toolbox/cvx\.zip 5 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/.DS_Store -------------------------------------------------------------------------------- /code/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/.DS_Store -------------------------------------------------------------------------------- /Multi-Normal_2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/Multi-Normal_2019.pdf -------------------------------------------------------------------------------- /code/toolbox/jjcao_io/read_noff.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/jjcao_io/read_noff.m -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_build.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_build.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_build.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_build.mexw64 -------------------------------------------------------------------------------- /code/toolbox/zj_fitting/pca_fitting.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/zj_fitting/pca_fitting.m -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_delete.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_delete.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_delete.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_delete.mexw64 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_ball_query.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_ball_query.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_ball_query.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_ball_query.mexw64 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_range_query.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_range_query.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_range_query.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_range_query.mexw64 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_nearest_neighbor.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_nearest_neighbor.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_nearest_neighbor.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_nearest_neighbor.mexw64 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_k_nearest_neighbors.mexw32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_k_nearest_neighbors.mexw32 -------------------------------------------------------------------------------- /code/toolbox/kdtree/kdtree_k_nearest_neighbors.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjcao/pcv/HEAD/code/toolbox/kdtree/kdtree_k_nearest_neighbors.mexw64 -------------------------------------------------------------------------------- /code/toolbox/jjcao_common/getoptions.m: -------------------------------------------------------------------------------- 1 | function v = getoptions(options, name, v, mendatory) 2 | 3 | % getoptions - retrieve options parameter 4 | % 5 | % v = getoptions(options, 'entry', v0); 6 | % is equivalent to the code: 7 | % if isfield(options, 'entry') 8 | % v = options.entry; 9 | % else 10 | % v = v0; 11 | % end 12 | % 13 | % Copyright (c) 2007 Gabriel Peyre 14 | 15 | if nargin<4 16 | mendatory = 0; 17 | end 18 | 19 | if isfield(options, name) 20 | v = eval(['options.' name ';']); 21 | elseif mendatory 22 | error(['You have to provide options.' name '.']); 23 | end -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_average_radius.m: -------------------------------------------------------------------------------- 1 | function [l sum_d] = compute_average_radius(V,number,kdtree) 2 | % compute the average edge length of the point cloud V 3 | % Input: V are the vertex and face of a mesh. 4 | % Output: l is the average radius of each point 5 | %% 6 | if nargin <2 7 | number = 2; 8 | end 9 | 10 | nSample = size(V,1); 11 | for i = 1:nSample 12 | short_edge_id = kdtree_k_nearest_neighbors(kdtree,V(i,:),number); 13 | v = repmat(V(i,:),number,1) - V(short_edge_id,:); 14 | d = sqrt(sum(v.*v,2)); 15 | sum_d(i) = sum(d)/(number - 1); 16 | end 17 | % direct link 18 | l = sum(sum_d)/(nSample); 19 | -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/check_face_vertex.m: -------------------------------------------------------------------------------- 1 | function [vertex,face] = check_face_vertex(vertex,face, options) 2 | 3 | % check_face_vertex - check that vertices and faces have the correct size 4 | % 5 | % [vertex,face] = check_face_vertex(vertex,face); 6 | % 7 | % Copyright (c) 2007 Gabriel Peyre 8 | 9 | vertex = check_size(vertex); 10 | face = check_size(face); 11 | 12 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 | function a = check_size(a) 14 | if isempty(a) 15 | return; 16 | end 17 | if size(a,1)>size(a,2) 18 | a = a'; 19 | end 20 | if size(a,1)<3 && size(a,2)==3 21 | a = a'; 22 | end 23 | if size(a,1)<=3 && size(a,2)>=3 && sum(abs(a(:,3)))==0 24 | % for flat triangles 25 | a = a'; 26 | end 27 | if size(a,1)~=3 && size(a,1)~=4 28 | error('face or vertex is not of correct size'); 29 | end 30 | -------------------------------------------------------------------------------- /code/fitting_threshold_selection.m: -------------------------------------------------------------------------------- 1 | function fitting_threshold = fitting_threshold_selection(errs,feature) 2 | 3 | feature_errs = errs(feature); 4 | errs(feature) = []; 5 | number = 50; 6 | fitting_threshold = 0; 7 | 8 | for i = 1:number 9 | num1 = min(errs) + (i-1)*(max(feature_errs) - min(errs))/number; 10 | num2 = min(errs) + i*(max(feature_errs) - min(errs))/number; 11 | y(i) = length(find(feature_errs>=num1)) - length(find(feature_errs>num2)); 12 | feature_y(i) = length(find(errs>=num1)) - length(find(errs>num2)); 13 | end 14 | y = y/sum(y);feature_y = feature_y/sum(feature_y); 15 | 16 | t = y - feature_y; 17 | for i = number-1:-1:2 18 | if t(i-1)*t(i) < 0 19 | fitting_threshold = min(errs) + i*(max(feature_errs) - min(errs))/number; 20 | break 21 | end 22 | end 23 | 24 | if fitting_threshold == 0 25 | for i = 2:number-1 26 | if y(i) ~= 0 27 | fitting_threshold = min(errs) + i*(max(feature_errs) - min(errs))/number; 28 | break 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /code/compute_normal_NWR_EACH2.m: -------------------------------------------------------------------------------- 1 | function [normal_one noncompute max_sort] = compute_normal_NWR_EACH2(points , local_W , ran_num , inner_threshold) 2 | 3 | num = size(points , 1) ; 4 | normal_one = zeros(3,1) ; 5 | max_sort = 0 ; 6 | noncompute = 0 ; 7 | x = ones(1 , 4) ; 8 | y = ones(num , 1) ; 9 | for i = 1 : ran_num 10 | ran_id = [ 1 , randperm(num - 1 , 3) + 1 ]; 11 | poins_3 = points(ran_id , :) ; 12 | 13 | mp = (x*poins_3)./4 ; 14 | points_center = points - y * mp ; 15 | tmp = points_center(ran_id , :); 16 | C = tmp'*tmp./size(poins_3 , 1); 17 | [V,~] = eig(C); 18 | fix_normal = V(:,1); 19 | 20 | dis = (points_center*fix_normal).^2; 21 | if dis(1) > inner_threshold.^2 22 | continue; 23 | end 24 | 25 | dis = exp(-dis./min( inner_threshold.^2 ) ); 26 | 27 | cur_sort = dis' * local_W * dis ; 28 | 29 | if cur_sort > max_sort 30 | max_sort = cur_sort; 31 | normal_one = fix_normal' ; 32 | end 33 | 34 | end 35 | 36 | if sum(normal_one.*normal_one) == 0 37 | normal_one = [1,0,0] ; 38 | noncompute = 1 ; 39 | end 40 | 41 | end -------------------------------------------------------------------------------- /code/compute_normal_NWR_EACH6_ExraFea.m: -------------------------------------------------------------------------------- 1 | function [normal_one noncompute max_sort dis_vect center] = compute_normal_NWR_EACH6_ExraFea(points , local_W , ran_num , inner_threshold) 2 | 3 | num = size(points , 1) ; 4 | normal_one = zeros(3,1) ; 5 | max_sort = 0 ; 6 | noncompute = 0 ; 7 | x = ones(1 , 3) ; 8 | y = ones(num , 1) ; 9 | dis_vect = zeros(num , 1) ; 10 | center = zeros(3 , 1) ; 11 | 12 | for i = 1 : ran_num 13 | ran_id = [randperm(num , 3)]; 14 | poins_3 = points(ran_id , :) ; 15 | 16 | mp = (x*poins_3)./3 ; 17 | points_center = points - y * mp ; 18 | tmp = points_center(ran_id , :); 19 | C = tmp'*tmp./size(poins_3 , 1); 20 | [V,~] = eig(C); 21 | fix_normal = V(:,1); 22 | 23 | temp_dis = (points_center*fix_normal).^2; 24 | 25 | dis = exp(-temp_dis./min( inner_threshold.^2 ) ); 26 | 27 | cur_sort = dis' * local_W * dis ; 28 | 29 | if cur_sort > max_sort 30 | max_sort = cur_sort; 31 | normal_one = fix_normal' ; 32 | dis_vect = temp_dis ; 33 | center = mp ; 34 | end 35 | 36 | end 37 | 38 | if sum(normal_one.*normal_one) == 0 39 | normal_one = [1,0,0] ; 40 | noncompute = 1 ; 41 | end 42 | -------------------------------------------------------------------------------- /code/compute_normal_NWR_EACH5_ExraFea.m: -------------------------------------------------------------------------------- 1 | function [normal_one noncompute max_sort dis_vect] = compute_normal_NWR_EACH5_ExraFea(points , local_W , ran_num , inner_threshold) 2 | 3 | num = size(points , 1) ; 4 | normal_one = zeros(3,1) ; 5 | max_sort = 0 ; 6 | noncompute = 0 ; 7 | x = ones(1 , 4) ; 8 | y = ones(num , 1) ; 9 | dis_vect = zeros(num , 1) ; 10 | 11 | for i = 1 : ran_num 12 | ran_id = [ 1 , randperm(num - 1 , 3) + 1 ]; 13 | poins_3 = points(ran_id , :) ; 14 | 15 | mp = (x*poins_3)./4 ; 16 | points_center = points - y * mp ; 17 | tmp = points_center(ran_id , :); 18 | C = tmp'*tmp./size(poins_3 , 1); 19 | [V,~] = eig(C); 20 | fix_normal = V(:,1); 21 | 22 | temp_dis = (points_center*fix_normal).^2; 23 | if temp_dis(1) > inner_threshold.^2 24 | continue; 25 | end 26 | 27 | 28 | dis = exp(-temp_dis./min( inner_threshold.^2 ) ); 29 | 30 | cur_sort = dis' * local_W * dis ; 31 | 32 | if cur_sort > max_sort 33 | max_sort = cur_sort; 34 | normal_one = fix_normal' ; 35 | dis_vect = temp_dis ; 36 | end 37 | end 38 | 39 | if sum(normal_one.*normal_one) == 0 40 | normal_one = [1,0,0] ; 41 | noncompute = 1 ; 42 | end 43 | -------------------------------------------------------------------------------- /code/feature_threshold_selection.m: -------------------------------------------------------------------------------- 1 | function [feat max_sigm_bar] = feature_threshold_selection(sigms,TP) 2 | 3 | DEBUG = TP.debug_taproot; 4 | length_taproot = 50; 5 | %% 6 | for i = 1:length_taproot 7 | taproot(i) = length(find(sigms>=((i-1)*(1/length_taproot)))) - length(find(sigms>=((i)*(1/length_taproot)))); 8 | end 9 | [m_value m_id] = max(taproot); 10 | max_sigm_bar = m_id/length_taproot; 11 | uni_taproot = taproot/m_value; 12 | y = uni_taproot'; 13 | n = length(y); 14 | e = ones(n,1); 15 | D = spdiags([e -e], 0:1, n-1, n); 16 | delt = D*y; 17 | 18 | %% solve L1 trend filtering problem 19 | % set regularization parameter 20 | lambda = 0.5; 21 | cvx_begin 22 | variable x1(n) 23 | minimize( 0.5*sum_square(y-x1)+lambda*norm(D*x1,1) ); 24 | cvx_end 25 | %% 26 | for i = (m_id+1):length_taproot-1 27 | diff_taproot = x1(i-1) - x1(i+1); 28 | if (diff_taproot < TP.sigma_threshold)&&(x1(i)<(x1(m_id)-TP.sigma_threshold)) 29 | feat = i * (1/length_taproot); 30 | break 31 | end 32 | end 33 | %% 34 | if DEBUG 35 | figure 36 | x = [1:50]*1/50; 37 | plot(x,taproot,'LineWidth',3.0); hold on 38 | plot(x,x1*m_value,'m-','LineWidth',3.0); hold on; 39 | xf = (ones(1,m_value+1) * feat * length_taproot)*1/50; 40 | yf = 0:m_value; 41 | end 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /code/toolbox/jjcao_io/read_off.m: -------------------------------------------------------------------------------- 1 | function [verts, faces, normals] = read_off(filename) 2 | 3 | fid = fopen(filename,'r'); 4 | if( fid==-1 ) 5 | error('Can''t open the file.'); 6 | return; 7 | end 8 | 9 | fgetl(fid); 10 | str = fgetl(fid); 11 | tmp = sscanf(str,'%d %d'); 12 | nverts = tmp(1); 13 | nfaces = tmp(2); 14 | str = fgetl(fid); 15 | tmp = sscanf(str,'%f %f %f'); 16 | 17 | frewind(fid); 18 | fgetl(fid); fgetl(fid); 19 | if length(tmp) < 4 % only x y z 20 | [A,cnt] = fscanf(fid,'%f %f %f', 3*nverts); 21 | if cnt~=3*nverts 22 | warning('Problem in reading vertices.'); 23 | end 24 | A = reshape(A, 3, cnt/3); 25 | verts = A'; 26 | normals = []; 27 | else % x y z nx ny nz 28 | [A,cnt] = fscanf(fid,'%f %f %f %f %f %f', 6*nverts); 29 | if cnt~=6*nverts 30 | warning('Problem in reading vertices.'); 31 | end 32 | 33 | A = reshape(A, 6, cnt/6); 34 | A = A'; 35 | verts = A(:,1:3); 36 | normals = A(:,4:6); 37 | end 38 | 39 | if nfaces > 0 40 | [A,cnt] = fscanf(fid,'%d %d %d %d\n', 4*nfaces); 41 | if cnt~=4*nfaces 42 | warning('Problem in reading faces.'); 43 | end 44 | A = reshape(A, 4, cnt/4); 45 | faces = A(2:4,:)+1; 46 | faces = faces'; 47 | else 48 | faces = []; 49 | end 50 | 51 | fclose(fid); 52 | return; -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_normal_mesh.m: -------------------------------------------------------------------------------- 1 | function [normal,normalf] = compute_normal_mesh(vertex,face) 2 | 3 | % compute_normal - compute the normal of a triangulation 4 | % 5 | % [normal,normalf] = compute_normal(vertex,face); 6 | % 7 | % normal(i,:) is the normal at vertex i. 8 | % normalf(j,:) is the normal at face j. 9 | % 10 | % changed by jjcao 2012 11 | % Copyright (c) 2004 Gabriel Peyr? 12 | 13 | [vertex,face] = check_face_vertex(vertex,face); 14 | 15 | nface = size(face,2); 16 | nvert = size(vertex,2); 17 | normal = zeros(3, nvert); 18 | 19 | % unit normals to the faces 20 | normalf = crossp( vertex(:,face(2,:))-vertex(:,face(1,:)), ... 21 | vertex(:,face(3,:))-vertex(:,face(1,:)) ); 22 | d = sqrt( sum(normalf.^2,1) ); d(d0) eps 34 | warning('GS.normalize() is wrong! the bounding box diagonal is: "%s" ', tmp); 35 | end 36 | end 37 | function [bbox, diameter] = compute_bbox(pts) 38 | bbox = [min(pts(:,1)), min(pts(:,2)), min(pts(:,3)), max(pts(:,1)), max(pts(:,2)), max(pts(:,3))]; 39 | rs = bbox(4:6)-bbox(1:3); 40 | diameter = sqrt(dot(rs,rs)); 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_normals.m: -------------------------------------------------------------------------------- 1 | function normVectors= compute_normals(pts, k_knn, kdtree) 2 | % COMPUTE_INITIAL_FEATURE - initial feature detection via PCA. 3 | % 4 | % Input: 5 | % 6 | % Output: 7 | % - 'featid' : sharp edge feature vertex id 8 | % 9 | % Reference: 10 | % (1) - Pauly, M., Keiser, R., and Gross, M.. Multi-scale feature extraction on point-sampled surface. 11 | % Computer. Graphics Forum. 2003, 22(3):281-289. 12 | % 13 | % Changed by JJCAO 14 | % Copyright (c) 2012 Wangxiaochao 2012-8-30 22:40:28 15 | 16 | DEBUG = 0; 17 | 18 | npts = size(pts,1); 19 | normVectors = zeros(npts,3); 20 | 21 | if DEBUG 22 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 23 | end 24 | for i = 1:npts 25 | knn = kdtree_k_nearest_neighbors(kdtree,pts(i,:),k_knn)'; 26 | P = pts(knn,:); 27 | mp = mean(P); 28 | tmp = P-repmat(mp,k_knn,1); 29 | C = tmp'*tmp./k_knn; 30 | if DEBUG 31 | [V, D] = eig(C); 32 | d = diag(D); 33 | 34 | [bbox, diameter] = compute_bbox(P); 35 | h1 = scatter3(P(:,1),P(:,2),P(:,3),30,'.','MarkerEdgeColor',[1.0, .65, .35]); 36 | VD = V*D; 37 | target = repmat(mp',1,3) + VD*(diameter/max(sqrt(sum(VD.^2)))); 38 | x(1:2:5) = mp(1);y(1:2:5) = mp(2);z(1:2:5) = mp(3); 39 | x(2:2:6) = target(1,:);y(2:2:6) = target(2,:);z(2:2:6) = target(3,:); 40 | h2 = line(x,y,z, 'LineWidth', 2, 'Color', [1.0,0.0,0.0]); 41 | delete(h1);delete(h2); 42 | else 43 | [V, D] = eig(C); 44 | d = diag(D); 45 | end 46 | normVectors(i,:) = V(:,1); 47 | % s=svd(tmp,0); 48 | end 49 | 50 | function [bbox, diameter] = compute_bbox(pts) 51 | bbox = [min(pts(:,1)), min(pts(:,2)), min(pts(:,3)), max(pts(:,1)), max(pts(:,2)), max(pts(:,3))]; 52 | rs = bbox(4:6)-bbox(1:3); 53 | diameter = sqrt(dot(rs,rs)); -------------------------------------------------------------------------------- /code/compute_normal_NWR_EACH4_ExraFea.m: -------------------------------------------------------------------------------- 1 | function [normal_one noncompute max_sort , feature_id , feature_normal] = compute_normal_NWR_EACH4_ExraFea(points , local_W , ran_num , inner_threshold , Par , des , T) 2 | 3 | num = size(points , 1) ; 4 | NonInner = true(1 , num) ; 5 | computedNum = 0; 6 | 7 | feature_normal = [] ; 8 | noncompute = 0 ; 9 | max_sort = 0; 10 | normal_one = zeros(1 , 3) ; 11 | 12 | while computedNum==0 || sum(des(NonInner)) > (sum(des)* ((1/(computedNum+1)) - Par.compart*(computedNum+1))) 13 | computedNum = computedNum + 1 ; 14 | 15 | curPoints = points(NonInner , :) ; 16 | curLocal_W = local_W(NonInner , NonInner) ; 17 | if computedNum == 1 18 | [curNormal curNonCompute curSort dis_vect] = compute_normal_NWR_EACH5_ExraFea(curPoints , curLocal_W , ran_num , inner_threshold) ; 19 | normal_one = curNormal ; 20 | noncompute = curNonCompute ; 21 | max_sort = curSort ; 22 | feature_normal = [feature_normal ; curNormal] ; 23 | end 24 | if computedNum > 1 25 | [curNormal curNonCompute curSort dis_vect center] = compute_normal_NWR_EACH6_ExraFea(curPoints , curLocal_W , ran_num , inner_threshold) ; 26 | end 27 | 28 | if curNonCompute == 1 29 | break ; 30 | end 31 | NonInnerFlag = (NonInner == 1) ; 32 | NewNonInner = dis_vect > inner_threshold.^2 ; 33 | if computedNum > 1 34 | dis1 = ((points(1 , :) - center)*curNormal').^2 ; 35 | if dis1 < T * (inner_threshold).^2 && (max(curNormal * feature_normal')) < cos((10/180)*pi) ; 36 | feature_normal = [feature_normal ; curNormal] ; 37 | end 38 | end 39 | 40 | if computedNum > 5 41 | break 42 | end 43 | 44 | NonInner(NonInnerFlag) = NewNonInner ; 45 | 46 | if sum(NonInner) < 3 47 | break; 48 | end 49 | end 50 | 51 | feature_id = 0 ; 52 | if size(feature_normal , 1) > 1 53 | feature_id = 1 ; 54 | end 55 | -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_orientation_normals.m: -------------------------------------------------------------------------------- 1 | function [normVectors sigma] = compute_orientation_normals(pts, k_knn, kdtree, orientation) 2 | % COMPUTE_INITIAL_FEATURE - initial feature detection via PCA. 3 | % 4 | % Input: 5 | % 6 | % Output: 7 | % - 'featid' : sharp edge feature vertex id 8 | % 9 | % Reference: 10 | % (1) - Pauly, M., Keiser, R., and Gross, M.. Multi-scale feature extraction on point-sampled surface. 11 | % Computer. Graphics Forum. 2003, 22(3):281-289. 12 | % 13 | % Changed by JJCAO 14 | % Copyright (c) 2012 Wangxiaochao 2012-8-30 22:40:28 15 | 16 | DEBUG = 0; 17 | 18 | npts = size(pts,1); 19 | normVectors = zeros(npts,3); 20 | sigma = zeros(npts,1); 21 | if DEBUG 22 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 23 | end 24 | for i = 1:npts 25 | knn = kdtree_k_nearest_neighbors(kdtree,pts(i,:),k_knn)'; 26 | P = pts(knn,:); 27 | mp = mean(P); 28 | tmp = P-repmat(mp,k_knn,1); 29 | C = tmp'*tmp./k_knn; 30 | if DEBUG 31 | [V, D] = eig(C); 32 | d = diag(D); 33 | 34 | [bbox, diameter] = compute_bbox(P); 35 | h1 = scatter3(P(:,1),P(:,2),P(:,3),30,'.','MarkerEdgeColor',[1.0, .65, .35]); 36 | VD = V*D; 37 | target = repmat(mp',1,3) + VD*(diameter/max(sqrt(sum(VD.^2)))); 38 | x(1:2:5) = mp(1);y(1:2:5) = mp(2);z(1:2:5) = mp(3); 39 | x(2:2:6) = target(1,:);y(2:2:6) = target(2,:);z(2:2:6) = target(3,:); 40 | h2 = line(x,y,z, 'LineWidth', 2, 'Color', [1.0,0.0,0.0]); 41 | delete(h1);delete(h2); 42 | else 43 | [V, D] = eig(C); 44 | d = diag(D); 45 | end 46 | sigma(i) = min(d)/sum(d); 47 | if orientation(i,:)*V(:,1) < 0 48 | normVectors(i,:) = -V(:,1); 49 | else 50 | normVectors(i,:) = V(:,1); 51 | end 52 | % s=svd(tmp,0); 53 | end 54 | sigma = sigma/max(sigma); 55 | 56 | function [bbox, diameter] = compute_bbox(pts) 57 | bbox = [min(pts(:,1)), min(pts(:,2)), min(pts(:,3)), max(pts(:,1)), max(pts(:,2)), max(pts(:,3))]; 58 | rs = bbox(4:6)-bbox(1:3); 59 | diameter = sqrt(dot(rs,rs)); -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_points_sigms_normals.m: -------------------------------------------------------------------------------- 1 | function [sigms normVectors errs max_errs]= compute_points_sigms_normals(pts, k_knn, kdtree) 2 | % COMPUTE_INITIAL_FEATURE - initial feature detection via PCA. 3 | % 4 | % Input: 5 | % 6 | % Output: 7 | % - 'featid' : sharp edge feature vertex id 8 | % 9 | % Reference: 10 | % (1) - Pauly, M., Keiser, R., and Gross, M.. Multi-scale feature extraction on point-sampled surface. 11 | % Computer. Graphics Forum. 2003, 22(3):281-289. 12 | % 13 | % Changed by JJCAO 14 | % Copyright (c) 2012 Wangxiaochao 2012-8-30 22:40:28 15 | 16 | DEBUG = 0; 17 | SHOW_RESULT = 0; 18 | 19 | npts = size(pts,1); 20 | sigma = zeros(npts,1); 21 | normVectors = zeros(npts,3); 22 | errs = zeros(npts,1); 23 | 24 | if DEBUG 25 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 26 | end 27 | for i = 1:npts 28 | knn = kdtree_k_nearest_neighbors(kdtree,pts(i,:),k_knn)'; 29 | P = pts(knn,:); 30 | mp = mean(P); 31 | tmp = P-repmat(mp,k_knn,1); 32 | C = tmp'*tmp./k_knn; 33 | if DEBUG 34 | [V, D] = eig(C); 35 | d = diag(D); 36 | 37 | [bbox, diameter] = compute_bbox(P); 38 | h1 = scatter3(P(:,1),P(:,2),P(:,3),30,'.','MarkerEdgeColor',[1.0, .65, .35]); 39 | VD = V*D; 40 | target = repmat(mp',1,3) + VD*(diameter/max(sqrt(sum(VD.^2)))); 41 | x(1:2:5) = mp(1);y(1:2:5) = mp(2);z(1:2:5) = mp(3); 42 | x(2:2:6) = target(1,:);y(2:2:6) = target(2,:);z(2:2:6) = target(3,:); 43 | h2 = line(x,y,z, 'LineWidth', 2, 'Color', [1.0,0.0,0.0]); 44 | delete(h1);delete(h2); 45 | else 46 | [V, D] = eig(C); 47 | d = diag(D); 48 | end 49 | sigma(i) = min(d)/sum(d); 50 | d = tmp * V(:,1); 51 | errs(i) = mean(abs(d)); 52 | max_errs(i) = max(abs(d)); 53 | normVectors(i,:) = V(:,1); 54 | % s=svd(tmp,0); 55 | end 56 | 57 | %% set threshold to filter the false feature points 58 | sigma = sigma/max(sigma); 59 | sigms = sigma; 60 | if SHOW_RESULT 61 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 62 | scatter3(pts(:,1),pts(:,2),pts(:,3),20,'.','MarkerEdgeColor',[1.0, .65, .35]); 63 | scatter3(pts(featid,1),pts(featid,2),pts(featid,3),40,'.','MarkerEdgeColor',[1.0, .0, .0]); 64 | end 65 | 66 | function [bbox, diameter] = compute_bbox(pts) 67 | bbox = [min(pts(:,1)), min(pts(:,2)), min(pts(:,3)), max(pts(:,1)), max(pts(:,2)), max(pts(:,3))]; 68 | rs = bbox(4:6)-bbox(1:3); 69 | diameter = sqrt(dot(rs,rs)); -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/compute_points_sigms_normals_two.m: -------------------------------------------------------------------------------- 1 | function [sigms normVectors errs normVectors1 neighbours_all]= compute_points_sigms_normals_two(pts, k_knn, kdtree , k_knn1) 2 | % COMPUTE_INITIAL_FEATURE - initial feature detection via PCA. 3 | % 4 | % Input: 5 | % 6 | % Output: 7 | % - 'featid' : sharp edge feature vertex id 8 | % 9 | % Reference: 10 | % (1) - Pauly, M., Keiser, R., and Gross, M.. Multi-scale feature extraction on point-sampled surface. 11 | % Computer. Graphics Forum. 2003, 22(3):281-289. 12 | % 13 | % Changed by JJCAO 14 | % Copyright (c) 2012 Wangxiaochao 2012-8-30 22:40:28 15 | 16 | DEBUG = 0; 17 | SHOW_RESULT = 0; 18 | 19 | npts = size(pts,1); 20 | sigma = zeros(npts,1); 21 | normVectors = zeros(npts,3); 22 | normVectors1 = zeros(npts,3); 23 | errs = zeros(npts,1); 24 | neighbours_all = zeros(k_knn1,npts); 25 | 26 | if DEBUG 27 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 28 | end 29 | for i = 1:npts 30 | knn = kdtree_k_nearest_neighbors(kdtree,pts(i,:),k_knn)'; 31 | P = pts(knn,:); 32 | mp = mean(P); 33 | tmp = P-repmat(mp,k_knn,1); 34 | C = tmp'*tmp./k_knn; 35 | 36 | neighbours_all(: , i) = knn(1 : k_knn1); 37 | P1 = P(1 : k_knn1 , : ); 38 | mp1 = mean(P1); 39 | tmp1 = P1-repmat(mp1,k_knn1,1); 40 | C1 = tmp1'*tmp1./k_knn1; 41 | if DEBUG 42 | [V, D] = eig(C); 43 | d = diag(D); 44 | 45 | [bbox, diameter] = compute_bbox(P); 46 | h1 = scatter3(P(:,1),P(:,2),P(:,3),30,'.','MarkerEdgeColor',[1.0, .65, .35]); 47 | VD = V*D; 48 | target = repmat(mp',1,3) + VD*(diameter/max(sqrt(sum(VD.^2)))); 49 | x(1:2:5) = mp(1);y(1:2:5) = mp(2);z(1:2:5) = mp(3); 50 | x(2:2:6) = target(1,:);y(2:2:6) = target(2,:);z(2:2:6) = target(3,:); 51 | h2 = line(x,y,z, 'LineWidth', 2, 'Color', [1.0,0.0,0.0]); 52 | delete(h1);delete(h2); 53 | else 54 | [V, D] = eig(C); 55 | d = diag(D); 56 | [V1 , D1] = eig(C1); 57 | end 58 | sigma(i) = min(d)/sum(d); 59 | d = tmp * V(:,1); 60 | errs(i) = mean(abs(d)); 61 | %max_errs(i) = max(abs(d)); 62 | normVectors(i,:) = V(:,1); 63 | normVectors1(i , :) = V1(: , 1) ; 64 | % s=svd(tmp,0); 65 | end 66 | 67 | %% set threshold to filter the false feature points 68 | sigma = sigma/max(sigma); 69 | sigms = sigma; 70 | if SHOW_RESULT 71 | figure('Name','PCA'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); axis off;axis equal;view3d rot; hold on; 72 | scatter3(pts(:,1),pts(:,2),pts(:,3),20,'.','MarkerEdgeColor',[1.0, .65, .35]); 73 | scatter3(pts(featid,1),pts(featid,2),pts(featid,3),40,'.','MarkerEdgeColor',[1.0, .0, .0]); 74 | end 75 | 76 | function [bbox, diameter] = compute_bbox(pts) 77 | bbox = [min(pts(:,1)), min(pts(:,2)), min(pts(:,3)), max(pts(:,1)), max(pts(:,2)), max(pts(:,3))]; 78 | rs = bbox(4:6)-bbox(1:3); 79 | diameter = sqrt(dot(rs,rs)); -------------------------------------------------------------------------------- /code/toolbox/jjcao_point/pcd_noise_point.m: -------------------------------------------------------------------------------- 1 | function [overts] = pcd_noise_point(varargin) 2 | %pcd_noise Add noise to point cloud. 3 | % overts = pcd_noise(verts,TYPE,...) Add noise of a given TYPE to the point cloud verts. 4 | % TYPE is a string that can have one of these values: 5 | % 6 | % 'random' implement of Random vertex displacement in MeshLab 7 | % 'gaussian' Gaussian white noise with constant mean (0) and variance 8 | % 9 | % 'salt & pepper' Generate some random samples with uniform distribution in the bounding box 10 | % of the shape 11 | % 12 | % Depending on TYPE, you can specify additional parameters to pcd_noise. 13 | % 14 | % overts = pcd_noise(verts,'gaussian',V) adds Gaussian white noise of mean 0 and 15 | % variance V*R to the point cloud verts, where R is the bounding radius mentioned in 16 | % Reconstruction of Solid Models from Oriented Point Sets_sgp05. 17 | % When unspecified, V default to 0.01. 18 | % 19 | % overts = pcd_noise(verts,'salt & pepper',D) adds "salt and pepper" noise to the point cloud 20 | % verts, where D is the noise density. This affects approximately D*numel(I) pixels. 21 | % The default for D is 0.05. 22 | % 23 | % Example 24 | % ------- 25 | % [M.verts,M.faces] = read_mesh('../data/horse_v11912.off'); 26 | % overts = imnoise(M.verts,'salt & pepper', 0.02); 27 | % plot_mesh(M.verts, M.faces); 28 | % plot_mesh(overts, M.faces); 29 | % 30 | % Copyright 2010 JJCAO 31 | verts = varargin{1}; 32 | TYPE = varargin{2}; 33 | base = varargin{3}; 34 | p3 = varargin{4}; 35 | kdtree = varargin{5}; 36 | % 37 | switch base 38 | case 'average_edge' % Random vertex displacement in MeshLab 39 | diameter = compute_average_radius(verts, 2, kdtree); 40 | case 'diagonal_line' % Gaussian white noise 41 | bbox = [min(verts(:,1)), min(verts(:,2)), min(verts(:,3)), max(verts(:,1)), max(verts(:,2)), max(verts(:,3))]; 42 | bx = bbox(4)-bbox(1);by = bbox(5)-bbox(2);bz = bbox(6)-bbox(3); 43 | rs = bbox(4:6)-bbox(1:3); 44 | diameter = sqrt(dot(rs,rs)); 45 | end 46 | 47 | 48 | switch TYPE 49 | case 'random' % Random vertex displacement in MeshLab 50 | overts = verts + (2.0*rand(size(verts)) - 1) * diameter * p3;%rand: uniform distribution 51 | case 'gaussian' % Gaussian white noise 52 | % overts = verts + sqrt(p3*diameter*0.5)*randn(size(verts));%randn: normal distribution 53 | overts = verts + p3*diameter*randn(size(verts));%randn: normal distribution 54 | % x = rand(length(verts),1); 55 | % tmp = find(x < 0.05); 56 | % overts(tmp,1) = overts(tmp,1) + 0.05*diameter*randn(length(tmp),1); 57 | % overts(tmp,2) = overts(tmp,2) + 0.05*diameter*randn(length(tmp),1); 58 | % overts(tmp,3) = overts(tmp,3) + 0.05*diameter*randn(length(tmp),1); 59 | %overts = verts + sqrt(p3)*diameter*randn(size(verts)); 60 | case 'salt & pepper' % Salt & pepper noise 61 | overts = verts; 62 | x = rand(length(verts),1); 63 | tmp =x < p3; 64 | overts(tmp,1) = bbox(1) + bx*rand(sum(tmp), 1); 65 | overts(tmp,2) = bbox(2) + by*rand(sum(tmp), 1); 66 | overts(tmp,3) = bbox(3) + bz*rand(sum(tmp), 1); 67 | end 68 | end -------------------------------------------------------------------------------- /code/toolbox/jjcao_plot/plot_mesh.m: -------------------------------------------------------------------------------- 1 | function h = plot_mesh(vertex,face,options) 2 | 3 | % plot_mesh - plot a 3D mesh. 4 | % 5 | % turn off vsync in the nvidia control panel, will speed up renderering!!!! 6 | % 7 | % 'options' is a structure that may contains: 8 | % - 'normal' : a (nvertx x 3) array specifying the normals at each vertex. 9 | % - 'fnormal': a (nface x 3) array specifying the normals at each face. 10 | % - 'normal_scaling': a double normal scale. 11 | % - 'subsample_normal': a double normal sample ratio. 12 | % - 'edge_color' : a float specifying the color of the edges. 13 | % - 'face_color' : a float specifying the color of the faces. 14 | % - 'face_vertex_color' : a color per vertex or face. 15 | % - 'vertex' 16 | % 17 | % See also: mesh_previewer. 18 | % changed by jjcao 2009, 2012 19 | % Copyright (c) 2004 Gabriel Peyr? 20 | 21 | if nargin<2 22 | error('Not enough arguments.'); 23 | end 24 | 25 | options.null = 0; 26 | 27 | normal = getoptions(options, 'normal', []); 28 | fnormal = getoptions(options, 'fnormal', []); 29 | face_color = getoptions(options, 'face_color', [194 212 246]/255); 30 | edge_color = getoptions(options, 'edge_color', [1 1 1]*0); 31 | normal_scaling = getoptions(options, 'normal_scaling', .8); 32 | alfa = getoptions(options, 'alfa', 1); 33 | if (alfa > 1) alfa = 1;end 34 | if (alfa < 0) alfa = 0;end; 35 | 36 | if ~isfield(options, 'face_vertex_color') 37 | %don't need 'face_vertex_color' indexing 38 | face_vertex_color=[]; 39 | elseif isempty(options.face_vertex_color) 40 | %default indexing color per vertex 41 | options.face_vertex_color = ones(size(vertex,1),1)*192/255; 42 | face_vertex_color = options.face_vertex_color; 43 | else 44 | %customized indexing color per face/vertex 45 | face_vertex_color = options.face_vertex_color; 46 | end 47 | 48 | if isempty(face_vertex_color) 49 | h = patch('vertices',vertex,'faces',face,'facecolor',face_color,'edgecolor',edge_color, 'FaceAlpha', alfa); 50 | else 51 | if size(face_vertex_color,1)==size(vertex,1) 52 | shading_type = 'interp'; 53 | else 54 | shading_type = 'flat'; 55 | end 56 | h = patch('vertices',vertex,'faces',face,'FaceVertexCData',face_vertex_color,... 57 | 'FaceColor',shading_type, 'FaceAlpha', alfa); 58 | colormap hsv; 59 | colorbar; 60 | end 61 | % alpha(0.8); 62 | lighting phong; 63 | camproj('perspective'); 64 | axis square; 65 | axis off; 66 | 67 | if ~isempty(normal) 68 | % plot the normals 69 | n = size(vertex,1); 70 | subsample_normal = getoptions(options, 'subsample_normal', min(4000/n,1) ); 71 | sel = randperm(n); sel = sel(1:floor(end*subsample_normal)); 72 | hold on; 73 | quiver3(vertex(sel,1),vertex(sel,2),vertex(sel,3),normal(sel,1),normal(sel,2),normal(sel,3),normal_scaling); 74 | hold off; 75 | end 76 | 77 | if ~isempty(fnormal) 78 | % plot the face's normals 79 | n = size(face,1); 80 | subsample_normal = getoptions(options, 'subsample_normal', min(4000/n,1) ); 81 | sel = randperm(n); sel = sel(1:floor(end*subsample_normal)); 82 | bc = getoptions(options, 'barycentre', barycentre(vertex,face) ); 83 | hold on; 84 | quiver3(bc(sel,1),bc(sel,2),bc(sel,3),fnormal(sel,1),fnormal(sel,2),fnormal(sel,3),normal_scaling); 85 | hold off; 86 | end 87 | 88 | cameramenu; 89 | axis equal; 90 | camlight; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-Normal Estimation via Pair Consistency Voting 2 | ie~Zhang, Junjie Cao (co-first authors), Xiuping Liu*, He Chen, Bo Li, Ligang Liu. 3 | TVCG 2019 4 | ![Teaser Image](http://jjcao.github.io/images/MultiNormal.png) 5 | The normals of feature points, the intersection points of multiple smooth surfaces, are ambiguous and undefined.
6 | 7 | ### Introduction 8 |
9 | This paper presents a unified definition for point cloud normal of feature and non-feature points, which allows 10 | feature points to possess multiple normals. 11 | This definition facilitates several succeeding operations, such as feature points extraction and point cloud filtering. 12 | We also develop a feature preserving normal estimation method which outputs multiple normals per feature point. 13 |
14 |
15 | The core of the method is a pair consistency voting scheme. All neighbor point pairs vote for the local tangent 16 | plane. Each vote takes the fitting residuals of the pair of points and their preliminary normal consistency into 17 | consideration. Thus the pairs from the same subspace and relatively far off features dominate the voting. An adaptive 18 | strategy is designed to overcome sampling anisotropy. 19 |
20 |
21 | In addition, we introduce an error measure compatible with traditional normal estimators, and present the 22 | first benchmark for normal estimation, composed of 152 synthesized data with various features and sampling 23 | densities, and 288 real scans with different noise levels. Comprehensive and quantitative experiments show 24 | that our method generates faithful feature preserving normals and outperforms previous cutting edge normal 25 | estimation methods, including the latest deep learning based method. 26 | 27 | ### Normal Estimation Benchmark 28 | #### Ground truth dataset 29 | https://pan.baidu.com/s/1VZVWcjSr6TxqfQtfJfhk1A 30 | 31 | Synthesized Data Set
32 | Scanned Data Set
33 | 34 | #### Comparisons 35 | ... 36 | 37 | ### Previous work 38 | - Jie~Zhang, Junjie Cao (co-first authors), Xiuping Liu*, He Chen, Bo Li, Ligang Liu. [Multi-Normal Estimation via Pair Consistency Voting](Multi-Normal_2019.pdf). IEEE Transactions on Visualization and Computer Graphics, 25(4), 2019, 1693-1706. 39 | - Junjie Cao, He Chen, Jie Zhang*, Yujiao Li, Xiuping Liu, Changqing Zou. [Normal Estimation via Shifted Neighborhood for point cloud](https://github.com/AnkaChan/NormalEstimatePatchShift). Journal of Computational and Applied Mathematics, 2018, 329, 57-67. 40 | - Xiuping Liu, Jie Zhang, Junjie Cao*, Bo Li, Ligang Liu. "Quality Point Cloud Normal Estimation by Guided Least Squares Representation", Computers & Graphics (Special Issue of SMI 2014), 2015, 46, 106-116. 41 | - Jian Liu, Junjie Cao*, Xiuping Liu, Jun Wang, XiaoChao Wang, Xiquan Shi. [Mendable consistent orientation of point clouds](https://github.com/jjcao/jjcao-orientation), Computer-Aided Design, 2014, 55: 26-36. 42 | - Jie Zhang, Junjie Cao*, Xiuping Liu, Jun Wang, Jian Liu, Xiquan Shi. [Point cloud normal estimation via low-rank subspace clustering](https://github.com/jjcao/sf-pcd2013), Computer & Graphics (Special issue of SMI), 2013, 37(6): 697-706. 43 | - Junjie Cao*, Ying He, Zhiyang Li, Xiuping Liu, Zhixun Su. [Orienting Raw Point Sets by Global Contraction and Visibility Voting](https://github.com/jjcao/orientation1), Computer & Graphics (Special Issue of SMI 2011), 2011. 44 | -------------------------------------------------------------------------------- /code/normalEstimatePCV.m: -------------------------------------------------------------------------------- 1 | function [pts, normVectors ] = normalEstimatePCV( filepath, neiSize) 2 | originalPath = cd; 3 | path = which('normalEstimatePCV'); 4 | cd(path(1:end - length('normalEstimatePCV.m') )); 5 | 6 | addpath ('toolbox/jjcao_io') 7 | addpath ('toolbox/jjcao_point') 8 | addpath ('toolbox/jjcao_common') 9 | addpath ('toolbox/jjcao_interact') 10 | addpath ('toolbox/jjcao_plot') 11 | 12 | addpath('toolbox/zj_fitting') 13 | addpath('toolbox/zj_deviation') 14 | 15 | addpath('toolbox/cvx') 16 | cvx_setup 17 | %% debug options 18 | ADDNOISE = 0; 19 | ADDOUTLIER = 0 ; 20 | TP.debug_data = 0; 21 | TP.debug_taproot = 0 ; 22 | 23 | %% algorithm options 24 | TP.k_knn_feature = ceil(neiSize/2); 25 | TP.k_knn_normals = ceil(neiSize/4); 26 | TP.k_knn = ceil(neiSize); 27 | 28 | TP.sigma_threshold = 0.05; 29 | TP.ran_num_min = 150 ; TP.ran_num_max = 250 ; 30 | TP.density_num = 10 ; 31 | %% read input && add noise && plot it && build kdtree 32 | [P.pts ] = read_off(filepath); 33 | pts = P.pts; 34 | nSample = size(P.pts,1); 35 | 36 | alpha = 4 ; 37 | beta = 2 ; 38 | % add noise & outliers 39 | if ADDNOISE 40 | type = 'gaussian';%type = 'random';% type = 'gaussian';% type = 'salt & pepper'; 41 | base = 'average_edge';%base = 'average_edge'% base = 'diagonal_line'; 42 | p3 = 0.3; 43 | kdtree = kdtree_build(P.pts); 44 | P.pts = pcd_noise_point(P.pts, type, base, p3,kdtree); 45 | kdtree_delete(kdtree); 46 | end 47 | 48 | % build kdtree 49 | P.kdtree = kdtree_build(P.pts); 50 | %% show the density 51 | [l density] = compute_average_radius(P.pts ,TP.density_num ,P.kdtree) ; 52 | 53 | %% compute initial features (a ribbon) 54 | [sigms , normVectors , errs , normals_comW] = compute_points_sigms_normals_two(P.pts, TP.k_knn_feature, P.kdtree, TP.k_knn_normals); 55 | 56 | TP.feature_threshold = feature_threshold_selection(sigms,TP); 57 | 58 | P.init_feat = find(sigms > TP.feature_threshold); 59 | feature_sigms = sigms(P.init_feat); 60 | [~, id_sigms] = sort(feature_sigms); 61 | TP.id_feature = P.init_feat(id_sigms); 62 | 63 | if TP.debug_data; 64 | figure('Name','Input'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); 65 | movegui('northeast'); 66 | non_feature = setdiff(1:nSample , TP.id_feature); 67 | scatter3(P.pts(non_feature,1),P.pts(non_feature,2),P.pts(non_feature,3),30,'.','MarkerEdgeColor',GS.CLASS_COLOR5); hold on; 68 | scatter3(P.pts(TP.id_feature,1),P.pts(TP.id_feature,2),P.pts(TP.id_feature,3),30,'.','MarkerEdgeColor',GS.CLASS_COLOR5); hold on; 69 | axis off;axis equal; 70 | view3d rot; % vidw3d zoom; % r for rot; z for zoom; 71 | end 72 | 73 | nFeature = length(TP.id_feature) 74 | 75 | %% compute nomals 76 | density_r = zeros(1 , nFeature) ; 77 | 78 | for i = 1:nFeature 79 | ii = TP.id_feature(i); 80 | 81 | knn_big = kdtree_k_nearest_neighbors(P.kdtree,P.pts(ii,:),ceil(neiSize*1.7))'; 82 | knn = knn_big(1:TP.k_knn); 83 | 84 | plane_knn = setdiff(knn_big , TP.id_feature) ; 85 | inner_threshold = beta * mean(errs(plane_knn)) ; 86 | 87 | points = P.pts(knn , :) ; 88 | temp_nor = normals_comW(knn , :) ; 89 | local_density = density(knn) ; 90 | 91 | temp_W = abs(temp_nor * temp_nor'); 92 | local_W = exp((temp_W.^alpha)./(0.85.^alpha)); 93 | 94 | s_v = sort(local_density) ; 95 | density_r(i) = mean(s_v(TP.k_knn - 14:TP.k_knn))/mean(s_v(1:15)) ; 96 | local_density = (local_density.^2) ; 97 | density_w = local_density' * local_density; 98 | local_W = density_w .* local_W; 99 | 100 | if density_r(i) > 2 101 | ran_num = TP.ran_num_max ; 102 | else 103 | ran_num = TP.ran_num_min ; 104 | end 105 | [normVectors(ii,:)] = compute_normal_NWR_EACH2(points , local_W , ran_num , inner_threshold) ; 106 | end 107 | %% 108 | kdtree_delete(P.kdtree); 109 | 110 | cd(originalPath); 111 | end 112 | 113 | -------------------------------------------------------------------------------- /code/normalEstimatePCVMN.m: -------------------------------------------------------------------------------- 1 | function [pts, normVectors , feature_normal , feature_id] = normalEstimatePCVMN( filepath , neiSize) 2 | originalPath = cd; 3 | path = which('normalEstimatePCVMN'); 4 | cd(path(1:end - length('normalEstimatePCVMN.m') )); 5 | 6 | addpath ('toolbox/jjcao_io') 7 | addpath ('toolbox/jjcao_point') 8 | addpath ('toolbox/jjcao_common') 9 | addpath ('toolbox/jjcao_interact') 10 | addpath ('toolbox/jjcao_plot') 11 | 12 | addpath('toolbox/zj_fitting') 13 | addpath('toolbox/zj_deviation') 14 | 15 | addpath('toolbox/cvx') 16 | cvx_setup 17 | %% debug options 18 | ADDNOISE = 0; 19 | TP.debug_data = 1; 20 | TP.debug_taproot = 0 ; 21 | 22 | %% algorithm options 23 | TP.k_knn_feature = ceil(neiSize/2); 24 | TP.k_knn_normals = ceil(neiSize/4); 25 | TP.k_knn = ceil(neiSize); 26 | TP.k_knn_big = ceil(neiSize*1.7); 27 | 28 | TP.sigma_threshold = 0.05; 29 | TP.ran_num_min = 150 ; TP.ran_num_max = 250 ; 30 | TP.density_num = 10 ; 31 | 32 | alpha = 4 ; 33 | beta = 2 ; 34 | %% read input && add noise && plot it && build kdtree 35 | [P.pts ] = read_off(filepath); 36 | pts = P.pts; 37 | nSample = size(P.pts,1); 38 | 39 | % add noise & outliers 40 | if ADDNOISE 41 | type = 'gaussian';%type = 'random';% type = 'gaussian';% type = 'salt & pepper'; 42 | base = 'average_edge';%base = 'average_edge'% base = 'diagonal_line'; 43 | p3 = 0.1; 44 | kdtree = kdtree_build(P.pts); 45 | P.pts = pcd_noise_point(P.pts, type, base, p3,kdtree); 46 | kdtree_delete(kdtree); 47 | end 48 | 49 | % build kdtree 50 | P.kdtree = kdtree_build(P.pts); 51 | %% show the density 52 | [~ , density] = compute_average_radius(P.pts ,TP.density_num ,P.kdtree) ; 53 | 54 | %% compute initial features (a ribbon) 55 | [sigms , normVectors , errs , normals_comW] = compute_points_sigms_normals_two(P.pts, TP.k_knn_feature, P.kdtree, TP.k_knn_normals); 56 | 57 | TP.feature_threshold = feature_threshold_selection(sigms,TP); 58 | P.init_feat = find(sigms > TP.feature_threshold); 59 | feature_sigms = sigms(P.init_feat); 60 | [~, id_sigms] = sort(feature_sigms); 61 | TP.id_feature = P.init_feat(id_sigms); 62 | 63 | Feature_flag = false(1 , nSample) ; 64 | Feature_flag(TP.id_feature) = true ; 65 | 66 | if TP.debug_data; 67 | figure('Name','Input'); set(gcf,'color','white');set(gcf,'Renderer','OpenGL'); 68 | movegui('northeast'); 69 | non_feature = setdiff(1:nSample , TP.id_feature); 70 | scatter3(P.pts(non_feature,1),P.pts(non_feature,2),P.pts(non_feature,3),30,'.','MarkerEdgeColor',GS.CLASS_COLOR4); hold on; 71 | scatter3(P.pts(TP.id_feature,1),P.pts(TP.id_feature,2),P.pts(TP.id_feature,3),30,'.','MarkerEdgeColor',GS.CLASS_COLOR1); hold on; 72 | axis off;axis equal; 73 | view3d rot; % vidw3d zoom; % r for rot; z for zoom; 74 | end 75 | 76 | %% compute nomals 77 | feature_id = false(1 , nSample) ; 78 | feature_normal = cell(1 , nSample) ; 79 | T = 2 ; 80 | for ii = 1:nSample 81 | 82 | if ~Feature_flag(ii) 83 | feature_normal{ii} = normVectors(ii,:) ; 84 | continue; 85 | end 86 | 87 | knn_big = kdtree_k_nearest_neighbors(P.kdtree,P.pts(ii,:),TP.k_knn_big)'; 88 | knn = knn_big(1:TP.k_knn); 89 | 90 | plane_knn = setdiff(knn_big , TP.id_feature) ; 91 | inner_threshold = beta * mean(errs(plane_knn)) ; 92 | 93 | points = P.pts(knn , :) ; 94 | temp_nor = normals_comW(knn , :) ; 95 | local_density = density(knn) ; 96 | 97 | temp_W = abs(temp_nor * temp_nor'); 98 | local_W = exp((temp_W.^alpha)./(0.85.^alpha)); 99 | 100 | s_v = sort(local_density) ; 101 | density_r = mean(s_v(TP.k_knn - 14:TP.k_knn))/mean(s_v(1:15)) ; 102 | local_density = (local_density.^2) ; 103 | density_w = local_density' * local_density; 104 | local_W = density_w .* local_W; 105 | 106 | if density_r > 2 107 | ran_num = TP.ran_num_max ; 108 | else 109 | ran_num = TP.ran_num_min ; 110 | end 111 | 112 | Par.compart = 0.3 ; 113 | [normVectors(ii,:), ~, ~, feature_id(ii), feature_normal{ii}] = compute_normal_NWR_EACH4_ExraFea(points , local_W , ran_num , inner_threshold , Par , local_density , T) ; 114 | end 115 | %% 116 | kdtree_delete(P.kdtree); 117 | 118 | cd(originalPath); 119 | end 120 | 121 | -------------------------------------------------------------------------------- /code/toolbox/jjcao_interact/view3d.m: -------------------------------------------------------------------------------- 1 | function view3d(arg,arg2) 2 | 3 | 4 | % view3d Interactively rotate, zoom and pan the view of a 3-D plot 5 | % -------------------------------------------------------------------- 6 | % 7 | % VIEW3D ROT turns on mouse-based 3-D rotation 8 | % VIEW3D ZOOM turns on mouse-based 3-D zoom and pan 9 | % VIEW3D OFF turns it off 10 | % 11 | % VIEW3D(FIG,...) works on the figure FIG 12 | % 13 | % Double click to restore the original view 14 | % 15 | % hit "z" key over the figure to switch from ROT to ZOOM 16 | % hit "r" key over the figure to switch from ZOOM to ROT 17 | % 18 | % in ROT mode: 19 | % press and hold left mouse button to rotate about screen xy axis 20 | % press and hold middle mouse button to rotate about screen z axis 21 | % in ZOOM mode: 22 | % press and hold left mouse button to zoom in and out 23 | % press and hold middle mouse button to move the plot 24 | % 25 | % -------------------------------------------------------------------- 26 | % inspired from rotate3d by The MathWorks, Inc. 27 | % 28 | % Torsten Vogel 09.04.1999 29 | % tv.volke@bmw.de 30 | % tested under Matlab 5.2 31 | % -------------------------------------------------------------------- 32 | 33 | 34 | % ---------------------------------------------- inputs -------------- 35 | if nargin == 0 36 | error('not enough inputs') 37 | elseif nargin == 1 38 | if ishandle(arg) 39 | error('not enough inputs') 40 | return 41 | else 42 | switch(lower(arg)) 43 | case 'rot' 44 | viewact(gcf,'rot') 45 | case 'zoom' 46 | viewact(gcf,'zoom') 47 | case 'off' 48 | viewact(gcf,'off') 49 | case 'down' 50 | view3dDownFcn 51 | case 'up' 52 | view3dUpFcn 53 | case 'keypress' 54 | view3dkeypressFcn 55 | case 'view_xy' % rotate via screen xy axis 56 | view3dxyFcn 57 | case 'view_z' % rotate via screen z axis 58 | view3dzFcn 59 | case 'view_zoom' % zoom in and out 60 | view3dzoomFcn 61 | case 'view_pan' % move the plot 62 | view3dpanFcn 63 | otherwise 64 | error('misspelled command argument') 65 | end 66 | end 67 | elseif nargin==2 68 | if ~ishandle(arg) 69 | error('bad figure handle') 70 | end 71 | switch(lower(arg2)) 72 | case 'rot' 73 | viewact(arg,'rot') 74 | case 'zoom' 75 | viewact(arg,'zoom') 76 | case 'off' 77 | viewact(arg,'off') 78 | otherwise 79 | error('misspelled command argument') 80 | end 81 | end 82 | 83 | 84 | % ---------------------------------------------- activation ---------- 85 | function viewact(fig,what) 86 | 87 | 88 | % de-/activates view3d for the given figure 89 | 90 | 91 | view3dObj = findobj(allchild(fig),'Tag','view3dObj'); 92 | 93 | 94 | if strcmp(what,'rot') 95 | if isempty(view3dObj) 96 | view3dObj = makeview3dObj(fig); %the small text box at the lower left corner 97 | end 98 | vdata = get(view3dObj,'UserData'); 99 | vdata.what = 'rot'; 100 | set(view3dObj,'UserData',vdata); 101 | elseif strcmp(what,'zoom') 102 | if isempty(view3dObj) 103 | view3dObj = makeview3dObj(fig); %the small text box at the lower left corner 104 | end 105 | vdata = get(view3dObj,'UserData'); 106 | vdata.what = 'zoom'; 107 | set(view3dObj,'UserData',vdata); 108 | elseif strcmp(what,'off') 109 | if isempty(view3dObj) 110 | return 111 | end 112 | vdata = get(view3dObj,'UserData'); 113 | uirestore(vdata.uistate); 114 | set(fig,'KeyPressFcn',vdata.oldkeypressfcn) 115 | delete(view3dObj); 116 | end 117 | 118 | 119 | % ---------------------------------------------- view3dDownFcn ------- 120 | function view3dDownFcn 121 | 122 | 123 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 124 | mouseclick = get(gcf,'SelectionType'); 125 | if isempty(view3dObj) 126 | return 127 | end 128 | vdata = get(view3dObj,'UserData'); 129 | vdata.oldunits = get(gcf,'Units'); 130 | set(gcf,'Units','pixels'); 131 | vdata.old_pt = get(0,'PointerLocation'); 132 | % ----------------- store or restore previous view 133 | ViewData = get(get(gca,'zlabel'),'UserData'); 134 | if isempty(ViewData) 135 | ViewData = manageViewData('get_from_axes'); 136 | set(get(gca,'zlabel'),'UserData',ViewData) 137 | end 138 | if strcmp(mouseclick,'open') 139 | manageViewData('set_axes',ViewData); 140 | set(gcf,'Units',vdata.oldunits) 141 | return 142 | end 143 | % ----------------- display text box 144 | fig_color = get(gcf,'Color'); 145 | c = sum([.3 .6 .1].*fig_color); 146 | set(vdata.textbox,'BackgroundColor',fig_color); 147 | if(c > .5) 148 | set(vdata.textbox,'ForegroundColor',[0 0 0]); 149 | else 150 | set(vdata.textbox,'ForegroundColor',[1 1 1]); 151 | end 152 | % ----------------- what to do? 153 | if strcmp(vdata.what,'rot') 154 | if strcmp(mouseclick,'normal') 155 | set(vdata.textbox,'string','Screen XY Rotation'); 156 | set(gcf,'WindowButtonMotionFcn','view3d(''view_xy'')'); 157 | set(gcf,'Pointer','custom','pointershapecdata',pointershapes('rot')); 158 | elseif strcmp(mouseclick,'extend') 159 | set(vdata.textbox,'string','Screen Z Rotation'); 160 | set(gcf,'WindowButtonMotionFcn','view3d(''view_z'')'); 161 | set(gcf,'Pointer','custom','pointershapecdata',pointershapes('rot')); 162 | end 163 | else 164 | if strcmp(mouseclick,'normal') 165 | set(vdata.textbox,'string','Zoom'); 166 | set(gcf,'WindowButtonMotionFcn','view3d(''view_zoom'')'); 167 | set(gcf,'Pointer','custom','pointershapecdata',pointershapes('zoom')); 168 | elseif strcmp(mouseclick,'extend') 169 | set(vdata.textbox,'string','Pan'); 170 | set(gcf,'WindowButtonMotionFcn','view3d(''view_pan'')'); 171 | set(gcf,'Pointer','custom','pointershapecdata',pointershapes('pan')); 172 | end 173 | end 174 | set(view3dObj,'UserData',vdata) 175 | set(vdata.textbox,'visi','on') 176 | 177 | 178 | % ---------------------------------------------- view3dUpFcn --------- 179 | function view3dUpFcn 180 | 181 | 182 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 183 | if isempty(view3dObj) 184 | return 185 | end 186 | vdata = get(view3dObj,'UserData'); 187 | set(gcf,'WindowButtonMotionFcn','','Units',vdata.oldunits,'pointer','arrow') 188 | set(view3dObj,'visi','off') 189 | 190 | 191 | % ---------------------------------------------- view3dkeypressFcn --- 192 | function view3dkeypressFcn 193 | 194 | 195 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 196 | if isempty(view3dObj) 197 | return 198 | end 199 | vdata = get(view3dObj,'UserData'); 200 | currchar = lower(get(gcf,'currentchar')); 201 | if strcmp(currchar,'r') 202 | vdata.what = 'rot'; 203 | elseif strcmp(currchar,'z') 204 | vdata.what = 'zoom'; 205 | end 206 | set(view3dObj,'UserData',vdata) 207 | 208 | 209 | % ---------------------------------------------- view3dxyFcn --------- 210 | function view3dxyFcn 211 | 212 | 213 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 214 | vdata = get(view3dObj,'UserData'); 215 | new_pt = get(0,'PointerLocation'); 216 | old_pt = vdata.old_pt; 217 | dx = (new_pt(1) - old_pt(1))*.5; 218 | dy = (new_pt(2) - old_pt(2))*.5; 219 | direction = [0 0 1]; 220 | coordsys = 'camera'; 221 | pos = get(gca,'cameraposition' ); 222 | targ = get(gca,'cameratarget' ); 223 | dar = get(gca,'dataaspectratio'); 224 | up = get(gca,'cameraupvector' ); 225 | [newPos newUp] = camrotate(pos,targ,dar,up,-dx,-dy,coordsys,direction); 226 | set(gca,'cameraposition', newPos, 'cameraupvector', newUp); 227 | vdata.old_pt = new_pt; 228 | set(view3dObj,'UserData',vdata) 229 | 230 | 231 | % ---------------------------------------------- view3dzFcn ---------- 232 | function view3dzFcn 233 | 234 | 235 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 236 | vdata = get(view3dObj,'UserData'); 237 | new_pt = get(0,'PointerLocation'); 238 | old_pt = vdata.old_pt; 239 | dy = (new_pt(2) - old_pt(2))*.5; 240 | camroll(gca,-dy) 241 | vdata.old_pt = new_pt; 242 | set(view3dObj,'UserData',vdata) 243 | 244 | 245 | % ---------------------------------------------- view3dzoomFcn ------- 246 | function view3dzoomFcn 247 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 248 | vdata = get(view3dObj,'UserData'); 249 | new_pt = get(0,'PointerLocation'); 250 | old_pt = vdata.old_pt; 251 | dy = (new_pt(2) - old_pt(2))/abs(old_pt(2)); 252 | camzoom(gca,1-dy) 253 | vdata.old_pt = new_pt; 254 | set(view3dObj,'UserData',vdata) 255 | 256 | 257 | % ---------------------------------------------- view3dpanFcn -------- 258 | function view3dpanFcn 259 | 260 | 261 | view3dObj = findobj(allchild(gcf),'Tag','view3dObj'); 262 | vdata = get(view3dObj,'UserData'); 263 | new_pt = get(0,'PointerLocation'); 264 | old_pt = vdata.old_pt; 265 | dx = (new_pt(1) - old_pt(1))/old_pt(1)*4; 266 | dy = (new_pt(2) - old_pt(2))/old_pt(2)*4; 267 | campan(gca,-dx,-dy,'camera') 268 | vdata.old_pt = new_pt; 269 | set(view3dObj,'UserData',vdata) 270 | 271 | 272 | % ---------------------------------------------- make view3dObj ------ 273 | function view3dObj = makeview3dObj(fig) 274 | 275 | 276 | % save the previous state of the figure window 277 | vdata.uistate = uisuspend(fig); 278 | % the data structure 279 | vdata.what = []; 280 | vdata.olp_pt = []; 281 | vdata.textbox = []; 282 | vdata.oldunits = []; 283 | vdata.oldkeypressfcn = get(fig,'KeyPressFcn'); 284 | % view3dObj 285 | view3dObj = uicontrol('style','text','parent',fig,'Units','Pixels',... 286 | 'Position',[2 2 130 20],'Visible','off', ... 287 | 'HandleVisibility','off','tag','view3dObj'); 288 | vdata.textbox = view3dObj; 289 | % store current view 290 | ViewData = manageViewData('get_from_axes'); 291 | set(get(gca,'zlabel'),'UserData',ViewData); 292 | % functions 293 | set(fig,'WindowButtonDownFcn','view3d(''down'')'); 294 | set(fig,'WindowButtonUpFcn','view3d(''up'')'); 295 | set(fig,'WindowButtonMotionFcn',''); 296 | set(fig,'ButtonDownFcn',''); 297 | set(fig,'KeyPressFcn','view3d(''keypress'')'); 298 | 299 | 300 | set(view3dObj,'UserData',vdata); 301 | % ---------------------------------------------- manage ViewData ----- 302 | function ViewData = manageViewData(how,data) 303 | 304 | 305 | if nargin == 1 ; data = [];end 306 | props = { 307 | 'DataAspectRatio' 308 | 'DataAspectRatioMode' 309 | 'CameraPosition' 310 | 'CameraPositionMode' 311 | 'CameraTarget' 312 | 'CameraTargetMode' 313 | 'CameraUpVector' 314 | 'CameraUpVectorMode' 315 | 'CameraViewAngle' 316 | 'CameraViewAngleMode' 317 | 'PlotBoxAspectRatio' 318 | 'PlotBoxAspectRatioMode' 319 | 'Units' 320 | 'Position' 321 | 'View' 322 | 'Projection' 323 | }; 324 | if strcmp(how,'get_from_axes') 325 | ViewData = get(gca,props); 326 | elseif strcmp(how,'get_stored') 327 | ViewData = get(get(gca,'zlabel'),'UserData'); 328 | elseif strcmp(how,'set_axes') 329 | set(gca,props,data) 330 | ViewData = []; 331 | end 332 | % ------------------------------------------------------------------------- 333 | % get some pointer shapes 334 | function shape = pointershapes(arg) 335 | 336 | 337 | if strcmp(arg,'zoom') 338 | % -- zoom 339 | shape=[ 2 2 2 2 2 2 2 2 2 2 NaN NaN NaN NaN NaN NaN ; 340 | 2 1 1 1 1 1 1 1 1 2 NaN NaN NaN NaN NaN NaN ; 341 | 2 1 2 2 2 2 2 2 2 2 NaN NaN NaN NaN NaN NaN ; 342 | 2 1 2 1 1 1 1 1 1 2 NaN NaN NaN NaN NaN NaN ; 343 | 2 1 2 1 1 1 1 1 2 NaN NaN NaN NaN NaN NaN NaN ; 344 | 2 1 2 1 1 1 1 2 NaN NaN NaN NaN NaN NaN NaN NaN ; 345 | 2 1 2 1 1 1 1 1 2 NaN NaN NaN 2 2 2 2 ; 346 | 2 1 2 1 1 2 1 1 1 2 NaN 2 1 2 1 2 ; 347 | 2 1 2 1 2 NaN 2 1 1 1 2 1 1 2 1 2 ; 348 | 2 2 2 2 NaN NaN NaN 2 1 1 1 1 1 2 1 2 ; 349 | NaN NaN NaN NaN NaN NaN NaN NaN 2 1 1 1 1 2 1 2 ; 350 | NaN NaN NaN NaN NaN NaN NaN 2 1 1 1 1 1 2 1 2 ; 351 | NaN NaN NaN NaN NaN NaN 2 1 1 1 1 1 1 2 1 2 ; 352 | NaN NaN NaN NaN NaN NaN 2 2 2 2 2 2 2 2 1 2 ; 353 | NaN NaN NaN NaN NaN NaN 2 1 1 1 1 1 1 1 1 2 ; 354 | NaN NaN NaN NaN NaN NaN 2 2 2 2 2 2 2 2 2 2 ]; 355 | elseif strcmp(arg,'pan') 356 | % -- pan 357 | shape=[ NaN NaN NaN NaN NaN NaN NaN 2 2 NaN NaN NaN NaN NaN NaN NaN ; 358 | NaN NaN NaN NaN NaN NaN 2 1 1 2 NaN NaN NaN NaN NaN NaN ; 359 | NaN NaN NaN NaN NaN 2 1 1 1 1 2 NaN NaN NaN NaN NaN ; 360 | NaN NaN NaN NaN NaN 1 1 1 1 1 1 NaN NaN NaN NaN NaN ; 361 | NaN NaN NaN NaN NaN NaN 2 1 1 2 NaN NaN NaN NaN NaN NaN ; 362 | NaN NaN 2 1 NaN NaN 2 1 1 2 NaN NaN 1 2 NaN NaN ; 363 | NaN 2 1 1 2 2 2 1 1 2 2 2 1 1 2 NaN ; 364 | 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 ; 365 | 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 ; 366 | NaN 2 1 1 2 2 2 1 1 2 2 2 1 1 2 NaN ; 367 | NaN NaN 2 1 NaN NaN 2 1 1 2 NaN NaN 1 2 NaN NaN ; 368 | NaN NaN NaN NaN NaN NaN 2 1 1 2 NaN NaN NaN NaN NaN NaN ; 369 | NaN NaN NaN NaN NaN 1 1 1 1 1 1 NaN NaN NaN NaN NaN ; 370 | NaN NaN NaN NaN NaN 2 1 1 1 1 2 NaN NaN NaN NaN NaN ; 371 | NaN NaN NaN NaN NaN NaN 2 1 1 2 NaN NaN NaN NaN NaN NaN ; 372 | NaN NaN NaN NaN NaN NaN NaN 2 2 NaN NaN NaN NaN NaN NaN NaN ]; 373 | elseif strcmp(arg,'rot') 374 | % -- rot 375 | shape=[ NaN NaN NaN 2 2 2 2 2 NaN 2 2 NaN NaN NaN NaN NaN ; 376 | NaN NaN NaN 1 1 1 1 1 2 1 1 2 NaN NaN NaN NaN ; 377 | NaN NaN NaN 2 1 1 1 1 2 1 1 1 2 NaN NaN NaN ; 378 | NaN NaN 2 1 1 1 1 1 2 2 1 1 1 2 NaN NaN ; 379 | NaN 2 1 1 1 2 1 1 2 NaN NaN 2 1 1 2 NaN ; 380 | NaN 2 1 1 2 NaN 2 1 2 NaN NaN 2 1 1 2 NaN ; 381 | 2 1 1 2 NaN NaN NaN NaN NaN NaN NaN NaN 2 1 1 2 ; 382 | 2 1 1 2 NaN NaN NaN NaN NaN NaN NaN NaN 2 1 1 2 ; 383 | 2 1 1 2 NaN NaN NaN NaN NaN NaN NaN NaN 2 1 1 2 ; 384 | 2 1 1 2 NaN NaN NaN NaN NaN NaN NaN NaN 2 1 1 2 ; 385 | NaN 2 1 1 2 NaN NaN 2 1 2 NaN 2 1 1 2 NaN ; 386 | NaN 2 1 1 2 NaN NaN 2 1 1 2 1 1 1 2 NaN ; 387 | NaN NaN 2 1 1 1 2 2 1 1 1 1 1 2 NaN NaN ; 388 | NaN NaN NaN 2 1 1 1 2 1 1 1 1 2 NaN NaN NaN ; 389 | NaN NaN NaN NaN 2 1 1 2 1 1 1 1 1 NaN NaN NaN ; 390 | NaN NaN NaN NaN NaN 2 2 NaN 2 2 2 2 2 NaN NaN NaN ]; 391 | 392 | 393 | end 394 | --------------------------------------------------------------------------------