├── general_tools ├── model_has_faces.m ├── model_has_colors.m ├── model_has_normals.m ├── model_center_of_mass.m ├── sample_model.m ├── model_center_of_bbox.m ├── RotateCameras.m ├── rotate_3D_model_around_z.m ├── make_empty_3D_model.m ├── generate_ssd_file_name_for_model.m ├── find_line_intersection.m ├── model_to_components.m ├── find_intersections_of_a_set_of_lines_IGNORE_Z.m ├── cache_file_is_there_and_newer_than.m ├── downsample_model.m ├── components_to_model.m ├── find_frame_file_names_and_viewpoints.m ├── model_index.m ├── transform_3d_rigid.m ├── load_camera_from_nvm.m ├── visualize_model_and_normals_2D.m ├── visualize_model_and_normals.m ├── pointcloud_to_voxels_2.m ├── export_3D_model.m ├── load_camera_v2.m └── import_3D_model.m ├── nply ├── nplyRead.mexa64 └── nplyWrite.mexa64 ├── segmentation ├── find_ground_plane.m ├── load_segmentation_transformation.m ├── remove_surrounding_ground_points_new.m ├── remove_clutter2.m ├── cut_clutter_out_of_camera_circle.m ├── remove_remaining_clutter_ssd_based.m ├── align_ground_plane_to_xy.m └── segment_the_model.m ├── extern ├── COMPUTE_mesh_normals │ ├── COMPUTE_mesh_normals │ │ ├── mesh_normals.png │ │ ├── EXAMPLE_mesh_normals.m │ │ ├── README.txt │ │ ├── PLOT_3D_stl_patch.m │ │ ├── WRITE_stl.m │ │ ├── COMPUTE_mesh_normals.m │ │ └── READ_stl.m │ └── license.txt └── ransac │ ├── fitplane.m │ ├── iscolinear.m │ ├── ransacfitplane.m │ └── ransac.m ├── bbox_from_3D ├── refine_bw_img_for_bbox.m ├── bbox_from_3d_single_frame.m └── back_project_3d_2d.m ├── ssd ├── refine_the_ssd.m ├── compute_mesh_normals.m └── ssd_the_model.m ├── visualize_all_aligned_models.m ├── 3D_reconst ├── is_reconstruction_good.m ├── sort_cameras.m ├── create_pairs_from_images_list.m ├── loop_closure_suggestions.m ├── estimate_object_center_based_on_cameras.m └── reconstruct_single_video.m ├── videos_list.txt ├── HOH ├── HOH_distance.m ├── EGI.m └── HOH.m ├── setup.m ├── generate_dataset_annotations.m ├── reconstruct_videos.m ├── optimize_viewpoints.m ├── 3d_model_matching └── match_two_cars_HOH_plus_GradientDescent.m ├── segment_the_models.m ├── README.md ├── main.m └── LICENSE /general_tools/model_has_faces.m: -------------------------------------------------------------------------------- 1 | function has = model_has_faces(model) 2 | has = isfield(model,'face'); -------------------------------------------------------------------------------- /nply/nplyRead.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmb-freiburg/unsup-car-dataset/HEAD/nply/nplyRead.mexa64 -------------------------------------------------------------------------------- /nply/nplyWrite.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmb-freiburg/unsup-car-dataset/HEAD/nply/nplyWrite.mexa64 -------------------------------------------------------------------------------- /general_tools/model_has_colors.m: -------------------------------------------------------------------------------- 1 | function r = model_has_colors(model) 2 | r = isfield(model,'r') && length(model.r) > 1; -------------------------------------------------------------------------------- /general_tools/model_has_normals.m: -------------------------------------------------------------------------------- 1 | function r = model_has_normals(model) 2 | r = isfield(model,'nx') && ~isempty(model.nx); -------------------------------------------------------------------------------- /general_tools/model_center_of_mass.m: -------------------------------------------------------------------------------- 1 | function c = model_center_of_mass(model) 2 | 3 | c = [mean(model.x),mean(model.y),mean(model.z)]; -------------------------------------------------------------------------------- /general_tools/sample_model.m: -------------------------------------------------------------------------------- 1 | function new_model = sample_model(model,sample_indices) 2 | new_model = model_index(model,sample_indices); 3 | -------------------------------------------------------------------------------- /segmentation/find_ground_plane.m: -------------------------------------------------------------------------------- 1 | function [P, inliers] = find_ground_plane(points,tolerance) 2 | 3 | [B, P, inliers] = ransacfitplane(points,tolerance); -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/mesh_normals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmb-freiburg/unsup-car-dataset/HEAD/extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/mesh_normals.png -------------------------------------------------------------------------------- /bbox_from_3D/refine_bw_img_for_bbox.m: -------------------------------------------------------------------------------- 1 | function [A,x,y] = refine_bw_img_for_bbox(A) 2 | 3 | A = imclose(A,strel('disk',4)); 4 | A = imerode(A,strel('diamond',2)); 5 | [y,x] = ind2sub(size(A),find(A)); -------------------------------------------------------------------------------- /general_tools/model_center_of_bbox.m: -------------------------------------------------------------------------------- 1 | function c = model_center_of_bbox(model) 2 | 3 | xc = mean(minmax(model.x)); 4 | yc = mean(minmax(model.y)); 5 | zc = mean(minmax(model.z)); 6 | 7 | c = [xc,yc,zc]; -------------------------------------------------------------------------------- /segmentation/load_segmentation_transformation.m: -------------------------------------------------------------------------------- 1 | function [R,T] = load_segmentation_transformation(filename) 2 | 3 | trans = importdata(filename); 4 | R = trans(1:3,1:3); 5 | T = trans(4:end,1); 6 | -------------------------------------------------------------------------------- /general_tools/RotateCameras.m: -------------------------------------------------------------------------------- 1 | function cameras = RotateCameras(cameras,R,T) 2 | 3 | for i = 1 : length(cameras) 4 | cameras{i}.Position = R*cameras{i}.Position+T; 5 | cameras{i}.HeadingVector = R*cameras{i}.HeadingVector; 6 | end -------------------------------------------------------------------------------- /general_tools/rotate_3D_model_around_z.m: -------------------------------------------------------------------------------- 1 | function new_model = rotate_3D_model_around_z(model,phi) 2 | 3 | r = [cosd(phi) -sind(phi) 0 ; sind(phi) cosd(phi) 0 ; 0 0 1]; 4 | new_model = transform_3d_rigid(model,r,[0;0;0]); 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /ssd/refine_the_ssd.m: -------------------------------------------------------------------------------- 1 | function ssd = refine_the_ssd(ssd,original_model) 2 | 3 | [~,D0] = knnsearch([original_model.x original_model.y original_model.z],[ssd.x ssd.y ssd.z]); 4 | ssd = sample_model(ssd, find(D0 < 5*median(D0))); 5 | 6 | 7 | -------------------------------------------------------------------------------- /general_tools/make_empty_3D_model.m: -------------------------------------------------------------------------------- 1 | function model = make_empty_3D_model() 2 | 3 | model.x = []; 4 | model.y = []; 5 | model.z = []; 6 | model.nx = []; 7 | model.ny = []; 8 | model.nz = []; 9 | model.r = []; 10 | model.g = []; 11 | model.b = []; 12 | -------------------------------------------------------------------------------- /segmentation/remove_surrounding_ground_points_new.m: -------------------------------------------------------------------------------- 1 | function [new_model, new_indices] = remove_surrounding_ground_points_new(model,th) 2 | 3 | ground_points = (model.z < th | ( abs(model.nz) > 0.5 & model.z < th) ); 4 | new_indices = ~(ground_points); 5 | 6 | new_model = model_index(model,new_indices); -------------------------------------------------------------------------------- /general_tools/generate_ssd_file_name_for_model.m: -------------------------------------------------------------------------------- 1 | function ssd_file_name = generate_ssd_file_name_for_model(ply_file_name,OC_level) 2 | 3 | if(exist('OC_level','var')) 4 | ssd_file_name = sprintf('%s.ssd%d.ply',ply_file_name ,OC_level); 5 | else 6 | ssd_file_name = sprintf('%s.ssd.ply',ply_file_name); 7 | end -------------------------------------------------------------------------------- /general_tools/find_line_intersection.m: -------------------------------------------------------------------------------- 1 | function X = find_line_intersection(P1,V1,P2,V2) 2 | 3 | if(length(P1) ~= 2) 4 | error('This function only works in 2D'); 5 | end 6 | 7 | A = [V1(1),-V2(1) ; V1(2) -V2(2)]; 8 | B = P2-P1; 9 | if(det(A) == 0) 10 | X = []; 11 | return; 12 | end 13 | 14 | K = inv(A)*B; 15 | 16 | X = P1 + K(1)*V1; 17 | -------------------------------------------------------------------------------- /general_tools/model_to_components.m: -------------------------------------------------------------------------------- 1 | function [x,y,z,nx,ny,nz,r,g,b] = model_to_components(model) 2 | x = model.x; 3 | y = model.y; 4 | z = model.z; 5 | 6 | if(nargout > 3) 7 | nx = model.nx; 8 | ny = model.ny; 9 | nz = model.nz; 10 | end 11 | 12 | if(nargout > 6) 13 | r = model.r; 14 | g = model.g; 15 | b = model.b; 16 | end 17 | -------------------------------------------------------------------------------- /visualize_all_aligned_models.m: -------------------------------------------------------------------------------- 1 | function visualize_all_aligned_models(ssd,ids,list,phi,e) 2 | 3 | n = length(list); 4 | for i = 1 : n 5 | model = ssd{list(i)}; 6 | model = rotate_3D_model_around_z(model,-phi(i)); 7 | 8 | subplot(4,ceil(n/4),i); 9 | visualize_model_and_normals(model); 10 | title(sprintf('%d(%d) -> e = %.2f',i,ids(list(i)),e(i))); 11 | axis tight 12 | axis equal 13 | view(2); 14 | end 15 | -------------------------------------------------------------------------------- /general_tools/find_intersections_of_a_set_of_lines_IGNORE_Z.m: -------------------------------------------------------------------------------- 1 | function X = find_intersections_of_a_set_of_lines_IGNORE_Z(P,V) 2 | 3 | X = {}; 4 | for i = 1 : length(P) 5 | for j = i+1 : length(P) 6 | p1 = P{i}(1:2); 7 | v1 = V{i}(1:2); 8 | p2 = P{j}(1:2); 9 | v2 = V{j}(1:2); 10 | x = find_line_intersection(p1,v1,p2,v2); 11 | if(~isempty(x)) 12 | X{end+1} = x; 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /general_tools/cache_file_is_there_and_newer_than.m: -------------------------------------------------------------------------------- 1 | function r = cache_file_is_there_and_newer_than(cache_file,main_file) 2 | 3 | r = false; 4 | main_info = dir(main_file); 5 | main_date = datenum(main_info.date); 6 | cache_info = dir(cache_file); 7 | if(~isempty(cache_info)) %if there is a cache file (and it is newer than the main file) 8 | cache_date = datenum(cache_info.date); 9 | if cache_date>main_date 10 | r = true; 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /3D_reconst/is_reconstruction_good.m: -------------------------------------------------------------------------------- 1 | function instability = is_reconstruction_good(cameras_file_name) 2 | 3 | [Cameras, nCameras] = load_camera_v2(cameras_file_name,1); 4 | 5 | pos = []; 6 | for i = 1 : length(Cameras) 7 | pos = [pos; Cameras{i}.Position']; 8 | end 9 | 10 | 11 | %--- Obtain a reliability measure 12 | v = -diff(pos); 13 | 14 | l = size(v,1); 15 | z = zeros(l,1); 16 | d = sum(v'.^2)'; %a measure of the step sizes through the video 17 | 18 | instability = (max(d)-median(d))/mean(d); 19 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/EXAMPLE_mesh_normals.m: -------------------------------------------------------------------------------- 1 | 2 | %Read an stl mesh: 3 | [coordVERTICES] = READ_stl('sample.stl'); 4 | 5 | %Compute the normals for each facet of the mesh: 6 | [coordNORMALSnew,coordVERTICESnew] = COMPUTE_mesh_normals(coordVERTICES); 7 | 8 | %Save the mesh to a new stl file: 9 | WRITE_stl('sample2.stl',coordVERTICESnew,coordNORMALSnew) 10 | 11 | %Plot the mesh with the normals: 12 | figure; 13 | PLOT_3D_stl_patch(coordVERTICESnew,coordNORMALSnew) 14 | -------------------------------------------------------------------------------- /3D_reconst/sort_cameras.m: -------------------------------------------------------------------------------- 1 | function new_cameras = sort_cameras(cameras) 2 | % This function sorts the cameras obtained from the function 3 | % "load_camera_v2" based on the frame file names. 4 | 5 | frame_id = []; 6 | for i = 1 : length(cameras) 7 | [pathstr, name, ext] = fileparts(cameras{i}.ImageFileName); 8 | frame_id = [frame_id ; {name}]; %the id is actually the name of the file (which we hope contains the right number in it) 9 | end 10 | [frame_id,idx] = sort(frame_id); 11 | 12 | new_cameras = cameras(idx); 13 | -------------------------------------------------------------------------------- /general_tools/downsample_model.m: -------------------------------------------------------------------------------- 1 | function new_model = downsample_model(model,st) 2 | 3 | new_model.x = model.x(1:st:end); 4 | new_model.y = model.y(1:st:end); 5 | new_model.z = model.z(1:st:end); 6 | if(isfield(model,'nx')) 7 | new_model.nx = model.nx(1:st:end); 8 | new_model.ny = model.ny(1:st:end); 9 | new_model.nz = model.nz(1:st:end); 10 | end 11 | if(isfield(model,'r')) 12 | new_model.r = model.r(1:st:end); 13 | new_model.g = model.g(1:st:end); 14 | new_model.b = model.b(1:st:end); 15 | end 16 | -------------------------------------------------------------------------------- /3D_reconst/create_pairs_from_images_list.m: -------------------------------------------------------------------------------- 1 | function list2 = create_pairs_from_images_list(list,range) 2 | 3 | if(size(range) == 1) 4 | list2 = {}; 5 | for i = 1:length(list) 6 | for j = 1 : min(range,length(list)-i) 7 | list2{end+1} = [list{i},' ',list{i+j}]; 8 | end 9 | end 10 | else %in this case we explicitly have the indices to the pairs 11 | list2 = {}; 12 | for i = 1:length(range) 13 | list2{end+1} = [list{range(i,1)},' ',list{range(i,2)}]; 14 | end 15 | end 16 | 17 | list2 = list2'; -------------------------------------------------------------------------------- /general_tools/components_to_model.m: -------------------------------------------------------------------------------- 1 | function model3d = components_to_model(x,y,z,nx,ny,nz,r,g,b) 2 | 3 | if(nargin == 1) 4 | y = x(:,2); 5 | z = x(:,3); 6 | if(size(x,2) > 3) 7 | nx = x(:,4); 8 | ny = x(:,5); 9 | nz = x(:,6); 10 | end 11 | x = x(:,1); 12 | end 13 | 14 | model3d.x = x; 15 | model3d.y = y; 16 | model3d.z = z; 17 | 18 | if(nargin > 3) 19 | model3d.nx = nx; 20 | model3d.ny = ny; 21 | model3d.nz = nz; 22 | end 23 | 24 | if(nargin > 6) 25 | model3d.r = r; 26 | model3d.g = g; 27 | model3d.b = b; 28 | end 29 | -------------------------------------------------------------------------------- /general_tools/find_frame_file_names_and_viewpoints.m: -------------------------------------------------------------------------------- 1 | function [frame_file_names,view_points] = find_frame_file_names_and_viewpoints(cameras,R,T,azimuth_correction) 2 | 3 | for i = 1 : numel(cameras) 4 | C = cameras{i}.Position; 5 | C = R*C+T; 6 | 7 | view_points(i,:) = atan2(C(2),C(1))*180/pi + azimuth_correction; % (i,:) is used only to make it a column vector 8 | frame_file_names(i,:) = {cameras{i}.ImageFileName}'; 9 | 10 | end 11 | 12 | view_points(view_points < 0) = view_points(view_points < 0)+360; 13 | view_points(view_points > 360) = view_points(view_points > 360)-360; 14 | -------------------------------------------------------------------------------- /3D_reconst/loop_closure_suggestions.m: -------------------------------------------------------------------------------- 1 | function [p,q] = loop_closure_suggestions(nvm_file,max_distance_percentage,min_index_distance) 2 | 3 | Cameras = load_camera_from_nvm(nvm_file,1); 4 | 5 | pos = []; 6 | for i = 1 : length(Cameras) 7 | pos = [pos; Cameras{i}.Position']; 8 | end 9 | 10 | 11 | % find the distances 12 | [x1,x2] = meshgrid(pos(:,1),pos(:,1)); 13 | [y1,y2] = meshgrid(pos(:,2),pos(:,2)); 14 | [z1,z2] = meshgrid(pos(:,3),pos(:,3)); 15 | d = sqrt( (x1-x2).^2 + (y1-y2).^2 + (z1-z2).^2 ); 16 | 17 | % pick the appropriate ones 18 | M = max(d(:)); 19 | f = find(d < M*max_distance_percentage); 20 | [i,j] = ind2sub(size(d),f); 21 | ind = (abs(i-j) > min_index_distance) & (j > i); 22 | p = i(ind); 23 | q = j(ind); -------------------------------------------------------------------------------- /general_tools/model_index.m: -------------------------------------------------------------------------------- 1 | function new_model = model_index(model,index) 2 | 3 | new_model = make_empty_3D_model(); 4 | 5 | new_model.x = model.x(index); 6 | new_model.y = model.y(index); 7 | new_model.z = model.z(index); 8 | 9 | if(model_has_normals(model)) 10 | new_model.nx = model.nx(index); 11 | new_model.ny = model.ny(index); 12 | new_model.nz = model.nz(index); 13 | end 14 | if(model_has_colors(model)) 15 | new_model.r = model.r(index); 16 | new_model.g = model.g(index); 17 | new_model.b = model.b(index); 18 | end 19 | if(model_has_faces(model)) 20 | if(islogical(index)) 21 | index = find(index); 22 | end 23 | new_model.face = keep_valid_faces(model.face,index); 24 | end -------------------------------------------------------------------------------- /general_tools/transform_3d_rigid.m: -------------------------------------------------------------------------------- 1 | function model3d_new = transform_3d_rigid(model3d,R,T) 2 | 3 | if(model_has_normals(model3d)) 4 | [x,y,z,nx,ny,nz] = model_to_components(model3d); 5 | else 6 | [x,y,z] = model_to_components(model3d); 7 | end 8 | 9 | xyz = [x y z]; 10 | xyz = xyz * R'; 11 | xyz = xyz + repmat(T',length(x),1); 12 | 13 | if(model_has_normals(model3d)) 14 | nxyz = [nx ny nz] * R'; 15 | model3d_new = components_to_model(xyz(:,1),xyz(:,2),xyz(:,3),nxyz(:,1),nxyz(:,2),nxyz(:,3)); 16 | else 17 | model3d_new = components_to_model(xyz(:,1),xyz(:,2),xyz(:,3),[],[],[]); 18 | end 19 | 20 | 21 | if(model_has_colors(model3d)) 22 | model3d_new.r = model3d.r; 23 | model3d_new.g = model3d.g; 24 | model3d_new.b = model3d.b; 25 | end 26 | 27 | if(model_has_faces(model3d)) 28 | model3d_new.face = model3d.face; 29 | end -------------------------------------------------------------------------------- /3D_reconst/estimate_object_center_based_on_cameras.m: -------------------------------------------------------------------------------- 1 | function C0 = estimate_object_center_based_on_cameras(cameras,model,Rot,Trans) 2 | 3 | for i = 1 : length(cameras) 4 | C = cameras{i}.Position; 5 | C = Rot*C+Trans; 6 | P{i} = C; 7 | 8 | h = cameras{i}.HeadingVector; 9 | h = Rot*h; 10 | H{i} = h; 11 | end 12 | 13 | XX = find_intersections_of_a_set_of_lines_IGNORE_Z(P,H); 14 | 15 | % try to remove the too distant instances (outliers) 16 | X = cell2mat(XX); 17 | X = X'; 18 | Xm = mean(X); 19 | 20 | d = X - repmat(Xm,size(X,1),1); 21 | d = abs(d); 22 | 23 | md = mean(d); 24 | md = mean(md); 25 | 26 | f = (d(:,1) > 3*md) | (d(:,2) > 3*md); 27 | f = find(f); 28 | 29 | XX(f) = []; 30 | 31 | % find the mean of all intersection points 32 | X = cell2mat(XX); 33 | X = X'; 34 | C0 = mean(X)'; 35 | 36 | %--- And finally we estimate the z element, based on the model points 37 | C0(3) = mean(model.z); -------------------------------------------------------------------------------- /bbox_from_3D/bbox_from_3d_single_frame.m: -------------------------------------------------------------------------------- 1 | function [bbox, truncated] = bbox_from_3d_single_frame(model,camera) 2 | 3 | try 4 | frame_size = size(imread(camera.ImageFileName)); 5 | catch 6 | error('failed to read the image'); 7 | end 8 | 9 | [A,xp,yp, truncated] = back_project_3d_2d(model,camera,frame_size); 10 | [A] = refine_bw_img_for_bbox(A); 11 | 12 | [yy,xx] = ind2sub(size(A),find(A)); 13 | bbox.x1 = min(xx); 14 | bbox.y1 = min(yy); 15 | bbox.x2 = max(xx); 16 | bbox.y2 = max(yy); 17 | 18 | if(bbox.x1 < 1); 19 | bbox.x1 = 1; 20 | truncated = 1; 21 | end; 22 | 23 | if(bbox.y1 < 1); 24 | bbox.y1 = 1; 25 | truncated = 1; 26 | end; 27 | 28 | if(bbox.x2 > frame_size(2)); 29 | bbox.x2 = frame_size(2); 30 | truncated = 1; 31 | end; 32 | 33 | if(bbox.y2 > frame_size(1)); 34 | bbox.y2 = frame_size(1); 35 | truncated = 1; 36 | end; 37 | 38 | bbox.w = bbox.x2 - bbox.x1 + 1; 39 | bbox.h = bbox.y2 - bbox.y1 + 1; 40 | 41 | -------------------------------------------------------------------------------- /videos_list.txt: -------------------------------------------------------------------------------- 1 | videos/car001 2 | videos/car002 3 | videos/car003 4 | videos/car004 5 | videos/car005 6 | videos/car006 7 | videos/car007 8 | videos/car008 9 | videos/car009 10 | videos/car010 11 | videos/car011 12 | videos/car012 13 | videos/car013 14 | videos/car014 15 | videos/car015 16 | videos/car016 17 | videos/car017 18 | videos/car018 19 | videos/car019 20 | videos/car020 21 | videos/car021 22 | videos/car022 23 | videos/car023 24 | videos/car024 25 | videos/car025 26 | videos/car026 27 | videos/car027 28 | videos/car028 29 | videos/car029 30 | videos/car030 31 | videos/car031 32 | videos/car032 33 | videos/car033 34 | videos/car034 35 | videos/car035 36 | videos/car036 37 | videos/car037 38 | videos/car038 39 | videos/car039 40 | videos/car040 41 | videos/car041 42 | videos/car042 43 | videos/car043 44 | videos/car044 45 | videos/car045 46 | videos/car046 47 | videos/car047 48 | videos/car048 49 | videos/car049 50 | videos/car050 51 | videos/car051 52 | videos/car052 53 | -------------------------------------------------------------------------------- /general_tools/load_camera_from_nvm.m: -------------------------------------------------------------------------------- 1 | % Inspired by: http://buntworthy.github.io/Read-VisualSFM-NVM-file/ 2 | function Cameras = load_camera_from_nvm(filename,do_sort) 3 | 4 | if(~exist('do_sort','var')) 5 | do_sort = 1; %by default we want sorting 6 | end 7 | 8 | fid = fopen(filename); 9 | c = textscan(fid,'%s'); 10 | 11 | if strcmp(c{1}(2),'FixedK') 12 | offset = 9; 13 | numCameras = str2double(c{1}(8)); 14 | else 15 | offset = 3; 16 | numCameras = str2double(c{1}(2)); 17 | end 18 | 19 | for i = 1:numCameras 20 | Cameras{i}.ImageFileName = c{1}{11*(i-1) + offset}; 21 | Cameras{i}.FocalLength = str2double(c{1}(11*(i-1) + offset + 1)); 22 | Cameras{i}.RQuaternation = str2double(c{1}(11*(i-1) + offset + 2:11*(i-1) + offset + 5)); 23 | Cameras{i}.Position = str2double(c{1}(11*(i-1) + offset + 6:11*(i-1) + offset + 8)); 24 | %Cameras{i}.distortion = str2double(c{1}(11*(i-1) + offset + 9)); 25 | end 26 | 27 | 28 | if(do_sort) 29 | Cameras = sort_cameras(Cameras); 30 | end -------------------------------------------------------------------------------- /HOH/HOH_distance.m: -------------------------------------------------------------------------------- 1 | function dist = HOH_distance(f1,f2) 2 | 3 | if(~iscell(f1) && ~isstruct(f1)) %single_vector fast comparison 4 | fm1 = f1; 5 | fm2 = f2; 6 | else 7 | 8 | if(~isfield(f1,'main')) %Then it is in the old-style. 9 | f = f1; 10 | f1.main = f(1); 11 | f1.child = f(2:end); 12 | end 13 | 14 | if(~isfield(f2,'main')) %Then it is in the old-style. 15 | f = f2; 16 | f2.main = f(1); 17 | f2.child = f(2:end); 18 | end 19 | 20 | if(~isempty(f1.child)) 21 | fc1 = cell2mat(f1.child(:)); 22 | fm1 = [f1.main{1}(:); fc1(:)]; 23 | else 24 | fm1 = [f1.main{1}(:)]; 25 | end 26 | 27 | if(~isempty(f2.child)) 28 | fc2 = cell2mat(f2.child(:)); 29 | fm2 = [f2.main{1}(:); fc2(:)]; 30 | else 31 | fm2 = [f2.main{1}(:)]; 32 | end 33 | 34 | end 35 | 36 | %-- Chi-squared distance 37 | dist = sum( (fm1(:) - fm2(:)).^2 ./ (fm1(:) + fm2(:) + 1e-20) ); 38 | dist = dist ./ length(fm1); -------------------------------------------------------------------------------- /segmentation/remove_clutter2.m: -------------------------------------------------------------------------------- 1 | function [new_model, new_indices] = remove_clutter2(model) 2 | 3 | if(isempty(model.x)) 4 | new_model = model; 5 | new_indices = []; 6 | return; 7 | end 8 | 9 | [x y z] = model_to_components(model); 10 | 11 | [A,ax,bx,ay,by,az,bz] = pointcloud_to_voxels_2(x,y,z,200,1); 12 | 13 | t = sum(A,3); 14 | 15 | foreground_mask = imerode(t,strel('disk',4)); 16 | 17 | foreground_mask = imdilate(foreground_mask,strel('disk',4)); 18 | 19 | foreground_mask = im2bw(foreground_mask); 20 | 21 | CC = bwconncomp(foreground_mask); 22 | numPixels = cellfun(@numel,CC.PixelIdxList); 23 | [~,idx] = max(numPixels); 24 | foreground_mask = zeros(size(foreground_mask)); 25 | foreground_mask(CC.PixelIdxList{idx}) = 1; 26 | 27 | B = A; 28 | for iz = 1 : size(B,3) 29 | B(:,:,iz) = B(:,:,iz) .* foreground_mask; 30 | end 31 | 32 | X2 = round(ax*(x+bx)+1); 33 | Y2 = round(ay*(y+by)+1); 34 | Z2 = round(az*(z+bz)+1); 35 | 36 | i = sub2ind(size(B),X2,Y2,Z2); 37 | new_indices = B(i)>0; 38 | 39 | new_model = model_index(model,new_indices); -------------------------------------------------------------------------------- /segmentation/cut_clutter_out_of_camera_circle.m: -------------------------------------------------------------------------------- 1 | function [new_model,Ng,inliers] = cut_clutter_out_of_camera_circle(model,cams) 2 | 3 | for i = 1 : length(cams) 4 | P{i} = cams{i}.Position; 5 | V{i} = cams{i}.HeadingVector; 6 | end 7 | 8 | C = mean(cell2mat(P)'); 9 | 10 | sV = 0; 11 | for i = 1 : length(V) 12 | for j = i : length(V) 13 | sV = sV + cross(V{i},V{j}); 14 | end 15 | end 16 | 17 | Ng = sV ./ norm(sV); 18 | 19 | for i = 1 : length(cams) 20 | r(i) = norm(P{i} - C'); 21 | end 22 | 23 | 24 | D = [model.x-C(1),model.y-C(2),model.z-C(3)]; 25 | NN = repmat(Ng',size(D,1),1); 26 | d = cross(NN,D); 27 | d2 = sum(d.^2,2); 28 | 29 | new_model = make_empty_3D_model(); 30 | thresh = max(r).^2 * .8; 31 | ind = d2 0) 37 | hold on; 38 | h_normals = quiver(model.x,model.y,model.nx,model.ny,vector_scale); 39 | end 40 | 41 | 42 | xlabel('x'); 43 | ylabel('y'); -------------------------------------------------------------------------------- /general_tools/visualize_model_and_normals.m: -------------------------------------------------------------------------------- 1 | function [h,h_normals] = visualize_model_and_normals(model,vector_scale,NS,point_size,color) 2 | 3 | if(~exist('vector_scale','var')) 4 | vector_scale = 0; 5 | end 6 | 7 | if(~exist('NS','var')) 8 | NS = 1000; 9 | end 10 | 11 | if(~exist('point_size','var')) 12 | point_size = 20; 13 | end 14 | 15 | if(~exist('color','var')) 16 | color = 'black'; 17 | end 18 | 19 | % Downsample the model to have a lighter version of it 20 | N = length(model.x); 21 | 22 | if(NS ~= 0) 23 | st = ceil(N/NS); 24 | model = downsample_model(model,st); 25 | end 26 | 27 | %--- display the model and the vectors 28 | if(isfield(model,'r') && ~isempty(model.r)) 29 | h = scatter3(model.x,model.y,model.z,point_size,[model.r ,model.g ,model.b ],'fill'); 30 | 31 | else 32 | h = scatter3(model.x,model.y,model.z,point_size,color,'fill'); 33 | end 34 | 35 | if(vector_scale > 0) 36 | hold on; 37 | h_normals = quiver3(model.x,model.y,model.z,model.nx,model.ny,model.nz,vector_scale); 38 | end 39 | 40 | 41 | xlabel('x'); 42 | ylabel('y'); 43 | zlabel('z'); -------------------------------------------------------------------------------- /bbox_from_3D/back_project_3d_2d.m: -------------------------------------------------------------------------------- 1 | function [A,xp,yp, truncated] = back_project_3d_2d(model,camera,image_size) 2 | 3 | xyz = [model.x model.y model.z]; 4 | 5 | f = camera.FocalLength; 6 | K = [f, 0 0; 0 f 0; 0 0 1]; 7 | 8 | % here we do the real back-projection 9 | proj = xyz*camera.R'; 10 | proj = proj + repmat(camera.T',length(model.x),1); 11 | proj = proj*K'; 12 | 13 | % extract out the 3 components 14 | xp = proj(:,1); 15 | yp = proj(:,2); 16 | zp = proj(:,3); 17 | 18 | % find the principal points 19 | mx = camera.PrincipalPoint(1); 20 | my = camera.PrincipalPoint(2); 21 | 22 | xp = xp./zp+mx; 23 | yp = yp./zp+my; 24 | 25 | truncated = 0; 26 | if(any(xp < 1)); 27 | xp(xp<1)=1; 28 | truncated = 1; 29 | end; 30 | 31 | if(any(yp < 1)); 32 | yp(yp<1)=1; 33 | truncated = 1; 34 | end; 35 | 36 | if(any(xp > image_size(2))); 37 | xp(xp> image_size(2)) = image_size(2); 38 | truncated = 1; 39 | end; 40 | 41 | if(any(yp > image_size(1))); 42 | yp(yp> image_size(1)) = image_size(1); 43 | truncated = 1; 44 | end; 45 | 46 | 47 | A = zeros(image_size(1),image_size(2)); 48 | ind = sub2ind(size(A),round(yp),round(xp)); 49 | A(ind) = 1; 50 | -------------------------------------------------------------------------------- /setup.m: -------------------------------------------------------------------------------- 1 | %---- Setup the paths 2 | addpath('nply/'); 3 | addpath('general_tools/'); 4 | addpath('3D_reconst/'); 5 | addpath('segmentation/'); 6 | addpath('ssd/'); 7 | addpath('HOH/'); 8 | addpath('3d_model_matching/'); 9 | addpath('bbox_from_3D/'); 10 | 11 | addpath('extern/ransac/'); 12 | addpath('extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/'); 13 | 14 | %---- 15 | videos_list_file = 'videos_list.txt'; 16 | reconstructions_destination = 'temp_reconstructions_cars/r120_3_5/car%03d'; 17 | reconstructions_on_cluster = 0; 18 | annot_dir = './temp_annotations'; 19 | 20 | %-- HOH parameters 21 | settings.HOH.root_phi_divisions = 32; 22 | settings.HOH.root_theta_divisions = 8; 23 | settings.HOH.nchildren = 8; 24 | settings.HOH.child_phi_divisions = 16; 25 | settings.HOH.child_theta_divisions = 4; 26 | 27 | %-- A manually defined phi value to be dedeucted from the floating phi 28 | % values (i.e. define the ground 0 viewpoint) 29 | manual_rotation = 0; 30 | 31 | %-- Try to generate deterministic (reproducible) results. 32 | % Note that there are other random-ness sources in the process, which 33 | % should be taken care of, to get exact same results in multuple 34 | % runs -- more specifically the ones in the VSFM package. 35 | settings.deterministic = 1; -------------------------------------------------------------------------------- /ssd/compute_mesh_normals.m: -------------------------------------------------------------------------------- 1 | function new_model = compute_mesh_normals(model) 2 | 3 | meshdata.vertices = [model.x,model.y,model.z]; 4 | meshdata.faces = cell2mat(model.face.vertex_indices)+1; 5 | normals = COMPUTE_mesh_normals(meshdata); 6 | 7 | N = size(meshdata.faces,1); %number of faces 8 | for i = 1 : N 9 | p = meshdata.vertices(meshdata.faces(i,1),:); 10 | p = p + meshdata.vertices(meshdata.faces(i,2),:); 11 | p = p + meshdata.vertices(meshdata.faces(i,3),:); 12 | 13 | p = p/3; 14 | 15 | new_model.x(i,:) = p(1); 16 | new_model.y(i,:) = p(2); 17 | new_model.z(i,:) = p(3); 18 | new_model.nx(i,:) = normals(i,1); 19 | new_model.ny(i,:) = normals(i,2); 20 | new_model.nz(i,:) = normals(i,3); 21 | 22 | if(model_has_colors(model)) 23 | c = [model.r(meshdata.faces(i,1)) model.g(meshdata.faces(i,1)) model.b(meshdata.faces(i,1))]; 24 | c = c + [model.r(meshdata.faces(i,2)) model.g(meshdata.faces(i,2)) model.b(meshdata.faces(i,2))]; 25 | c = c + [model.r(meshdata.faces(i,3)) model.g(meshdata.faces(i,3)) model.b(meshdata.faces(i,3))]; 26 | c = c ./ 3; 27 | 28 | new_model.r(i,:) = c(1); 29 | new_model.g(i,:) = c(2); 30 | new_model.b(i,:) = c(3); 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /general_tools/pointcloud_to_voxels_2.m: -------------------------------------------------------------------------------- 1 | function [voxel_model,ax,bx,ay,by,az,bz] = pointcloud_to_voxels_2(X,Y,Z,N,keep_aspect_ratio) 2 | 3 | if(~exist('keep_aspect_ratio','var')) 4 | keep_aspect_ratio = 0; 5 | end 6 | 7 | if(isempty(X)) 8 | warning('warning(pointcloud_to_voxels_2): empty data passed to this function! Aborting...'); 9 | voxel_model = []; 10 | ax = []; 11 | bx = []; 12 | ay = []; 13 | by = []; 14 | az = []; 15 | bz = []; 16 | end 17 | 18 | if(keep_aspect_ratio) 19 | maxmax = max([max(X)-min(X),max(Y)-min(Y),max(Z)-min(Z)]); 20 | 21 | N1 = round(N*(max(X)-min(X))/maxmax); 22 | N2 = round(N*(max(Y)-min(Y))/maxmax); 23 | N3 = round(N*(max(Z)-min(Z))/maxmax); 24 | voxel_model = zeros(N1,N2,N3); 25 | 26 | ax = 1/(max(X)-min(X))*(N1-1); 27 | bx = -min(X); 28 | ay = 1/(max(Y)-min(Y))*(N2-1); 29 | by = -min(Y); 30 | az = 1/(max(Z)-min(Z))*(N3-1); 31 | bz = -min(Z); 32 | else 33 | voxel_model = zeros(N,N,N); 34 | 35 | ax = 1/(max(X)-min(X))*(N-1); 36 | bx = -min(X); 37 | ay = 1/(max(Y)-min(Y))*(N-1); 38 | by = -min(Y); 39 | az = 1/(max(Z)-min(Z))*(N-1); 40 | bz = -min(Z); 41 | end 42 | 43 | X2 = round(ax*(X+bx))+1; 44 | Y2 = round(ay*(Y+by))+1; 45 | Z2 = round(az*(Z+bz))+1; 46 | 47 | voxel_model(sub2ind(size(voxel_model),X2,Y2,Z2)) = 1; 48 | -------------------------------------------------------------------------------- /general_tools/export_3D_model.m: -------------------------------------------------------------------------------- 1 | function export_3D_model(model,FileName,psz,should_diffuse) 2 | 3 | if(isempty(model)) 4 | disp('Error: export_3D_model: Can not write empty data to a file. Skipping.'); 5 | return; 6 | end 7 | 8 | if(~exist('psz','var')) 9 | psz = []; 10 | end 11 | 12 | if(~exist('should_diffuse','var')) 13 | should_diffuse = 0; 14 | end 15 | 16 | data.vertex.x = model.x; 17 | data.vertex.y = model.y; 18 | data.vertex.z = model.z; 19 | 20 | data.vertex.nx = model.nx; 21 | data.vertex.ny = model.ny; 22 | data.vertex.nz = model.nz; 23 | 24 | if(model_has_colors(model)) 25 | if(should_diffuse) 26 | data.vertex.diffuse_red = model.r*255; 27 | data.vertex.diffuse_green = model.g*255; 28 | data.vertex.diffuse_blue = model.b*255; 29 | else 30 | data.vertex.red = model.r*255; 31 | data.vertex.green = model.g*255; 32 | data.vertex.blue = model.b*255; 33 | end 34 | end 35 | 36 | if(~isempty(psz)) 37 | data.vertex.psz = 0.1*ones(length(model.x),1); 38 | end 39 | 40 | if(isfield(model,'face')) 41 | data.face = model.face; 42 | end 43 | 44 | [pathstr, name, ext] = fileparts(FileName); 45 | 46 | if(strcmpi(ext,'.ply')) 47 | nplyWrite(FileName,data); 48 | elseif(strcmpi(ext,'.mat')) 49 | save(FileName,'data'); 50 | else 51 | error('export_3D_model: Invalid file extension'); 52 | end 53 | 54 | 55 | -------------------------------------------------------------------------------- /generate_dataset_annotations.m: -------------------------------------------------------------------------------- 1 | function generate_dataset_annotations(models,phi_abs,inliers_list_index,ids,scenes_dir_list,annot_dir) 2 | 3 | if(exist(annot_dir,'dir')) 4 | error('Annotations directory already exists! You probably don''t want to overwrite it'); 5 | end 6 | 7 | mkdir(annot_dir); 8 | 9 | parfor i = 1:length(inliers_list_index) 10 | fprintf('Generating annotations file #%d \n',ids(inliers_list_index(i))); 11 | 12 | model = models{inliers_list_index(i)}; 13 | 14 | base_path = scenes_dir_list{inliers_list_index(i)}; 15 | segment_dir = [base_path,'/refined/']; 16 | transformation_file_name = [segment_dir,'/transformation_parameters.txt']; 17 | camera_file = [base_path,'/results.nvm.cmvs/00/cameras_v2.txt']; 18 | 19 | [R,T] = load_segmentation_transformation(transformation_file_name); 20 | 21 | % transform back the model to its original position 22 | model = transform_3d_rigid(model,R',-R'*T); 23 | 24 | cameras = load_camera_v2(camera_file,1); 25 | [frame_file_names,view_points] = find_frame_file_names_and_viewpoints(cameras,R,T,-phi_abs(i)); 26 | 27 | 28 | fp = fopen(sprintf('%s/%d_annot.txt',annot_dir,ids(inliers_list_index(i))),'wt'); 29 | for c = 1 : length(cameras) %--- loop on all the frames 30 | [bbox,trunc] = bbox_from_3d_single_frame(model,cameras{c}); 31 | fprintf(fp,'%s\t%d %d %d %d\t%d\n',frame_file_names{c},bbox.x1,bbox.y1,bbox.x2,bbox.y2,wrapTo360(round(view_points(c)))); 32 | end 33 | fclose(fp); 34 | 35 | end 36 | -------------------------------------------------------------------------------- /general_tools/load_camera_v2.m: -------------------------------------------------------------------------------- 1 | function [Cameras, nCameras] = load_camera_v2(FileName,do_sort) 2 | 3 | if(~exist('do_sort','var')) 4 | do_sort = 1; %by default we want sorting 5 | end 6 | 7 | fp = fopen(FileName); 8 | if(fp == -1) 9 | error(['Could not open the file: ' FileName]); 10 | return; 11 | end 12 | 13 | for i = 1:16, 14 | fgets(fp); 15 | end; %skip the header 16 | 17 | nCameras = fscanf(fp,'%d',1); 18 | 19 | for i = 1 : nCameras 20 | fgets(fp); %this is needed! 21 | fgets(fp); %skip the empty line 22 | fgets(fp); %skip the non-useful filename 23 | 24 | Cameras{i}.ImageFileName = fgetl(fp); 25 | Cameras{i}.FocalLength = fscanf(fp,'%f',1); 26 | Cameras{i}.PrincipalPoint = fscanf(fp,'%d',2); 27 | Cameras{i}.T = fscanf(fp,'%f',3); 28 | Cameras{i}.Position = fscanf(fp,'%f',3); 29 | Cameras{i}.RByAxisAngle = fscanf(fp,'%f',3); 30 | Cameras{i}.RQuaternation = fscanf(fp,'%f',4); 31 | 32 | R = fscanf(fp,'%f',9); 33 | Cameras{i}.R = reshape(R,3,3)'; 34 | Cameras{i}.NormRadialDistortion = fscanf(fp,'%f',1); 35 | Cameras{i}.EXIFPos = fscanf(fp,'%f',3); 36 | 37 | C = Cameras{i}.Position; 38 | R = Cameras{i}.R; 39 | T = Cameras{i}.T; 40 | f = Cameras{i}.FocalLength; 41 | K = [f 0 0 ; 0 f 0; 0 0 1]; 42 | heading_point = inv(R) * (inv(K)*[0;0;1] - T); 43 | heading_vector = heading_point-C; 44 | heading_vector = heading_vector / norm(heading_vector); 45 | Cameras{i}.HeadingVector = heading_vector; 46 | 47 | end 48 | 49 | fclose(fp); 50 | 51 | if(do_sort) 52 | Cameras = sort_cameras(Cameras); 53 | end 54 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Adam H. Aitkenhead 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | * Neither the name of the The Christie NHS Foundation Trust nor the names 14 | of its contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /reconstruct_videos.m: -------------------------------------------------------------------------------- 1 | function [scenes_dir_list,ids] = reconstruct_videos(videos_list_files,reconstructions_destination,reconst_params,on_cluster) 2 | 3 | if(~exist('reconst_params','var')), 4 | reconst_params = [inf 3 5]; 5 | end; 6 | 7 | if(~exist('on_cluster','var')) 8 | on_cluster = 0; 9 | end; 10 | 11 | %--- Load the video list 12 | fid = fopen(videos_list_files,'rt'); 13 | list = textscan(fid,'%s\n'); 14 | list = list{1}; 15 | fclose(fid); 16 | 17 | %--- Reconstruct them all 18 | nFrames = reconst_params(1); 19 | ranges = reconst_params(2:3); 20 | wait_files = {}; 21 | for i = 1 : numel(list) 22 | destination_dir = sprintf(reconstructions_destination,i); 23 | scenes_dir_list{i,:} = destination_dir; 24 | ids(i) = i; 25 | 26 | %--- Skip if the folder currently exists 27 | if(exist(destination_dir,'dir')) 28 | fprintf(['!Skipping. Reconstruction destination dir already contains nvm files: ' destination_dir '\n']); 29 | continue; 30 | end 31 | 32 | 33 | %--- Do the real reconstruction (on cluster or locally) 34 | if(on_cluster) 35 | wait_files{i} = cluster_submit_job(); % You should implement this function based on your own cluster manager's settings. 36 | fprintf('Job #%d submitted.\n',i); 37 | else 38 | tic 39 | reconstruct_single_video(list{i},destination_dir,nFrames,ranges); 40 | fprintf('\n\nsingle video reconstruction took %d seconds\n\n',toc); 41 | end 42 | 43 | end 44 | 45 | if(on_cluster) 46 | tic 47 | fprintf('Waiting for jobs to complete...\n'); 48 | wait_for_files(wait_files); 49 | fprintf('Done. All reconstructions together took %d seconds\n\n',toc); 50 | end -------------------------------------------------------------------------------- /HOH/EGI.m: -------------------------------------------------------------------------------- 1 | function [f] = EGI(model,n_phi,n_theta,normalize,min_points) 2 | 3 | if(~exist('min_points','var')) 4 | min_points = 0; 5 | end 6 | 7 | % suppress models with too few points 8 | if isempty(model.x) || numel(model.x) < min_points 9 | f = zeros(n_theta,n_phi); 10 | return; 11 | end 12 | 13 | [x,y,z,nx,ny,nz] = model_to_components(model); 14 | 15 | phi = atan2(ny,nx); 16 | phi = phi + pi; %now phi is in [0,2pi] 17 | phi_ = ceil(phi/(2*pi/n_phi)); % Discretization 18 | phi_(phi_ == 0) = 1; % This is to put all the numbers in [1,n]. 19 | 20 | theta = atan(nz./(sqrt(nx.^2+ny.^2))); 21 | theta = theta + pi/2; %now theta is in [0,pi] 22 | theta_ = ceil(theta/(pi/n_theta)); % Discretization 23 | theta_(theta_ == 0) = 1;% This is to put all the numbers in [1,n]. 24 | 25 | %--- Do the binning 26 | f = zeros(n_theta,n_phi); 27 | for i = 1 : n_phi 28 | p_ = (phi_ == i); 29 | for j = 1 : n_theta 30 | f(j,i) = sum( p_ & (theta_ == j) ); 31 | end 32 | end 33 | 34 | %--- Do the cell weighting 35 | mid_angle = (pi/n_theta)*( (1:n_theta)-0.5 ); 36 | w = abs(sin(mid_angle)); 37 | w2d = meshgrid(w,1:n_phi)'; 38 | f = f .* w2d; 39 | 40 | %--- Do smoothing (circular in phi, simple in Theta direction) 41 | if(1) 42 | %smoothing kernel 43 | krn = fspecial('gaussian',[3 3], 0.7); 44 | 45 | %do some padding 46 | f = [f(:,end) f f(:,1)]; 47 | f = [f(1,:) ; f ; f(end,:)]; 48 | 49 | %apply the kernel 50 | f = conv2(f,krn,'same'); 51 | 52 | %undo the padding 53 | f = f(2:end-1,2:end-1); 54 | end 55 | 56 | %--- Do normalization 57 | switch(normalize) 58 | case 1 59 | f = f ./ sum(f(:)); 60 | case 2 61 | f = f ./ max(f(:)); 62 | end 63 | -------------------------------------------------------------------------------- /HOH/HOH.m: -------------------------------------------------------------------------------- 1 | function [f,f_better_structure,f_single_vector] = HOH(model,model_center,n_phi0,n_theta0,nChilds,n_phi,... 2 | n_theta,do_gridding,do_normalize_each_item,do_normalize_hier,min_points_per_child) 3 | 4 | if(~exist('do_normalize_each_item','var')) 5 | do_normalize_each_item = 1; 6 | end 7 | 8 | if(~exist('do_normalize_hier','var')) 9 | do_normalize_hier = 0; 10 | end 11 | 12 | if(~exist('min_points_per_child','var')) 13 | min_points_per_child = 0; 14 | end 15 | 16 | 17 | %--- Obtian the root feature 18 | f{1} = EGI(model,n_phi0,n_theta0,do_normalize_each_item); 19 | f_single_vector = f{1}(:); 20 | 21 | %--- do the slicing and obtain the features 22 | [x,y,z,nx,ny,nz] = model_to_components(model); 23 | 24 | if(isempty(model_center)) 25 | 26 | error('Object centroid missing -- not computed inside this function any more'); 27 | 28 | % Find the center of the containing box. 29 | xc = mean(minmax(x')); 30 | yc = mean(minmax(y')); 31 | zc = mean(minmax(z')); 32 | else 33 | xc = model_center(1); 34 | yc = model_center(2); 35 | zc = model_center(3); 36 | end 37 | 38 | x = x-xc; 39 | y = y-yc; 40 | z = z-zc; 41 | 42 | phi = atan2(y,x); 43 | phi = wrapTo2Pi(phi); 44 | phi_ = ceil(phi/(2*pi/nChilds)); % Discretization 45 | phi_(phi_ == 0) = 1; % This is to put all the numbers in [1,n]. 46 | 47 | for i = 1 : nChilds 48 | ind = find(phi_ == i); 49 | 50 | f{i+1} = EGI(components_to_model(x(ind),y(ind),z(ind),nx(ind),ny(ind),nz(ind)),n_phi,n_theta,do_normalize_each_item,min_points_per_child); 51 | f{i+1} = 1 * f{i+1}; 52 | f_single_vector = cat(1,f_single_vector,f{i+1}(:)); 53 | end 54 | 55 | 56 | f_better_structure.main = f(1); 57 | f_better_structure.child = f(2:end); -------------------------------------------------------------------------------- /segmentation/remove_remaining_clutter_ssd_based.m: -------------------------------------------------------------------------------- 1 | function [new_model, new_indices] = remove_remaining_clutter_ssd_based(model,cameras,R,T) 2 | 3 | if(isempty(model.x)) 4 | warning('empty data passed to this function! Aborting...'); 5 | new_points = []; 6 | new_indices = []; 7 | end 8 | 9 | %--- ssd the model 10 | ssd = ssd_the_model(model,'',9); 11 | 12 | %--- Find the biggest component in the ssd 13 | [x, y, z] = model_to_components(ssd); 14 | [A,ax,bx,ay,by,az,bz] = pointcloud_to_voxels_2(x,y,z,200,1); 15 | 16 | B = A; 17 | 18 | %find the estimated object center 19 | C0 = estimate_object_center_based_on_cameras(cameras,model,R,T); 20 | XC = round(ax*(C0(1)+bx)+1); 21 | YC = round(ay*(C0(2)+by)+1); 22 | ZC = round(az*(C0(3)+bz)+1); 23 | 24 | % Find the connected components and pick the best one 25 | CC = bwconncomp(B); 26 | if CC.NumObjects 27 | for i = 1 : CC.NumObjects 28 | ind = CC.PixelIdxList{i}; 29 | numPixels(i) = length(ind); 30 | 31 | [xx yy zz] = ind2sub(size(B),ind); 32 | xxm = mean(xx); 33 | yym = mean(yy); 34 | zzm = mean(zz); 35 | d(i) = norm([xxm yym zzm]-[XC YC ZC]); 36 | end 37 | 38 | rank = d./numPixels; 39 | 40 | [dummy,idx] = min(rank); 41 | B = zeros(size(B)); 42 | B(CC.PixelIdxList{idx}) = 1; 43 | else 44 | B = zeros(size(B)); 45 | end 46 | 47 | %--- Now generate a new point cloud, by applying this 3D mask to the main 48 | % point cloud 49 | X2 = round(ax*(x+bx)+1); 50 | Y2 = round(ay*(y+by)+1); 51 | Z2 = round(az*(z+bz)+1); 52 | 53 | i = sub2ind(size(B),X2,Y2,Z2); 54 | ind = B(i)>0; 55 | new_ssd = model_index(ssd,ind); 56 | 57 | %--- Map the ssd back to the main model 58 | [~,D0] = knnsearch([new_ssd.x new_ssd.y new_ssd.z],[model.x model.y model.z]); 59 | new_indices = find(D0 < 5*median(D0)); 60 | new_model = sample_model(model, new_indices); 61 | -------------------------------------------------------------------------------- /extern/ransac/fitplane.m: -------------------------------------------------------------------------------- 1 | % FITPLANE - solves coefficients of plane fitted to 3 or more points 2 | % 3 | % Usage: B = fitplane(XYZ) 4 | % 5 | % Where: XYZ - 3xNpts array of xyz coordinates to fit plane to. 6 | % If Npts is greater than 3 a least squares solution 7 | % is generated. 8 | % 9 | % Returns: B - 4x1 array of plane coefficients in the form 10 | % b(1)*X + b(2)*Y +b(3)*Z + b(4) = 0 11 | % The magnitude of B is 1. 12 | % 13 | % See also: RANSACFITPLANE 14 | 15 | % Copyright (c) 2003-2005 Peter Kovesi 16 | % School of Computer Science & Software Engineering 17 | % The University of Western Australia 18 | % http://www.csse.uwa.edu.au/ 19 | % 20 | % Permission is hereby granted, free of charge, to any person obtaining a copy 21 | % of this software and associated documentation files (the "Software"), to deal 22 | % in the Software without restriction, subject to the following conditions: 23 | % 24 | % The above copyright notice and this permission notice shall be included in 25 | % all copies or substantial portions of the Software. 26 | % 27 | % The Software is provided "as is", without warranty of any kind. 28 | 29 | % June 2003 30 | 31 | function B = fitplane(XYZ) 32 | 33 | [rows,npts] = size(XYZ); 34 | 35 | if rows ~=3 36 | error('data is not 3D'); 37 | end 38 | 39 | if npts < 3 40 | error('too few points to fit plane'); 41 | end 42 | 43 | % Set up constraint equations of the form AB = 0, 44 | % where B is a column vector of the plane coefficients 45 | % in the form b(1)*X + b(2)*Y +b(3)*Z + b(4) = 0. 46 | 47 | A = [XYZ' ones(npts,1)]; % Build constraint matrix 48 | 49 | if npts == 3 % Pad A with zeros 50 | A = [A; zeros(1,4)]; 51 | end 52 | 53 | [u d v] = svd(A); % Singular value decomposition. 54 | B = v(:,4); % Solution is last column of v. 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /optimize_viewpoints.m: -------------------------------------------------------------------------------- 1 | function [phi,J,iterations,s1,s2,phi_hist,J_hist,dJ_hist] = optimize_viewpoints(matching,precision,initialization,learning_rate) 2 | 3 | %------------------------------------------------------ 4 | % Gradient Descent 5 | %------------------------------------------------------ 6 | if(exist('learning_rate','var')) 7 | alpha = learning_rate; 8 | else 9 | alpha = .1; 10 | end 11 | 12 | delta = .1 *pi/180; 13 | 14 | J_hist = []; 15 | dJ_hist = []; 16 | phi_hist = []; 17 | 18 | matching = wrapTo2Pi(matching); 19 | 20 | n = size(matching,1); 21 | 22 | if(exist('initialization','var')) 23 | phi = initialization; 24 | else 25 | phi = matching(:,1); %Initialization 26 | end 27 | 28 | A = exp(1i*matching); %precompute the constants 29 | 30 | iterations = 0; 31 | while(1) 32 | iterations = iterations + 1; 33 | 34 | [J,s1,s2] = cost_function(phi,A); 35 | 36 | %compute the approximation of the derivative 37 | batch_size = n; 38 | b = []; 39 | while(nnz(b) < batch_size) 40 | b(randi(n)) = 1; 41 | end 42 | fb = find(b); 43 | dJ = zeros(n,1); 44 | for ik = 1 : length(fb) 45 | J_ = zeros(n,1); 46 | k = fb(ik); 47 | 48 | d = zeros(n,1); 49 | d(k) = delta; 50 | phi_ = phi+d; 51 | J_(k) = cost_function(phi_,A); 52 | 53 | dJ(k) = J_(k) - J; 54 | end 55 | 56 | % update phi 57 | phi = phi - alpha*dJ; 58 | 59 | J_hist(end+1) = J; 60 | phi_hist(:,end+1) = phi; 61 | dJ_hist(:,end+1) = dJ; 62 | 63 | fprintf('%d \n',var(dJ)); 64 | if(var(dJ) < precision) 65 | break; 66 | end 67 | 68 | % if(numel(J_hist) > 5000) 69 | % break 70 | % end 71 | end 72 | 73 | function [J,s1,s2] = cost_function(phi,A) 74 | [p1,p2] = meshgrid(phi,phi); 75 | ephi2 = exp(1i*p1).*exp(-1i*p2); 76 | d = abs(ephi2 .* A' - 1).^2; 77 | s1 = sum(d); 78 | s2 = sum(d,2); 79 | J = sum(s1); 80 | 81 | n = size(d,1); 82 | s1 = s1./n; 83 | s2 = s2./n; 84 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/README.txt: -------------------------------------------------------------------------------- 1 | Compute mesh normals 2 | ==================== 3 | 4 | Adam H. Aitkenhead 5 | adam.aitkenhead@physics.cr.man.ac.uk 6 | The Christie NHS Foundation Trust 7 | 1st Decmeber 2010 8 | 9 | Calculate the normal vectors for each facet of a triangular mesh. The ordering of the vertices (clockwise/anticlockwise) is also checked for all facets if this is requested as one of the outputs. 10 | 11 | 12 | USAGE: 13 | ====== 14 | 15 | [coordNORMALS] = COMPUTE_mesh_normals(meshdataIN); 16 | ..or.. 17 | [coordNORMALS,meshdataOUT] = COMPUTE_mesh_normals(meshdataIN); 18 | 19 | 20 | INPUT PARAMETERS 21 | ================ 22 | 23 | % meshdataIN - structure - Structure containing the faces and 24 | % vertices of the mesh, in the same format 25 | % as that produced by the isosurface 26 | % command. 27 | % ..or.. - Nx3x3 array - The vertex coordinates for each facet, with: 28 | % 1 row for each facet 29 | % 3 columns for the x,y,z coordinates 30 | % 3 pages for the three vertices 31 | 32 | 33 | OUTPUT PARAMETERS 34 | ================= 35 | 36 | % coordNORMALS - Nx3 array - The normal vectors for each facet, with: 37 | % 1 row for each facet 38 | % 3 columns for the x,y,z components 39 | % 40 | % meshdataOUT - (optional) - The mesh data with the ordering of the vertices 41 | % (clockwise/anticlockwise) checked. Uses 42 | % the same format as . 43 | 44 | 45 | EXAMPLES 46 | ======== 47 | 48 | To run an example of the code: 49 | 50 | >> EXAMPLE_mesh_normals 51 | 52 | 53 | NOTES 54 | ===== 55 | 56 | - Computing to check the ordering of the vertices in each facet may be slow for large meshes. 57 | - Also, it may not be possible to compute for non-manifold meshes. 58 | 59 | 60 | -------------------------------------------------------------------------------- /ssd/ssd_the_model.m: -------------------------------------------------------------------------------- 1 | function ssd = ssd_the_model(model,cache_file,OctreeLevels) 2 | 3 | if(isempty(model)) 4 | error('model is empty!'); 5 | end 6 | 7 | caching = 0; 8 | if(exist('cache_file','var')) 9 | if(~isempty(cache_file)) 10 | caching = 1; 11 | end 12 | end 13 | 14 | if(~exist('OctreeLevels','var')) 15 | OctreeLevels = 5; 16 | end 17 | 18 | temp_file = ['temp_for_ssd_' datestr(now,'HHMMSSFFF') num2str(randi(1000)) '.ply']; 19 | 20 | if ischar(model) %model file name 21 | 22 | if(~exist(model,'file')) 23 | error('Input file does not exist: %s',model); 24 | end 25 | 26 | % First try to load the cache_file, if it is there, and is up to date 27 | if(caching && cache_file_is_there_and_newer_than(cache_file,model)) 28 | fprintf(['ssd: loading currently cached file: ',cache_file,'\n']); 29 | ssd = import_3D_model(cache_file); 30 | return; 31 | end 32 | 33 | input_filename = model; 34 | extra_randomization = strrep(input_filename,'/','_'); 35 | extra_randomization = strrep(extra_randomization,'.','_'); 36 | temp_file = [extra_randomization,temp_file]; 37 | 38 | if(caching) 39 | temp_file = cache_file; 40 | end 41 | else %model in memory 42 | export_3D_model(model,temp_file); 43 | input_filename = temp_file; 44 | 45 | if(caching) 46 | error('ssd: caching is only possible when the input is a file name!'); 47 | return; 48 | end 49 | end 50 | 51 | cmd = sprintf('ssd_recon -oL %d -c %s %s >/dev/null',OctreeLevels,input_filename,temp_file); 52 | [~,cmd_output] = system(cmd) 53 | 54 | try 55 | ssd = import_3D_model(temp_file); 56 | catch err 57 | error(['failed importing: ' temp_file]); 58 | end 59 | 60 | delete(temp_file); 61 | 62 | ssd = compute_mesh_normals(ssd); 63 | 64 | %--- Try to remove the "out-skirts"!! of the reconstructed model 65 | if(ischar(model)) 66 | ssd = refine_the_ssd(ssd,import_3D_model(model)); 67 | else 68 | ssd = refine_the_ssd(ssd,model); 69 | end 70 | 71 | %--- Caching... 72 | if (ischar(model) && caching) 73 | fprintf(['ssd: saving cache file: ',cache_file,'\n']); 74 | export_3D_model(ssd,cache_file); 75 | end 76 | -------------------------------------------------------------------------------- /3d_model_matching/match_two_cars_HOH_plus_GradientDescent.m: -------------------------------------------------------------------------------- 1 | function [needed_rotation,J] = match_two_cars_HOH_plus_GradientDescent(settings,model1,model2) 2 | 3 | % Some settings 4 | root_phi_divisions = settings.HOH.root_phi_divisions; 5 | root_theta_divisions = settings.HOH.root_theta_divisions; 6 | nchildren = settings.HOH.nchildren; 7 | child_phi_divisions = settings.HOH.child_phi_divisions; 8 | child_theta_divisions = settings.HOH.child_theta_divisions; 9 | 10 | 11 | %---- Compute the feature for the reference model 12 | [~,f_ref] = HOH(model1,model_center_of_mass(model1),root_phi_divisions,root_theta_divisions,nchildren,child_phi_divisions,child_theta_divisions,0); 13 | 14 | %---- Now quickly sample the search space (possibly without recomputation of 15 | % the features) 16 | 17 | J = []; 18 | nsamples = 32; 19 | rotations = [0:nsamples-1] * 360/nsamples; 20 | for R = 0:nsamples-1 21 | phi = rotations(R+1); 22 | 23 | model_t = rotate_3D_model_around_z(model2,phi); 24 | [~,ft] = HOH(model_t,model_center_of_mass(model_t),root_phi_divisions,root_theta_divisions,nchildren,child_phi_divisions,child_theta_divisions,0); 25 | 26 | 27 | %-- Compute the distance 28 | J = [J; HOH_distance(f_ref,ft)]; 29 | 30 | fprintf('%.2f ',phi); 31 | end 32 | fprintf('\n'); 33 | 34 | 35 | %---- Pick the k best minima of the samples, and perform gradient descent 36 | %to find the global minimum 37 | [M,im] = min(J); 38 | phi = rotations(im); 39 | alpha = 20; 40 | delta = 5; 41 | 42 | dJ_hist = []; 43 | while(1) 44 | model_t = rotate_3D_model_around_z(model2,phi); 45 | [~,ft] = HOH(model_t,model_center_of_mass(model_t),root_phi_divisions,root_theta_divisions,nchildren,child_phi_divisions,child_theta_divisions,0); 46 | J = HOH_distance(f_ref,ft); 47 | 48 | model_t_ = rotate_3D_model_around_z(model2,phi+delta); 49 | [~,ft_] = HOH(model_t_,model_center_of_mass(model_t_),root_phi_divisions,root_theta_divisions,nchildren,child_phi_divisions,child_theta_divisions,0); 50 | J_ = HOH_distance(f_ref,ft_); 51 | 52 | dJ = J_ - J; 53 | phi = phi - alpha*dJ; 54 | 55 | dJ_hist(end+1) = dJ; 56 | 57 | if(numel(dJ_hist)>5 && nnz(abs(dJ_hist(end-4:end)) <1e-3) == 5) 58 | break; 59 | end 60 | 61 | if(numel(dJ_hist) > 500) 62 | break 63 | end 64 | end 65 | 66 | needed_rotation = phi; -------------------------------------------------------------------------------- /segmentation/align_ground_plane_to_xy.m: -------------------------------------------------------------------------------- 1 | function [new_model, rotation_matrix, translation_vector] = align_ground_plane_to_xy(model,cameras) 2 | 3 | 4 | scene_points = [model.x,model.y,model.z]; 5 | scene_normals = [model.nx,model.ny,model.nz]; 6 | 7 | [P, ~] = find_ground_plane(scene_points',.01); 8 | 9 | v1 = P(:,2)-P(:,1); 10 | v2 = P(:,3)-P(:,1); 11 | N = cross(v1,v2); 12 | 13 | n = [N(2),-N(1),0]; %this is the unit vector around which we should rotate N 14 | n = n./norm(n); % so that it is aligned with the z axis 15 | 16 | theta = acos(N(3)/norm(N)); 17 | 18 | n_ = [0 -n(3) n(2); ... 19 | n(3) 0 -n(1);... 20 | -n(2) n(1) 0]; % This is the skew symmetric matrix of n 21 | 22 | rotation_matrix = expm(theta*n_); 23 | 24 | new_points = (rotation_matrix * scene_points')'; 25 | 26 | %--- Adjust the ground plane to lie exactly on the z=0 plane 27 | P2 = (rotation_matrix*P)'; 28 | z_ = mean(P2(:,3)); 29 | l = size(new_points,1); 30 | new_points = new_points - [zeros(l,2),repmat(z_,l,1)]; 31 | translation_vector = [0 ; 0 ; -z_]; 32 | 33 | 34 | if(isempty(cameras)) %then we have to use the old method 35 | warning('No cameras provided! Using an unreliable method to check for upsidedown scenes'); 36 | up_side_down = (mean(new_points(:,3)) < 0); 37 | else %use camera positions to find the correct side of the ground plane 38 | cameras = RotateCameras(cameras,rotation_matrix,translation_vector); 39 | c = cell2mat(cameras); 40 | p = cell2mat({c.Position})'; 41 | up_side_down = mean(p(:,3)) < 0; 42 | end 43 | if(up_side_down) 44 | R2 = [1 0 0 ; 0 -1 0 ; 0 0 -1]; 45 | new_points = (R2 * new_points')'; 46 | rotation_matrix = R2*rotation_matrix; 47 | translation_vector = R2*translation_vector; 48 | end 49 | 50 | %--- And update the normals too 51 | if(~isempty(scene_normals)) 52 | new_normals = scene_normals * rotation_matrix'; 53 | else 54 | new_normals = []; 55 | end 56 | 57 | new_model.x = new_points(:,1); 58 | new_model.y = new_points(:,2); 59 | new_model.z = new_points(:,3); 60 | if(~isempty(new_normals)) 61 | new_model.nx = new_normals(:,1); 62 | new_model.ny = new_normals(:,2); 63 | new_model.nz = new_normals(:,3); 64 | end 65 | if(model_has_colors(model)) 66 | new_model.r = model.r; 67 | new_model.g = model.g; 68 | new_model.b = model.b; 69 | end 70 | if(model_has_faces(model)) 71 | new_model.face = model.face; 72 | end -------------------------------------------------------------------------------- /segment_the_models.m: -------------------------------------------------------------------------------- 1 | function [segmented_models, Rotations, Translations, segmented_filenames] = segment_the_models(settings,scenes_dir_list,overwrite) 2 | 3 | if(~exist('overwrite','var')) 4 | overwrite = 0; 5 | end 6 | 7 | if(overwrite) 8 | r = round(rand*10,2); 9 | a = input(sprintf('WARNING: going to overwrite all the currently existing segmentations. If you are OK with that enter the number %g: ',r)); 10 | if(a ~= r) 11 | fprintf('Wrong ---> WILL NOT OVERWRITE!\n'); 12 | overwrite = 0; 13 | end 14 | end 15 | 16 | N = length(scenes_dir_list); 17 | segmented_models = cell(N,1); 18 | Rotations = cell(N,1); 19 | Translations = cell(N,1); 20 | 21 | parfor i = 1:N 22 | if(settings.deterministic),rng(1,'twister');end 23 | 24 | 25 | base_path = scenes_dir_list{i}; 26 | 27 | if(isempty(base_path)) 28 | continue; 29 | end 30 | 31 | %--- setup some file and dir names 32 | segment_dir = [base_path,'/refined/']; 33 | segment_file_name_ply = [segment_dir,'/refined.ply']; 34 | transformation_file_name = [segment_dir,'/transformation_parameters.txt']; 35 | SceneFileName = [base_path,'/results.0.ply']; 36 | camera_file = [base_path,'/results.nvm.cmvs/00/cameras_v2.txt']; 37 | 38 | %--- Skip if the folder currently exists 39 | if(~overwrite && exist(segment_dir,'dir')) 40 | fprintf(['!Skipping. Segmentation destination dir already exists: ' segment_dir '\n']); 41 | 42 | segmented_models{i} = import_3D_model(segment_file_name_ply); 43 | [Rotations{i}, Translations{i}] = load_segmentation_transformation(transformation_file_name); 44 | segmented_filenames{i} = segment_file_name_ply; 45 | 46 | continue; 47 | end 48 | 49 | %--- Do the segmentation 50 | model = import_3D_model(SceneFileName); 51 | 52 | cameras = load_camera_v2(camera_file); 53 | [segmented_models{i},Rotations{i},Translations{i}] = segment_the_model(model,cameras); 54 | 55 | mkdir(segment_dir); 56 | fprintf('writing: %s\n',segment_file_name_ply); 57 | export_3D_model(segmented_models{i},segment_file_name_ply); 58 | myparsave(transformation_file_name,Rotations{i},Translations{i}); %simple save command does not work with parfor 59 | segmented_filenames{i} = segment_file_name_ply; 60 | end 61 | 62 | function myparsave(filename,Rotation,Translation) 63 | save(filename,'Rotation','Translation','-ASCII'); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Code for the paper "Unsupervised Generation of a Viewpoint Annotated Car Dataset from Videos", ICCV 2015 2 | 3 | Terms of use 4 | ------------ 5 | 6 | The code is provided for research purposes only. Any commercial 7 | use is prohibited. If you are interested in a commercial use, please 8 | contact the copyright holder. 9 | 10 | Please cite the following paper if you use this code or its parts in your research: 11 | 12 | @InProceedings{SB15, 13 | author = "N. Sedaghat and T. Brox", 14 | title = "Unsupervised Generation of a Viewpoint Annotated Car Dataset from Videos", 15 | booktitle = "IEEE International Conference on Computer Vision (ICCV)", 16 | year = "2015", 17 | url = "http://lmb.informatik.uni-freiburg.de//Publications/2015/SB15" 18 | } 19 | 20 | Please report bugs and problems to Nima Sedaghat ( nima@cs.uni-freiburg.de ) 21 | 22 | Installation Requirements 23 | -------------------------- 24 | 25 | External software/libraries -- not included: 26 | * VisualSFM (http://ccwu.me/vsfm/) + SiftGPU (http://www.cs.unc.edu/~ccwu/siftgpu/) 27 | * SSD (http://mesh.brown.edu/ssd/) 28 | 29 | External software/libraries -- included in the 'extern' directory: 30 | * Compute mesh normals (http://de.mathworks.com/matlabcentral/fileexchange/29585-compute-mesh-normals) 31 | * ransac (http://www.peterkovesi.com/matlabfns/index.html) 32 | only these files are necessary: ransac.m fitplane.m ransacfitplane.m iscolinear.m 33 | 34 | Data 35 | ----- 36 | Download input car videos from [here](https://lmb.informatik.uni-freiburg.de/resources/datasets/FreiburgStaticCars52/freiburg_static_car_vids.tar.gz) -- videos of 52 static cars, covering almost the whole 360 degrees around the car. 37 | Download resulting dataset from [here](https://lmb.informatik.uni-freiburg.de/resources/datasets/FreiburgStaticCars52/freiburg_static_cars_52_v1.1.tar.gz) -- viewpoint- and bounding box-annotated car images. 38 | 39 | 40 | (Non-)Deterministic Behaviour 41 | ------------------------------ 42 | 43 | Some external components show random behaviour, which result in non-deterministic results that cannot be exactly reproduced 44 | across multiple runs. 45 | The closest you can get to a deterministic behaviour is by: 46 | 1. setting 'settings.deterministic = 1' in the file setup.m 47 | 2. setting 'param_deterministic_behaviour 1' in the file [VisualSFM Path]/bin/nv.ini 48 | 49 | However, the results can still vary noticeably -- unless you use another SFM package, where you can control its behaviour completely. 50 | 51 | -------------------------------------------------------------------------------- /extern/ransac/iscolinear.m: -------------------------------------------------------------------------------- 1 | % ISCOLINEAR - are 3 points colinear 2 | % 3 | % Usage: r = iscolinear(p1, p2, p3, flag) 4 | % 5 | % Arguments: 6 | % p1, p2, p3 - Points in 2D or 3D. 7 | % flag - An optional parameter set to 'h' or 'homog' 8 | % indicating that p1, p2, p3 are homogneeous 9 | % coordinates with arbitrary scale. If this is 10 | % omitted it is assumed that the points are 11 | % inhomogeneous, or that they are homogeneous with 12 | % equal scale. 13 | % 14 | % Returns: 15 | % r = 1 if points are co-linear, 0 otherwise 16 | 17 | % Copyright (c) 2004-2005 Peter Kovesi 18 | % School of Computer Science & Software Engineering 19 | % The University of Western Australia 20 | % http://www.csse.uwa.edu.au/ 21 | % 22 | % Permission is hereby granted, free of charge, to any person obtaining a copy 23 | % of this software and associated documentation files (the "Software"), to deal 24 | % in the Software without restriction, subject to the following conditions: 25 | % 26 | % The above copyright notice and this permission notice shall be included in 27 | % all copies or substantial portions of the Software. 28 | % 29 | % The Software is provided "as is", without warranty of any kind. 30 | 31 | % February 2004 32 | % January 2005 - modified to allow for homogeneous points of arbitrary 33 | % scale (thanks to Michael Kirchhof) 34 | 35 | 36 | function r = iscolinear(p1, p2, p3, flag) 37 | 38 | if nargin == 3 % Assume inhomogeneous coords 39 | flag = 'inhomog'; 40 | end 41 | 42 | if ~all(size(p1)==size(p2)) | ~all(size(p1)==size(p3)) | ... 43 | ~(length(p1)==2 | length(p1)==3) 44 | error('points must have the same dimension of 2 or 3'); 45 | end 46 | 47 | % If data is 2D, assume they are 2D inhomogeneous coords. Make them 48 | % homogeneous with scale 1. 49 | if length(p1) == 2 50 | p1(3) = 1; p2(3) = 1; p3(3) = 1; 51 | end 52 | 53 | if flag(1) == 'h' 54 | % Apply test that allows for homogeneous coords with arbitrary 55 | % scale. p1 X p2 generates a normal vector to plane defined by 56 | % origin, p1 and p2. If the dot product of this normal with p3 57 | % is zero then p3 also lies in the plane, hence co-linear. 58 | r = abs(dot(cross(p1, p2),p3)) < eps; 59 | else 60 | % Assume inhomogeneous coords, or homogeneous coords with equal 61 | % scale. 62 | r = norm(cross(p2-p1, p3-p1)) < eps; 63 | end 64 | 65 | -------------------------------------------------------------------------------- /segmentation/segment_the_model.m: -------------------------------------------------------------------------------- 1 | function [new_model,Rotation,Translation,inliers] = segment_the_model(model,cameras) 2 | 3 | Rotation = eye(3); 4 | Translation = [0;0;0]; 5 | 6 | %--- Mean normalize the loaded coordinates 7 | Translation = [-mean(model.x) ; -mean(model.y) ; -mean(model.z)]; 8 | model = transform_3d_rigid(model,Rotation,Translation); 9 | cameras = RotateCameras(cameras,Rotation,Translation); 10 | 11 | 12 | 13 | %---------------------------------- 14 | % Align the ground plane 15 | % to the x-y plane 16 | %---------------------------------- 17 | [model, R, T] = align_ground_plane_to_xy(model,cameras); 18 | cameras = RotateCameras(cameras,R,T); 19 | inliers = [1:length(model.x)]; 20 | 21 | Rotation = R*Rotation; 22 | Translation = R*Translation+T; 23 | 24 | %---------------------------------- 25 | % Normalize out the rotation around 26 | % z axis (for multiple runs) 27 | %---------------------------------- 28 | p = cameras{1}.Position; 29 | phi = -atan2d(p(2),p(1)); 30 | R = [cosd(phi) -sind(phi) 0 ; sind(phi) cosd(phi) 0 ; 0 0 1]; 31 | T = [0;0;0]; 32 | model = transform_3d_rigid(model,R,T); 33 | cameras = RotateCameras(cameras,R,T); 34 | 35 | Rotation = R*Rotation; 36 | Translation = R*Translation+T; 37 | 38 | %------------------------------------------------------------- 39 | % Try to use the camera positions to obtain the 40 | % region of attention, and cut the rest out 41 | %------------------------------------------------------------- 42 | [model,Ng,ind] = cut_clutter_out_of_camera_circle(model,cameras); 43 | inliers = inliers(ind); 44 | 45 | %------------------------------------------- 46 | % Remove the culutter in the background 47 | %------------------------------------------- 48 | [model,ind] = remove_clutter2(model); 49 | inliers = inliers(ind); 50 | 51 | %------------------------------------------- 52 | % Remove the ground points (dumb, hacky version) 53 | %------------------------------------------- 54 | [model,ind] = remove_surrounding_ground_points_new(model,.3); 55 | inliers = inliers(ind); 56 | 57 | %------------------------------------------- 58 | % Remove the clutter again! 59 | %------------------------------------------- 60 | if(~isempty(model.x)) 61 | [model,ind] = remove_remaining_clutter_ssd_based(model,cameras,Rotation,Translation); 62 | inliers = inliers(ind); 63 | else 64 | model = make_empty_3D_model(); 65 | inliers = []; 66 | end 67 | 68 | %------------------------------------------- 69 | % And put the model into the center of the 70 | % scene -- and on the ground! 71 | %------------------------------------------- 72 | if(~isempty(model.x)) %if the model is already empty = we have lost all the points 73 | T = [-mean(model.x) ; -mean(model.y) ; -min(model.z)]; 74 | model = transform_3d_rigid(model,eye(3),T); 75 | cameras = RotateCameras(cameras,eye(3),T); 76 | Translation = Translation + T; 77 | end 78 | 79 | new_model = model; -------------------------------------------------------------------------------- /general_tools/import_3D_model.m: -------------------------------------------------------------------------------- 1 | function [x,y,z,nx,ny,nz,red,green,blue] = import_3D_model(FileName,prefer_mat_file) 2 | 3 | if(~exist(FileName,'file')) 4 | error('Specified file does not exist:%s',FileName); 5 | end 6 | 7 | if(~exist('prefer_mat_file','var')) 8 | prefer_mat_file = 0; 9 | end 10 | 11 | [pathstr, name, ext] = fileparts(FileName); 12 | 13 | if(strcmpi(ext,'.ply')) 14 | 15 | % check to see if there's a .mat file corresponding to the .ply file 16 | ply_info = dir(FileName); 17 | ply_date = datenum(ply_info.date); 18 | 19 | [pathstr, name, ext] = fileparts(FileName); 20 | mat_file = [pathstr, '/' ,name, '.mat']; 21 | mat_info = dir(mat_file); 22 | 23 | mat_file_is_there = 0; 24 | if(length(mat_info) ~= 0) %if there is a mat file (and it is newer than the ply file) 25 | mat_date = datenum(mat_info.date); 26 | if mat_date>ply_date 27 | mat_file_is_there = 1; 28 | end 29 | end 30 | 31 | 32 | %now try to open the right file 33 | if(prefer_mat_file) 34 | if(mat_file_is_there) 35 | load(mat_file); 36 | else 37 | data = nplyRead(FileName); 38 | save([pathstr, '/' ,name, '.mat'],'data'); 39 | msgbox('A .mat version of your .ply file is now saved in the same location. You can use that instead!','replace'); 40 | end 41 | else 42 | data = nplyRead(FileName); 43 | end 44 | 45 | elseif(strcmpi(ext,'.mat')) 46 | load(FileName); 47 | else 48 | error('Invalid file extension'); 49 | end 50 | 51 | x = data.vertex.x; 52 | y = data.vertex.y; 53 | z = data.vertex.z; 54 | 55 | try 56 | nx = data.vertex.nx; 57 | ny = data.vertex.ny; 58 | nz = data.vertex.nz; 59 | catch 60 | try 61 | nx = data.vertex.vsfm_cnx; 62 | ny = data.vertex.vsfm_cny; 63 | nz = data.vertex.vsfm_cnz; 64 | catch 65 | nx = []; 66 | ny = []; 67 | nz = []; 68 | end 69 | end 70 | 71 | try 72 | red = data.vertex.r/255; 73 | green = data.vertex.g/255; 74 | blue = data.vertex.b/255; 75 | catch 76 | try 77 | red = data.vertex.red/255; 78 | green = data.vertex.green/255; 79 | blue = data.vertex.blue/255; 80 | catch 81 | try 82 | red = data.vertex.diffuse_red/255; 83 | green = data.vertex.diffuse_green/255; 84 | blue = data.vertex.diffuse_blue/255; 85 | catch 86 | red = .2; 87 | green = .2; 88 | blue = .2; 89 | end 90 | end 91 | end 92 | 93 | if(nargout == 1) 94 | model.x = x; 95 | model.y = y; 96 | model.z = z; 97 | model.nx = nx; 98 | model.ny = ny; 99 | model.nz = nz; 100 | model.r = red; 101 | model.g = green; 102 | model.b = blue; 103 | 104 | if(isfield(data,'face')) 105 | model.face = data.face; 106 | end 107 | 108 | x = model; 109 | end 110 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/PLOT_3D_stl_patch.m: -------------------------------------------------------------------------------- 1 | function [varargout] = PLOT_3D_stl_patch(coordVERTICES,varargin) 2 | % PLOT_3D_stl_patch Plot an stl triangular mesh using patch 3 | %========================================================================== 4 | % FILENAME PLOT_3D_stl_patch.m 5 | % AUTHOR Adam H. Aitkenhead 6 | % INSTITUTION The Christie NHS Foundation Trust 7 | % CONTACT adam.aitkenhead@physics.cr.man.ac.uk 8 | % DATE 29th March 2010 9 | % PURPOSE Plot an stl triangular mesh using patch 10 | % 11 | % USAGE PLOT_3D_stl_patch(coordVERTICES) 12 | % ..or.. PLOT_3D_stl_patch(coordVERTICES,coordNORMALS) 13 | % ..or.. [handlePATCH] = PLOT_3D_stl_patch(coordVERTICES,coordNORMALS) 14 | % 15 | % coordVERTICES - An Nx3x3 array defining the vertex positions for 16 | % each facet, with: 17 | % 1 row for each facet 18 | % 3 cols for the x,y,z coordinates 19 | % 3 pages for the three vertices 20 | % 21 | % coordNORMALS - An Nx3 array defining the normal vectors for each 22 | % facet, with: 23 | % 1 row for each facet 24 | % 3 cols for the x,y,z components 25 | % 26 | % handlePATCH - The handle to the patch object. 27 | %========================================================================== 28 | 29 | %========================================================================== 30 | % VERSION USER CHANGES 31 | % ------- ---- ------- 32 | % 100412 AHA Original version 33 | % 101130 AHA Also plot the normals, if required. 34 | %========================================================================== 35 | 36 | %================================ 37 | % PREPARE THE DATA 38 | %================================ 39 | 40 | xco = squeeze( coordVERTICES(:,1,:) )'; 41 | yco = squeeze( coordVERTICES(:,2,:) )'; 42 | zco = squeeze( coordVERTICES(:,3,:) )'; 43 | 44 | if nargin==2 45 | coordNORMALS = varargin{1}; 46 | end 47 | 48 | 49 | colourPATCHedge = 'b'; 50 | colourPATCHface = 'c'; 51 | colourNORMALS = 'r'; 52 | 53 | %================================ 54 | % PLOT THE FACETS 55 | %================================ 56 | 57 | [hpat] = patch(xco,yco,zco,colourPATCHedge,'EdgeColor',colourPATCHedge,'FaceColor',colourPATCHface); 58 | axis equal tight 59 | 60 | %================================ 61 | % PLOT THE NORMALS 62 | %================================ 63 | 64 | if nargin==2 65 | hold on 66 | 67 | for loopN = 1:size(coordNORMALS,1) 68 | 69 | costart = mean(coordVERTICES(loopN,:,:),3); 70 | coend = costart + coordNORMALS(loopN,:); 71 | plot3([costart(1),coend(1)],[costart(2),coend(2)],[costart(3),coend(3)],'r','LineWidth',2); 72 | 73 | end 74 | 75 | hold off 76 | end 77 | 78 | %================================ 79 | % PREPARE THE OUTPUT ARGUMENTS 80 | %================================ 81 | 82 | if nargout == 1 83 | varargout(1) = {hpat}; 84 | end 85 | -------------------------------------------------------------------------------- /3D_reconst/reconstruct_single_video.m: -------------------------------------------------------------------------------- 1 | function reconstruct_single_video(video_dir,destination_dir,nFrames,ranges) 2 | 3 | if(~exist('nFrames','var')) 4 | nFrames = 0; %to use all the existing frames 5 | end 6 | 7 | if(~exist('ranges','var')) 8 | ranges = [3 5]; 9 | end 10 | 11 | %-- Check for currently existing stuff in the folder 12 | d = dir(fullfile(destination_dir,'*.nvm')); 13 | if(size(d,1)) 14 | fprintf(['!Skipping. Reconstruction destination dir already contains nvm files: ' destination_dir '\n']); 15 | return; 16 | end 17 | 18 | %-- Create the destination dir 19 | mkdir(destination_dir); 20 | 21 | %-- Load the basic list (containing absolute file names) 22 | list_file = [video_dir '/list_absolute.txt']; 23 | if(~exist(list_file,'file')) 24 | system(sprintf('find %s -iname ''*ppm'' | sort > %s',video_dir,list_file)); 25 | end 26 | fp = fopen(list_file,'rt'); 27 | list = textscan(fp,'%s\n'); 28 | list = list{1}; 29 | fclose(fp); 30 | 31 | %-- Do down-sampling in time, if necessary 32 | if(nFrames > 0) 33 | nFrames = min(nFrames,length(list)); %In the cases where there are not enough frames, we just use all of them 34 | 35 | %ind = ceil(linspace(1,length(list),nFrames)); 36 | ind = 1:floor(length(list)/nFrames):length(list); 37 | list = list(ind); 38 | end 39 | ds_listfile = [destination_dir '/list_downsampled.txt']; 40 | fp = fopen(ds_listfile,'wt'); 41 | fprintf(fp,'%s\n',list{:}); 42 | fclose(fp); 43 | 44 | %-- In a loop, try to suggest a pair of frames for initialization (hacky) 45 | counter = 0; 46 | while(1) 47 | %-- create the Initialization list (for manual initialization pair 48 | %specification) 49 | init_list_file = [destination_dir '/init.txt']; 50 | fp = fopen(init_list_file,'wt'); 51 | fprintf(fp,'%s %s',list{1},list{5+counter}); 52 | fclose(fp); 53 | 54 | %-- Run sfm initialization (just a hack to force it use our own 55 | %initial pair) 56 | init_nvm_file = [destination_dir '/init.nvm']; 57 | cmd = ['VisualSFM sfm ' init_list_file ' ' init_nvm_file]; 58 | fprintf('\n\nCUSTOM LOG - Running this command:\n %s \n\n',cmd); 59 | [~,cmdout] = system(cmd) 60 | 61 | %Check if it has been successful to do the initialization or not 62 | if(isempty(strfind(cmdout,'Failed to initialize with the chosen pair'))) 63 | break; 64 | end 65 | 66 | counter = counter + 1; 67 | if(counter == 10) 68 | break; 69 | end 70 | end 71 | 72 | if(counter == 10) 73 | warning('Failed to initialize sfm for this item.'); 74 | return; 75 | end 76 | 77 | %-- Create the manual list for pair matching of frames 78 | range1 = ranges(1); 79 | list1 = create_pairs_from_images_list(list,range1); 80 | 81 | pairs_list_file = [destination_dir '/pairs_list.txt']; 82 | fp = fopen(pairs_list_file,'wt'); 83 | fprintf(fp,'%s\n',list1{:}); 84 | fclose(fp); 85 | 86 | %--- Do the actual sfm by resuming the init phase 87 | results_nvm_file1 = [destination_dir '/results1.nvm']; 88 | cmd = ['VisualSFM sfm+pairs ' ds_listfile ' ' results_nvm_file1 ' ' pairs_list_file ' +resume ' init_nvm_file ' +sort']; 89 | fprintf('\n\nCUSTOM LOG - Running this command:\n %s \n\n',cmd); 90 | system(cmd); 91 | 92 | %--- Try to find suggestions for the loop closure 93 | [p,q] = loop_closure_suggestions([destination_dir '/results1.nvm'],0.05,5); 94 | 95 | %--- Create a new list, including the loop closure pairs, in addition to 96 | % more regular pairs (higher range) 97 | range2 = ranges(2); 98 | list2 = create_pairs_from_images_list(list,range2); 99 | list_closure = create_pairs_from_images_list(list,[p,q]); 100 | list2 = cat(1,list2,list_closure); 101 | 102 | pairs_list_file2 = [destination_dir '/pairs_list2.txt']; 103 | fp = fopen(pairs_list_file2,'wt'); 104 | fprintf(fp,'%s\n',list2{:}); 105 | fclose(fp); 106 | 107 | %--- Do the actual sfm by resuming the init phase (for the second time) 108 | results_nvm_file = [destination_dir '/results.nvm']; 109 | cmd = ['VisualSFM sfm+pmvs+pairs ' ds_listfile ' ' results_nvm_file ' ' pairs_list_file2 ' +resume ' init_nvm_file ' +sort']; 110 | fprintf('\n\nCUSTOM LOG - Running this command:\n %s \n\n',cmd); 111 | system(cmd); 112 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | clear; 2 | clc; 3 | 4 | %-------------------------------------------------------------------------- 5 | %--- Settings 6 | %-------------------------------------------------------------------------- 7 | setup; 8 | if(settings.deterministic),rng(1); end 9 | 10 | %-------------------------------------------------------------------------- 11 | %--- Reconstruct the 3D models from videos 12 | %-------------------------------------------------------------------------- 13 | fprintf('Reconstructing 3D models from videos...\n'); 14 | [scenes_dir_list,ids] = reconstruct_videos(videos_list_file,... 15 | reconstructions_destination,[120 3 5],reconstructions_on_cluster); 16 | fprintf('Done\n'); 17 | N = numel(scenes_dir_list); 18 | 19 | %-------------------------------------------------------------------------- 20 | %--- Segment out the cars 21 | %-------------------------------------------------------------------------- 22 | fprintf('Performing segmentation on the reconstructed scenes...\n'); 23 | [models, Rotations, Translations, segmented_files] = ... 24 | segment_the_models(settings,scenes_dir_list,0); 25 | segmented_files = segmented_files'; 26 | fprintf('Done\n'); 27 | 28 | %-------------------------------------------------------------------------- 29 | %--- Throw away empty models resulting from segmentation 30 | %-------------------------------------------------------------------------- 31 | empties = find(cellfun(@(m)(isempty(m.x)),models)); 32 | 33 | scenes_dir_list(empties) = []; 34 | segmented_files(empties) = []; 35 | models(empties) = []; 36 | ids(empties) = []; 37 | N = numel(models); 38 | 39 | 40 | %-------------------------------------------------------------------------- 41 | %--- Do pairwise matching 42 | %-------------------------------------------------------------------------- 43 | 44 | % pre-create the ssd of the models 45 | parfor i = 1 : N 46 | ssd{i} = ssd_the_model(segmented_files{i},... 47 | generate_ssd_file_name_for_model(segmented_files{i},5),5); 48 | end 49 | 50 | [I,J] = meshgrid(1:N,1:N); 51 | whole_timer = tic; 52 | matching = zeros(N); 53 | matching_cost = zeros(N); 54 | timing = zeros(N); 55 | parfor ind = 1 : N*N 56 | i = I(ind); 57 | j = J(ind); 58 | 59 | model1 = models{i}; 60 | fprintf('matching %02dx%02d ...',i,j) 61 | 62 | model2 = models{j}; 63 | 64 | local_timer = tic; 65 | [matching(ind), matching_cost(ind)] = ... 66 | match_two_cars_HOH_plus_GradientDescent(settings,ssd{i},ssd{j}); 67 | timing(ind) = toc(local_timer); 68 | end 69 | 70 | matching = matching'; 71 | timing = timing'; 72 | 73 | toc(whole_timer) 74 | save main_last 75 | 76 | %% 77 | clear;load main_last 78 | 79 | 80 | %%------------------------------------------------------------------------- 81 | % Find each model's pose, by optimizing the cost function 82 | %-------------------------------------------------------------------------- 83 | new_matching = matching; 84 | inliers1 = 1 : size(matching,1); 85 | 86 | fprintf('Optimizing the estimated viewpoints...\n'); 87 | [phi,J,niter,s1,s2] = optimize_viewpoints(new_matching*pi/180,1e-10); 88 | phi = phi * 180/pi; 89 | ss = s1' + s2; 90 | fprintf('Done:\n'); 91 | disp(J); 92 | 93 | 94 | outliers2 = find(ss >= 2.5); 95 | fprintf('outlier ids: %d \n',ids(inliers1(outliers2))); 96 | inliers2 = 1:numel(inliers1); 97 | inliers2(outliers2) = [] 98 | 99 | figure; 100 | visualize_all_aligned_models(ssd,ids,inliers1,phi,ss); 101 | 102 | pause 103 | 104 | fprintf('Re-optimizing the estimated viewpoints...\n'); 105 | new_matching2 = new_matching(:,inliers2); 106 | new_matching2 = new_matching2(inliers2,:); 107 | [phi,J,niter,s1_,s2_] = optimize_viewpoints(new_matching2*pi/180,1e-10); 108 | phi = phi * 180/pi; 109 | fprintf('Done:\n'); 110 | disp(J); 111 | 112 | inliers_list_index = inliers1(inliers2); 113 | 114 | %%------------------------------------------------------------------------- 115 | % Deduct a manually defined phi value from the view-points 116 | % = define the ground 0 viewpoint 117 | %-------------------------------------------------------------------------- 118 | phi_abs = phi - manual_rotation; 119 | figure;visualize_all_aligned_models(ssd,ids,inliers_list_index,phi_abs,ss); 120 | 121 | %%------------------------------------------------------------------------- 122 | % Generate the dataset annotations and save them 123 | %-------------------------------------------------------------------------- 124 | generate_dataset_annotations(models,phi_abs,inliers_list_index,ids,... 125 | scenes_dir_list,annot_dir); 126 | -------------------------------------------------------------------------------- /extern/ransac/ransacfitplane.m: -------------------------------------------------------------------------------- 1 | % RANSACFITPLANE - fits plane to 3D array of points using RANSAC 2 | % 3 | % Usage [B, P, inliers] = ransacfitplane(XYZ, t, feedback) 4 | % 5 | % This function uses the RANSAC algorithm to robustly fit a plane 6 | % to a set of 3D data points. 7 | % 8 | % Arguments: 9 | % XYZ - 3xNpts array of xyz coordinates to fit plane to. 10 | % t - The distance threshold between data point and the plane 11 | % used to decide whether a point is an inlier or not. 12 | % feedback - Optional flag 0 or 1 to turn on RANSAC feedback 13 | % information. 14 | % 15 | % Returns: 16 | % B - 4x1 array of plane coefficients in the form 17 | % b(1)*X + b(2)*Y +b(3)*Z + b(4) = 0 18 | % The magnitude of B is 1. 19 | % This plane is obtained by a least squares fit to all the 20 | % points that were considered to be inliers, hence this 21 | % plane will be slightly different to that defined by P below. 22 | % P - The three points in the data set that were found to 23 | % define a plane having the most number of inliers. 24 | % The three columns of P defining the three points. 25 | % inliers - The indices of the points that were considered 26 | % inliers to the fitted plane. 27 | % 28 | % See also: RANSAC, FITPLANE 29 | 30 | % Copyright (c) 2003-2008 Peter Kovesi 31 | % School of Computer Science & Software Engineering 32 | % The University of Western Australia 33 | % http://www.csse.uwa.edu.au/ 34 | % 35 | % Permission is hereby granted, free of charge, to any person obtaining a copy 36 | % of this software and associated documentation files (the "Software"), to deal 37 | % in the Software without restriction, subject to the following conditions: 38 | % 39 | % The above copyright notice and this permission notice shall be included in 40 | % all copies or substantial portions of the Software. 41 | % 42 | % The Software is provided "as is", without warranty of any kind. 43 | 44 | % June 2003 - Original version. 45 | % Feb 2004 - Modified to use separate ransac function 46 | % Aug 2005 - planeptdist modified to fit new ransac specification 47 | % Dec 2008 - Much faster distance calculation in planeptdist (thanks to 48 | % Alastair Harrison) 49 | 50 | 51 | function [B, P, inliers] = ransacfitplane(XYZ, t, feedback) 52 | 53 | if nargin == 2 54 | feedback = 0; 55 | end 56 | 57 | [rows, npts] = size(XYZ); 58 | 59 | if rows ~=3 60 | error('data is not 3D'); 61 | end 62 | 63 | if npts < 3 64 | error('too few points to fit plane'); 65 | end 66 | 67 | s = 3; % Minimum No of points needed to fit a plane. 68 | 69 | fittingfn = @defineplane; 70 | distfn = @planeptdist; 71 | degenfn = @isdegenerate; 72 | 73 | [P, inliers] = ransac(XYZ, fittingfn, distfn, degenfn, s, t, feedback); 74 | 75 | % Perform least squares fit to the inlying points 76 | B = fitplane(XYZ(:,inliers)); 77 | 78 | %------------------------------------------------------------------------ 79 | % Function to define a plane given 3 data points as required by 80 | % RANSAC. In our case we use the 3 points directly to define the plane. 81 | 82 | function P = defineplane(X); 83 | P = X; 84 | 85 | %------------------------------------------------------------------------ 86 | % Function to calculate distances between a plane and a an array of points. 87 | % The plane is defined by a 3x3 matrix, P. The three columns of P defining 88 | % three points that are within the plane. 89 | 90 | function [inliers, P] = planeptdist(P, X, t) 91 | 92 | n = cross(P(:,2)-P(:,1), P(:,3)-P(:,1)); % Plane normal. 93 | n = n/norm(n); % Make it a unit vector. 94 | 95 | npts = length(X); 96 | d = zeros(npts,1); % d will be an array of distance values. 97 | 98 | % The following loop builds up the dot product between a vector from P(:,1) 99 | % to every X(:,i) with the unit plane normal. This will be the 100 | % perpendicular distance from the plane for each point 101 | for i=1:3 102 | d = d + (X(i,:)'-P(i,1))*n(i); 103 | end 104 | 105 | inliers = find(abs(d) < t); 106 | 107 | 108 | %------------------------------------------------------------------------ 109 | % Function to determine whether a set of 3 points are in a degenerate 110 | % configuration for fitting a plane as required by RANSAC. In this case 111 | % they are degenerate if they are colinear. 112 | 113 | function r = isdegenerate(X) 114 | 115 | % The three columns of X are the coords of the 3 points. 116 | r = iscolinear(X(:,1),X(:,2),X(:,3)); -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/WRITE_stl.m: -------------------------------------------------------------------------------- 1 | function WRITE_stl(fileOUT,coordVERTICES,coordNORMALS,varargin) 2 | % WRITE_stl Write an STL file in either ascii or binary 3 | %========================================================================== 4 | % FILENAME: WRITE_stl.m 5 | % AUTHOR: Adam H. Aitkenhead 6 | % DATE: 12th May 2010 7 | % PURPOSE: Write an STL file in either ascii or binary 8 | % 9 | % EXAMPLE: WRITE_stl(fileOUT,coordVERTICES,coordNORMALS,STLformat) 10 | % 11 | % OUTPUT FILES: 12 | % 13 | % INPUT PARAMETERS: 14 | % fileOUT - A string defining the filename, eg. 'filename.stl' 15 | % 16 | % coordVERTICES - an Nx3x3 array defining the vertices for each facet: 17 | % - 1 row for each facet 18 | % - 3 cols for the x,y,z coordinates 19 | % - 3 pages for the three vertices 20 | % 21 | % coordNORMALS - an Nx3 array defining the normal of each facet: 22 | % - 1 row for each facet 23 | % - 3 cols for the x,y,z components of 24 | % the vector 25 | % 26 | % STLformat - string (optional) - STL format: 'binary' or 'ascii' 27 | %========================================================================== 28 | 29 | %========================================================================== 30 | % VERSION USER CHANGES 31 | % ------- ---- ------- 32 | % 100512 AHA Original version 33 | % 100630 AHA Improved speed by avoiding the use of a 'for' loop 34 | % 101123 AHA Now writes either ascii or binary STL files 35 | %========================================================================== 36 | 37 | if nargin==4 38 | STLformat = lower(varargin{1}); 39 | if strfind(STLformat,'binary') 40 | WRITE_stlbinary(fileOUT,coordVERTICES,coordNORMALS) 41 | else 42 | WRITE_stlascii(fileOUT,coordVERTICES,coordNORMALS) 43 | end 44 | else 45 | WRITE_stlascii(fileOUT,coordVERTICES,coordNORMALS) 46 | end 47 | 48 | end %function 49 | 50 | 51 | 52 | %-------------------------------------------------------------------------- 53 | function WRITE_stlascii(fileOUT,coordVERTICES,coordNORMALS) 54 | % Write an ASCII STL file 55 | %---------------------- 56 | 57 | facetcount = size(coordNORMALS,1); 58 | filePREFIX = fileOUT(1:end-4); % Get the prefix of the output filename 59 | 60 | %---------------------- 61 | % Prepare data in a suitable format for easy writing to the STL file 62 | 63 | outputALL = zeros(facetcount,3,4); 64 | 65 | outputALL(:,:,1) = coordNORMALS; 66 | outputALL(:,:,2:end) = coordVERTICES; 67 | 68 | outputALL = permute(outputALL,[2,3,1]); 69 | 70 | %---------------------- 71 | % Write to the file 72 | 73 | fidOUT = fopen([fileOUT],'w'); % Open the output file for writing 74 | 75 | fprintf(fidOUT,'solid %s\n',filePREFIX); 76 | 77 | % Write the data for each facet: 78 | fprintf(fidOUT,[ 'facet normal %e %e %e\n',... 79 | ' outer loop\n',... 80 | ' vertex %e %e %e\n',... 81 | ' vertex %e %e %e\n',... 82 | ' vertex %e %e %e\n',... 83 | ' endloop\n',... 84 | 'endfacet\n',... 85 | ],outputALL); 86 | 87 | fprintf(fidOUT,'endsolid %s\n',filePREFIX); 88 | 89 | fclose(fidOUT); % Close the output file 90 | 91 | end %function 92 | 93 | 94 | 95 | %-------------------------------------------------------------------------- 96 | function WRITE_stlbinary(fileOUT,coordVERTICES,coordNORMALS) 97 | % Write a binary STL file 98 | %---------------------- 99 | 100 | facetcount = size(coordNORMALS,1); 101 | filePREFIX = fileOUT(1:end-4); % Get the prefix of the output filename 102 | 103 | %---------------------- 104 | % Prepare data in a suitable format for easy writing to the STL file 105 | 106 | outputALL = zeros(facetcount,3,4); 107 | 108 | outputALL(:,:,1) = coordNORMALS; 109 | outputALL(:,:,2:end) = coordVERTICES; 110 | 111 | outputALL = permute(outputALL,[2,3,1]); 112 | 113 | %---------------------- 114 | % Write to the file 115 | 116 | fidOUT = fopen([fileOUT],'w'); % Open the output file for writing 117 | 118 | %Write the 80 byte header 119 | headerdata = int8(zeros(80,1)); 120 | bytecount = fwrite(fidOUT,headerdata,'int8'); 121 | 122 | %Write the 4 byte facet count 123 | facetcount = int32(size(coordNORMALS,1)); 124 | bytecount = fwrite(fidOUT,facetcount,'int32'); 125 | 126 | % Write the data for each facet: 127 | for loopB = 1:facetcount 128 | fwrite(fidOUT,outputALL(loopB*12-11:loopB*12),'float'); 129 | fwrite(fidOUT,0,'int16'); % Move to the start of the next facet. 130 | end 131 | 132 | fclose(fidOUT); % Close the output file 133 | 134 | end %function 135 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/COMPUTE_mesh_normals.m: -------------------------------------------------------------------------------- 1 | function [coordNORMALS,varargout] = COMPUTE_mesh_normals(meshdataIN) 2 | % COMPUTE_mesh_normals Calculate the normals for each facet of a triangular mesh 3 | %========================================================================== 4 | % FILENAME COMPUTE_mesh_normals.m 5 | % AUTHOR Adam H. Aitkenhead 6 | % CONTACT adam.aitkenhead@physics.cr.man.ac.uk 7 | % DATE 31st March 2010 8 | % PURPOSE Calculate the normal vectors for each facet of a 9 | % triangular mesh. The ordering of the vertices 10 | % (clockwise/anticlockwise) is also checked for all facets 11 | % if this is requested as one of the outputs. 12 | % 13 | % USAGE [coordNORMALS] = COMPUTE_mesh_normals(meshdataIN); 14 | % ..or.. [coordNORMALS,meshdataOUT] = COMPUTE_mesh_normals(meshdataIN); 15 | % 16 | % INPUTS 17 | % 18 | % meshdataIN - structure - Structure containing the faces and 19 | % vertices of the mesh, in the same format 20 | % as that produced by the isosurface 21 | % command. 22 | % ..or.. - Nx3x3 array - The vertex coordinates for each facet, with: 23 | % 1 row for each facet 24 | % 3 columns for the x,y,z coordinates 25 | % 3 pages for the three vertices 26 | % 27 | % OUTPUTS 28 | % 29 | % coordNORMALS - Nx3 array - The normal vectors for each facet, with: 30 | % 1 row for each facet 31 | % 3 columns for the x,y,z components 32 | % 33 | % meshdataOUT - (optional) - The mesh data with the ordering of the vertices 34 | % (clockwise/anticlockwise) checked. Uses 35 | % the same format as . 36 | % 37 | % NOTES Computing to check the ordering of the 38 | % vertices in each facet may be slow for large meshes. 39 | % Also, it may not be possible to compute for 40 | % non-manifold meshes. 41 | %========================================================================== 42 | 43 | %========================================================================== 44 | % VERSION USER CHANGES 45 | % ------- ---- ------- 46 | % 100331 AHA Original version 47 | % 101129 AHA Can now check the ordering of the facet vertices. 48 | % 101130 AHA can now be in either of two formats. 49 | % 101201 AHA Only check the vertex ordering if that is required as one 50 | % of the outputs, as it can be slow for large meshes. 51 | %========================================================================== 52 | 53 | %====================================================== 54 | % Read the input parameters 55 | %====================================================== 56 | 57 | if isstruct(meshdataIN)==1 58 | faces = meshdataIN.faces; 59 | vertex = meshdataIN.vertices; 60 | coordVERTICES = zeros(size(faces,1),3,3); 61 | for loopa = 1:size(faces,1) 62 | coordVERTICES(loopa,:,1) = vertex(faces(loopa,1),:); 63 | coordVERTICES(loopa,:,2) = vertex(faces(loopa,2),:); 64 | coordVERTICES(loopa,:,3) = vertex(faces(loopa,3),:); 65 | end 66 | else 67 | coordVERTICES = meshdataIN; 68 | end 69 | 70 | %====================== 71 | % Initialise array to hold the noral vectors 72 | %====================== 73 | 74 | facetCOUNT = size(coordVERTICES,1); 75 | coordNORMALS = zeros(facetCOUNT,3); 76 | 77 | %====================== 78 | % Check the vertex ordering for each facet 79 | %====================== 80 | 81 | if nargout==2 82 | startfacet = 1; 83 | edgepointA = 1; 84 | checkedlist = false(facetCOUNT,1); 85 | waitinglist = false(facetCOUNT,1); 86 | 87 | while min(checkedlist)==0 88 | 89 | checkedlist(startfacet) = 1; 90 | 91 | edgepointB = edgepointA + 1; 92 | if edgepointB==4 93 | edgepointB = 1; 94 | end 95 | 96 | %Find points which match edgepointA 97 | sameX = coordVERTICES(:,1,:)==coordVERTICES(startfacet,1,edgepointA); 98 | sameY = coordVERTICES(:,2,:)==coordVERTICES(startfacet,2,edgepointA); 99 | sameZ = coordVERTICES(:,3,:)==coordVERTICES(startfacet,3,edgepointA); 100 | [tempa,tempb] = find(sameX & sameY & sameZ); 101 | matchpointA = [tempa,tempb]; 102 | matchpointA = matchpointA(matchpointA(:,1)~=startfacet,:); 103 | 104 | %Find points which match edgepointB 105 | sameX = coordVERTICES(:,1,:)==coordVERTICES(startfacet,1,edgepointB); 106 | sameY = coordVERTICES(:,2,:)==coordVERTICES(startfacet,2,edgepointB); 107 | sameZ = coordVERTICES(:,3,:)==coordVERTICES(startfacet,3,edgepointB); 108 | [tempa,tempb] = find(sameX & sameY & sameZ); 109 | matchpointB = [tempa,tempb]; 110 | matchpointB = matchpointB(matchpointB(:,1)~=startfacet,:); 111 | 112 | %Find edges which match both edgepointA and edgepointB -> giving the adjacent edge 113 | [memberA,memberB] = ismember(matchpointA(:,1),matchpointB(:,1)); 114 | matchfacet = matchpointA(memberA,1); 115 | 116 | if numel(matchfacet)~=1 117 | if exist('warningdone','var')==0 118 | warning('Mesh is non-manifold.') 119 | warningdone = 1; 120 | end 121 | else 122 | matchpointA = matchpointA(memberA,2); 123 | matchpointB = matchpointB(memberB(memberA),2); 124 | 125 | if checkedlist(matchfacet)==0 && waitinglist(matchfacet)==0 126 | %Ensure the adjacent edge is traveled in the opposite direction to the original edge 127 | if matchpointB-matchpointA==1 || matchpointB-matchpointA==-2 128 | %Direction needs to be flipped 129 | [ coordVERTICES(matchfacet,:,matchpointA) , coordVERTICES(matchfacet,:,matchpointB) ] = deal( coordVERTICES(matchfacet,:,matchpointB) , coordVERTICES(matchfacet,:,matchpointA) ); 130 | end 131 | end 132 | end 133 | 134 | waitinglist(matchfacet) = 1; 135 | 136 | if edgepointA<3 137 | edgepointA = edgepointA + 1; 138 | elseif edgepointA==3 139 | edgepointA = 1; 140 | checkedlist(startfacet) = 1; 141 | startfacet = find(waitinglist==1 & checkedlist==0,1,'first'); 142 | end 143 | 144 | end 145 | end 146 | 147 | %====================== 148 | % Compute the normal vector for each facet 149 | %====================== 150 | 151 | for loopFACE = 1:facetCOUNT 152 | 153 | %Find the coordinates for each vertex. 154 | cornerA = coordVERTICES(loopFACE,1:3,1); 155 | cornerB = coordVERTICES(loopFACE,1:3,2); 156 | cornerC = coordVERTICES(loopFACE,1:3,3); 157 | 158 | %Compute the vectors AB and AC 159 | AB = cornerB-cornerA; 160 | AC = cornerC-cornerA; 161 | 162 | %Determine the cross product AB x AC 163 | ABxAC = cross(AB,AC); 164 | 165 | %Normalise to give a unit vector 166 | ABxAC = ABxAC / norm(ABxAC); 167 | coordNORMALS(loopFACE,1:3) = ABxAC; 168 | 169 | end %loopFACE 170 | 171 | %====================================================== 172 | % Prepare the output parameters 173 | %====================================================== 174 | 175 | if nargout==2 176 | if isstruct(meshdataIN)==1 177 | [faces,vertices] = CONVERT_meshformat(coordVERTICES); 178 | meshdataOUT = struct('vertices',vertices,'faces',faces); 179 | else 180 | meshdataOUT = coordVERTICES; 181 | end 182 | varargout(1) = {meshdataOUT}; 183 | end 184 | 185 | -------------------------------------------------------------------------------- /extern/COMPUTE_mesh_normals/COMPUTE_mesh_normals/READ_stl.m: -------------------------------------------------------------------------------- 1 | function [coordVERTICES,varargout] = READ_stl(stlFILENAME,varargin) 2 | % READ_stlascii Read mesh data in the form of an <*.stl> file 3 | %========================================================================== 4 | % FILENAME: READ_stl.m 5 | % AUTHOR: Adam H. Aitkenhead 6 | % INSTITUTION: The Christie NHS Foundation Trust 7 | % CONTACT: adam.aitkenhead@physics.cr.man.ac.uk 8 | % DATE: 29th March 2010 9 | % PURPOSE: Read mesh data in the form of an <*.stl> file. 10 | % 11 | % USAGE: 12 | % 13 | % [coordVERTICES,coordNORMALS,stlNAME] = READ_stl(stlFILENAME,stlFORMAT) 14 | % 15 | % INPUT PARAMETERS: 16 | % 17 | % stlFILENAME - String - Mandatory - The filename of the STL file. 18 | % 19 | % stlFORMAT - String - Optional - The format of the STL file: 20 | % 'ascii' or 'binary' 21 | % 22 | % OUTPUT PARAMETERS: 23 | % 24 | % coordVERTICES - Nx3x3 array - Mandatory 25 | % - An array defining the vertex positions 26 | % for each of the N facets, with: 27 | % 1 row for each facet 28 | % 3 cols for the x,y,z coordinates 29 | % 3 pages for the three vertices 30 | % 31 | % coordNORMALS - Nx3 array - Optional 32 | % - An array defining the normal vector for 33 | % each of the N facets, with: 34 | % 1 row for each facet 35 | % 3 cols for the x,y,z components of the vector 36 | % 37 | % stlNAME - String - Optional - The name of the STL object. 38 | % 39 | %========================================================================== 40 | 41 | %========================================================================== 42 | % VERSION USER CHANGES 43 | % ------- ---- ------- 44 | % 100329 AHA Original version 45 | % 100513 AHA Totally reworked the code. Now use textscan to read the 46 | % file all at once, rather than one line at a time with 47 | % fgetl. Major speed improvment. 48 | % 100623 AHA Combined code which reads ascii STLS and code which reads 49 | % binary STLs into a single function. 50 | % 101126 AHA Small change to binary read code: Now use fread instead 51 | % of fseek. Gives large speed increase. 52 | %========================================================================== 53 | 54 | %========================================================================== 55 | % STL ascii file format 56 | %====================== 57 | % ASCII STL files have the following structure. Technically each facet 58 | % could be any 2D shape, but in practice only triangular facets tend to be 59 | % used. The present code ONLY works for meshes composed of triangular 60 | % facets. 61 | % 62 | % solid object_name 63 | % facet normal x y z 64 | % outer loop 65 | % vertex x y z 66 | % vertex x y z 67 | % vertex x y z 68 | % endloop 69 | % endfacet 70 | % 71 | % 72 | % 73 | % endsolid object_name 74 | %========================================================================== 75 | 76 | %========================================================================== 77 | % STL binary file format 78 | %======================= 79 | % Binary STL files have an 84 byte header followed by 50-byte records, each 80 | % describing a single facet of the mesh. Technically each facet could be 81 | % any 2D shape, but that would screw up the 50-byte-per-facet structure, so 82 | % in practice only triangular facets are used. The present code ONLY works 83 | % for meshes composed of triangular facets. 84 | % 85 | % HEADER: 86 | % 80 bytes: Header text 87 | % 4 bytes: (int) The number of facets in the STL mesh 88 | % 89 | % DATA: 90 | % 4 bytes: (float) normal x 91 | % 4 bytes: (float) normal y 92 | % 4 bytes: (float) normal z 93 | % 4 bytes: (float) vertex1 x 94 | % 4 bytes: (float) vertex1 y 95 | % 4 bytes: (float) vertex1 z 96 | % 4 bytes: (float) vertex2 x 97 | % 4 bytes: (float) vertex2 y 98 | % 4 bytes: (float) vertex2 z 99 | % 4 bytes: (float) vertex3 x 100 | % 4 bytes: (float) vertex3 y 101 | % 4 bytes: (float) vertex3 z 102 | % 2 bytes: Padding to make the data for each facet 50-bytes in length 103 | % ...and repeat for next facet... 104 | %========================================================================== 105 | 106 | if nargin==2 107 | stlFORMAT = lower(varargin{1}); 108 | else 109 | stlFORMAT = 'auto'; 110 | end 111 | 112 | %If necessary, identify whether the STL is ascii or binary: 113 | if strcmp(stlFORMAT,'ascii')==0 && strcmp(stlFORMAT,'binary')==0 114 | stlFORMAT = IDENTIFY_stl_format(stlFILENAME); 115 | end 116 | 117 | %Load the STL file: 118 | if strcmp(stlFORMAT,'ascii') 119 | [coordVERTICES,coordNORMALS,stlNAME] = READ_stlascii(stlFILENAME); 120 | elseif strcmp(stlFORMAT,'binary') 121 | [coordVERTICES,coordNORMALS] = READ_stlbinary(stlFILENAME); 122 | stlNAME = 'unnamed_object'; 123 | end %if 124 | 125 | %Prepare the output arguments 126 | if nargout == 2 127 | varargout(1) = {coordNORMALS}; 128 | elseif nargout == 3 129 | varargout(1) = {coordNORMALS}; 130 | varargout(2) = {stlNAME}; 131 | end 132 | 133 | end %function 134 | 135 | 136 | 137 | %========================================================================== 138 | function [stlFORMAT] = IDENTIFY_stl_format(stlFILENAME) 139 | % IDENTIFY_stl_format Test whether an stl file is ascii or binary 140 | 141 | % Open the file: 142 | fidIN = fopen(stlFILENAME); 143 | 144 | % Check the file size first, since binary files MUST have a size of 84+(50*n) 145 | fseek(fidIN,0,1); % Go to the end of the file 146 | fidSIZE = ftell(fidIN); % Check the size of the file 147 | 148 | if rem(fidSIZE-84,50) > 0 149 | 150 | stlFORMAT = 'ascii'; 151 | 152 | else 153 | 154 | % Files with a size of 84+(50*n), might be either ascii or binary... 155 | 156 | % Read first 80 characters of the file. 157 | % For an ASCII file, the data should begin immediately (give or take a few 158 | % blank lines or spaces) and the first word must be 'solid'. 159 | % For a binary file, the first 80 characters contains the header. 160 | % It is bad practice to begin the header of a binary file with the word 161 | % 'solid', so it can be used to identify whether the file is ASCII or 162 | % binary. 163 | fseek(fidIN,0,-1); % Go to the start of the file 164 | firsteighty = char(fread(fidIN,80,'uchar')'); 165 | 166 | % Trim leading and trailing spaces: 167 | firsteighty = strtrim(firsteighty); 168 | 169 | % Take the first five remaining characters, and check if these are 'solid': 170 | firstfive = firsteighty(1:min(5,length(firsteighty))); 171 | 172 | % Double check by reading the last 80 characters of the file. 173 | % For an ASCII file, the data should end (give or take a few 174 | % blank lines or spaces) with 'endsolid '. 175 | % If the last 80 characters contains the word 'endsolid' then this 176 | % confirms that the file is indeed ASCII. 177 | if strcmp(firstfive,'solid') 178 | 179 | fseek(fidIN,-80,1); % Go to the end of the file minus 80 characters 180 | lasteighty = char(fread(fidIN,80,'uchar')'); 181 | 182 | if findstr(lasteighty,'endsolid') 183 | stlFORMAT = 'ascii'; 184 | else 185 | stlFORMAT = 'binary'; 186 | end 187 | 188 | else 189 | stlFORMAT = 'binary'; 190 | end 191 | 192 | end 193 | 194 | % Close the file 195 | fclose(fidIN); 196 | 197 | end %function 198 | %========================================================================== 199 | 200 | 201 | 202 | %========================================================================== 203 | function [coordVERTICES,coordNORMALS,stlNAME] = READ_stlascii(stlFILENAME) 204 | % READ_stlascii Read mesh data in the form of an ascii <*.stl> file 205 | 206 | % Read the ascii STL file 207 | fidIN = fopen(stlFILENAME); 208 | fidCONTENTcell = textscan(fidIN,'%s','delimiter','\n'); %Read all the file 209 | fidCONTENT = fidCONTENTcell{:}(logical(~strcmp(fidCONTENTcell{:},''))); %Remove all blank lines 210 | fclose(fidIN); 211 | 212 | % Read the STL name 213 | if nargout == 3 214 | line1 = char(fidCONTENT(1)); 215 | if (size(line1,2) >= 7) 216 | stlNAME = line1(7:end); 217 | else 218 | stlNAME = 'unnamed_object'; 219 | end 220 | end 221 | 222 | % Read the vector normals 223 | if nargout >= 2 224 | stringNORMALS = char(fidCONTENT(logical(strncmp(fidCONTENT,'facet normal',12)))); 225 | coordNORMALS = str2num(stringNORMALS(:,13:end)); 226 | end 227 | 228 | % Read the vertex coordinates 229 | facetTOTAL = sum(strcmp(fidCONTENT,'endfacet')); 230 | stringVERTICES = char(fidCONTENT(logical(strncmp(fidCONTENT,'vertex',6)))); 231 | coordVERTICESall = str2num(stringVERTICES(:,7:end)); 232 | cotemp = zeros(3,facetTOTAL,3); 233 | cotemp(:) = coordVERTICESall; 234 | coordVERTICES = shiftdim(cotemp,1); 235 | 236 | end %function 237 | %========================================================================== 238 | 239 | 240 | 241 | %========================================================================== 242 | function [coordVERTICES,coordNORMALS] = READ_stlbinary(stlFILENAME) 243 | % READ_stlbinary Read mesh data in the form of an binary <*.stl> file 244 | 245 | % Open the binary STL file 246 | fidIN = fopen(stlFILENAME); 247 | 248 | % Read the header 249 | fseek(fidIN,80,-1); % Move to the last 4 bytes of the header 250 | facetcount = fread(fidIN,1,'int32'); % Read the number of facets in the stl file 251 | 252 | % Initialise arrays into which the STL data will be loaded: 253 | coordNORMALS = zeros(facetcount,3); 254 | coordVERTICES = zeros(facetcount,3,3); 255 | 256 | % Read the data for each facet: 257 | for loopF = 1:facetcount 258 | 259 | tempIN = fread(fidIN,3*4,'float'); 260 | 261 | coordNORMALS(loopF,1:3) = tempIN(1:3); % x,y,z components of the facet's normal vector 262 | coordVERTICES(loopF,1:3,1) = tempIN(4:6); % x,y,z coordinates of vertex 1 263 | coordVERTICES(loopF,1:3,2) = tempIN(7:9); % x,y,z coordinates of vertex 2 264 | coordVERTICES(loopF,1:3,3) = tempIN(10:12); % x,y,z coordinates of vertex 3 265 | 266 | fread(fidIN,1,'int16'); % Move to the start of the next facet. Using fread is much quicker than using fseek(fidIN,2,0); 267 | 268 | end %for 269 | 270 | % Close the binary STL file 271 | fclose(fidIN); 272 | 273 | end %function 274 | %========================================================================== 275 | -------------------------------------------------------------------------------- /extern/ransac/ransac.m: -------------------------------------------------------------------------------- 1 | % RANSAC - Robustly fits a model to data with the RANSAC algorithm 2 | % 3 | % Usage: 4 | % 5 | % [M, inliers] = ransac(x, fittingfn, distfn, degenfn s, t, feedback, ... 6 | % maxDataTrials, maxTrials) 7 | % 8 | % Arguments: 9 | % x - Data sets to which we are seeking to fit a model M 10 | % It is assumed that x is of size [d x Npts] 11 | % where d is the dimensionality of the data and Npts is 12 | % the number of data points. 13 | % 14 | % fittingfn - Handle to a function that fits a model to s 15 | % data from x. It is assumed that the function is of the 16 | % form: 17 | % M = fittingfn(x) 18 | % Note it is possible that the fitting function can return 19 | % multiple models (for example up to 3 fundamental matrices 20 | % can be fitted to 7 matched points). In this case it is 21 | % assumed that the fitting function returns a cell array of 22 | % models. 23 | % If this function cannot fit a model it should return M as 24 | % an empty matrix. 25 | % 26 | % distfn - Handle to a function that evaluates the 27 | % distances from the model to data x. 28 | % It is assumed that the function is of the form: 29 | % [inliers, M] = distfn(M, x, t) 30 | % This function must evaluate the distances between points 31 | % and the model returning the indices of elements in x that 32 | % are inliers, that is, the points that are within distance 33 | % 't' of the model. Additionally, if M is a cell array of 34 | % possible models 'distfn' will return the model that has the 35 | % most inliers. If there is only one model this function 36 | % must still copy the model to the output. After this call M 37 | % will be a non-cell object representing only one model. 38 | % 39 | % degenfn - Handle to a function that determines whether a 40 | % set of datapoints will produce a degenerate model. 41 | % This is used to discard random samples that do not 42 | % result in useful models. 43 | % It is assumed that degenfn is a boolean function of 44 | % the form: 45 | % r = degenfn(x) 46 | % It may be that you cannot devise a test for degeneracy in 47 | % which case you should write a dummy function that always 48 | % returns a value of 1 (true) and rely on 'fittingfn' to return 49 | % an empty model should the data set be degenerate. 50 | % 51 | % s - The minimum number of samples from x required by 52 | % fittingfn to fit a model. 53 | % 54 | % t - The distance threshold between a data point and the model 55 | % used to decide whether the point is an inlier or not. 56 | % 57 | % feedback - An optional flag 0/1. If set to one the trial count and the 58 | % estimated total number of trials required is printed out at 59 | % each step. Defaults to 0. 60 | % 61 | % maxDataTrials - Maximum number of attempts to select a non-degenerate 62 | % data set. This parameter is optional and defaults to 100. 63 | % 64 | % maxTrials - Maximum number of iterations. This parameter is optional and 65 | % defaults to 1000. 66 | % 67 | % Returns: 68 | % M - The model having the greatest number of inliers. 69 | % inliers - An array of indices of the elements of x that were 70 | % the inliers for the best model. 71 | % 72 | % 73 | % Note that the desired probability of choosing at least one sample free from 74 | % outliers is set at 0.99. You will need to edit the code should you wish to 75 | % change this (it should probably be a parameter) 76 | % 77 | % For an example of the use of this function see RANSACFITHOMOGRAPHY or 78 | % RANSACFITPLANE 79 | 80 | % References: 81 | % M.A. Fishler and R.C. Boles. "Random sample concensus: A paradigm 82 | % for model fitting with applications to image analysis and automated 83 | % cartography". Comm. Assoc. Comp, Mach., Vol 24, No 6, pp 381-395, 1981 84 | % 85 | % Richard Hartley and Andrew Zisserman. "Multiple View Geometry in 86 | % Computer Vision". pp 101-113. Cambridge University Press, 2001 87 | 88 | % Copyright (c) 2003-2013 Peter Kovesi 89 | % Centre for Exploration Targeting 90 | % The University of Western Australia 91 | % peter.kovesi at uwa edu au 92 | % http://www.csse.uwa.edu.au/~pk 93 | % 94 | % Permission is hereby granted, free of charge, to any person obtaining a copy 95 | % of this software and associated documentation files (the "Software"), to deal 96 | % in the Software without restriction, subject to the following conditions: 97 | % 98 | % The above copyright notice and this permission notice shall be included in 99 | % all copies or substantial portions of the Software. 100 | % 101 | % The Software is provided "as is", without warranty of any kind. 102 | % 103 | % May 2003 - Original version 104 | % February 2004 - Tidied up. 105 | % August 2005 - Specification of distfn changed to allow model fitter to 106 | % return multiple models from which the best must be selected 107 | % Sept 2006 - Random selection of data points changed to ensure duplicate 108 | % points are not selected. 109 | % February 2007 - Jordi Ferrer: Arranged warning printout. 110 | % Allow maximum trials as optional parameters. 111 | % Patch the problem when non-generated data 112 | % set is not given in the first iteration. 113 | % August 2008 - 'feedback' parameter restored to argument list and other 114 | % breaks in code introduced in last update fixed. 115 | % December 2008 - Octave compatibility mods 116 | % June 2009 - Argument 'MaxTrials' corrected to 'maxTrials'! 117 | % January 2013 - Separate code path for Octave no longer needed 118 | 119 | function [M, inliers] = ransac(x, fittingfn, distfn, degenfn, s, t, feedback, ... 120 | maxDataTrials, maxTrials) 121 | 122 | % Test number of parameters 123 | error ( nargchk ( 6, 9, nargin ) ); 124 | 125 | if nargin < 9; maxTrials = 1000; end; 126 | if nargin < 8; maxDataTrials = 100; end; 127 | if nargin < 7; feedback = 0; end; 128 | 129 | [rows, npts] = size(x); 130 | 131 | p = 0.99; % Desired probability of choosing at least one sample 132 | % free from outliers (probably should be a parameter) 133 | 134 | bestM = NaN; % Sentinel value allowing detection of solution failure. 135 | trialcount = 0; 136 | bestscore = 0; 137 | N = 1; % Dummy initialisation for number of trials. 138 | 139 | while N > trialcount 140 | 141 | % Select at random s datapoints to form a trial model, M. 142 | % In selecting these points we have to check that they are not in 143 | % a degenerate configuration. 144 | degenerate = 1; 145 | count = 1; 146 | while degenerate 147 | % Generate s random indicies in the range 1..npts 148 | % (If you do not have the statistics toolbox with randsample(), 149 | % use the function RANDOMSAMPLE from my webpage) 150 | if ~exist('randsample', 'file') 151 | ind = randomsample(npts, s); 152 | else 153 | ind = randsample(npts, s); 154 | end 155 | 156 | % Test that these points are not a degenerate configuration. 157 | degenerate = feval(degenfn, x(:,ind)); 158 | 159 | if ~degenerate 160 | % Fit model to this random selection of data points. 161 | % Note that M may represent a set of models that fit the data in 162 | % this case M will be a cell array of models 163 | M = feval(fittingfn, x(:,ind)); 164 | 165 | % Depending on your problem it might be that the only way you 166 | % can determine whether a data set is degenerate or not is to 167 | % try to fit a model and see if it succeeds. If it fails we 168 | % reset degenerate to true. 169 | if isempty(M) 170 | degenerate = 1; 171 | end 172 | end 173 | 174 | % Safeguard against being stuck in this loop forever 175 | count = count + 1; 176 | if count > maxDataTrials 177 | warning('Unable to select a nondegenerate data set'); 178 | 179 | %added by Nima Sedaghat 180 | % There was a bug here. If the input is too degenerate, 181 | % this function fails because M will be missing a few 182 | % lines below! 183 | M = []; 184 | inliers = []; 185 | return 186 | 187 | break 188 | end 189 | end 190 | 191 | % Once we are out here we should have some kind of model... 192 | % Evaluate distances between points and model returning the indices 193 | % of elements in x that are inliers. Additionally, if M is a cell 194 | % array of possible models 'distfn' will return the model that has 195 | % the most inliers. After this call M will be a non-cell object 196 | % representing only one model. 197 | [inliers, M] = feval(distfn, M, x, t); 198 | 199 | % Find the number of inliers to this model. 200 | ninliers = length(inliers); 201 | 202 | if ninliers > bestscore % Largest set of inliers so far... 203 | bestscore = ninliers; % Record data for this model 204 | bestinliers = inliers; 205 | bestM = M; 206 | 207 | % Update estimate of N, the number of trials to ensure we pick, 208 | % with probability p, a data set with no outliers. 209 | fracinliers = ninliers/npts; 210 | pNoOutliers = 1 - fracinliers^s; 211 | pNoOutliers = max(eps, pNoOutliers); % Avoid division by -Inf 212 | pNoOutliers = min(1-eps, pNoOutliers);% Avoid division by 0. 213 | N = log(1-p)/log(pNoOutliers); 214 | end 215 | 216 | trialcount = trialcount+1; 217 | if feedback 218 | fprintf('trial %d out of %d \r',trialcount, ceil(N)); 219 | end 220 | 221 | % Safeguard against being stuck in this loop forever 222 | if trialcount > maxTrials 223 | warning( ... 224 | sprintf('ransac reached the maximum number of %d trials',... 225 | maxTrials)); 226 | break 227 | end 228 | end 229 | 230 | if feedback, fprintf('\n'); end 231 | 232 | if ~isnan(bestM) % We got a solution 233 | M = bestM; 234 | inliers = bestinliers; 235 | else 236 | M = []; 237 | inliers = []; 238 | error('ransac was unable to find a useful solution'); 239 | end 240 | 241 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------