From efcbcf194335d9b292ed5e0de16137c7d3949aae Mon Sep 17 00:00:00 2001 From: Monica J Emerson <monj@dtu.dk> Date: Mon, 22 Feb 2021 09:12:10 +0100 Subject: [PATCH] Added code --- code/InSegtFibre_2D.m | 221 +++++ code/InsegtFibre_3D.m | 254 ++++++ code/InsegtFibre_orientations.m | 138 +++ code/InsegtFibre_validate3DTracks.m | 68 ++ code/functions/calc_eltsdict.m | 8 + code/functions/calcu_psize.m | 15 + code/functions/imshow3D.m | 291 +++++++ code/functions/loadVolumeRoI.m | 42 + code/functions/num_dictAtoms.m | 8 + code/functions/selectFoV.m | 29 + code/functions/track_slicer.m | 109 +++ code/scripts/addpaths_inSegtFibre.m | 12 + code/scripts/create_dict.m | 31 + code/scripts/indicate_dataFolder.m | 19 + code/scripts/input_depthRoI.m | 19 + code/scripts/load_multiple_trackSets.m | 15 + code/scripts/setup_dict.m | 15 + .../code/batch_processing_script.m | 39 + .../code/functions/biadjacency_matrix.cpp | 103 +++ .../code/functions/biadjacency_matrix.mexa64 | Bin 0 -> 13773 bytes .../functions/biadjacency_matrix.mexmaci64 | Bin 0 -> 13924 bytes .../code/functions/biadjacency_matrix.mexw64 | Bin 0 -> 18432 bytes .../code/functions/build_dictionary.m | 23 + .../code/functions/build_km_tree.cpp | 421 +++++++++ .../code/functions/build_km_tree.mexa64 | Bin 0 -> 19188 bytes .../code/functions/build_km_tree.mexmaci64 | Bin 0 -> 23416 bytes .../code/functions/build_km_tree.mexw64 | Bin 0 -> 26112 bytes .../code/functions/build_km_tree_xcorr.cpp | 427 ++++++++++ .../code/functions/build_km_tree_xcorr.mexa64 | Bin 0 -> 19157 bytes .../functions/build_km_tree_xcorr.mexmaci64 | Bin 0 -> 23344 bytes .../code/functions/build_km_tree_xcorr.mexw64 | Bin 0 -> 27136 bytes .../code/functions/compile_mex_functions.m | 8 + .../code/functions/compute_mappings.m | 21 + .../code/functions/image_texture_gui.m | 805 ++++++++++++++++++ .../code/functions/linkaxesicon.png | Bin 0 -> 568 bytes .../code/functions/normalize_image.m | 9 + .../functions/probability_search_km_tree.cpp | 305 +++++++ .../probability_search_km_tree.mexa64 | Bin 0 -> 18140 bytes .../probability_search_km_tree.mexmaci64 | Bin 0 -> 14168 bytes .../probability_search_km_tree.mexw64 | Bin 0 -> 20992 bytes .../code/functions/process_image.m | 16 + .../code/functions/search_km_tree.cpp | 239 ++++++ .../code/functions/search_km_tree.mexa64 | Bin 0 -> 13522 bytes .../code/functions/search_km_tree.mexmaci64 | Bin 0 -> 13604 bytes .../code/functions/search_km_tree.mexw64 | Bin 0 -> 16896 bytes .../code/functions/search_km_tree_xcorr.cpp | 239 ++++++ .../functions/search_km_tree_xcorr.mexa64 | Bin 0 -> 13546 bytes .../functions/search_km_tree_xcorr.mexmaci64 | Bin 0 -> 13628 bytes .../functions/search_km_tree_xcorr.mexw64 | Bin 0 -> 17408 bytes .../functions/segmentation_correction_gui.m | 406 +++++++++ .../code/functions/update_dictionary.m | 9 + code/texture_gui/code/reusing_labels_script.m | 23 + code/texture_gui/code/test.m | 31 + .../code/texture_gui_uses_script.m | 29 + 54 files changed, 4447 insertions(+) create mode 100755 code/InSegtFibre_2D.m create mode 100755 code/InsegtFibre_3D.m create mode 100755 code/InsegtFibre_orientations.m create mode 100755 code/InsegtFibre_validate3DTracks.m create mode 100755 code/functions/calc_eltsdict.m create mode 100755 code/functions/calcu_psize.m create mode 100755 code/functions/imshow3D.m create mode 100755 code/functions/loadVolumeRoI.m create mode 100755 code/functions/num_dictAtoms.m create mode 100755 code/functions/selectFoV.m create mode 100755 code/functions/track_slicer.m create mode 100755 code/scripts/addpaths_inSegtFibre.m create mode 100755 code/scripts/create_dict.m create mode 100755 code/scripts/indicate_dataFolder.m create mode 100644 code/scripts/input_depthRoI.m create mode 100755 code/scripts/load_multiple_trackSets.m create mode 100755 code/scripts/setup_dict.m create mode 100755 code/texture_gui/code/batch_processing_script.m create mode 100755 code/texture_gui/code/functions/biadjacency_matrix.cpp create mode 100755 code/texture_gui/code/functions/biadjacency_matrix.mexa64 create mode 100755 code/texture_gui/code/functions/biadjacency_matrix.mexmaci64 create mode 100755 code/texture_gui/code/functions/biadjacency_matrix.mexw64 create mode 100755 code/texture_gui/code/functions/build_dictionary.m create mode 100755 code/texture_gui/code/functions/build_km_tree.cpp create mode 100755 code/texture_gui/code/functions/build_km_tree.mexa64 create mode 100755 code/texture_gui/code/functions/build_km_tree.mexmaci64 create mode 100755 code/texture_gui/code/functions/build_km_tree.mexw64 create mode 100755 code/texture_gui/code/functions/build_km_tree_xcorr.cpp create mode 100755 code/texture_gui/code/functions/build_km_tree_xcorr.mexa64 create mode 100755 code/texture_gui/code/functions/build_km_tree_xcorr.mexmaci64 create mode 100755 code/texture_gui/code/functions/build_km_tree_xcorr.mexw64 create mode 100755 code/texture_gui/code/functions/compile_mex_functions.m create mode 100755 code/texture_gui/code/functions/compute_mappings.m create mode 100755 code/texture_gui/code/functions/image_texture_gui.m create mode 100755 code/texture_gui/code/functions/linkaxesicon.png create mode 100755 code/texture_gui/code/functions/normalize_image.m create mode 100755 code/texture_gui/code/functions/probability_search_km_tree.cpp create mode 100755 code/texture_gui/code/functions/probability_search_km_tree.mexa64 create mode 100755 code/texture_gui/code/functions/probability_search_km_tree.mexmaci64 create mode 100755 code/texture_gui/code/functions/probability_search_km_tree.mexw64 create mode 100755 code/texture_gui/code/functions/process_image.m create mode 100755 code/texture_gui/code/functions/search_km_tree.cpp create mode 100755 code/texture_gui/code/functions/search_km_tree.mexa64 create mode 100755 code/texture_gui/code/functions/search_km_tree.mexmaci64 create mode 100755 code/texture_gui/code/functions/search_km_tree.mexw64 create mode 100755 code/texture_gui/code/functions/search_km_tree_xcorr.cpp create mode 100755 code/texture_gui/code/functions/search_km_tree_xcorr.mexa64 create mode 100755 code/texture_gui/code/functions/search_km_tree_xcorr.mexmaci64 create mode 100755 code/texture_gui/code/functions/search_km_tree_xcorr.mexw64 create mode 100755 code/texture_gui/code/functions/segmentation_correction_gui.m create mode 100755 code/texture_gui/code/functions/update_dictionary.m create mode 100755 code/texture_gui/code/reusing_labels_script.m create mode 100755 code/texture_gui/code/test.m create mode 100755 code/texture_gui/code/texture_gui_uses_script.m diff --git a/code/InSegtFibre_2D.m b/code/InSegtFibre_2D.m new file mode 100755 index 0000000..bf5ee95 --- /dev/null +++ b/code/InSegtFibre_2D.m @@ -0,0 +1,221 @@ +% InSegtFibre_2D +% Written by Monica Jane Emerson, April 2018. +% MIT license + +%% Step 0: Add paths of GUI and other functions and scripts used in Insegt Fibre +close all, +clear all, +addpath(genpath('./texture_gui')) +addpath('./scripts') +addpath('./functions') + +disp('Step 0 completed') + +%% Step 1: Load and visualise the four 2D images +%the path of the 2Ddata folder in your computer +str_datafolder = '../data/2Ddata/'; %default value + +%Visualise all four datasets +contents_datafolder = dir(str_datafolder); +num_datasets = length(contents_datafolder)-2; + +figure + for k= 1:num_datasets + %read image + im = imread([str_datafolder,contents_datafolder(k+2).name]); + %create subtitle for image + subtitle = [num2str(k),'. ',contents_datafolder(k+2).name(1:end-4)]; + ind = find(subtitle=='_'); + subtitle(ind) = ' '; + %plot image + subplot(2,2,k), imagesc(im), axis image, colormap gray, title(subtitle) + end + +h = msgbox('Inspect the quality of the four different scans'); +waitfor(h) + +disp('Step 1 completed') + +%% Step 2: Select data-set and region of interest (RoI) +close all +%USER INPUT: select dataset +x = inputdlg('Choose a data-set (1, 2, 3 or 4?): ','User input',[1,20],{num2str(4)}); + +%read the 2D image chosen by the user +dataset = str2double(x{1}); +im = imread([str_datafolder,contents_datafolder(dataset+2).name]); + +%print the name of the selected data-set +disp(['Selected data-set: ', contents_datafolder(dataset+2).name(1:end-4)]); + +%USER INPUT: select RoI +h = msgbox('Crop a region of interest containing 100-1000 fibres.'); +waitfor(h) +im_crop = imcrop(im); + +%visualise the selected RoI +if isempty(im_crop) + h = msgbox('There was an error in the cropping. Run the section again.'); + waitfor(h) +else + figure, imagesc(im_crop), axis image, colormap gray, + title('Region of Interest for training the dictionary') +end + +disp('Step 2 completed') + +%% Step 3: Set default parameters and the patch size at the scale of the fibres +close all +%USER INPUT: Parameters that give the scale of the fibres in pixels +prompt_px = ['Pixel size [', char(181),'m]: ']; +prompt_diam = ['Diameter size [', char(181),'m]: ']; +x = str2double(inputdlg({prompt_px, prompt_diam},'User input',[1,30],{num2str(0.96),num2str(7)})); + +%compute patch size as the closest odd integer to the fibre diameter +%measured in pixels diam/pixel_size +patch_size = calcu_psize(x(2)/x(1)); + +%check that the patch size is in the optimal range to ensure precision and +%speed +if ((x(2)/x(1)) < 7) + factor = 9/(x(2)/x(1)); + patch_size = calcu_psize(factor*x(2)/x(1)); + msg = ['We will upscale your images with a factor ',... + num2str(factor,'%0.2f'),' to obtain better precision. The patch size is now ',... + num2str(patch_size),'.']; + h = msgbox(msg); + waitfor(h) +elseif ((x(2)/x(1)) >= 17) + factor = 11/(x(2)/x(1)); + patch_size = calcu_psize(factor*x(2)/x(1)); + msg = ['We will downscale your images with a factor ',... + num2str(factor),' to obtain reduce the computational time. The patch size is now ',... + num2str(patch_size),'.']; + h = msgbox(msg); + waitfor(h) +else + factor = 1; +end + +%set patch size +dictopt.patch_size = patch_size; +%set the default ditionary parameters +dictopt.method = 'euclidean'; +dictopt.branching_factor = 3; %it should at least be 3 +dictopt.number_layers = 5; %at least 5. The higher the more accurate, but also slower and more computationally heavy +num_dict_elts = calc_eltsdict(dictopt.branching_factor,dictopt.number_layers); +dictopt.number_training_patches = 5000; %at least an order of magnitude more than the number of dictionary elements +dictopt.normalization = false; %set to true if the global intensity of the slices varies along the depth of the volume + +disp('Step 3 completed') + +%% Step 4: Run InSegt +%display the important dictionary parameters +fprintf('\nImportant Dictionary Parameters:\n'); +disp(['patch size: ', num2str(dictopt.patch_size)]); +disp(['number dictionary elements: ', num2str(calc_eltsdict(dictopt.branching_factor,dictopt.number_layers))]); + +%resize cropped region of interest with factor +im_train = imresize(im_crop, factor); +%open the GUI with 2 labels for pixel annotation (fibre centre region or not) +image_texture_gui(im_train,dictopt,2) + +disp('Step 4 completed') + +%% Step 5: Obtain the centre coordinates from the hard segmentation +close all +%USER INPUT: label used for the centre regions +label = 2; %1 (blue) or 2 (pink) + +%calculate the centroids of the connected centre regions +c = regionprops(gui_S==label,'centroid'); +centrePoints = cat(1,c.Centroid); + +%show fibre centre coordinates over slice +figure(1), subplot(1,2,1), imagesc(im_crop), axis image, colormap gray, +hold on, h = imagesc(ind2rgb(imresize(gui_S==label,1/factor), cool(2))); set(h, 'AlphaData',0.5); + +figure(1), subplot(1,2,2), imagesc(im_crop), axis image, colormap gray +hold on, plot(centrePoints(:,1)*1/factor,centrePoints(:,2)*1/factor,'r*'); + +disp('Step 5 completed') + +%% Step 6: Save the dictionary to process other regions of the scan +close all +%train the dictionary with the small RoI +dictionary = build_dictionary(im_train,dictopt); %create the dictionary of intensities +image_texture_gui(im_train,dictionary,2) %learn the dictionary of probabilities by annotating in the GUI +dictionary = update_dictionary(dictionary,gui_dictprob); %update dictionary to include the learnt probalilities + +%USER INPUT: select a RoI to process with the learnt dictionary +h = msgbox('Crop a region of interest, it can be as big as you like.'); +waitfor(h) +im_p = imcrop(im); +if isempty(im_crop) + h = msgbox('There was an error in the cropping. Run the section again from the cropping.'); + waitfor(h) +else + figure, imagesc(im_p), axis image, colormap gray, + title('Region of Interest for finding fibre centres') +end + +%apply the dictionary to process the larger RoI +im_process = imresize(im_p,factor); +[S,allP] = process_image(im_process,dictionary); + +%calculate the centroids of the connected centre regions +label = 2; %1 (blue) or 2 (pink) +c = regionprops(S==label,'centroid'); +centrePoints = cat(1,c.Centroid); + +%Centre regions and coordinates over the image to evaluate the accuracy of the segmentation +figure, imagesc(im_process), axis image, colormap gray, +hold on, h = imagesc(ind2rgb(S, cool(2))); set(h, 'AlphaData',0.5); +hold on, plot(centrePoints(:,1),centrePoints(:,2),'y.'); +title('Segmented centre regions and fibre detections over the processed image'); + +if factor ~=1 + figure, imagesc(im_p), axis image, colormap gray, + hold on, plot(centrePoints(:,1)*1/factor,centrePoints(:,2)*1/factor,'y.'); + title('Centre detections over the original resolution') +end + +disp('Step 6 completed') + +%% Step 7: Tune the threshold applied to the probability map +close all +%visualise the propabilistic segmentation of fibre centre regions +figure(1), imagesc(allP(:,:,2)), axis image off, colormap gray, title('Centre class probability map'), colorbar + +y = 'yes'; +while strcmp(y,'yes') + %USER INPUT: threshold value for the probability map + x = inputdlg('Choose threshold: ','User input',[1,20],{num2str(0.5)}); + + %Centre regions over the image to evaluate the accuracy of the segmentation + figure, imagesc(im_process), axis image, colormap gray, + hold on, h = imagesc(ind2rgb(allP(:,:,2)>str2double(x{1}), cool(2))); set(h, 'AlphaData',0.5); + + %USER INPUT: do you want to try another threshold?? + y = questdlg('Do you want to try another thresold?: ','User input','yes','no','yes'); +end + +disp('Step 7 completed') + +%% Step 8: Save your manual input to add additional markings later on +close all +%annotate a little in the GUI and save your manual input +h = msgbox(['Annotate a little in the GUI and press [S] before closing the GUI (then close by pressing export [E]). Choose to save',... + ' your annotations in the folder "InsegtFibre_workshopDTU/data/saved_data/trainingData".']); +waitfor(h) +image_texture_gui(im_train,dictopt,2) +%save the training region, over which you placed the markings +save('../data/saved_data/trainingData/training_image.mat','im_train'); + +%Call the GUI with a saved labelling +load('../data/saved_data/trainingData/training_image.mat'); +[filename,pathname] = uigetfile('*_labels_indexed.png', 'Choose label image', '../data/saved_data/trainingData/'); +labelling = imread([pathname,filename]); %read labelling +image_texture_gui(im_train,dictopt,2,labelling) %call the GUI with the saved labelling + +disp('Step 8 completed') diff --git a/code/InsegtFibre_3D.m b/code/InsegtFibre_3D.m new file mode 100755 index 0000000..33dc837 --- /dev/null +++ b/code/InsegtFibre_3D.m @@ -0,0 +1,254 @@ +% InSegtFibre_3D +% Written by Monica Jane Emerson, May 2018. +% MIT license + +%% Step 0: Add paths of Insegt GUI, other functions and scripts, and prepare workspace +close all, +clear all, +addpath(genpath('./texture_gui')) +addpath('./scripts') +addpath('./functions') + +disp('Step 0 completed') + +%% Step 1: Locate folder with the CT data +close all +indicate_dataFolder + +disp('Step 1 completed') + +%% Step 2: Load and visualise the data +close all +[V,depth] = loadVolumeRoI(path_volumeFolder); +figure, imshow3D(V) %visualise the loaded RoI + +disp('Step 2 completed') + +%% Step 3: Compute a dictionary model from the CT scan +close all +setup_dict %set the parameters of the dictionary model +create_dict %learn the dictionary model from training data + +disp('Step 3 completed') + +%% Step 4: Obtain the centre-class probabilistic segmentation for a slice +close all +% USER INPUT: Load a saved dictionary +[filename,pathname] = uigetfile('*.mat','Choose dictionary','../data/saved_data/dictionaries/dictionary'); +load([pathname,filename]); + +% USER INPUT: Choose slice +prompt = ['Choose a slice to segment: [1,', num2str(size(V,3)),']:']; +x = inputdlg(prompt,'User input',[1,30],{num2str(round(size(V,3)/4))}); +sliceAnalys = str2double(x{1}); +h = msgbox('Segmenting image...'); +[~,allP] = process_image(V(:,:,sliceAnalys),dictionary); +delete(h) +Pcentre = allP(:,:,2); + +%Visualise for each pixel its probabily of belonging to a fibre centre region +h = figure(1), %h.Units = 'normalized'; h.Position = [0 0 1 1]; +subplot(1,2,1), imagesc(Pcentre), axis image, colormap gray, colorbar, +title('Probability of belonging to the central region of a fibre') +figure(1), subplot(1,3,3), histogram(Pcentre(:)), +ylabel('Count of probabilities'), xlabel('Range of probabilities') + +disp('Step 4 completed') + +%% Step 5: Obtain the fibre centres by thresholding the probabilistic segmentation +% USER INPUT: threshold value for the probability map +thresh = inputdlg('Choose threshold: ','User input',[1,20],{num2str(0.50)}); + +%Threshold probability map +areas_th = Pcentre > str2double(thresh{1}); + +%Calculate centroids +concomp = logical(areas_th); +c = regionprops(concomp,'centroid'); +centrePoints_2D = cat(1,c.Centroid); + +%Visualisations to evaluate the chosen threshold +figure(2), subplot(1,2,1), imagesc(V(:,:,sliceAnalys)), axis image, colormap gray, +hold on, h = imagesc(ind2rgb(areas_th, cool(2))), set(h, 'AlphaData',0.5); +figure(2), subplot(1,2,2), imagesc(V(:,:,sliceAnalys)), axis image, colormap gray +hold on, plot(centrePoints_2D(:,1),centrePoints_2D(:,2),'r*'); + +disp('Step 5 completed') + +%% Step 6: Detect fibre centres in the batch of slices forming the volume +close all +clear centrePoints, clear allP, clear c +% USER INPUT: Load a saved dictionary +[filename,pathname] = uigetfile('*.mat','Choose dictionary','../data/saved_data/dictionaries/dictionary_'); +load([pathname,filename]); + +% USER INPUT: threshold value for the probability map +thresh = inputdlg('Choose threshold: ','User input',[1,20],{num2str(0.5)}); + +%Initialise variables +step = 1; +slices = 1:step:size(V,3); + +%Start timer +tstart = tic; +h = waitbar(0,'Processing volume...'); + +for l = 1:numel(slices) + %Compute the probabilistic segmentation + [~,allP] = process_image(V(:,:,slices(l)),dictionary); + %Save probability map + P(:,:,l) = allP(:,:,2); + %Compute the connected components of the thresholded probabilities + c = regionprops(bwlabel(allP(:,:,2) > str2num(thresh{1})),'centroid'); + %Store the coordinates of the centre points + centrePoints{l} = cat(1,c.Centroid); + %Update the data processing wait bar + waitbar(l/numel(slices),h) +end + +%disp processing time +t_load = toc(tstart); +f = msgbox(['time to process volume: ' , num2str(t_load), 's']); +close(h) +waitfor(f) + +%Compute the number of detected fibres per slice +clear len +close all +for l = 1:length(centrePoints) + len(l) = size(centrePoints{l},1); +end +aux = str2double(depth); +figure(1), plot([aux(1):aux(3):aux(2)],len,'.'), axis tight, title('Nr. fibres per slice'), +xlabel('slice number (1 is the top of the sample)') +ylabel('number of detected fibres') + +%Visualise the detections in 3D +if ~exist('vx_size') + vx_size = str2double(inputdlg(['Pixel size [', char(181),'m]: '],'User input',[1,30],{num2str(1.1)})); +end + +for l = 1:step:length(centrePoints) + figure(2), hold on, plot3(centrePoints{l}(:,1)*vx_size,centrePoints{l}(:,2)*vx_size,l*ones(size(centrePoints{l}(:,1)))*vx_size,'r.','MarkerSize',1) +end +h = figure(2), axis tight, view(3), axis equal +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') +aux = cellstr(num2str(vx_size*linspace(str2double(depth{1}),str2double(depth{2}),length(h.Children.ZTickLabel)+1)','%.f')); +h.Children.ZTickLabel = aux(2:end); + +% USER INPUT: Save 3D detections +uisave('centrePoints','../data/saved_data/centreCoordinates/centreCoords_?.mat') + +disp('Step 6 completed') + +%% Step 7: Compute the 3D fibre trajectories via tracking +% USER INPUT: Load the saved 3D detections +[filename,pathname] = uigetfile('*.mat','Choose the set of 3D coordinates to track','../data/saved_data/centreCoordinates'); +load([pathname,filename]); + +% USER INPUT: How many pixels is a fibre allowed to move from one slice to the +% next? (function of the step, the diameter and the pixel size). +x = inputdlg('How many pixels is a fibre allowed to move from one slice to the next? (a function of the step, the diameter and the pixel size): ','User input',[1,65],{num2str(5)}); +dist = str2num(x{1}); + +% USER INPUT: Should the tracking start with the detections of the bottom +% or the top slice? +if ~exist('depth') + depth = input_depthRoI(num_slices); +end +top = 'Top of the volume (slice 1)'; +bottom = ['Bottom of the volume (slice ', depth{2},')']; +direct = questdlg('Where shall we start the fibres?','Tracking direction',top, bottom,bottom); + +if strcmp(direct,bottom) + m = length(centrePoints); +else + m = 1; +end + +%Initialise variables +thisCt = centrePoints{m}; %we start tracking from the bottom of the volume (end slice) +f_x = nan(size(thisCt,1),length(centrePoints)); +f_y = nan(size(thisCt,1),length(centrePoints)); +f_x(:,1) = centrePoints{m}(:,1); +f_y(:,1) = centrePoints{m}(:,2); + +%Starting time +tstart = tic; + +%Track the coordinates in the z direction +for l = 2:length(centrePoints) + % coordinates of next slice + if strcmp(direct,bottom) + ctNext = centrePoints{end-l+1}; + else + ctNext = centrePoints{l}; + end + + % build kd-tree + tree_down = KDTreeSearcher(ctNext); + % search the kd-tree + [id_down, d_down] = knnsearch(tree_down,thisCt,'K',1); + % build kd-tree + tree_up = KDTreeSearcher(thisCt); + % search the kd-tree + [id_up, d_up] = knnsearch(tree_up,ctNext,'K',1); + + % keep matches that are bidirectional + id_top = unique(id_up(id_down)); + id_bottom = id_down(unique(id_up(id_down))); + + % keep matches that are closer than a distance + idMatch = find(d_down(id_top) < dist); + id_top_th = id_top(idMatch); + id_bottom_th = id_bottom(idMatch); + + if strcmp(direct,bottom) + f_x(id_top_th,l) = centrePoints{end-l+1}(id_bottom_th,1); + f_y(id_top_th,l) = centrePoints{end-l+1}(id_bottom_th,2); + else + f_x(id_top_th,l) = centrePoints{l}(id_bottom_th,1); + f_y(id_top_th,l) = centrePoints{l}(id_bottom_th,2); + end + + %points to be matched with the next slice + thisCt(id_top_th,:) = ctNext(id_bottom_th,:); +end +t_load = toc(tstart); +disp(['time to track coordinates along the volume: ' , num2str(t_load), 's']); + +%Save track coordinates in the structure 'fibres' +fibres.x = f_x; +fibres.y = f_y; + +%Visualise fibre tracks +% USER INPUT: How many fibres would you like to visualise (in percentage)? +questions = {'What percentage of the tracked fibres would you like to visualise (%)?: '}; +x = inputdlg(questions,'User input',[1,40],{num2str(10)}); %ask for patch size + +perc = str2double(x{1})/100; +fib_ind = randperm(size(fibres.x,1),round(perc*size(fibres.x,1))); %indices of fibre to plot +z = [1:size(fibres.x,2)]; %z-values + +if ~exist('vx_size') + vx_size = str2double(inputdlg(['Pixel size [', char(181),'m]: '],'User input',[1,30],{num2str(1.1)})); +end +figure(3),clf +for l= 1:numel(fib_ind) + h = figure(3); + hold on, plot3(fibres.x(fib_ind(l),:)*vx_size,fibres.y(fib_ind(l),:)*vx_size,z*vx_size,'LineWidth',1), +end +axis tight, axis equal, view(3), +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') +aux = cellstr(num2str(vx_size*linspace(str2double(depth{1}),str2double(depth{2}),length(h.Children.ZTickLabel)+1)','%.f')); +h.Children.ZTickLabel = aux(2:end); + +% USER INPUT: Save fibre tracks +if strcmp(direct,bottom) + direction = [depth{2},'to',depth{1}]; +else + direction = [depth{1},'to',depth{2}]; +end +uisave('fibres',['../data/saved_data/fibreTracks/fibreTracks_dataSetX_',direction,'step',depth{3},'.mat']) + + \ No newline at end of file diff --git a/code/InsegtFibre_orientations.m b/code/InsegtFibre_orientations.m new file mode 100755 index 0000000..86cbcc3 --- /dev/null +++ b/code/InsegtFibre_orientations.m @@ -0,0 +1,138 @@ +% InSegtFibre_orientations +% Written by Monica Jane Emerson, May 2019. +% MIT license + +%% Prepare workspace +close all +clear all +addpath('./scripts') +addpath('./functions') + +%% Read tracks, z-values (analysed slices) and voxel size +[filename_tracks,pathname_tracks] = uigetfile('*.mat','Choose tracks','../data/saved_data/fibreTracks'); +fibres = getfield(load([pathname_tracks,filename_tracks]),'fibres'); + +depth = input_depthRoI; %get z coordinates (start, end and step) +z_axis = [str2double(depth{1}):str2double(depth{3}):str2double(depth{2})]; + +vx_size = str2double(inputdlg(['What is the pixel size of your scan [', char(181),'m]: '],'Pixel/voxel size',[1,45],{num2str(1.1)})); + +%% Display tracks +h1 = figure(1); +for l= 1:size(fibres.x,1) + figure(1), hold on, + plot3(fibres.x(l,:)*vx_size,fibres.y(l,:)*vx_size,z_axis*vx_size,'k','LineWidth',1), +end +figure(1), axis tight, axis equal, view(2), view(3) +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') + +%% Display locations of missed detections +missed_det = isnan(fibres.x'); +figure(2), imagesc(missed_det), axis image, axis xy +title('Missed fibre centres') +ylabel('z axis [slices]'),xlabel('fibre ID'), + +%% Select fibres for the orientation plot +%Indices of fibres that miss start and/or end detection +%(will be excluded from the orientation plot) +exc_fibres = find((missed_det(1,:)|missed_det(end,:))==1); + +%Indices of the fibre kept for the orientation plot +keep_fibres = 1:size(fibres.x,1); +keep_fibres(exc_fibres) = []; + +display(['The orientation can been computed for ',num2str(length(keep_fibres)/size(fibres.x,1)*100,'%.2f') ,'% of the tracked fibres']); + +%% Compute orientation (inclination and it's direction, azimuth) +%displacement between end and start points +disp_x = fibres.x(keep_fibres,end)-fibres.x(keep_fibres,1); +disp_y = fibres.y(keep_fibres,end)-fibres.y(keep_fibres,1); +disp_r = sqrt(disp_x.^2 + disp_y.^2); + +%inclination and azimuth angles +inclination = atan2(disp_r,(size(fibres.x,2)-1)*ones(length(keep_fibres),1)); +azimuth = atan2(disp_y,disp_x); + +display('The orientations (inclination and azimuth angles) have been computed'); + +%% Histograms of orientation angles +close all +%Inclination +max_inc = str2double(inputdlg('What is the maximum misalignment angle you expect [0°, 90°]?','Max. Inclination',[1,35],{num2str(15)})); +bins_inc = [0:max_inc]; +int_inc = [0:0.1:max_inc]; + +h_inc = hist(inclination*180/pi,bins_inc)/length(inclination); +h_inc_int = interp1(bins_inc,h_inc,int_inc); +h1 = figure(1),hold on, plot(int_inc,smooth(h_inc_int)) +ylim([0,max(smooth(h_inc_int))]), xlim([0,max_inc]),h1.Children.XTick = linspace(0,max_inc,10); +xlabel('Inclination \phi [\circ] of the fibres'), ylabel('Relative frequency') +title('Amount of fibre misalignment with respect to the scanning z-axis'), + +%Azimuth +max_az = 360; +bins_az = [-max_az/2:24:max_az/2]; +int_az = [-max_az/2:2.4:max_az/2]; + +h_az = hist(azimuth*180/pi,bins_az)/length(azimuth); +h_az_int = interp1(bins_az,h_az,int_az); +h2 = figure(2),hold on, plot(int_az,smooth(h_az_int)) +xlim([-max_az/2,max_az/2]), h2.Children.XTick = [-180,-90,0,90,180]; +ylim([0,max(smooth(h_az_int))]) +xlabel('Azimuth \theta [\circ] of the fibres'), ylabel('Relative frequency') +title('Direction of misalignment with respect to scanning x-axis'), + +%% Colour-code fibre tracks according to orientation angles +%Azimuth (direction of misalingment) +cmap_az = circshift(colormap(hsv),32); +h3 = figure(3); +for l= 1:length(keep_fibres) + colouring = cmap_az(round(azimuth(l)/(2*pi)*63+1+63/2),:); + figure(3), hold on, plot3(fibres.x(keep_fibres(l),:)*vx_size,fibres.y(keep_fibres(l),:)*vx_size,z_axis*vx_size,'LineWidth',1,'Color',colouring), +end +figure(3), axis tight, axis equal, view(2), +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') +colormap(cmap_az), colorbar, caxis([-180,180]), h3.Children(1).Ticks = -180:30:180; +for k = 1:length(h3.Children(1).Ticks) + h3.Children(1).TickLabels(k) = {[h3.Children(1).TickLabels{k},'°']}; +end + +%Inclination (amount of misalignment) +cmap_inc = linspace(0,1,32)'*[1,1,1]; +sat_angle = max_inc/3; +sat = sat_angle*pi/180; +h4 = figure(4); +for l= 1:length(keep_fibres) + if inclination(l) < sat + colouring = [1,1,1]*inclination(l)/sat; + else + colouring = [1,1,1]; + end + figure(4), hold on, plot3(fibres.x(keep_fibres(l),:)*vx_size,fibres.y(keep_fibres(l),:)*vx_size,z_axis*vx_size,'LineWidth',1,'Color',colouring), +end +figure(4), axis tight, axis equal, view(2), +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') +colormap(cmap_inc), +colorbar, caxis([0,sat_angle]), h.Children(1).Ticks = linspace(0,sat_angle,5); +h4.Children(1).TickLabels(end) = {['>',num2str(sat_angle)]}; +for k = 1:length(h.Children(1).Ticks) + h4.Children(1).TickLabels(k) = {[h4.Children(1).TickLabels{k},'°']}; +end + +%Azimuth weighed by inclination (amount of misalignment) +h5 = figure(5); +for l= 1:length(keep_fibres) + if inclination(l) < sat + colouring = cmap_az(round(azimuth(l)/(2*pi)*63+1+63/2),:)*inclination(l)/sat; + else + colouring = cmap_az(round(azimuth(l)/(2*pi)*63+1+63/2),:); + end + figure(5), hold on, plot3(fibres.x(keep_fibres(l),:)*vx_size,fibres.y(keep_fibres(l),:)*vx_size,z_axis*vx_size,'LineWidth',1,'Color',colouring), +end +figure(5), axis tight, axis equal, view(2), +xlabel('x axis [\mum]'),ylabel('y axis [\mum]'), zlabel('z axis [\mum]') +colormap(cmap_az), colorbar, caxis([-180,180]), h5.Children(1).Ticks = -180:30:180; +for k = 1:length(h.Children(1).Ticks) + h5.Children(1).TickLabels(k) = {[h5.Children(1).TickLabels{k},'°']}; +end + diff --git a/code/InsegtFibre_validate3DTracks.m b/code/InsegtFibre_validate3DTracks.m new file mode 100755 index 0000000..29ae8d1 --- /dev/null +++ b/code/InsegtFibre_validate3DTracks.m @@ -0,0 +1,68 @@ +% VALIDATION_3DFIBREDETECTION +% Written by Monica Jane Emerson, April 2019. +% MIT license + +%% Add paths of Insegt, functions and scripts, and prepare workspace +close all, clear all, +addpath('./scripts') +addpath('./functions') + +%% Get the measured fibre tracks in the format required by the validation GUI +% USER INPUT: Load the fibre tracks to validate +[filename_tracks,pathname_tracks] = uigetfile('*.mat','Choose the fibre tracks to validate','saved_data/fibreTracks'); +load([pathname_tracks,filename_tracks]); + +%USER INPUT: Deregister the tracks if necessary +x = questdlg('Have the tracks undergone a transformation (rotation and/or translation) since they were measured from the CT scan?','2D de-registration','Yes','No','No'); +if strcmp(x,'Yes') + [filename,pathname] = uigetfile('*.mat','Load the transformation matrix','saved_data/2Dregistration/'); + t = load([pathname,filename],'tform'); + %USER INPUT: Offset between registration FoV and analysis FoV + offset = str2double(inputdlg('Offset between the FoV used for the analysis and the FoV used for the 2D registration','Offset registration-analysis',[1,50],{num2str(100)})); + aux = [fibres.x(:) + offset*ones(size(fibres.x(:))),fibres.y(:) + offset*ones(size(fibres.x(:))),ones(size(fibres.y(:)))]*t.tform.tdata.Tinv; + fibres.x = reshape(aux(:,1), size(fibres.x)) - offset*ones(size(fibres.x)); + fibres.y = reshape(aux(:,2), size(fibres.x)) - offset*ones(size(fibres.x)); +end + +for l = 1:size(fibres.x,2) + centers{l} = [fibres.x(:,l),fibres.y(:,l),[1:size(fibres.x,1)]']; +end; + +%% Get the analysed volume of interest (VoI) in the format read by the validaiton GUI +close all +% USER INPUT: Option to open a volume of interest saved as a tif file +x = questdlg('Is the analysed volume of interest saved as one .tif file, with the slices ordered in the direction of tracking?','Load analysed volume','Yes','No','Yes'); + +if strcmp(x,'No') + indicate_dataFolder; + [V,depth] = loadVolumeRoI(path_volumeFolder); + % USER INPUT: Tracking direction + top = ['Top to bottom (from slice ',depth{1},')']; + bottom = ['Bottom to top (from slice ',depth{2},')']; + direct = questdlg('In which direction did you track the coordinates?','Tracking direction',top, bottom, bottom); + % USER INPUT: Give a name to the .tif Volume of Interest + if strcmp(direct,bottom) + direction = [depth{2},'to',depth{1},'step',depth{3}]; + else + direction = [depth{1},'to',depth{2},'step',depth{3}]; + end + name_tif = inputdlg('Name for the .tif file of your volume of interest (VoI): ','Name your VOI',[1,55],{['V_?_',direction,'.tif']}); + filename_voi= name_tif{1}; + pathname_voi = 'saved_data/volumeOfInterest/'; + if strcmp(direct,bottom) + imwrite(V(:,:,end),[pathname_voi,filename_voi]); + for l= 2:size(V,3) + imwrite(V(:,:,end-l+1),[pathname_voi,filename_voi], 'WriteMode','append'); + end + else + imwrite(V(:,:,1),[pathname_voi,filename_voi]); + for l= 2:size(V,3) + imwrite(V(:,:,l),[pathname_voi,filename_voi], 'WriteMode','append'); + end + end +else + [filename_voi,pathname_voi] = uigetfile('*.tif','Choose volume of interest','saved_data/volumeOfInterest/VOI_v'); +end + +%% Validate all fibres +track_slicer([pathname_voi,filename_voi],centers) diff --git a/code/functions/calc_eltsdict.m b/code/functions/calc_eltsdict.m new file mode 100755 index 0000000..6edad49 --- /dev/null +++ b/code/functions/calc_eltsdict.m @@ -0,0 +1,8 @@ +function [num] = calc_eltsdict(k,n) +%CALC_ELTSDICT Summary of this function goes here +% Written by Monica Jane Emerson, April 2018, MIT License +vect = [1:n]; +series = k.^(vect-1); +num = k*sum(series); +end + diff --git a/code/functions/calcu_psize.m b/code/functions/calcu_psize.m new file mode 100755 index 0000000..76ee312 --- /dev/null +++ b/code/functions/calcu_psize.m @@ -0,0 +1,15 @@ +function [patch_size] = calcu_psize(pixPerDiam) +%CALCU_PSIZE Loads a selected region of interest from a volume. +% Details... +%by Monica Jane Emerson, April 2019, MIT License +if mod(round(pixPerDiam),2)==0 + if (round(pixPerDiam)-pixPerDiam)<0 + patch_size = round(pixPerDiam)+1; + else + patch_size = round(pixPerDiam)-1; + end +else + patch_size = round(pixPerDiam); +end +end + diff --git a/code/functions/imshow3D.m b/code/functions/imshow3D.m new file mode 100755 index 0000000..15f49c4 --- /dev/null +++ b/code/functions/imshow3D.m @@ -0,0 +1,291 @@ +function imshow3D( Img, disprange ) +%IMSHOW3D displays 3D grayscale images in slice by slice fashion with mouse +%based slice browsing and window and level adjustment control. +% +% Usage: +% imshow3D ( Image ) +% imshow3D ( Image , [] ) +% imshow3D ( Image , [LOW HIGH] ) +% +% Image: 3D image MxNxK (K slices of MxN images) +% [LOW HIGH]: display range that controls the display intensity range of +% a grayscale image (default: the widest available range) +% +% Use the scroll bar or mouse scroll wheel to switch between slices. To +% adjust window and level values keep the mouse right button pressed and +% drag the mouse up and down (for level adjustment) or right and left (for +% window adjustment). +% +% "Auto W/L" button adjust the window and level automatically +% +% While "Fine Tune" check box is checked the window/level adjustment gets +% 16 times less sensitive to mouse movement, to make it easier to control +% display intensity rang. +% +% Note: The sensitivity of mouse based window and level adjustment is set +% based on the user defined display intensity range; the wider the range +% the more sensitivity to mouse drag. +% +% +% Example +% -------- +% % Display an image (MRI example) +% load mri +% Image = squeeze(D); +% figure, +% imshow3D(Image) +% +% % Display the image, adjust the display range +% figure, +% imshow3D(Image,[20 100]); +% +% See also IMSHOW. + +% +% - Maysam Shahedi (mshahedi@gmail.com) +% - Released: 1.0.0 Date: 2013/04/15 +% - Revision: 1.1.0 Date: 2013/04/19 +% + +sno = size(Img,3); % number of slices +S = round(sno/2); + +global InitialCoord; + +MinV = 0; +MaxV = max(Img(:)); +LevV = (double( MaxV) + double(MinV)) / 2; +Win = double(MaxV) - double(MinV); +WLAdjCoe = (Win + 1)/1024; +FineTuneC = [1 1/16]; % Regular/Fine-tune mode coefficients + +if isa(Img,'uint8') + MaxV = uint8(Inf); + MinV = uint8(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'uint16') + MaxV = uint16(Inf); + MinV = uint16(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'uint32') + MaxV = uint32(Inf); + MinV = uint32(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'uint64') + MaxV = uint64(Inf); + MinV = uint64(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'int8') + MaxV = int8(Inf); + MinV = int8(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'int16') + MaxV = int16(Inf); + MinV = int16(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'int32') + MaxV = int32(Inf); + MinV = int32(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'int64') + MaxV = int64(Inf); + MinV = int64(-Inf); + LevV = (double( MaxV) + double(MinV)) / 2; + Win = double(MaxV) - double(MinV); + WLAdjCoe = (Win + 1)/1024; +elseif isa(Img,'logical') + MaxV = 0; + MinV = 1; + LevV =0.5; + Win = 1; + WLAdjCoe = 0.1; +end + +SFntSz = 9; +LFntSz = 10; +WFntSz = 10; +LVFntSz = 9; +WVFntSz = 9; +BtnSz = 10; +ChBxSz = 10; + +if (nargin < 2) + [Rmin Rmax] = WL2R(Win, LevV); +elseif numel(disprange) == 0 + [Rmin Rmax] = WL2R(Win, LevV); +else + LevV = (double(disprange(2)) + double(disprange(1))) / 2; + Win = double(disprange(2)) - double(disprange(1)); + WLAdjCoe = (Win + 1)/1024; + [Rmin Rmax] = WL2R(Win, LevV); +end + +axes('position',[0,0.2,1,0.8]), imshow(Img(:,:,S), [Rmin Rmax]) + +FigPos = get(gcf,'Position'); +S_Pos = [50 45 uint16(FigPos(3)-100)+1 20]; +Stxt_Pos = [50 65 uint16(FigPos(3)-100)+1 15]; +Wtxt_Pos = [50 20 60 20]; +Wval_Pos = [110 20 60 20]; +Ltxt_Pos = [175 20 45 20]; +Lval_Pos = [220 20 60 20]; +BtnStPnt = uint16(FigPos(3)-250)+1; +if BtnStPnt < 300 + BtnStPnt = 300; +end +Btn_Pos = [BtnStPnt 20 100 20]; +ChBx_Pos = [BtnStPnt+110 20 100 20]; + +if sno > 1 + shand = uicontrol('Style', 'slider','Min',1,'Max',sno,'Value',S,'SliderStep',[1/(sno-1) 10/(sno-1)],'Position', S_Pos,'Callback', {@SliceSlider, Img}); + stxthand = uicontrol('Style', 'text','Position', Stxt_Pos,'String',sprintf('Slice# %d / %d',S, sno), 'BackgroundColor', [0.8 0.8 0.8], 'FontSize', SFntSz); +else + stxthand = uicontrol('Style', 'text','Position', Stxt_Pos,'String','2D image', 'BackgroundColor', [0.8 0.8 0.8], 'FontSize', SFntSz); +end +ltxthand = uicontrol('Style', 'text','Position', Ltxt_Pos,'String','Level: ', 'BackgroundColor', [0.8 0.8 0.8], 'FontSize', LFntSz); +wtxthand = uicontrol('Style', 'text','Position', Wtxt_Pos,'String','Window: ', 'BackgroundColor', [0.8 0.8 0.8], 'FontSize', WFntSz); +lvalhand = uicontrol('Style', 'edit','Position', Lval_Pos,'String',sprintf('%6.0f',LevV), 'BackgroundColor', [1 1 1], 'FontSize', LVFntSz,'Callback', @WinLevChanged); +wvalhand = uicontrol('Style', 'edit','Position', Wval_Pos,'String',sprintf('%6.0f',Win), 'BackgroundColor', [1 1 1], 'FontSize', WVFntSz,'Callback', @WinLevChanged); +Btnhand = uicontrol('Style', 'pushbutton','Position', Btn_Pos,'String','Auto W/L', 'FontSize', BtnSz, 'Callback' , @AutoAdjust); +ChBxhand = uicontrol('Style', 'checkbox','Position', ChBx_Pos,'String','Fine Tune', 'BackgroundColor', [0.8 0.8 0.8], 'FontSize', ChBxSz); + +set (gcf, 'WindowScrollWheelFcn', @mouseScroll); +set (gcf, 'ButtonDownFcn', @mouseClick); +set(get(gca,'Children'),'ButtonDownFcn', @mouseClick); +set(gcf,'WindowButtonUpFcn', @mouseRelease) +set(gcf,'ResizeFcn', @figureResized) + + +% -=< Figure resize callback function >=- + function figureResized(object, eventdata) + FigPos = get(gcf,'Position'); + S_Pos = [50 45 uint16(FigPos(3)-100)+1 20]; + Stxt_Pos = [50 65 uint16(FigPos(3)-100)+1 15]; + BtnStPnt = uint16(FigPos(3)-250)+1; + if BtnStPnt < 300 + BtnStPnt = 300; + end + Btn_Pos = [BtnStPnt 20 100 20]; + ChBx_Pos = [BtnStPnt+110 20 100 20]; + if sno > 1 + set(shand,'Position', S_Pos); + end + set(stxthand,'Position', Stxt_Pos); + set(ltxthand,'Position', Ltxt_Pos); + set(wtxthand,'Position', Wtxt_Pos); + set(lvalhand,'Position', Lval_Pos); + set(wvalhand,'Position', Wval_Pos); + set(Btnhand,'Position', Btn_Pos); + set(ChBxhand,'Position', ChBx_Pos); + end + +% -=< Slice slider callback function >=- + function SliceSlider (hObj,event, Img) + S = round(get(hObj,'Value')); + set(get(gca,'children'),'cdata',Img(:,:,S)) + caxis([Rmin Rmax]) + if sno > 1 + set(stxthand, 'String', sprintf('Slice# %d / %d',S, sno)); + else + set(stxthand, 'String', '2D image'); + end + end + +% -=< Mouse scroll wheel callback function >=- + function mouseScroll (object, eventdata) + UPDN = eventdata.VerticalScrollCount; + S = S - UPDN; + if (S < 1) + S = 1; + elseif (S > sno) + S = sno; + end + if sno > 1 + set(shand,'Value',S); + set(stxthand, 'String', sprintf('Slice# %d / %d',S, sno)); + else + set(stxthand, 'String', '2D image'); + end + set(get(gca,'children'),'cdata',Img(:,:,S)) + end + +% -=< Mouse button released callback function >=- + function mouseRelease (object,eventdata) + set(gcf, 'WindowButtonMotionFcn', '') + end + +% -=< Mouse click callback function >=- + function mouseClick (object, eventdata) + MouseStat = get(gcbf, 'SelectionType'); + if (MouseStat(1) == 'a') % RIGHT CLICK + InitialCoord = get(0,'PointerLocation'); + set(gcf, 'WindowButtonMotionFcn', @WinLevAdj); + end + end + +% -=< Window and level mouse adjustment >=- + function WinLevAdj(varargin) + PosDiff = get(0,'PointerLocation') - InitialCoord; + + Win = Win + PosDiff(1) * WLAdjCoe * FineTuneC(get(ChBxhand,'Value')+1); + LevV = LevV - PosDiff(2) * WLAdjCoe * FineTuneC(get(ChBxhand,'Value')+1); + if (Win < 1) + Win = 1; + end + + [Rmin, Rmax] = WL2R(Win,LevV); + caxis([Rmin, Rmax]) + set(lvalhand, 'String', sprintf('%6.0f',LevV)); + set(wvalhand, 'String', sprintf('%6.0f',Win)); + InitialCoord = get(0,'PointerLocation'); + end + +% -=< Window and level text adjustment >=- + function WinLevChanged(varargin) + + LevV = str2double(get(lvalhand, 'string')); + Win = str2double(get(wvalhand, 'string')); + if (Win < 1) + Win = 1; + end + + [Rmin, Rmax] = WL2R(Win,LevV); + caxis([Rmin, Rmax]) + end + +% -=< Window and level to range conversion >=- + function [Rmn Rmx] = WL2R(W,L) + Rmn = L - (W/2); + Rmx = L + (W/2); + if (Rmn >= Rmx) + Rmx = Rmn + 1; + end + end + +% -=< Window and level auto adjustment callback function >=- + function AutoAdjust(object,eventdata) + Win = double(max(Img(:))-min(Img(:))); + Win (Win < 1) = 1; + LevV = double(min(Img(:)) + (Win/2)); + [Rmin, Rmax] = WL2R(Win,LevV); + caxis([Rmin, Rmax]) + set(lvalhand, 'String', sprintf('%6.0f',LevV)); + set(wvalhand, 'String', sprintf('%6.0f',Win)); + end + +end +% -=< Maysam Shahedi (mshahedi@gmail.com), April 19, 2013>=- \ No newline at end of file diff --git a/code/functions/loadVolumeRoI.m b/code/functions/loadVolumeRoI.m new file mode 100755 index 0000000..4263133 --- /dev/null +++ b/code/functions/loadVolumeRoI.m @@ -0,0 +1,42 @@ +function [V,depth] = loadVolumeRoI(path) +%LOADVOLUMEROI Loads a selected region of interest from a volume. +% Details... +%by Monica Jane Emerson, March 2019, MIT License + +contents_datafolder = dir(path); +path_firstSlice = [path,contents_datafolder(3).name]; + +%select FoV in the cross-sectional direction +[rect,~] = selectFoV(path_firstSlice); + +%select slices +num_slices = length(contents_datafolder)-2; %depth of the volume +zmin = ['Lower limit in the depth direction [1, ',num2str(num_slices),']:']; +zmax = ['Upper limit in the depth direction [1, ',num2str(num_slices),']:']; +zstep = ['Step (1 to take every slice):']; +prompt = {zmin,zmax,zstep}; +depth = inputdlg(prompt,'RoI in the depth direction', [1,50], {'1',num2str(num_slices),'1'}); +slices = [str2double(depth{1}):str2double(depth{3}):str2double(depth{2})]; + +V = zeros([rect(4)-rect(2)+1,rect(3)-rect(1)+1,numel(slices)]); %volume preallocation (for speed) + +tstart = tic; +h = waitbar(0,'Loading volume...', 'CreateCancelBtn','setappdata(gcbf,''canceling'',1)'); + for k= 1:numel(slices) + im = imread([path,contents_datafolder(slices(k)+2).name]); + V(:,:,k) = double(im(rect(2):rect(4),rect(1):rect(3)))/2^16; + + %Processing data wait bar + waitbar(k/numel(slices),h) + if getappdata(h,'canceling') + break + end + if k == numel(slices) + delete(h) + t_load = toc(tstart); + f = msgbox(['time to load volume: ' , num2str(t_load), 's']); + end + end + +end + diff --git a/code/functions/num_dictAtoms.m b/code/functions/num_dictAtoms.m new file mode 100755 index 0000000..968a080 --- /dev/null +++ b/code/functions/num_dictAtoms.m @@ -0,0 +1,8 @@ +function [num] = num_dictAtoms(k,n) +%NUM_DICTATOMS Summary of this function goes here +%by Monica Jane Emerson, April 2018, MIT License +vect = [1:n]; +series = k.^(vect-1); +num = k*sum(series); +end + diff --git a/code/functions/selectFoV.m b/code/functions/selectFoV.m new file mode 100755 index 0000000..8b3af9a --- /dev/null +++ b/code/functions/selectFoV.m @@ -0,0 +1,29 @@ +function [rect,im_rect] = selectFoV(slice_path) +%SELECTFOV is a user-friendly function to select a region of interest from a 2D image. +% The user can choose to select interactively the region of interest (RoI) +% interactively with imcrop, or specify the coordinates of the RoI. +% by Monica Jane Emerson, March 2019, MIT License + +%USER INPUT: SLICE_PATH, The location of the slice to crop the RoI from. +% Other user input given through interaction with windows. + +autoManual = questdlg('How would you like to select the region of interest (RoI)?','Select analysis region','Interactively','I know the coordinates','Interactively'); +im = imread(slice_path); + +if strcmp(autoManual,'Interactively') + [~,rect] = imcrop(im); + rect = round(rect); + rect(3:4) = rect(1:2) + rect(3:4) - [1,1]; +else + xmin = ['Lower limit for the x value [1, ',num2str(size(im,2)),']:']; + xmax = ['Upper limit for the x value [1, ',num2str(size(im,2)),']:']; + ymin = ['Lower limit for the y value [1, ',num2str(size(im,1)),']:']; + ymax = ['Upper limit for the y value [1, ',num2str(size(im,1)),']:']; + prompt = {xmin,ymin,xmax,ymax}; + dimRoI = inputdlg(prompt,'RoI in the cross-sectional direction', [1,60], {'200','200','1799','1799'}); + rect = str2double(dimRoI); +end + im_rect = double(im(rect(2):rect(4),rect(1):rect(3)))/2^16; + +end + diff --git a/code/functions/track_slicer.m b/code/functions/track_slicer.m new file mode 100755 index 0000000..7500058 --- /dev/null +++ b/code/functions/track_slicer.m @@ -0,0 +1,109 @@ +function track_slicer(filename,centers,clim) +%TRACK_SLICER Slice trough data, centers and tracks. +% TRACK_SLICER(FILENAME,CENTERS,CLIM) +% FILENAME, name of the tiff stack with image data. +% CENTERS, detected fibre centers. If CENTERS contains track ids (as +% returned by fibre_tracking function) those will be shown as +% numbers. +% CLIM, optional color limits. +% Author: vand@dtu.dk 2018 + +if ~iscell(centers) % then tracks are given, and need to be converted + tracks = centers; + tracks = permute(tracks,[2,3,1]); + tracks(:,3,:) = repmat((1:size(tracks,1))',[1 1 size(tracks,3)]); % using slice number not z coordinate + centers = permute(mat2cell(tracks,size(tracks,1),size(tracks,2),ones(size(tracks,3),1)),[3 2 1]); +end + +Z = length(imfinfo(filename)); +z = round(0.5*(Z+1)); +%update_drawing +I = imread(filename,'Index',z); +dim = [size(I),Z]; +if nargin<3 || isempty(clim) + clim = [min(I(:)),max(I(:))]; +end + +show_numbers = true; + +fig = figure('Units','Normalized','Position',[0.1 0.3 0.5 0.6],... + 'KeyPressFcn',@key_press); +imagesc(I,clim) +hold on, colormap gray, axis image ij +plot(centers{z}(:,1),centers{z}(:,2),'r.') +title(['slice ',num2str(z),'/',num2str(Z)]) +drawnow + +% to capture zoom +LIMITS = [0.5,dim(2)+0.5,0.5,dim(1)+0.5]; +zoom_handle = zoom(fig); +pan_handle = pan(fig); +set(zoom_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); +set(pan_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); + +% if nargout>1 +% uiwait(fig) % waits with assigning output until a figure is closed +% end + +%%%%%%%%%% CALLBACK FUNCTIONS %%%%%%%%%% + + function key_press(~,object) + % keyboard commands + key = object.Key; + switch key + case 'uparrow' + z = min(z+1,dim(3)); + case 'downarrow' + z = max(z-1,1); + case 'rightarrow' + z = min(z+10,dim(3)); + case 'leftarrow' + z = max(z-10,1); + case 'pageup' + z = min(z+50,dim(3)); + case 'pagedown' + z = max(z-50,1); + case 'n' + show_numbers = ~show_numbers; + end + update_drawing + end + +%%%%%%%%%% HELPING FUNCTIONS %%%%%%%%%% + + function update_drawing + I = imread(filename,'Index',z); + cla, imagesc(I,clim), hold on + if ~show_numbers || size(centers{z},2)<3 || (LIMITS(2)-LIMITS(1))*(LIMITS(4)-LIMITS(3))>300^2 + plot(centers{z}(:,1),centers{z}(:,2),'r.') + else + present = (centers{z}(:,1)<LIMITS(2))&(centers{z}(:,1)>LIMITS(1))&... + (centers{z}(:,2)<LIMITS(4))&(centers{z}(:,2)>LIMITS(3)); + text(centers{z}(present,1),centers{z}(present,2),num2cell(centers{z}(present,3)'),... + 'HorizontalAlignment','center','VerticalAlignment','middle',... + 'Color','r') + end + title(['slice ',num2str(z),'/',num2str(Z)]) + drawnow + end + + function adjust_limits(~,~) + % response to zooming and panning + LIMITS([1,2]) = get(gca,'XLim'); + LIMITS([3,4]) = get(gca,'YLim'); + if show_numbers + update_drawing + end + end + + function force_keep_key_press(~,~) + % a hack to maintain my key_press while in zoom and pan mode + % http://undocumentedmatlab.com/blog/enabling-user-callbacks-during-zoom-pan + hManager = uigetmodemanager(fig); + [hManager.WindowListenerHandles.Enabled] = deal(false); + set(fig, 'KeyPressFcn', @key_press); + end + +end diff --git a/code/scripts/addpaths_inSegtFibre.m b/code/scripts/addpaths_inSegtFibre.m new file mode 100755 index 0000000..9e96bfd --- /dev/null +++ b/code/scripts/addpaths_inSegtFibre.m @@ -0,0 +1,12 @@ +%ADDPATHS_INSEGTFIBRE Makes all the code of Insegt Fibre accesible +% Written by Monica Jane Emerson, March 2019, MIT License + +%USER INPUT: The path of the textureGUI folder in your computer +x = inputdlg('Confirm the default path of the texture_GUI folder: ',... + 'User input',[1,30],{'./texture_gui'}); +path_textureGUI = x{1}; + +%Add the functions and Insegt software path +addpath(genpath(path_textureGUI)) +addpath('./scripts') +addpath('./functions') \ No newline at end of file diff --git a/code/scripts/create_dict.m b/code/scripts/create_dict.m new file mode 100755 index 0000000..5ef7e62 --- /dev/null +++ b/code/scripts/create_dict.m @@ -0,0 +1,31 @@ +%CREATE_DICT Updates the dictionary of intensities built by SETUP_DICT with +%a training data-set that can be loaded or created from scratch. +% Written by Monica Jane Emerson, March 2019, MIT License + +%USER INPUT: Option to load a labelling that was saved +x = questdlg('Would you like to load a labelling or create a new one?','Annotation of training data','New one','Load','New one'); + +if strcmp(x,'Load') + [filename,pathname] = uigetfile('*.mat','Choose training image','../data/saved_data/trainingData/imtrain.mat'); + load([pathname,filename]); + [filename,pathname] = uigetfile('*labels_indexed.png','Choose label image','../data/saved_data/trainingData/'); + labelling = imread([pathname,filename]); + + dictionary = build_dictionary(im_train,dictopt); %create the dictionary of intensities + image_texture_gui(im_train,dictionary,2,labelling) +else + %select a small RoI for training, containing 100 fibres approx + prompt = ['Choose a slice for training: [1,', num2str(size(V,3)),']:']; + x = inputdlg(prompt,... + 'User input',[1,30],{num2str(round(size(V,3)/2))}); %select slice + path_trainSlice = [path_volumeFolder,contents_datafolder(str2double(x{1})+2).name]; + [~,im_train] = selectFoV(path_trainSlice); + uisave('im_train','../data/saved_data/trainingData/imtrain.mat') + + dictionary = build_dictionary(im_train,dictopt); %create the dictionary of intensities + image_texture_gui(im_train,dictionary,2) %learn the dictionary of probabilities by annotating in the GUI +end +dictionary = update_dictionary(dictionary,gui_dictprob); %update dictionary to include the learnt probalilities + +%USER INPUT: Save dictionary to process the complete scan +uisave('dictionary','../data/saved_data/dictionaries/dictionary_v.mat') diff --git a/code/scripts/indicate_dataFolder.m b/code/scripts/indicate_dataFolder.m new file mode 100755 index 0000000..1899d0f --- /dev/null +++ b/code/scripts/indicate_dataFolder.m @@ -0,0 +1,19 @@ +%INDICATE_DATAFOLDER Allows the user to specify the location of their data, +%and displays a slice to ensure that the right data is contained at the +%specified location. +% Written by Monica Jane Emerson, March 2019, MIT License + +%USER INPUT: Path to CT volume? +path_volumeFolder = uigetdir('../data/','Open the folder with your CT data'); +if ~strcmp(path_volumeFolder(end),'/') + path_volumeFolder = [path_volumeFolder,'/']; +end + +%add the path and check the contents of the data folder +addpath(genpath(path_volumeFolder)) +contents_datafolder = dir(path_volumeFolder); + +%display cross-section, first slice +path_firstSlice = [path_volumeFolder,contents_datafolder(3).name]; +im = imread(path_firstSlice); +figure, imagesc(im), axis image, colormap gray, title('First slice of the loaded data-set') \ No newline at end of file diff --git a/code/scripts/input_depthRoI.m b/code/scripts/input_depthRoI.m new file mode 100644 index 0000000..a6c8c5d --- /dev/null +++ b/code/scripts/input_depthRoI.m @@ -0,0 +1,19 @@ +function [ depth ] = input_depthRoI(varargin) +%INPUT_DEPTHROI asks the user to specify the depth range to analyse +% Details... +%by Monica Jane Emerson, May 2019, MIT License + +if nargin == 1 + num_slices = varargin{1}; +else + num_slices = []; +end + +zmin = ['Lower limit in the depth direction [1, ',num2str(num_slices),']:']; +zmax = ['Upper limit in the depth direction [1, ',num2str(num_slices),']:']; +zstep = ['Step (1 to take every slice):']; +prompt = {zmin,zmax,zstep}; +depth = inputdlg(prompt,'RoI in the depth direction', [1,50], {'1',num2str(num_slices),'10'}); + +end + diff --git a/code/scripts/load_multiple_trackSets.m b/code/scripts/load_multiple_trackSets.m new file mode 100755 index 0000000..0a25847 --- /dev/null +++ b/code/scripts/load_multiple_trackSets.m @@ -0,0 +1,15 @@ +%LOAD_MULTIPLE_TRACKSETS is a user-friendly script to load tracks measured +%from multiple data-sets, e.g. acquired at progressive load steps. +% by Monica Jane Emerson, March 2019, MIT License + +%% USER INPUT: Load several sets of tracks corresponding to different steps of a time-lapse data-set +x = inputdlg('How many sets of tracks would you like to load [>=2]?','Nr. analysed matching volumes',[1,60],{num2str(2)}); + +for l = 1:str2double(x{1}) + [filename_tracks,pathname_tracks] = uigetfile('*.mat',['Choose tracks for data-set ',num2str(l)],'saved_data/fibreTracks'); + aux = load([pathname_tracks,filename_tracks]); + tracks{l} = aux.fibres; +end +num_datasets = length(tracks); +num_slices = size(tracks{1}.x,2); + diff --git a/code/scripts/setup_dict.m b/code/scripts/setup_dict.m new file mode 100755 index 0000000..b6e8f81 --- /dev/null +++ b/code/scripts/setup_dict.m @@ -0,0 +1,15 @@ +%SETUP_DICT Set-ups the default parameters for the dictionary, andasks for the +%data-dependent parameter (patch size) +% Written by Monica Jane Emerson, March 2019, MIT License + +%USER INPUT: select patch size, the closest odd integer to factor*diam/pixel_size +x = inputdlg('Choose patch size: ','User input',[1,20],{num2str(11)}); %ask for patch size +dictopt.patch_size = str2double(x{1}); %save patch size + +%Set the default parameters and initialise dictionary +dictopt.method = 'euclidean'; +dictopt.branching_factor = 3; %>=3 +dictopt.number_layers = 5; %>=5. The higher the more accurate, but also slower and more computationally heavy +dictopt.number_training_patches = 15000; %at least 10*num_dictAtoms(branching_factor,number_layers) +dictopt.normalization = false; %set to true if the global intensity of the slices varies along the depth of the volume + diff --git a/code/texture_gui/code/batch_processing_script.m b/code/texture_gui/code/batch_processing_script.m new file mode 100755 index 0000000..9d87ea3 --- /dev/null +++ b/code/texture_gui/code/batch_processing_script.m @@ -0,0 +1,39 @@ +clear +close all +addpath functions + +% VERSION 1: WITHOUT CORRECTION +% for batch processing dictionary is reused, and manual labeling might need +% correction, so easiest is to build dictionary outside the gui to be able +% to use it later +im = double(imread('../data/slice1.png'))/255; + +dictopt.method = 'euclidean'; +dictopt.patch_size = 9; +dictopt.branching_factor = 5; +dictopt.number_layers = 4; +dictopt.number_training_patches = 30000; +dictopt.normalization = false; + +dictionary = build_dictionary(im,dictopt); + +% IMPORTANT: +% once inside gui export dict_labels to workspace (E) +image_texture_gui(im,dictionary,2) +%% +dictionary = update_dictionary(dictionary,gui_dictprob); +%% +figure, imagesc(gui_S), axis image, title('results from gui') + +figure +for i=1:5 + I = imread(['../data/slice',num2str(i),'.png']); + S = process_image(I,dictionary); + subplot(1,5,i) + imagesc(S), axis image, title(i), drawnow +end + +%% + +% VERSION 2: WITH (OR WITHOUT) CORRECTION +% TODO: freezeing (F) and correcting diff --git a/code/texture_gui/code/functions/biadjacency_matrix.cpp b/code/texture_gui/code/functions/biadjacency_matrix.cpp new file mode 100755 index 0000000..efb16ea --- /dev/null +++ b/code/texture_gui/code/functions/biadjacency_matrix.cpp @@ -0,0 +1,103 @@ +/*================================================================= +* syntax: B = biadjacency_matrix(A,M,K) OR B = biadjacency_matrix(A,M) +* +* BIADJACENCY_MATRIX - build biadjacancy matrix from assignment image +* +* Input: - A: X-by-Y assignment image +* - M: patch size (length of edge) +* - K: number of dictionary patches, defaults to max(A(:)) +* +* Output: - B: XY-by-MMK sparse biadjacency matrix +* +* Author: Vedrana Andersen Dahl, vand@dtu.dk, december 2015. +*=================================================================*/ + +#include <math.h> +#include <stdio.h> +#include <vector> +#include <algorithm> +#include "mex.h" + +// struct containing i and j indices of a sparse matrix element +struct ij +{ + int i,j; + ij(int i_, int j_) : i(i_), j(j_){}; // constructor + bool operator<(const ij second) const{ // leq operator needed for sorting + return (j<second.j) || ((j==second.j) && (i<second.i)); + } +}; + +// The gateway mex routine +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + /* Check for proper number of input and output arguments */ + if ((nlhs != 1) || (nrhs != 2)) + mexErrMsgTxt("Usage: B = BIADJANCENCY_MATRIX(A,M) OR " + "B = BIADJANCENCY_MATRIX(A,M,K).\n"); + + /* Read input */ + double *A = mxGetPr(prhs[0]); // assignment image + int X = mxGetM(prhs[0]); // image size X + int Y = mxGetN(prhs[0]); // image size Y + int M = (int)mxGetPr(prhs[1])[0]; // patch size + int K; // number dict patches + if (nrhs==3) + K = (int)mxGetPr(prhs[2])[0]; + else{ // assumes number of dict patches is max(A) + K = 0; + for (int a=0; a<X*Y; a++) + if (A[a]>K) + K = A[a]; + } + + /* Compute some useful sizes */ + int c = (M-1)/2; // width of boundary having no assignment + int n = X*Y; // number of image pixels + int m = M*M*K; // number of dict pixels + int s = (X-M+1)*(Y-M+1)*M*M; // number image-dict links (elements in B) + + /* Finding elements of B as row-column indices */ + std::vector<ij> bij; + bij.reserve(s); + int ic,i,j; + for (int y=0+c; y<Y-c; y++){ // visiting patches centered around pixels + for (int x=0+c; x<X-c; x++){ + ic = x+y*X; // central patch pixel + for (int dy=-c; dy<=c; dy++){ // visiting pixels around central + for (int dx=-c; dx<=c; dx++){ + i = (x+dx)+(y+dy)*X; + j = (c+dx)+(c+dy)*M+(A[ic]-1)*M*M; + bij.push_back(ij(i,j)); + } + } + } + } + + /* Sorting elements in bij columnwise */ + std::sort (bij.begin(), bij.end()); + + /* Placeholder for output */ + plhs[0] = mxCreateSparseLogicalMatrix(n,m,s); // output mxArray, sparse logical matrix B + if (plhs[0]==NULL) + mexErrMsgTxt("Could not allocate enough memory!\n"); + + /* Access fields of output mxArray via pointers */ + mwIndex *ir = mxGetIr(plhs[0]); // row index (0 indexed) + mwIndex *jc = mxGetJc(plhs[0]); // cumulative number of elements per column + mxLogical *pr = mxGetLogicals(plhs[0]); // element values (will be all true) + + /* Converting row-column indices into row-cumulative column */ + int k = 0; // for visiting elements of bij + jc[0] = 0; // first element of cumulative sum is 0 + for (int bc=0; bc<m; bc++){ // all columns of B + jc[bc+1] = jc[bc]; + while (k<bij.size() && bij[k].j==bc){ + jc[bc+1]++; + ir[k] = bij[k].i; + pr[k] = true; + k++; + } + } +} + diff --git a/code/texture_gui/code/functions/biadjacency_matrix.mexa64 b/code/texture_gui/code/functions/biadjacency_matrix.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..703f486d9d5d8524e9e27c48c259071a086f90a1 GIT binary patch literal 13773 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 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/biadjacency_matrix.mexmaci64 b/code/texture_gui/code/functions/biadjacency_matrix.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..f40f6e00dc3e5bf9fbbabf158abccc084bfc6a93 GIT binary patch literal 13924 zcmeHOeRLF6mair)wh&Uu0Geggv`*_1h68C}#PVZEK{vdD>WC&8&@q;wX-LL=*&h&Q z5;xk!<C~&9IqSIN8Fd%WS@(E$K4ACgoXon8X@Vrc3<lg0MqNPN)sFEa#1#@idVlv- zcRref&g}fP=j5Hd_wK##zFYU*d*7|<SC!Y#esbY*Npj>!lH@|or+%s=wMz~_lJr-o z%~K@FFe?1(D+Fm`<DQ60X&f@8%TTGOVi?g-bJT`ye4@O;8TOcy6hI-|X@hp_l-kCl zh7pOzHbh2A+4}v@HTF<hnuBnAMK-~emWQQ^V#_mXLUm1{FeusbeAn81580hWILtCu zF48a9*bo_8aJIbOv^=so6~f5}mnw#_IT$oT&B0Jp)QDDZ$d=camdDZp2%n%|mSDpO zMxx=`hRtbuw!Fe3d*Ffecm~gi$(C&xv4*X+4I7QxhE0uN%9gi1EiYpc!n0*$WK&OZ zcQYJj7(TT^9XH}m!|6E5#G43Q3{kw19W%Cs-5N93q?wnJm+=G1%giNyijQ<?5{mf^ zbw*@c{f5RmqbVA`NS@0r*fQ!!glEgkvspxI7<JWOC9e?ksnpn=MEH1lwBC#<h=uG% zF<=<FR<_3P(<+FHQet<b%xQdMdIOLgn0vHbFG+2J1r6E8g`RY1LQnLaX!0>(w^x!1 zfs-$(KL-3N^#8tCl3oCDI^eaaROlL+>yS*Dh?gh~^V3^+0>?Lj(RE&aCiwGZ@0FM@ zyVm6F|8@OOJ)q?i=k#f!&dcCLL&-&5kDAd{lCA{ikQSD2i$p{93-#I!;p*_Vg)76= z^`Whe;RhlMS2PA2s~2p_5Egzf6pqwlQ!iBQA<Vbsl0C-c3bIrhZmN#fEUc^DP~W^@ z<2Lj*o%Hw`{?Pad7x6oqApv^FB4LtFHTdmsFYu->Li{(QX7q7PJAgDgk>6hOg5~4& zx)F28e!cJ?^~GtN{5e@BEih?;NefI`VA2AU7Wn^dfl9{zTV1DqPpwod%GtvoAH#Aq zen{m<8GorXl}g1^j@Wb`AC8~A5Fefr&1Kf@gLoiFp2E|bW_~yMK6=LeI|)80;BEr< z39!8x5>&odGaqs#e*@BxPo6(lji+*Ag`njppC_6;|7MM!2=EX3hj4WtKPm6(_NLmq zY@+=5JA!(anL?brtIt}U@^+oxlk+Oso|#oUqc^1o)zcr!zwV2_m*eg7rsSeh2+wzR z%j@<J$@5;{BrizdVuSLk3oO;8?1{Y>AIXs)X%*r}nE51WEAKi`oM2|<PG<S`6n4n# zx-hi|VptN*+Cd2Eg^<JY%A@w!9#Hx~8Io5HfunTTzS^Pm)%xU>Z-Ok@)B|biu)Ojt zi+8!>iSw~%_$wR9OjcDYF;L{=AIZCp@F#UaTIKK+CsnHqUBA4m7s_qIRX54S{@tv{ z-zC-`-0$sb-yi)Jvu^u65j6h0WC*`0R$I9z;hEj2os0*%bp9^m*3Ck$eogA|h+2F| z=P#`V!Ka*(t@rWx?&D{R5AY|i295WgImX-T01c_~yw`^00{>Gw|78gX%p!^Ha|sM4 z0J#LT2ap>OX7=&+8bIC@Kd76bo&4GLM1SR8w8~-GT8T^XW2(HX2UGIJ(EVuPL?MZ{ zHvzlVqKnHr`mrXA5F30%BbIXtdy0b2d&GQ}CYnM|P}+FaW4v;Q&RaVW!anntq{XI9 zn-<7L+sQVP$`8?UdYI|o2_Qdr?~uHH#v%NqgOe(s8j|lmVAu8J<pi&Kf%|^}=1z92 zzbjI}%)kqbyI9=c=^DtFizH^X?q*i(h2#qSxKUHDrMlHrkB^^HmEX5Kp_{E8X4PXp z$2sU<LV8y2aO{D!Hl^1pb;vtb+uDuO4~(=z#3w96P^w*6=2l@@Q&`Xx)|7X=eF4MW zJ_p%tEkLE)6rPiJ?7#>!p&1m{&F4!osGGud1GAYGfb*=mpfTQ)9yOn(c$0TMOOeQ| z%09;TC3j;<5KX(nC+`~8g7Eg|Z~=u-vIoE9H171U_O4hylESxO!sI(dG(qG&$zKSD zeVTcjC;3kT?h)`#f_vz9pVa=g{P=6=nQ5zIULlk&9$|@*9P>%BE96~&K%D$R<%hj* zdyjZe56p%;$NZ_8_a1fWe2;D|cW4~&z;zSHQ@*1v7F=>d<4cb3a~3+Rh=T=>Gv#1p zx*0v;WXhY-xA}3u(i?kmpp@}$p+S2pI#)Pd8II1GAP<{%m?=H6GmN)(CWrCklJOJC z^VE~QF|jB!v4HZ=ME+jldw4G;+3&0Os#WTJs!`QF@L0!anV65`9~Wx;3yq%-m`glZ zs>h`lf6fx;a#*UH@nbBJygiz$^AD4?5KKG)v&EAiC_ba}VU`%qVZVW^sK`v|jV#o8 zeXo{CP0^GSy1e|H5UBD03>1H%CBDq@?hmvhB;M*I*TjkjN(B3>Vty)Bl*PT;y6sD1 zOrE`hZ19%OM;Nc_TWwvP)UDfku~AU3Sj}^jWma7hdJnPUH(2|Tho-||IzOE}gn1gj z+7sY)9#?=vZ_Me_l~d6Ioe#3&Ub)Bz$?+~%>};U@bo5G{pJDO6g=2>6-$s|7K<du6 z-(1Ekdo_L<HXN9#S$~6AIgRYzn|uT73h)b>wQ?AFqYwQc`c9p{PZ2blFJN4qUl22% z!wfCn0}1Ce{*KO1>BUH>y~57%;mhPl=infQJv?#-R*@gSgAl%lB|cilu;+hEdlh9F zyKX38&HoAb6dZZAEMq)@XkW$>{oiGd1as_X@pp$cYe}x|=oc6N2w9{rz=w5MH2Dh7 zP%qZ+Q)1CM?vP-ahu`;_8N<cTu*7F>)vWM1m9s5Z#yqOB#M62$h4c9JjUZX}4Xl!j z+Q^*{(K(PeHqPRSVNLm+ynO{ywzAFHGBx(G;%!aRB+Z=VVWt;53md*5)kX0huv$DW zO?f@?qk(G(*UYN~%TmD+P`=b<Uq4w<Gb^Fun=J9Ed;IliNL98vTTaJjsmfI?p9yux z^f9-cCzX^lEwhy~u~SMx>n9V|gG+BQ1IlO7#hNKIegO7&t4gI4>%UG_0#fTYg}_n& zk{TMnO5*`3)rC~H%yxKyf1#VPUY!pm=fUKU`Fk|WaEZwMl!&uI+}<U(&xTdnT6?4b z|5R@O;7g=gII8%h2#yi#IeEv+Aoj=6Y!dOTS=hvydAV?Vzgs!ba%XI+syIC@ny#Fa zx9@{M&C)%%x1{~?AmUSaQaBO3OFS)kumP-mD0Z^{NlakY-C$COHNKR^yIn$1E9g*W zpm5@P8DA{!U)?TdVkaIV`->16m}T!ba}Dlp7`2C#p_UoSP^@27ay_l@)BTR}pII`d z(x;?8RQiA^-QnjbEc%b1Po-?5T#&av4WxgNBFmc5NV*1{4>JdHLsehuq<ycz2Iz;A z%Fegs$?bGMP;zAJ6%mZnHJ*#$%IW_VP>QX5&59xm(yp|_{Z?VH3Qu7t9a9Zll@%|V zwGQj)f=z~^^~g~g9&Al7<3n~}taG-^h`p^UOIzPe`^Q8qEY+2Fqqn4^6P_BkUyucJ zVe1pLwUi4jF6FJ(Jmo@c8Zucf1!nBH?m$*37<d$kY3^9A>K1xyR)r@Y$}|2)-SqcP zWa%#zOR<-Sj4D#-1R?$O_Cm;@5Gl-<LgU9MIb=!uTS(i~@k8Vss(+9Cvl~4}J%XBf z9@5Rrb>4*=iQ6o5s{i6p66(J?TJn`d><XVURh6IFGmzu=9-*{auAA5D{Ey@;^B$*i z5L^d|YYw;)#I@hQ{mA#LqO<(l_eXuqoWeL{Pw9jnQn$B@sO5q>Rp)aU?M~#*q+40( zY?&^YVoQ{~))Hn;#YD#Q2HZL?()m<nxw9qLW|-+yrnKf|giPtg1B%o=VgHZj|B7-v z2Q&VL#?NT{g3c4k0xX-EGZ{Z@$D8hPwoHrNqbjpJJkeUAnG|UVxLF>HIVf-0*-42| z()*4wv(<|&qjBgx(_&LJ^U5@XNLQD~W+Gj+<{|57JP#Qq-j%}>yq6+C+~m{yc~n38 zy|p&LUt|2B#{a|CGQfM2w`OS>aMG@bW1&c65)^IK%m6YcG^vz?qDaEyHLZfCcMDBv zJ+`WuwWvxIi+05nT68N?WUdDbvX`9m^(%7HiV$Y5adDls-$vJmUz5BKH(tiiAsez{ z#LY+!i;rBkb%pw(gITj15Em?%`(x%foO+j;Iq8fj@<6`aejA2WW#zDJEdr#OW!QCC z^UC4=>w)XmkMPhLu`}{nOieCEie=V{LUp%8{WnSlx-uwl{~Q1UbL{?so4LmE^bhCt zQS_<GDvE-Q<U9lU4XGK<W6E*b|2?$Jb^hkK{m)E&F*FhP73C=Qe=qj`(bzP`a~HGt zKJ0(yIL`P~HI>k;RY*I7s{E6^YJ9{M-An{6zK4i%djx(SSVwEnFiqT9hG-oRAYTqF z7i+W1^GC-{X}l|0gf$M_$UtY-nnE#V+aSOPHO&178lURNUUThL+y2Ypd+g-6_>(@t zC8<7*T<`??pQHAmrf#?v8!2^zTeR1U)+5>*MLSot^F%vev<pOABHBfwT_W0>(2{em zd5yeRp1R>B^d0n`Nb3Ip7pnPdg5DwM7J?`f)dUFYC1?Xdza^-Npce^xh@gKa$RcPb zLC+ENcLaS%&<=v=VYL4dK<G!y;H#;pyW6B3l_+mO7f}eH_f6Yz6PHTyoQ9t+QXU~* zJUjU_BU^ybD=kqfBh{Njx44(PZ+9=(RNrc~Y=ysU#ob0gt+-2DKUbX}D0bg**H?|t zUsJqbnzW)ZR=3gJ&=_@B*VQ!!tD_-zsG%{oxyD@|s&5Q$yABWXbj_<#$(SBg!sn*( zg6U4j;q{j}B)9!eoPl17`o%S9Pza}Y<@CN6E5m!PsgNPulGAY+MCD3T`tVYyX@=xj zi$em3<RE!>(%i2q73ufM_oe%qbRSOlE$RNzbl;Kgf0pk5G2Lfh<5y{V=OxCo`6uy7 z3rt#I(gKqfn6$v81tu*pX@N-#Oj=;l0+SY)w7{eV{&EWxTvvGa^1CEyR;gj!v$1aN z7U|9h@83C$a}`k>9Yk-~S}$eMjLpr>20oRKG&WS%)ke1&TS}z1j<IR8OYza`M)8rp zu>s$O<D>cx()Q1}4Dml$_>EGdzIjDBgrk%4rs{Acq&IG^4OZ6$s`34Lv$1q>2_!%0 z7Hq3RQ7xPy&CQTj2Q#FS3@I%mLQ`+f5CepjX0S5CSO!~5`aigKCX@*^`@`WtWOD`n zp8>S>!gOV8%A<=7!@H=&Ft&t((Z;Z`p*j*Wf{pd{jSZT<!Rz<aShObGxYei&HEfR7 z7@=^uG3?($QkrJNGL`lx^mm0KvHB2En?c1nQLt$nFuOQNft9g_U=-gXOZhp{Abk>z zwqttNl-<+%96KE+aezc8K-W6jq_&y%G&*!6yla+?(*ZN#gY>~T3cY?2<8uTmolWES z3+a+{3uMxGDIHXyJPMqS&@%sb@Dy+vmCmB5JP#Ou;IIoF4B`(*Y<BrV;BBM$msz+I z3mDHo$HqtH(WxcI>BvhII-3NJ)-HEu;rC_X4`ks#%)%eZ!hf8FKVjpe`rx}J@!fO* z`fs3;*~!$iQE}`h;mx&#qdxIHJe@_7ndn<>`U)MT^#3y7En#n#*>QK19dz2F+8>ma z8%p}Hu`#TbdksHA&ubV>u}BSqGWdWIsY49X_v!ZNmOCnp@uKj*Fpc4;b{7QQg#S+n zMIt1&d=Uh#744Ra5~KVkW5TSZ_N>KUG3^KTv`frf@+Zx_#N_Y%sgtwkFM{5+4Utfo zwv$*lP;A<!x==Lq6+$<Rnqbw6732Odr!G_?P1vE?yP90~m*k2}nog%>1OMQ1aVE|r p4bo&RS$%CD1;bbdwUKo0w2{%QX`=zGlqQs=d*sNAl0F6Z{{nuQ(Om!l literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/biadjacency_matrix.mexw64 b/code/texture_gui/code/functions/biadjacency_matrix.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..6985266719edbacda108ffd1a788c7c78c26a019 GIT binary patch literal 18432 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 NwH?KOKK}q3`2SohCrSVS literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_dictionary.m b/code/texture_gui/code/functions/build_dictionary.m new file mode 100755 index 0000000..6750331 --- /dev/null +++ b/code/texture_gui/code/functions/build_dictionary.m @@ -0,0 +1,23 @@ +function dictionary = build_dictionary(image,dictionary_options) + +image = normalize_image(image); +dictionary.options = dictionary_options; + +switch dictionary.options.method%dictionary_options.method + case 'euclidean' + dictionary.tree = build_km_tree(image,... + dictionary_options.patch_size,... + dictionary_options.branching_factor,... + dictionary_options.number_training_patches,... + dictionary_options.number_layers,... + dictionary_options.normalization); + case 'nxcorr' + dictionary.tree = build_km_tree_xcorr(image,... + dictionary_options.patch_size,... + dictionary_options.branching_factor,... + dictionary_options.number_training_patches,... + dictionary_options.number_layers); + otherwise + error('Unknown dictionary method.') + +end diff --git a/code/texture_gui/code/functions/build_km_tree.cpp b/code/texture_gui/code/functions/build_km_tree.cpp new file mode 100755 index 0000000..a1e1116 --- /dev/null +++ b/code/texture_gui/code/functions/build_km_tree.cpp @@ -0,0 +1,421 @@ +/*================================================================= +* syntax: T = build_km_tree(I, M, b, t, L, n); OR T = build_km_tree(I, M, b, t, L); +* +* build_km_tree - build km-tree matrix from image +* +* Input: - I: X-by-Y intensity image +* - M: patch size (length of edge) +* - L: number of dictionary layers. This parameter is limited +* such that the average number of patches in a leafnode is +* greater than five +* - b: branching factor +* - t: number of training patches +* - n: normalization (true or false), defaults to false +* +* Output: - T: MMl-by-K matrix where l is the number of layers +* in the image (1 for grayscale and 3 for RGB) +* and K is the number of nodes in the tree. +* +* Author: Anders Dahl, abda@dtu.dk, december 2015. +*=================================================================*/ + +#include "mex.h" +#include <stdio.h> +#include <math.h> +#include "matrix.h" +#include <vector> +#include <algorithm> + +#include <iostream> +using namespace std; + +// struct for image +struct im_st +{ + double *im_data; // pointer to image data + int rows, cols, layers, n_pix; // rows, cols and layers are the image dimensions and n_pix = rows*cols +}; + +// struct for the tree +struct tree_st +{ + double *tree_data; + int n_dim, n_nodes, branch_fac, M, Mh; +}; + +// struct for image patches +struct im_patch +{ + double *patch_data; // pointer to the data + int idx; // id used for sorting the patches according to the tree + bool operator<(const im_patch& rhs) const {return idx < rhs.idx;} // operator needed for sorting +}; + + +// Function for sampling patches from the image into the patch arrays +// inputs reference to the image struct, tree struct, patch struct and position of the sampling coordinate. +// There is no check if the sampling is outside the image +void sample_patch(im_st& im, tree_st& tree, im_patch& patch, int r_im, int c_im, bool normalize) +{ + int id_l, id_r, id_i; // iterators for looking up image data + int id_p = 0; // iterator for looking up patch data + double sum_sq = 0, pix_val; // variables for normalization + + for ( int l = 0; l < im.layers; l++ ){ // image is sampled by three nested loops + id_l = im.n_pix*l; + for ( int i = c_im-tree.Mh; i <= c_im+tree.Mh; i++ ){ + id_r = id_l + i*im.rows; + for ( int j = r_im-tree.Mh; j <= r_im+tree.Mh; j++ ){ + id_i = id_r + j; + pix_val = *(im.im_data + id_i); + *(patch.patch_data + id_p) = pix_val; + sum_sq += pix_val*pix_val; + id_p++; + } + } + } + + if ( normalize ){ // normalization to unit length + double inv_sq = 1; + if ( sum_sq > 0 ){ + inv_sq = 1/sqrt(sum_sq); // divide by sum of squares + } + for ( int i = 0; i < tree.n_dim; i++ ){ + *(patch.patch_data + i) = (*(patch.patch_data + i))*inv_sq; + } + } + + +} + + +// function for randomly permuted set of indices +// n is the numbers to choose from, n_set is the number of random indices +// that is returned. Returns a vector of indices +vector<int> randperm( int n, int n_set ) { + if ( n_set > n ){ // check if n_set exceeds n + n_set = n; + } + + vector<int> rid; + rid.reserve(n); // vector of indices + for ( int i = 0; i < n; i++ ){ // set all indices in order + rid.push_back(i); + } + + int t, id; // place holders for id and temporary number + int r_max = RAND_MAX; // maximum random number + for ( int i = 0; i < n_set; i++ ){ + // choose a random number between i and n-1 and swap place between i and id + if ( LONG_MAX > RAND_MAX && n-i-1>RAND_MAX ){ // not enough with a random integer up til RAND_MAX + id = ((rand()*(r_max+1)+rand()) % (n-i)) + i; + } + else{ + id = (rand() % (n-i)) + i; + } + t = rid[id]; + rid[id] = rid[i]; + rid[i] = t; + } + rid.resize(n_set); // set size to n_set + return rid; +} + + +// copy values from a patch array into the tree array at node +void set_values(tree_st& tree, im_patch& patch, int node){ + int idx = tree.n_dim*node; + for ( int i = 0; i < tree.n_dim; i++ ){ + *(tree.tree_data + idx) = *(patch.patch_data + i); + idx++; + } +} + +// add values to vector of cluster center points +void add_values(vector<double>& center_sum, im_patch& patch, int id, int n_dim){ + int idx = n_dim*id; + for ( int i = 0; i < n_dim; i++ ){ + center_sum[idx] += *(patch.patch_data + i); + idx++; + } +} + +// estimates the squared Euclidian distance between an image patch and a tree node +double get_dist(tree_st& tree, im_patch& patch, int node) +{ + double d = 0, tmp; + int id = tree.n_dim*node; + + for ( int i = 0; i < tree.n_dim; i++ ){ + tmp = *(tree.tree_data + id) - *(patch.patch_data + i); + d += tmp*tmp; + id++; + + } + + return d; +} + +// k-means-function taking a reference to the vector of image patches and a +// tree struct as input f and t gives the image patches that should be clustered. +// node is the first node in the tree included in the clustering +void k_means( vector<im_patch>& patches, tree_st& tree, int f, int t, int node ) +{ + // vectors holding the sum of values in the cluster and a vector containing the change + vector<double> centSum(tree.branch_fac*tree.n_dim), centChange(tree.branch_fac); + vector<int> centCount(tree.branch_fac); // vector for counting the number of points in a cluster + double min_d, d, tmp;//, diff; // variables for clustering + // variables for cluster id and index of previous cluseter, which will be overwritten by new cluster id + int id = 0, id_in = patches[f].idx; + + if ( t-f > tree.branch_fac ){ // check if there are enough point to carry out the clustering + // initialize the center positions + for ( int i = 0; i < tree.branch_fac; i++ ){ + set_values(tree, patches[f+i], node+i); + } + + // run clutering for 20 iterations - only little change happens after 10 iterations + for ( int n_run = 0; n_run < 30; n_run++){ + + for ( int i = f; i < t; i++ ){ // go through the patches from f to t + min_d = get_dist(tree, patches[i], node); // initially set min distance and id to the first + id = 0; + for ( int j = 1; j < tree.branch_fac; j++ ){ // run throgh the other points + d = get_dist(tree, patches[i], node + j); // get the distance + if ( d < min_d ){ // if the this cluster is closer set this as min distance + min_d = d; + id = j; + } + } + add_values(centSum, patches[i], id, tree.n_dim); // add the patch to the closest cluster + centCount[id]++; // count the extra patch + // update the idx to the child idx - note that all layers start with idx = 0 + patches[i].idx = (id + id_in*tree.branch_fac); + } + + // update the clusters in the tree and calculate the center change (not used for anything) + id = node*tree.n_dim; + int id_c = 0; + + for ( int i = 0; i < tree.branch_fac; i++ ){ // go through all new clusters + if ( centCount[i] == 0 ){ + centCount[i] = 1; + } + for ( int j = 0; j < tree.n_dim; j++ ){ // go through cluster pixels + tmp = centSum[id_c]/centCount[i]; + //diff = (tmp - *(tree.tree_data + id)); // for calculating center change + //centChange[i] += diff*diff; + *(tree.tree_data + id) = tmp; + id_c++; + id++; + } + } + + // set counter and sum to zero + fill(centSum.begin(), centSum.end(), 0); + fill(centCount.begin(), centCount.end(),0); + fill(centChange.begin(), centChange.end(), 0); + } + } +} + +// runs through the patches vector to find the last element with id +int get_to( vector<im_patch>& patches, int id ) +{ + int to = 0; + for ( int i = 0; i < patches.size(); i++ ){ + if ( patches[i].idx == id ){ + to = i; + } + } + return to+1; +} + +// Main function for building the km-tree. Takes the image and tree struct +// and the number of training patches as argument +void build_km_tree ( im_st& im, tree_st& tree, int n_train, bool normalize ) { + // allocate memory for the image patches + double* im_patch_data = new double[n_train*tree.M*tree.M*im.layers]; + + int rows_c = im.rows-tree.M+1, cols_c = im.cols-tree.M+1; // number of rows and cols within sampling area + int n_im_patches = rows_c*cols_c; // number of pixels in the image for sampling - inside boundary + + // checks that the number of training patches is not exceeding the number of pixels in the sample area + if ( n_im_patches < n_train ){ + n_train = n_im_patches; + } + + vector<int> r_id = randperm(n_im_patches, n_train); // indices of random patches + + vector<im_patch> patches; // vector of image patches + patches.resize(n_train); // allocate memory + + int r, c, idx = 0; // variables used for sampling the image + // sample image patches + for (int i = 0; i < n_train; i++ ) + { + c = r_id[i]/rows_c; // column is floored because of int + r = r_id[i]-c*rows_c; // row is rest after column + patches[i].idx = 0; // inital id is 0 + patches[i].patch_data = im_patch_data + idx; // pointer to patch memory + sample_patch(im, tree, patches[i], r + tree.Mh, c + tree.Mh, normalize); // sampel in image with added boundary + idx += tree.n_dim; // step number of patch pixels forward + } + + // k-means tree + int n_layer = (int)ceil(log((double)tree.n_nodes)/log((double)tree.branch_fac)); // number of layers in the tree + int n_in_layer; // number of nodes in layer + int t, f; // keeps track of patches that belong to a certain cluster + int node = 0; // node number that will be updated + + // go through the layers in the tree + for (int i = 0; i < n_layer; i++ ) + { + t = 0; // start at 0 + n_in_layer = pow((double)tree.branch_fac,i); // number of nodes in current layer of the tree + sort(patches.begin(), patches.end()); // sort the patches according to their current id + for ( int j = 0; j < n_in_layer; j++ ) // go through the nodes in the layer and cluster that node + { + f = t; // patch j from + t = get_to(patches,j); // patch j to + // check that the node does not exceed the size of the tree + if ( node + tree.branch_fac <= tree.n_nodes ){ + k_means( patches, tree, f, t, node ); + } + else { + break; + } + node += tree.branch_fac; // next node + } + } + + delete[] im_patch_data; // free up patch memory +} + + +// The gateway routine +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + // input image (I), patch size (M*M), number of nodes in the tree (n), branching + // factor (b), and number of training patches (n_train). Outputs the km-tree (tree) + double *I, *tree; // pointers to image and tree + int b, M, L, n, ndim, n_train; // variables + const int *dim; // image dimensinos + int dtree[2]; // tree dimensions + bool normalize = false; // decide if vectors of image patches should be normalized to unit length + + /* Check for proper number of arguments. */ + /* NOTE: You do not need an else statement when using + mexErrMsgTxt within an if statement. It will never + get to the else statement if mexErrMsgTxt is executed. + (mexErrMsgTxt breaks you out of the MEX-file.) + */ + if(nrhs < 5 || nrhs > 6) + mexErrMsgTxt("Five or six inputs required."); + if(nlhs != 1) + mexErrMsgTxt("One output required."); + + if ( !mxIsDouble(prhs[0]) ) + mexErrMsgTxt("Image should be double!"); + + // Create a pointer to the input matrix. + I = mxGetPr(prhs[0]); + + // input passing + double *Md, *bd, *Ld, *n_train_d; + Md = mxGetPr(prhs[1]); + M = (int)Md[0]; + + bd = mxGetPr(prhs[2]); + b = (int)bd[0]; + + // check if number of clusters is smaller than branching factor + if ( n < b ){ + n = b; + } + + n_train_d = mxGetPr(prhs[3]); + n_train = (int)n_train_d[0]; + + // determine number of tree nodes + Ld = mxGetPr(prhs[4]); + L = (int)Ld[0]; // layers in tree + n = 0; + int n_tmp = 0; + int max_n = (double)n_train/1.0; +// int max_n = (double)n_train/5.0; + for ( int i = 0; i < L; i++ ){ + n_tmp += pow((double)b,(i+1)); + if ( n_tmp > max_n ){ + L = i+1; + break; + } + n = n_tmp; + } + printf("Number of nodes in resulting tree: %d in %d layers.\n", n, L); + + if ( nrhs == 6 ){ + bool *normalize_d; + normalize_d = (bool *)mxGetData(prhs[5]); + normalize = normalize_d[0]; + } + + // check input properties + if ( 1 - (M % 2) || M < 1) + mexErrMsgTxt("M must be odd and positive."); + + if ( n < 1 ) + mexErrMsgTxt("n must be positive."); + + if ( b < 1 ) + mexErrMsgTxt("b must be positive."); + + // Get the dimensions of the matrix input. + ndim = mxGetNumberOfDimensions(prhs[0]); + if (ndim != 2 && ndim != 3) + mexErrMsgTxt("search_km_tree only works for 2-dimensional or 3-dimensional images."); + + // image dimensions + dim = mxGetDimensions(prhs[0]); + + + // image struct + im_st Im; + Im.im_data = I; + Im.rows = dim[0]; + Im.cols = dim[1]; + if ( ndim == 3 ) + { + Im.layers = dim[2]; + } + else + { + Im.layers = 1; + } + Im.n_pix = Im.rows*Im.cols; + + dtree[0] = Im.layers*M*M; + dtree[1] = n; + + // Set the output pointer to the output matrix. Array initialized to zero. + plhs[0] = mxCreateNumericArray(2, dtree, mxDOUBLE_CLASS, mxREAL); + + // Create a C pointer to a copy of the output matrix. + tree = mxGetPr(plhs[0]); + for (int i = 0; i < dtree[0]*dtree[1]; i++ ) + *(tree + i) = -1; + + // tree struct + tree_st Tree; + Tree.tree_data = tree; + Tree.n_dim = dtree[0]; + Tree.n_nodes = dtree[1]; + Tree.branch_fac = b; + Tree.M = M; + Tree.Mh = (int)(0.5*((double)M-1.0)); + + // build the km-tree using C++ subroutine + build_km_tree ( Im, Tree, n_train, normalize ); + +} diff --git a/code/texture_gui/code/functions/build_km_tree.mexa64 b/code/texture_gui/code/functions/build_km_tree.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..64f090ccc0f775fd55f0c576b8e29df61ad8ff29 GIT binary patch literal 19188 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�e0~Xh zWeL2w1irNdPS4jCv*+g}@W+9>iqxyS1pn_!;O9%=4y*$%LH{g#Z+a5LFD`gq349^& zVs@@8!G90%V((>dEukk}0{@2+`lV1T5l<xXrcwy6FfXoOq574TtMH=t@H^gWqFD{m zTi$U=yxeVqRJAdxZfJ_G3pS~Zxb+-ox0<oB(%KYG;CAti?m4rdxd;=z4<0c#Y*cK* z#I{JrrcYzCxp@mjOq5D{MR~0GZzG3VTP1aoe_5T!ud4CnI#p4NFyK{i{~T3~MA^=h zsxDmN3HVgsQZK#N&g#z3Z=YEkY;06F2b+@N_==T@8;$$pO5@6)(&+Q8m=kGMTZ4(v z#z;g`!)!kZHH=5qjkv*$Z7J_+Y<R7&Df(hMt2N%*o<r}R<E8ax(sBDuo7CoTu*F<h zt`HQuvQ|a0H_?4=yon!SB6w5&MpOoli^ijPH@_(wZB>@KQD{pN?|!$_d-N^QSTous zf;aVpXd7kuu+m(mR?bqXgjp!T=WFt9UZt*_qpqrjOKzXDA)HVf@uGjOXj9dVH_wG@ zBG3`>`DVI{Jb0ieE0!S7x7pjeY!&nx?)QFUsVAV+!}IuvfM^AJTmJ@CO}1=E24juk zMztx@f;;#S!Gj5j|8n)HcCh<Ks#hXvYT04GaA#Aa2oF=y*yB{m&HpVXz`@16cN4sq z|7eDfzIrh8`n=w};s*3TQ=>%Ip%=0b8NlM}RW;c7Ej)9fZVU(i8#)Aa40sJ4Vx?PM zH4}!b*g7@72SKLIiU(<^8{?tSGE;XUBk5BGqQhL|arD<>mFJ^+*}{b@eK5mQ=l7Lx zUeT_%IKS&*WXI9=d}NCOWik`h8~%>Nf(7E62K2!K_MHe0@$VTlx0m=@1AR$iSem|w z&|KpC5flarIO($xB|aH}uT<nSm-=ExzMX2`_ct!qQ1S~I`D*AJ9r?n=K4D=LW_)=f zpBtan$OnuMdgK?vKJ}3g<-V~|<hvopr#kZai+mG<zLk;B#J>>2LH^wev{LSiAH~A= vf(528TNst6I~M=pS5fl5R)XUA=Q8Ni87ShSkE@6|HviKq#lP~AU!#8qpOBhf literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_km_tree.mexmaci64 b/code/texture_gui/code/functions/build_km_tree.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..af834dcdddc581cf19474646a2383c938c2441ca GIT binary patch literal 23416 zcmeHve|%KcweOiEn5m?miABxrwGb$HiM5y!RHmVtLuOzfI0F%gq9u-DGJ(|ma(;wR z6HFV@54(rsC%2z+A6~Eb@u~WGeC~aHaC@J<)L!Z&K@zCNpcX}};*a9Qsdb1J!jCfV zyY@LVnM_dY=e_&a%h7%IUTg2Y_S$Q&z4qGc40-YVhnMmt$(Acgk{!u`wDt-~>XvLM zNz&gS4dzIa&$q_2c8x%df0d61B?|^kX$lh2q{rtA2in4B*@VZ_8=`^noV?M@3C=T% z<~vX7%mjVDP&l$altyLqx3|o!+G@2R*qo7xuv_#nRq>egd>aD|ErB2~+4L4HGTR+6 zGl^h`W$c;2pTD^&G&bREdeXPeuB9mofXB@@g|~8_Z-d|O3$*zIEn#1{W_>ojQj6Yu zR(pcS@i&2<KNJquH*HAMAHN>c?l3Ftme5jgicB_bpD)t1slKV!SKn0Ej7Hh~U4N(9 zbC*Sc;A|RJmB@U)z(!wPu%<C!VzcQ*EqV*BP6U$<f7(NOybyEo`P|AHC8JfjRkrj` z%7RIciEfIv&)3x83vF&(-`wD92?sxwUWy#iOZ5dAiS+GSDp(KJwRucSCi3SfHyftt z5u8o0$f8A=&(~1%S^O2FKRtWROd>dw9?kcwRvl#|H-_Z%Y3hpQ9=DpZ0hLsXC&icr zw@o$CL??5<&61?fQmR5bvI%zNdy(%$z81OTE=k&n4!c)L63r(EiSm<xzlpr%ZuEgc zA@b-Vp0D6diN=|RM7)05v$sl;N-tCSx4_Xjul!5H)xVD3e&=KV{#?&@W?oo08#o8G zpL(T8WR?U>I6MVN#CM8Ub5(89&8s(u!hyz{wfgnJn&9S}myil?Y7Raay1Am+-&`|K z=rkd2zB>>M)x${Kte90qR?sCr$LI>QR32=p32(f)p?-a1+q~M%$W1))@j2}SnH?sy zx3z%+_>P2vM4giV8{e4cv<9K}3z1U%*seSbGc~@wIpukaGx@p!eMnwWtY0zm5(}pF zoO~uFFe!ma2~0|0QUa3_n3TYz1STc$|4IT?jQ>HoN4Z<6Qr4_?bMIEhceEvwNya1F zn188*anD}X9j%k^iOP>o$IXH1o^5V@ZBd0WEtgk4<tceh<DT7&dxsR``l(Fs7QJ-| z9S51e-|LT}ey8UDK;efyC9iw^=b3+#UFD~}@}5Dj?VOr?QSE&@SId=|f8{iWB53un z-cz|Wrt^@;`CeUJ-8^~5RxxE%?$fw8%5?AUGVfDN@7VdsLmBmo>-9&f-26DxtA<LB zx?L}O_%XNa{aCRbQT;CfJIIWJ!(Kk9@$>SGa;Z+9J2;}q4rxR#w2jF3Mn~key{xO( z&XRp<SJcj2eeEZf8np{-s_nGq`iL2i*xLUQqwizMUQPE#wOnvp=G`lAuYuTf&(^I` zdF%fKuVDXBojk9%^%@jL<huq(WXF{KxEGDc_xHx_1nmJt@?`#UJA}NYjp>FdzArh? zbVtLhoM#yCV_olD4Bs&#Uk%nD!h#I2uJ<pd<WVO(?oQw}y-vRODAMsdxsVWr(^=QS zV%9ZqG4clE$CxV-{wmEE!xo&zJi$$2!D*H}$Xo~8KlqNaPN`PzSA5m|5O$`#$LU9< z!e3SRCn~?>alP-6S6);HmWWFpauK*v2Sj|x)BRq!UG=}gj73k-s>Oai7Zc^Z%vkW4 z%CA!S3MfFUW5DwY)uQ#5DrG<2N|{mpO8mOa@l_pd^;U<He4e>ZhaHOHNpc$&D37Sb zr)vBx<8Q|PaJ3}4dE+Y{*B{zni07v%C$}v0`jgHRbt!qp>cPrm%Gryy*O~vYs$Zc` z+omtr$ro(n({{3Czmj}Gb6seEEq(>+(3Qq#{4gu&W4-5dG=7Pd9BVDWsIed8DjlB{ zUl5wU%*flvT)p9Im~q!dYI*i0S~F%ltn$0<8c(pU1NICN$03V;RlkFEC&OP?^(9cU zQR>Xl`5ZfYly$w0&b>6jSli!`<lc(?06DE6tq1m{P?xVW{VI9S5c9vpdf&-qxz{<* z^e}zVX~q|gSSy>b7BszosQ>EjL-M0C<Sp+xs_9o~x~%zMH#R3V;~twc%6@$jicG7a z%{&$ksm8R?<;L~LQQ>-d)0Y93Astm+L#_9yM!93TvF5l#wSCMCAC~3ai$Z<I_A6Ag zob=O<w2BH}t&`UdA(5gIqVTJ%D+)#JL+Za6d6Dsi>go%J_~DYil7TaQwr~;c&%EeJ zg}gTJINpVM$JJy%OGY6+jfS@~qs^uo;ix^?TXIe?ke)B}eaR^H^kTdU>E*ZrP!50` zjEmIgz(#R&^<Ij+@}27bo(%cI{A~GFwus3loyo8_w4X*s<aJ_GVVlEvn0x9}b$@ET z()N{o|Bpowtj4=5g^4xn$Q1sH%11Q5;{k!&--68R4?0x;eRfse^Cp?WI=RFXWdoie z+ArNF!ppq;n3oT1{t`1fqA@lHG>w~nPn8<~C{_s=<~PcW>tAEMdI%;%<9~{I1iE_A z%SSwV(Bb8yMB2@t&clc_!Y+)kmxy?}&&XR3q2T4Oq=wlQdmAwx)&FMZNT-0pNV}|& z_F5yI5a{aNUjB~Pzuw_I;q87eHH}UT(nEtNzofT#ft7H?l8TqV3WFeK)){L8p%uo0 zr>Q0FxmIF<Kd<q|&gI6sr!jVQ4`!_KGqDZknA=o-+{@1^`Z9;c<C-oc9WR<Ny7Pp` zdB}N)@h41!+f7TM?;uf>47mp7M_+>_WBxwIyB)wWeFv?Jrhkj|p3GBpcfOr>uLFj* z(htC$MaZkq()f_(KdSj#VB?-w>G%^oU?~r(#`OnQ7}x)qrsF&$&u|M-Z<&WvQw(C{ z*@(QV=jCHc*!FeU&#?H{!H(OZt~ivAL_Va0$96(<9c}OP@(-9UW)QzJB|m)@PNyj2 z1~H7&WssE&Vit)@bf}XR%T-H`Yup$G!po1Sy_a&B9@)kCGbgDD&Q(?d6Jo>wsr)x; z?}c2ZKSSbi8%;?!|Ct*_bsuW!nV}jjHpX9N&a<p*6bkDdWwuwDk@pum7%=W@L^Rd< zPU65=Y}1?xwd-6Slf8YKErHhEbbO%o5L#=_H(A#?B9+i=Z=&+g7O6oZWv-}EZsXky z<Kx5>^E4F5se2(|3<VauyA(y$aL*?6T&qm@Vw9cOP-%anyNEjJLIKLUNdYwd30e;M z(Pl(dv+SAbUk2NYgB`}9T;s2q>oqOk;!EDs%XX?p-T`XB_>msi2ORb5uEddRS_fLi zu0FAn?|Ssb4v+JU(lwgXUa#+``N(_D{fcZa&_X}pJ9Hn~GN1Veh^oe4hpC5xEVvF8 z&X?+l{<I+sFzpydcnS9BFdP@=I^2GWd=Iz0^y3k^dNA2<d(rLchqKe${xee9DvZGo zEApOW)*QT~bN<7If>WT<rom%)E_DpBnU}%~amc8~qNt|-jKr-P>wYIO*M&{9nXL~} z{+*<{qMLC3#9OlD1yn6OC)HT%aPx!^5B8F|PK)|59PBvjI+^LuRM0Uo73m%-%*Z$| z2vIl#=L9)6NKu>@ZrxN!*NCPI6B=&E(J=zbB?&q`W{A@xVQt`%#W+L`<6W3{SWS|x zhMTn<i%06R%vdyNs5WDXt)v%|O4#KY<S&)e`LUGF4|<WOxHca^KF$rC9|Jf)qUQNQ z_FnZLv36uKPgk(MyieY<gDi#ZsI__u*RHdBTyd}Q9h=<!uYXA<<6lneBX#M9tNkt( z2xU7z2o0|kigB8p;SaOqt54IrUy3*e@^sP~>hUZUh(r$xn*WT~|Dmxq32WCiYEzy? zoutVtUuMR`HkFT#9ro(X1e<wwM82_anXzU*w)BpTH0svJ(LpuVd<~B3Gqm)~n09Qr zvEUQtI<{%LCDmodv=3Bc!E_|xah84H<sY|x*K13t{G4X{NcgV$4%&ujIs!_h0LzR8 ze}cy)oQCvyjTKM%v-iWFy+6}UAQyprTDS=~*Uo!+fBXt_EH(MO=6b?pxC;y`#zQt& zf}e#o;uqW{ze&F6E_v}xzby=opP*S_9SdHDBR)v}M8O;6>?ix<xo&<0PKBH4`$-=# z)mKp@iviVmOYBE<Tm~&bN4an%sRMSl0A!&J<V_4X;8MR@VT7->d2GM4w9--v*lyLo z%E4Sm<?Ra08P0LmwV!OWwB;)pOGsFMhKy!x$JbyIyY`QYN*SjC<VN-})^1jiftD=w z7oAP}{S2AO*jj3%3)_~Ac8yNQp)YsK7s%38cQUvTH;9Xe>$Q~Qd3?<A#2srJhKpUi zo@jLJ5`HS0?@sxtqiH`C#=aNLcgKdzw$W7E!B1^F7<-D^e*XZuLK$8$eB$^6^tu@@ z3=1yd9-CZV&OIhBWA}am7QgugShP`Fh+rym+Hq<96#gL`W__uBA8j<XWH3Bm?H$dd zJEJoRlPsQedzTZwO%5MoNnG1{9hz%^_XR&lM&XwWeIj~e{$bZ2H)2+}WvTqnD!-`c z)zf(OR{k0c^?<ymU-fG^&vr7H(O-%K^X%JfU;yfwWU?|ETZNBv@Wy&PdU&?o%_E)g zQ)U<N?sha#O5RkG2Z7xFYht_PP1`{bJ>zy=a`OxF3LLW?`}pgzFOQIhczbYqWjci2 zC*-X|=%(?GohpBk`Qh$&?25gGua?~>G=2`Nv{U2n%a8sm8tKo_fXRcpcWcQJy{a?W zS8^B!&DodE4!Qbp;-4J?lRI&*$IVy%MU;ECc_28tqQM^mrG1O_f_;1tm$XZ~kB+F4 zqqw*4T0G#MMU#eCb^;Dd4=$5$F5?Sk!J^Fa=$<0Ctyi%{aW{pp>(-$gGe5#nj_V<& z<JKeFmE3B(njBEO4mgNiw`(-=W9U$iqI(K3U0A+oYym#yVq=3lJ>npwBHws0HeFyH z-kr47afHV51>z>KzMTv$NyZs<o`_#j2iZp)m|d!`xcc8X82b?4T1VXSO((^E(j&W> zQ8g5kF&{X)%5yX=VVbvW&e9*Sx{G`=MJyXD=?&k=df_?2jc}fECVSkjg$h3t&Q)+T z#l-_h5px}J^RvNISZ++sVS7y}^AtQXlNO95!%r*xwOHU|s6=ixuGIs$%DeeU`#(S? z$!M8pyS!bT&U!~r?9cd$d07LHNE?7e$^g7*w%wQ7wYV;R%C3#Zo}{*22R4r1{eSI9 zfiEzUi`rrzS0Il+j`KWwzqOG%r|w?Qv_WtA1*T6iZ3XS7A!G3c_-DZaW_<fASoheG z>m;e`f-Ovl8HCsmgsR^e+k<>-G(!063IDW(|9j+cJn3{&^+)Wn?*kr@Z<2U-*hD#E zO(v=cn;lJxV)qjab(ssOI96#vrO?rSQqOJ9D7KH-FTCI5`lMs7k~~1V`T`Cl&$iej z=wNEzZ{V!psJf}w?TT%AIiBm`pMbp`&_+0z&M1G=&EJXTpgGP8x_@?!UJ-F%(7CZg z_|-+{LP?Q#9CYhX6Q>lXxz^23_QqzoZ6~`<!g9P_(%W^;6h$j&#(s`Y-6tL)5&VGQ zp8|%>+1AyuRT9`v!fpqa1QtZD@PnWP0SkFXsJhXr_9BP#Lvq7D-K*OX`qL}(c-4Q3 zy`Xem%8lgeEp{G>B9OtW4uCg#56TA-dgRqFFe5Vf9Q`ihSqe%y55*GK2!gc83^61= z5IaK{-j7R??tOv7N)2_t1Y5Cf%T31OWY>8cE+5a6&3RT>TY2RWGj9DVNee194eXg6 z=@yDT&%OVI@E>l&ZXS4wR^tGKKjmdKgKKW(WHWXv@-L2HJMIO8J#PL%Z1fx%-6_As z<e4t9GS5&%#`b8-*dDP*!D$bkZ2c`#`5Be}Mb)by(rP;^d71TI$RW3i?k2H2!iZIU z6uSYvsfkzbaHu7(YH<8|M|0ROAqjeBFxOyco(4gxN0X}SElpm0!K+8?YRNIis|LLY z`So7RaYnt}$zMSlQe;+qzG(fN=$=g8Ik7c7rv215)`y>(K*lttk=I2#3N|gOON)8l zlPiqcC-WdoP47TJ7M6P?_8(}j@@j-00(LYUQn|;0al_YYJPDx`jR|FV9Z?>KGUGRy z=V@9#)pnXZaZv)njOJ*rMCd`1i6t@(AuLN|X!TL-V}$oUPh>1pTmgvd6^#dQ*{&MO zlGmcR7vAnOBo9)ZUx7A)hX0Pa*@u4!q?#ef33SquY(qCwl0ZGF8H>77L}T@+^4dN@ ztmH`{&cKkyPf@fxwt?m$<QcJhc?V?q9?A0k@q!z65bg1@oamoas{AYtSXI9t!fb+= zVx}<jG}mY-fPg70>4*B|X|@XtA^6_mMPZr3Hva?BSqi73k6ME0&9FGap0$)GAKq|C zQ+xlCtGa#<)r))eI>_<>WSHkX<n8`&AIU9pXIz8*W&;(b6EGltg+;93Lf}yO{W#oT zjGP?)DT#BxMOw}vg+*n(@8`vC0W)Ah<L|QGck`GH9KVeW;nv?yq%UZLZp^FSq3Q_c zmYiq&C}x(Ymb{?$zK5xvR5|YD??ZiK7YZq4b^|fv<xQDtn-TsUej2SH>I_u>c>gj} ze-}cV_L_S<{&m%8pAD&>!NTs1y@WY>`Kc^_0o!t)YAi`$pG6S>i_e)*k7yZb#@^W9 zg8+F$kc7&AkG-dre8AwT3TJk7iu}XJrhw7!pZ^FBqx|Sz0uaLQ9ln!}{G;boiXENG zLmci_YV{C)04Z;;Mq9`OOLwQ*`^P&}8x;{YKS^;C@U$CY+9{wD*`?Zq{8k$6bFdsA zG5$LINKN<bjSa)Agv+x}S4S(#v^jR!*Z``_4HQ-VI#u6<Fir$MWYr*je^K>9ReuPv zHu|V^9jQE=Ls$($73ziPcB{RjY|X78^B*9?bX>DSX$onAghEZKw;-k$g4Axs?EEp! z%m5wmxJ%VbRlN;eI>1K+BhF{~!x*p)sT7@np_+%$V!nR0QdWgtk$olyrm2o(uz&-X zf`zcR9Ofv^1K}K9(Y)bp7&;ZNGUua%j}%o!ZyhB;bkw?z$;O+c{<;UL3L{j33D2Oa z+f@B7qK@Rj2#5*d^O(vBXA{?$;n`x0auPF55#P1I@bFc@3I$5vXPi+Q<qSq)I^u$> zvj<_EP^cL<hF&N}A(mz1mzQDDB9_gfMJ*mon*40}!d(^p?olyzSz$-T7h!oHc@A;N zKZhg{&+m%nQhWzD4ZH4CTnX4UjUVoSeLECiHMW15vGSzq`mMY*1kt%R#V9n2X0hy5 zXQW)DqvKbdL2nr^Oh#3s3Z6VZAiWa$ZoFcQK0<%9RlR*STBUT)$@I-21f0qj%pTLe z&1mC<;9T!A*?k;JFXB`9+F-1i?ND5}BetLJaXo}%Sxt1DRb4N%{#13n5%$vORed@1 zOPg))s8IC+47i2{ByI9Sb?}vUHO2x?<pmg!a8yStKQ$SR%Nh4&BGw&^lw*s-?sy?I zJ+hb1`6sc<w~;+qM<Lp`tkd5B79UW%3n0DDj@_`Pm*npI5w?H{(Mn<(KdqI#4R1x> zJ|D>8_<4M}peL=Ls^M{X%9f0_*J&=e6n~Pp260e~)x(6j%M^S2GzukYt|R!|gfML+ zHoP3om~La(C|Uc4VV{t{I9{dcHqY=GnMOGjSui{m{ptJ|Z(posW;_HEY5QWWvLg0q zKQk&EWvlHSpV0c0-6hte{Va3+Cirub-mnjoG-Yv0-g-TN;r|ZZWX6|wQ|8b6KC@u~ zs-wx1ioRqNw>|8!4pha<gwVxlf%9fzhUe6pN5;(KP;_sRQs%YOE|HCPQrW|@aSFL< zL%iVHi<PC#j6J;^3+_cq*$}E)jwZRoU&eT<fvba2RV=LeaWdsuqdKfcFqyl(Mn^IH zO0N+q6<nUDr6ubh$KW^&D0D${StuL-CfLFESo#v1;s)O=mVp!ckbH%?GE{@uJkP<c zcOiPtm5oiJXTGYhO!tHvB0eeemFS76J<ZGuQz!_b=UntGl8tW@7kbrhri+16<Epwv zObj&ETM$EU2$Q6rOq;1p0a`3)3s8_6egR!b_6LwN-Olt%usm+VF-|DdjN5Lh6_kx1 zi9x*?mY*65N~T%$f^e#@AQit))xSu4MPGp>@fLN=`b-Xo-$F=x{L=ULB;agN3ZU-C zNbe%q5vZj5j2oZ8-DOMdZY*iXogP=OZ2T`wM$@rA_|nZiqxcC)LZhWv3~;Gk<N44P z#PmI*j4uY5V;*Afp%<>kMBum}0M)?P^=tx5&;wt+#}ObN1QB9HHvaK)<6G3S#uwD$ z8zyl38Li!zhc(y|dxsO>;Qwkq#j)hp6$orG`225zPaILpFek4iC!40L011T}k{s0# z%7%7jE9@NsF{Ic}W3nYhT!BWGXz(G=B!PWbw$k2lE7N7B--AX~sPRJjS21|Rj1vkq zgrOQjU4_%Kx}Tz)I6vtp2x<E)?yq;R#!n53HDjp*^z@}gt|5Kp+>RS*-3&~K(ZY)@ z8^hY2RP`Jcd^2oW#`A~CeV~?6<f@MU8PML$mMUl1+#usQ3ASP?rlIm2*FD(R`sK)5 zGz9svPJ%|fS}-GKZ4H6ew0j6e_5(vyVTei^v20~g$4y^^CuZDJia<=fF#Y{UHC8!P z{2&3JQNj=7Q?klmQuI~!vPyf$dtUuYd@;m@{`8nUaRf14%P(g7y8O9}ughP^xRzgv zZ?Pv4JU3XPY)yVq`*F=!CzmqAonIWkX54uxa4Nsx<x>?L;BH=CFiZiG?<n`v4;UuI z%ZhMAz%7vR3l#P(!6olpp7IO)^5fi;);l$0bulxRJK!GZ6|}0MxgFng9h8mV3$cq6 zWXzOWtGXP5%pLv`8C>I<TX8Tkf5DHK?P&6CMPE8vRsk(9Agp-+jdfp|fL({aiT+p! z$bk&`%2AasO$;B$aZDqVmWX4W{HlmJ%7i`z0TRkO*6GF&6k4)6ttDW0%)L#!2IJgY z%QYY^`RiMkg`g#AdSv6tMTaz2fu9UlgJvT%ml>f_=ON8?N#2@+X?YMUPwyw8A|r|( zw3oHmI~pt%fmX;fIx%$|r2|ZNmq9<)l+9&)O&RpVT?YL~3jJ7`C<~WCKcEJ=(2w@A ziTVK?0wWr?^A@Pb(gFoPlIpr(4_~VqL456^ZyNB?C_p$|O9woDQR8);SLB}@99{#M zyc^Y6nb21k!O2ndO{22m$70hp;)pn9KSOfEmxNk`91NPJb`2zir--X(G}e!x*)Uzo zG~+>sW`tl){H3D$Pjr>#Z&RaxMk7;;7RvbAN(Lhj-Ygv3Y&}vrkSfoEJ3C&J^ooUL z6?0{yn}*`}cA4c6r0I!se5|)cPOq3-R#Ai<BIC0%jZaszzt$_jCNlLOJtydYicpsR zo9@Lhoqr_aqof16gW-mmx;3YLQbM7o3Ok2(25pBzeR)yYa%{QBL_gu_kNF|OnXvr) zY+vF*%rRa!!Tq&H&T@an$clc?XxZwb4%}ZplRsJy+9hIZ#B(u@ds_njMMfyFzvn~f zAEj4tEqDyR#R;VINXa?hh4DzvX%O!w@oo|Cpm>MHoBZMAoHp_Po_M#5_ZIQ)6z?C1 z_g3-#p?Gf-?;nZxcJY2xynDp^$Kw4Hyfx!%zs0^)Rww7|N8Ym$vXzEUfw_&vRC<F- zPAXAMXQM`?K`PNd3?2S8mFVI-{J*KxL#2PA(o<C0MI{O>4*vs{E>LMZm9B=)4F7;i zv#3NiePbDwnyAF6R7<5bRJxB!v@3@zsYDln;U!d}(8KT|lt4G*N#I|P(vuKhuJEr8 zE!f14;S<j#AMfv!aupA1C~j8_deSbWsYrJdKp)Q#T9Swl=~}$f&87or0#ZE5$A_fc z6+V>z2I<Bx;njw;0!cgv@y54W>1L{V2k9Op@wh;SY8Frp{qChn1P}pOyiqF=VoVjN z;e%KxsTXNCdWfeU#tflD$xby70~j&!lC-40HBj6fEDqJT71uYlM8cusVBn!heK1fv zPg>c8x=0w<45ZpvvmsC%+SnXvs4ZR}D6VaetZxX+lvYF<*9U^d&2`01&9#9LsG(CR z(h$Z!JT49g1A%W9IW-VHUI@ed;BT`75s8zZ4Gk!h~2Ev{**EpBNJ)rY~<JgF&N zH-=k(8C)n(6ZCKNJ=o}@QHq<J8a5Y;e}!3Ghl$<t&D#3LKokAz%bEt7>HKksdYVmW z9{!c1DQObWTqJ^TLV~R5u^nAIh0<M)axA+;lGXv~M*6ohyzmg5fQaeeVwd8xNgMLY zVo7=dIngY@e~V0Nw&ENqXD^V6Tq(~1{dr)Dlv64rXdp{DpkeErg?|G(TgolIL9)%1 za%?sU|Kq_Y1M%d3Z<S<gbJ!dO`F8rD3~2~Gx!<eQL{0wVa{xM0^`i2xc)1sujaFFs zS}R{~<qcNeYUQ0){v#{@iIsoeQ2%VT+im4PxAOnA@`F}BWaY10`FmFWCoBKFaVG00 zB`_(0NeN6!U{V5;5}1_0qy#1<Fe!ma2~0|0QUd=!N}zCN@xAVQ5l1NZ`R=Q&sce<* zdT8UWQAEhZW9t$42H>-iO*M@ZahoRKd>h)@eE6GsA&Rxshd29LOQp_jWAv}V0Qg&e zHU0;E{*4d%HbiQIwNg(x{=`+S_!ED=_4uoD_>+8{C&&6=q5ezQ1>J{=uP*mBwp9cJ zHQ@llSAk%?UkL_lHv4WbEtPtthvtg<rGc=!CR{^^(ljC!QwpWpE=*OKaj=zj?g>rG zQ%x#^M2rbg4Y@@CjWCs80Y8B@PcY~WZCHapp$Obs6IY49-xsbU!~<gaRxDo~p6~NH zZz=WpS_A%YbI`ZGCKT}bn;RROn^bMR)8nDK@Wx>CCSOCKX+wCUFAxki2R*GsWt)u_ zqssg{eyb3t!yn%Rx<{aW>%SKWHWR$lgvZvbuBN_$Dt1wYKVSm83212EKoxtb+!$!| zw`?YGp9O{jVITggSvU}Ea^6N2QL1QZ-bCd=DhCn$13-_Bq_8B?<PRfiCpmJZ=u$!t z&D@pOlUu$kr*h~km5D2&Q+DMH<s@<vg^64Wr-|qq1zuBsUy{Po1XEm&o^m9DJFP%k ztp!Idn1a$&e-ghqrssJif+?Oy&s3BNmWm0&$1Dq`I3GRV6>z82d4mblk3Xi{I;CBI zXTtATaDoDic)o_<BGo@Ye{c@Zy%t<K--Q3kg3E6;;Xhb#;x-e$9h1H)59}buNDm`; ziW!3>_&y7!SR_3S7ECcodbR+je@Y+yx(Vxm0fK`0JZ{1m!F)~#n7$~O&%0T0JPRJl zg3k$fjD8L%VZ1cGA`?!Je`6N>?JT$=3uakxWfr{Ngu#XR(6<zfpN1bcVMy70c4Wc3 zvfyX3;Fq%C6It;4CY+w%AG6?*EO<89n1e*>L@GpTQ-U-L>FY?OrlgwqQNQ?`ee`vK zR2lz(L&SuqBcFj(j5HI8)a)Bb6xYU6{<nC24e7sXoMH**jPsUIO=C+#fOfxs<EmTh z8)52K-A<u;Dl7ynYU=S<4$*uenE@OjjrH}ScPSZ;)|!S$AauD-W}BMY+Ekkrrdhnr zw25l%iq$?>O+!PozXm2!t@Yr^98(Or0YlW*U!E&!aQlO>M>S2MRawN-ypJDbwbLht zNzJj|oXTx9Ot|@r5ix}@e|@ALN2K+IBU^^1;IeqDAJ0=gIJ(8@^R+}m8*xzJ01h?a zT%f-*sjga%+SZk8d^Fe3C$rG!t7&NoG}U?<FVo>OW%4Oq;S)3m!zv`>yA6NSG!P1@ zX)>#CfiNq@yLC;eZ}n}yaeUrx_FeF~oxf{#M*nOQPH<J<6bc0Cuo0a2=6^1^%kgpx zn8_kk--PoR${!AVF2(g@vzv$GGvmCJmR3|`{59Z)fO7)o)(M!~rEzCy_EAqO{eR;@ zpucb+DJO>!Ir)f`!7YTBXL=AQT*f6F=L8CeVjRYF3r*)jcoivkg7OTxz>lCgj(JN2 rS$@)|yLG5ZIdoQWyq{(wKgEeNE8)JOkapaJ7h}3F6Fgb6v(o<ole3;D literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_km_tree.mexw64 b/code/texture_gui/code/functions/build_km_tree.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..fc2e3cefa7e292a1cc3fd9baf7d06414b15fa888 GIT binary patch literal 26112 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 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_km_tree_xcorr.cpp b/code/texture_gui/code/functions/build_km_tree_xcorr.cpp new file mode 100755 index 0000000..0e15bbf --- /dev/null +++ b/code/texture_gui/code/functions/build_km_tree_xcorr.cpp @@ -0,0 +1,427 @@ +/*================================================================= +* syntax: T = build_km_tree_xcorr(I, M, L, b, t); +* +* build_km_tre:xcorr - build km-tree matrix from image based on cross correlation +* +* Input: - I: X-by-Y intensity image +* - M: patch size (length of edge) +* - L: number of dictionary layers. This parameter is limited +* such that the average number of patches in a leafnode is +* greater than five +* - b: brancing factor +* - t: number of training patches +* +* Output: - T: MMl-by-K matrix where l is the number of layers +* in the image (1 for grayscale and 3 for RGB) +* and K is the number of nodes in the tree. +* +* Author: Anders Dahl, abda@dtu.dk, december 2015. +*=================================================================*/ + +#include "mex.h" +#include <stdio.h> +#include <math.h> +#include "matrix.h" +#include <vector> +#include <algorithm> + +#include <iostream> +using namespace std; + +// struct for image +struct im_st +{ + double *im_data; // pointer to image data + int rows, cols, layers, n_pix; // rows, cols and layers are the image dimensions and n_pix = rows*cols +}; + +// struct for the tree +struct tree_st +{ + double *tree_data; + int n_dim, n_nodes, branch_fac, M, Mh; + double inv_ndim; +}; + +// struct for image patches +struct im_patch +{ + double *patch_data; // pointer to the data + int idx; // id used for sorting the patches according to the tree + bool operator<(const im_patch& rhs) const {return idx < rhs.idx;} // operator needed for sorting +}; + +// nomalize patch vectors to zero mean and unit Euclidean length +void norm_patch(double* patch_data, double& mu, int n_dim){ + + double sum_sq = 0; + + for ( int i = 0; i < n_dim; i++ ){ + *(patch_data + i) = *(patch_data + i) - mu; + sum_sq += (*(patch_data + i))*(*(patch_data + i)); + } + + double inv_sq = 1; + if ( sum_sq > 0 ){ + inv_sq = 1.0/sqrt(sum_sq); // divide by sum of squares + } + + for ( int i = 0; i < n_dim; i++ ){ + *(patch_data + i) = (*(patch_data + i))*inv_sq; + } +} + + +// Function for sampling patches from the image into the patch arrays +// inputs reference to the image struct, tree struct, patch struct and position of the sampling coordinate. +// There is no check if the sampling is outside the image +void sample_patch(im_st& im, tree_st& tree, im_patch& patch, int r_im, int c_im) +{ + int id_l, id_r, id_i; // iterators for looking up image data + int id_p = 0; // iterator for looking up patch data + double sum_sq = 0, sum = 0, pix_val, mu, tmp; // variables for normalization + + for ( int l = 0; l < im.layers; l++ ){ // image is sampled by three nested loops + id_l = im.n_pix*l; + for ( int i = c_im-tree.Mh; i <= c_im+tree.Mh; i++ ){ + id_r = id_l + i*im.rows; + for ( int j = r_im-tree.Mh; j <= r_im+tree.Mh; j++ ){ + id_i = id_r + j; + pix_val = *(im.im_data + id_i); + *(patch.patch_data + id_p) = pix_val; + sum += pix_val; + id_p++; + } + } + } + + mu = sum*tree.inv_ndim; + + norm_patch(patch.patch_data, mu, tree.n_dim); +} + + +// function for randomly permuted set of indices +// n is the numbers to choose from, n_set is the number of random indices +// that is returned. Returns a vector of indices +vector<int> randperm( int n, int n_set ) { + if ( n_set > n ){ // check if n_set exceeds n + n_set = n; + } + + vector<int> rid; + rid.reserve(n); // vector of indices + for ( int i = 0; i < n; i++ ){ // set all indices in order + rid.push_back(i); + } + + int t, id; // place holders for id and temporary number + int r_max = RAND_MAX; // maximum random number + for ( int i = 0; i < n_set; i++ ){ + // choose a random number between i and n-1 and swap place between i and id + if ( LONG_MAX > RAND_MAX && n-i-1>RAND_MAX ){ // not enough with a random integer up til RAND_MAX + id = ((rand()*(r_max+1)+rand()) % (n-i)) + i; + } + else{ + id = (rand() % (n-i)) + i; + } + t = rid[id]; + rid[id] = rid[i]; + rid[i] = t; + } + rid.resize(n_set); // set size to n_set + return rid; +} + + +// copy values from a patch array into the tree array at node +void set_values(tree_st& tree, im_patch& patch, int node){ + int idx = tree.n_dim*node; + for ( int i = 0; i < tree.n_dim; i++ ){ + *(tree.tree_data + idx) = *(patch.patch_data + i); + idx++; + } +} + +// add values to vector of cluster center points +void add_values(vector<double>& center_sum, im_patch& patch, int id, int n_dim){ + int idx = n_dim*id; + for ( int i = 0; i < n_dim; i++ ){ + center_sum[idx] += *(patch.patch_data + i); + idx++; + } +} + +// estimates the normalized cross correlation between an image patch and a tree node +double get_corr(tree_st& tree, im_patch& patch, int node) +{ + double corr = 0, tmp_t, tmp_p; + int id = tree.n_dim*node; + + for ( int i = 0; i < tree.n_dim; i++ ){ + tmp_t = *(tree.tree_data + id); + tmp_p = *(patch.patch_data + i); + corr += tmp_t*tmp_p; + id++; + } + + return corr; +} + +// k-means-function taking a reference to the vector of image patches and a +// tree struct as input f and t gives the image patches that should be clustered. +// node is the first node in the tree included in the clustering +void k_means( vector<im_patch>& patches, tree_st& tree, int f, int t, int node ) +{ + // vectors holding the sum of values in the cluster and a vector containing the change + vector<double> centSum(tree.branch_fac*tree.n_dim), centChange(tree.branch_fac); + vector<int> centCount(tree.branch_fac); // vector for counting the number of points in a cluster + double max_corr, corr, tmp, mu, sum;//, diff; // variables for clustering + // variables for cluster id and index of previous cluseter, which will be overwritten by new cluster id + int id = 0, id_in = patches[f].idx; + + if ( t-f > tree.branch_fac ){ // check if there are enough point to carry out the clustering + // initialize the center positions + for ( int i = 0; i < tree.branch_fac; i++ ){ + set_values(tree, patches[f+i], node+i); + } + + // run clutering for 10 iterations - only little change happens after 10 iterations + for ( int n_run = 0; n_run < 10; n_run++){ + + for ( int i = f; i < t; i++ ){ // go through the patches from f to t + max_corr = get_corr(tree, patches[i], node); // initially set min distance and id to the first + id = 0; + for ( int j = 1; j < tree.branch_fac; j++ ){ // run throgh the other points + corr = get_corr(tree, patches[i], node + j); // get the cross correlation + if ( corr > max_corr ){ // if the this cluster is closer set this as max correlation + max_corr = corr; + id = j; + } + } + add_values(centSum, patches[i], id, tree.n_dim); // add the patch to the closest cluster + centCount[id]++; // count the extra patch + // update the idx to the child idx - note that all layers start with idx = 0 + patches[i].idx = (id + id_in*tree.branch_fac); + } + + // update the clusters in the tree and calculate the center change (not used for anything) + id = node*tree.n_dim; + int id_c = 0; + + + for ( int i = 0; i < tree.branch_fac; i++ ){ // go through all new clusters + sum = 0; + if ( centCount[i] == 0 ){ + centCount[i] = 1; + } + for ( int j = 0; j < tree.n_dim; j++ ){ // go through cluster pixels + tmp = centSum[id_c]/centCount[i]; + //diff = (tmp - *(tree.tree_data + id)); // for calculating center change + //centChange[i] += diff*diff; + *(tree.tree_data + id) = tmp; + sum += tmp; + id_c++; + id++; + } + mu = sum*tree.inv_ndim; + norm_patch(tree.tree_data+id-tree.n_dim, mu, tree.n_dim); + } + + // set counter and sum to zero + fill(centSum.begin(), centSum.end(), 0); + fill(centCount.begin(), centCount.end(),0); + fill(centChange.begin(), centChange.end(), 0); + } + } +} + +// runs through the patches vector to find the last element with id +int get_to( vector<im_patch>& patches, int id ) +{ + int to = 0; + for ( int i = 0; i < patches.size(); i++ ){ + if ( patches[i].idx == id ){ + to = i; + } + } + return to+1; +} + +// Main function for building the km-tree. Takes the image and tree struct +// and the number of training patches as argument +void build_km_tree ( im_st& im, tree_st& tree, int n_train ) { + // allocate memory for the image patches + double* im_patch_data = new double[n_train*tree.M*tree.M*im.layers]; + + int rows_c = im.rows-tree.M+1, cols_c = im.cols-tree.M+1; // number of rows and cols within sampling area + int n_im_patches = rows_c*cols_c; // number of pixels in the image for sampling - inside boundary + + // checks that the number of training patches is not exceeding the number of pixels in the sample area + if ( n_im_patches < n_train ){ + n_train = n_im_patches; + } + + vector<int> r_id = randperm(n_im_patches, n_train); // indices of random patches + + vector<im_patch> patches; // vector of image patches + patches.resize(n_train); // allocate memory + + int r, c, idx = 0; // variables used for sampling the image + // sample image patches + for (int i = 0; i < n_train; i++ ) + { + c = r_id[i]/rows_c; // column is floored because of int + r = r_id[i]-c*rows_c; // row is rest after column + patches[i].idx = 0; // inital id is 0 + patches[i].patch_data = im_patch_data + idx; // pointer to patch memory + sample_patch(im, tree, patches[i], r + tree.Mh, c + tree.Mh); // sampel in image with added boundary + idx += tree.n_dim; // step number of patch pixels forward + } + + // k-means tree + int n_layer = (int)ceil(log((double)tree.n_nodes)/log((double)tree.branch_fac)); // number of layers in the tree + int n_in_layer; // number of nodes in layer + int t, f; // keeps track of patches that belong to a certain cluster + int node = 0; // node number that will be updated + + // go through the layers in the tree + for (int i = 0; i < n_layer; i++ ) + { + t = 0; // start at 0 + n_in_layer = pow((double)tree.branch_fac,i); // number of nodes in current layer of the tree + sort(patches.begin(), patches.end()); // sort the patches according to their current id + for ( int j = 0; j < n_in_layer; j++ ) // go through the nodes in the layer and cluster that node + { + f = t; // patch j from + t = get_to(patches,j); // patch j to + // check that the node does not exceed the size of the tree + if ( node + tree.branch_fac <= tree.n_nodes ){ + k_means( patches, tree, f, t, node ); + } + else { + break; + } + node += tree.branch_fac; // next node + } + } + + delete[] im_patch_data; // free up patch memory +} + + +// The gateway routine +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + // input image (I), patch size (M*M), number of nodes in the tree (n), branching + // factor (b), and number of training patches (n_train). Outputs the km-tree (tree) + double *I, *tree; // pointers to image and tree + int b, M, L, n, ndim, n_train; // variables + const int *dim; // image dimensinos + int dtree[2]; // tree dimensions + + /* Check for proper number of arguments. */ + /* NOTE: You do not need an else statement when using + mexErrMsgTxt within an if statement. It will never + get to the else statement if mexErrMsgTxt is executed. + (mexErrMsgTxt breaks you out of the MEX-file.) + */ + if(nrhs != 5) + mexErrMsgTxt("Five inputs required."); + if(nlhs != 1) + mexErrMsgTxt("One output required."); + + // Create a pointer to the input matrix. + I = mxGetPr(prhs[0]); + + // input passing + double *Md, *bd, *Ld, *n_train_d; + Md = mxGetPr(prhs[1]); + M = (int)Md[0]; + + bd = mxGetPr(prhs[2]); + b = (int)bd[0]; + + // check if number of clusters is smaller than branching factor + if ( n < b ){ + n = b; + } + + n_train_d = mxGetPr(prhs[3]); + n_train = (int)n_train_d[0]; + + // determine number of tree nodes + Ld = mxGetPr(prhs[4]); + L = (int)Ld[0]; // layers in tree + n = 0; + int n_tmp = 0; + int max_n = (double)n_train/5.0; + for ( int i = 0; i < L; i++ ){ + n_tmp += pow((double)b,(i+1)); + if ( n_tmp > max_n ){ + L = i+1; + break; + } + n = n_tmp; + } + printf("Number of nodes in resulting tree: %d in %d layers.\n", n, L); + + // check input properties + if ( 1 - (M % 2) || M < 1) + mexErrMsgTxt("M must be odd and positive."); + + if ( n < 1 ) + mexErrMsgTxt("n must be positive."); + + if ( b < 1 ) + mexErrMsgTxt("b must be positive."); + + // Get the dimensions of the matrix input. + ndim = mxGetNumberOfDimensions(prhs[0]); + if (ndim != 2 && ndim != 3) + mexErrMsgTxt("search_km_tree only works for 2-dimensional or 3-dimensional images."); + + // image dimensions + dim = mxGetDimensions(prhs[0]); + + + // image struct + im_st Im; + Im.im_data = I; + Im.rows = dim[0]; + Im.cols = dim[1]; + if ( ndim == 3 ) + { + Im.layers = dim[2]; + } + else + { + Im.layers = 1; + } + Im.n_pix = Im.rows*Im.cols; + + dtree[0] = Im.layers*M*M; + dtree[1] = n; + + // Set the output pointer to the output matrix. Array initialized to zero. + plhs[0] = mxCreateNumericArray(2, dtree, mxDOUBLE_CLASS, mxREAL); + + // Create a C pointer to a copy of the output matrix. + tree = mxGetPr(plhs[0]); + for (int i = 0; i < dtree[0]*dtree[1]; i++ ) + *(tree + i) = -1; + + // tree struct + tree_st Tree; + Tree.tree_data = tree; + Tree.n_dim = dtree[0]; + Tree.inv_ndim = 1/(double)Tree.n_dim; + Tree.n_nodes = dtree[1]; + Tree.branch_fac = b; + Tree.M = M; + Tree.Mh = (int)(0.5*((double)M-1.0)); + // build the km-tree using C++ subroutine + build_km_tree ( Im, Tree, n_train ); + +} diff --git a/code/texture_gui/code/functions/build_km_tree_xcorr.mexa64 b/code/texture_gui/code/functions/build_km_tree_xcorr.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..4f23c01adc36f93d1ea6e2b3a2df0915bac3247f GIT binary patch literal 19157 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 K{w<LFdi)=mfd0n- literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_km_tree_xcorr.mexmaci64 b/code/texture_gui/code/functions/build_km_tree_xcorr.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..592f56e947462978fc07a59c067768e67277fcc6 GIT binary patch literal 23344 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 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/build_km_tree_xcorr.mexw64 b/code/texture_gui/code/functions/build_km_tree_xcorr.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..a0b030b73702276a175b4c59a684ad0e0d2e5ff6 GIT binary patch literal 27136 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 D6!K%N literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/compile_mex_functions.m b/code/texture_gui/code/functions/compile_mex_functions.m new file mode 100755 index 0000000..bab817b --- /dev/null +++ b/code/texture_gui/code/functions/compile_mex_functions.m @@ -0,0 +1,8 @@ +% compile mex files +mex -largeArrayDims biadjacency_matrix.cpp +mex build_km_tree.cpp % based on Euclidean distance +mex search_km_tree.cpp % based on Euclidean distance +mex build_km_tree_xcorr.cpp % based on normalized cross correlation +mex search_km_tree_xcorr.cpp % based on normalized cross correlation +mex probability_search_km_tree.cpp % based on normalized cross correlation + diff --git a/code/texture_gui/code/functions/compute_mappings.m b/code/texture_gui/code/functions/compute_mappings.m new file mode 100755 index 0000000..9abe4f7 --- /dev/null +++ b/code/texture_gui/code/functions/compute_mappings.m @@ -0,0 +1,21 @@ +function [mappings,A] = compute_mappings(image,dictionary) + +switch dictionary.options.method + case 'euclidean' + A = search_km_tree(image,... + dictionary.tree,... + dictionary.options.branching_factor,... + dictionary.options.normalization); + case 'nxcorr' + A = search_km_tree_xcorr(image,... + dictionary.tree,... + dictionary.options.branching_factor); + otherwise + error('Unknown dictionary method.') +end +B = biadjacency_matrix(A,dictionary.options.patch_size); + +[rc,nm] = size(B); +mappings.T1 = sparse(1:nm,1:nm,1./(sum(B,1)+eps),nm,nm)*B'; +mappings.T2 = sparse(1:rc,1:rc,1./(sum(B,2)+eps),rc,rc)*B; + diff --git a/code/texture_gui/code/functions/image_texture_gui.m b/code/texture_gui/code/functions/image_texture_gui.m new file mode 100755 index 0000000..a314577 --- /dev/null +++ b/code/texture_gui/code/functions/image_texture_gui.m @@ -0,0 +1,805 @@ +function image_texture_gui(image,dict,nr_labels,labeling) +%IMAGE_TEXTURE_GUI Interactive texture segmentation of the image +% IMAGE_TEXTURE_GUI(IMAGE,DICTOP,NR_LABELS,LABELING) +% Called without imput arguments starts in a demo mode. Input: +% IMAGE, may be rgb or grayscale, 0-to-1 or uint8 +% DICT, may be either: +% - dictopt struct containing dictinary parameters, +% - dictionary struct containing the dictionary, +% - mappings struct containing the transformation matrices, +% - not given or empty, so default options are loaded +% NR_LABELS, number of labels, defaults to 2. +% LABELING, optional initial labeling, given as either +% - binary matrix of the size nr_pixels-by-nr_labels, +% - labeling image of the same dimensions as IMAGE +% +% Keyboard controls (TODO update this for newest version): +% L, shift+L and numerical values change label +% T, shift+T, uparrow and downarros change pen thickness +% W and shift+W change show option +% M and shift+M change method +% O and shift+O change overwrite option +% R and shift+R change regularize option +% U and shift+U change live update option +% C and shift+C change colormap +% A and shift+A change opacity (alpha) +% S saves a project +% E export +% F freeze +% +% Author: vand@dtu.dk, 2015 +% char(100+[18 -3 10 0 -36 0 16 17 -54 0 7]) +% +% TODO: +% - when LABELING given, update segmentation upon startup - DONE, TESTING +% - when zooming, make sure mouse overlay circle not displayed (frozen) +% - option for changing opacity - DONE, TESTING + +%%%%%%%%%% DEFAULTS %%%%%%%%%% +if nargin<1 % default example image + image = imread('bag.png'); + dict.method = 'euclidean'; + dict.patch_size = 15; + dict.branching_factor = 2; + dict.number_layers = 5; + dict.number_training_patches = 1000; + dict.normalization = false; + nr_labels = 2; +else + if nargin<2 || isempty(dict)% default dictionary + dict.method = 'euclidean'; + dict.patch_size = 3; + dict.branching_factor = 2; + dict.number_layers = 5; + dict.number_training_patches = 1000; + dict.normalization = false; + end + if nargin<3 || isempty(nr_labels) % defalult number of labels + nr_labels = 2; + else + nr_labels = double(nr_labels); % for colormaps to work properly + end +end + +[r,c,~] = size(image); +if nargin<4 || isempty(labeling) % default, unlabeled initial labeling + LABELING = zeros(r*c,nr_labels); +else + parse_labeling_input(labeling) +end + +%%%%%%%%%% PROBABILITY COMPUTING METHODS %%%%%%%%%% +% to add a new method, add a function to the method_options list +method_options = {... + @(labeling)distributed(labeling),... + @(labeling)two_max(labeling),... + @(labeling)two_max_over(labeling),... + @(labeling)two_cont(labeling),... + @(labeling)two_cont_over(labeling),... + }; + +%%%%%%%%%% SETTINGS %%%%%%%%%% + +% method options +METHOD_INDEX = 1; % initial method is the first on the list +METHOD = method_options{METHOD_INDEX}; +METHOD_NAME = method_name_str; + +% brush label options +LABEL = 1; % initial label is 1 + +% brush thickness options +thickness_options = [1 2 3 4 5 10 20 30 40 50 100 -1]; % last option (value -1) is 'fill' +thickness_options_string = num2cell(thickness_options); +thickness_options_string{end} = 'fill'; +THICKNESS_INDEX = 5; % initial pencil thickness is the fifth option +RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); % pencil radius + +% results visualization options +show_options(1:2) = {'segmentation','overlay'}; +show_options((1:nr_labels)+2) = num2cell([repmat('probability ',[nr_labels,1]),num2str((1:nr_labels)')],2); +SHOW_INDEX = 1; + +% overwrite option +overwrite_options = {'no','yes'}; +OVERWRITE = false; % initialy no overwrite + +% regularization (smoothness) options +regularize_options = [0 1 2 3 4 5 10 15 20 25 30 50]; +REGULARIZE_INDEX = 1; % initialy no regularization + +% visualization colormap options (for labelings and results) +colormap_options = {@(x)[0.5,0.5,0.5;cool(x)], @(x)[0.5,0.5,0.5;spring(x)],... + @(x)[0.5,0.5,0.5;parula(x)]}; % gray color for unlabeled +COLORMAP_INDEX = 1; % current colormap +COLORS = colormap_options{COLORMAP_INDEX}(nr_labels); % visualization colors for label overlay + +% visualization opacity options (for labelings and results) +color_weight_options = 0.1:0.1:0.9; +COLOR_WEIGHT_INDEX = 2; +COLOR_WEIGHT = color_weight_options(COLOR_WEIGHT_INDEX); + +% live update option +live_update_options = {'off','on'}; +LIVE_UPDATE = true; % initially on + +% other settings +nr_circle_pts = 16; % number of points defining a circular pencil + +%%%%%%%%%% INITIALIZATION AND LAYOUT %%%%%%%%%% + +[image,image_rgb,image_gray] = normalize_image(image); % impose 0-to-1 rgb +LABELING_OVERLAY = image_rgb; % image overlaid labeling +SEGMENTATION_OVERLAY = 0.5+zeros(r,c,3); % segmentation, optionally overlay + +fmar = [0.2 0.2]; % discance from screen edge to figure (x and y) +amar = [0.02 0.02]; % margin around axes, relative to figure +my = 0.85:0.04:0.95; % menu items y position +mw = 0.15; % menu items width +mx = 0.05:0.15:0.8; +mh = 0.03; % menu items height +cw = (0.25-0.15)/(nr_labels+1); % colorcube width +cx = 0.15:cw:0.25; % colorcubes x position +pointer_char = 'X'; + +fig = figure('Units','Normalized','Position',[fmar,1-2*fmar],... + 'Pointer','watch','KeyPressFcn',@key_press,'InvertHardCopy', 'off',... + 'Name','Texture segmentation GUI'); + +labeling_axes = axes('Units','Normalized','Position',[0,0,0.5,0.85]+[amar,-2*amar]); +imagesc(LABELING_OVERLAY), axis image off, hold on +segmentation_axes = axes('Units','Normalized','Position',[0.5,0,0.5,0.85]+[amar,-2*amar]); +imagesc(SEGMENTATION_OVERLAY,[0,nr_labels]), axis image off, hold on + +clean_toolbar % also defines linkaxes tool + +uicontrol('String','Label [L] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(1),my(3),mw,mh]); +labels_text = uicontrol('String',num2str(LABEL),... + 'BackgroundColor',COLORS(LABEL+1,:),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(2),my(3),0.03,mh]); +label_pointer = cell(nr_labels+1,1); +for k = 1:nr_labels+1 + label_pointer{k} = uicontrol('String',' ','Style','text',... + 'HorizontalAlignment','center','BackgroundColor',COLORS(k,:),... + 'Units','Normalized','Position',[cx(k),my(2),cw,mh],... + 'Enable','Inactive','ButtonDownFcn',@label_click,'UserData',k-1); +end +set(label_pointer{LABEL+1},'String',pointer_char); + +uicontrol('String','Thickness [T] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(1),my(1),mw,mh]); +thickness_text = uicontrol('String',thickness_options_string(THICKNESS_INDEX),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(2),my(1),mw,mh]); + +uicontrol('String','Show [W] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(5),my(2),mw,mh]); +show_text = uicontrol('String',show_options{SHOW_INDEX},... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(6),my(2),mw,mh]); + +uicontrol('String','Live update [U] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(5),my(3),mw,mh]); +update_text = uicontrol('String',live_update_options(LIVE_UPDATE+1),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(6),my(3),mw,mh]); + +uicontrol('String','Method [M] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(3),my(3),mw,mh]); +method_text = uicontrol('String',METHOD_NAME,... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(4),my(3),mw,mh]); + +uicontrol('String','Overwrite [O] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(3),my(2),mw,mh]); +overlay_text = uicontrol('String',overwrite_options(OVERWRITE+1),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(4),my(2),mw,mh]); + +uicontrol('String','Regularize [R] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[mx(3),my(1),mw,mh]); +regularize_text = uicontrol('String',regularize_options(REGULARIZE_INDEX),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[mx(4),my(1),mw,mh]); + +drawnow % pointer shows busy system + +LIMITS = [1,c-0.5,1,r+0.5]; % to capture zoom +zoom_handle = zoom(fig); +pan_handle = pan(fig); +set(zoom_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); +set(pan_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); + +%%%%%%%%%% TEXTURE REPRESENTATION %%%%%%%%%% +% initiolization: building a texture representation of the image +T1 = 0; +T2 = 0; +parsing_dictionary_input; % T1 and T2 are assigned here + +if nargin>3 + PROBABILITY = METHOD(LABELING); +else + PROBABILITY = 1/nr_labels*ones(size(LABELING)); + %labeling_overwrite % relevant only if we allow loading all settings + %regularize % relevant only if we allow loading all settings +end +compute_overlays + +% ready to draw +set(fig,'Pointer','arrow','WindowButtonDownFcn',@start_draw,... + 'WindowButtonMotionFcn',@pointer_motion); +XO = []; % current drawing point + +show_overlays(get_pixel); +uiwait % waits with assigning output until a figure is closed + +%%%%%%%%%% CALLBACK FUNCTIONS %%%%%%%%%% + function key_press(~,object) + % keyboard commands + key = object.Key; + numkey = str2double(key); + if ~isempty(numkey) && numkey<=nr_labels; + change_label(numkey) + else + switch key + case 'l' + change_label(move_once(LABEL+1,nr_labels+1,... + any(strcmp(object.Modifier,'shift')))-1) + case 'uparrow' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),false); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 'downarrow' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),true); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 't' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),any(strcmp(object.Modifier,'shift'))); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 'w' + SHOW_INDEX = move_once(SHOW_INDEX,numel(show_options),... + any(strcmp(object.Modifier,'shift'))); + set(show_text,'String',show_options{SHOW_INDEX}) + compute_overlays + show_overlays(get_pixel); + case 'c' + COLORMAP_INDEX = move_once(COLORMAP_INDEX,... + length(colormap_options),... + any(strcmp(object.Modifier,'shift'))); + COLORS = colormap_options{COLORMAP_INDEX}(nr_labels); + set(labels_text,'BackgroundColor',COLORS(LABEL+1,:)); + for kk = 1:nr_labels+1 + set(label_pointer{kk},'BackgroundColor',COLORS(kk,:)) + end + compute_overlays + show_overlays(get_pixel); + case 'a' + COLOR_WEIGHT_INDEX = move_once(COLOR_WEIGHT_INDEX,... + length(color_weight_options),... + any(strcmp(object.Modifier,'shift'))); + COLOR_WEIGHT = color_weight_options(COLOR_WEIGHT_INDEX); + compute_overlays + show_overlays(get_pixel); + case 'm' + METHOD_INDEX = move_once(METHOD_INDEX,... + length(method_options),... + any(strcmp(object.Modifier,'shift'))); + METHOD = method_options{METHOD_INDEX}; + METHOD_NAME = method_name_str; + set(method_text,'String',METHOD_NAME) + PROBABILITY = METHOD(LABELING); + labeling_overwrite + regularize + compute_overlays + show_overlays(get_pixel); + case 'o' + OVERWRITE = ~OVERWRITE; + set(overlay_text,'String',overwrite_options{OVERWRITE+1}) + PROBABILITY = METHOD(LABELING); + labeling_overwrite + regularize + compute_overlays + show_overlays(get_pixel); + case 'u' + LIVE_UPDATE = ~LIVE_UPDATE; + set(update_text,'String',live_update_options{LIVE_UPDATE+1}) + if LIVE_UPDATE + PROBABILITY = METHOD(LABELING); + labeling_overwrite + regularize + compute_overlays + show_overlays(get_pixel); + end + case 'r' + REGULARIZE_INDEX = move_once(REGULARIZE_INDEX,... + numel(regularize_options),... + any(strcmp(object.Modifier,'shift'))); + set(regularize_text,'String',... + regularize_options(REGULARIZE_INDEX)) + PROBABILITY = METHOD(LABELING); + labeling_overwrite + regularize + compute_overlays + show_overlays(get_pixel); + case 's' + save_displayed + case 'e' + export_LS + case 'f' + freeze_LS + case 'q' + close(fig) + end + end + end + + function label_click(source,~) + change_label(source.UserData) + end + + function change_label(new_label) + set(label_pointer{LABEL+1},'String',' '); + LABEL = new_label; + set(label_pointer{LABEL+1},'String',pointer_char); + set(labels_text,'String',num2str(LABEL),... + 'BackgroundColor',COLORS(LABEL+1,:)); + end + + function adjust_limits(~,~) + % response to zooming and panning + LIMITS([1,2]) = get(labeling_axes,'XLim'); + LIMITS([3,4]) = get(labeling_axes,'YLim'); + end + + function force_keep_key_press(~,~) + % a hack to maintain my key_press while in zoom and pan mode + % http://undocumentedmatlab.com/blog/enabling-user-callbacks-during-zoom-pan + hManager = uigetmodemanager(fig); + [hManager.WindowListenerHandles.Enabled] = deal(false); + set(fig, 'KeyPressFcn', @key_press); + end + + function start_draw(~,~) + % click in the image + x = get_pixel; + if is_inside(x) + if RADIUS>0 % thickness>0 + XO = x; + M = disc(XO,RADIUS,nr_circle_pts,[r,c]); + set(fig,'WindowButtonMotionFcn',@drag_and_draw,... + 'WindowButtonUpFcn',@end_draw) + else % fill + M = fill(x); + end + update(M); + end + end + + function drag_and_draw(~,~) + % drag after clicking in the image + x = get_pixel; + M = stadium(XO,x,RADIUS,nr_circle_pts,[r,c]); + update(M); + XO = x; + end + + function end_draw(~,~) + % release click after clicking in the image + M = stadium(XO,get_pixel,RADIUS,nr_circle_pts,[r,c]); + update(M); + XO = []; + set(fig,'WindowButtonMotionFcn',@pointer_motion,... + 'WindowButtonUpFcn','') + end + + function pointer_motion(~,~) + % move around without clicking + if strcmp(zoom_handle.Enable,'off') && ... + strcmp(pan_handle.Enable,'off') % not zooming or panning + x = get_pixel; + if is_inside(x) + set(fig,'Pointer','crosshair') + else + set(fig,'Pointer','arrow') + end + show_overlays(x); + end + end + +%%%%%%%%%% HELPING FUNCTIONS %%%%%%%%%% + + function [L,S] = membership2indexed + % computes labeling and segmentation as indexed r-by-c images + % from membership r*c-by-nr_labels representation + [maxlab,L] = max(LABELING,[],2); + L(maxlab==0) = 0; + L = uint8(reshape(L,[r,c])); + [maxprob,S] = max(PROBABILITY,[],2); + S(sum(PROBABILITY==maxprob(:,ones(nr_labels,1)),2)>1) = 0; + S = uint8(reshape(S,[r,c])); + end + + function save_displayed + % saves mat file with user settings and images as separate files + [file,path] = uiputfile('settings.mat','Save settings as'); + if ~isequal(file,0) && ~isequal(path,0) + matfile = fullfile(path,file); + roothname = matfile(1:find(matfile=='.',1,'last')-1); + current_settings.method = METHOD_NAME; + current_settings.show = show_options{SHOW_INDEX}; + current_settings.overwrite = OVERWRITE; + current_settings.regularize = regularize_options(REGULARIZE_INDEX); + save(matfile,'current_settings') + % saving displayed images + imwrite(LABELING_OVERLAY,[roothname,'_labels_displayed.png']) + imwrite(SEGMENTATION_OVERLAY,[roothname,'_results_displayed.png']) + [L,S] = membership2indexed; + imwrite(L,COLORS,[roothname,'_labels_indexed.png']) + imwrite(S,COLORS,[roothname,'_segmentation_indexed.png']) + end + end + + function export_LS + % saves variables to workspace + % TODO, consider using: + % export2wsdlg({'Labeling','Segmentation'},{'gui_L','gui_S'},{L,S}) + button = questdlg({'Exporting variables to the base workspace',... + 'will close texture segmentation gui and',... + 'might overwrite existing variables'},... + 'Exporting variables','OK','Cancel','OK'); + if strcmp(button,'OK') + [L,S] = membership2indexed; + [~,dictprob] = METHOD(LABELING); + assignin('base','gui_L',L) + assignin('base','gui_S',S) + assignin('base','gui_dictprob',dictprob) + % assignin('base','gui_dictprob',T1*LABELING) + end + close(fig) + end + + function freeze_LS + % feezes, closes and opens segmentation_correction_gui + button = questdlg({'Freezing segmentation will close texture segmentation gui',... + 'and open segmentation correction gui.'},... + 'Freezing segmentation','OK','Cancel','OK'); + if strcmp(button,'OK') + [L,S] = membership2indexed; + close(fig) + segmentation_correction_gui(image_rgb,S,nr_labels,L); + end + end + + function a = is_inside(x) + % check if x is inside image limits + a = inpolygon(x(1),x(2),LIMITS([1,2,2,1]),LIMITS([3,3,4,4])); + end + + function p = get_pixel + % get cursor position + p = get(labeling_axes,'CurrentPoint'); + p = round(p(1,[1,2])); + end + + function show_overlays(x) + % overlay a circular region where the pointer is and show + shown_left = LABELING_OVERLAY; + shown_right = SEGMENTATION_OVERLAY; + if RADIUS>0 % thickness>0 + P = repmat(disc(x,RADIUS,nr_circle_pts,[r,c]),[1,1,3]); + shown_left(P(:)) = 0.5+0.5*LABELING_OVERLAY(P(:)); + shown_right(P(:)) = 0.5+0.5*SEGMENTATION_OVERLAY(P(:)); + end + % we have to imagesc(shown) to remove overlay if needed + axes(labeling_axes), cla, imagesc(shown_left) + axes(segmentation_axes), cla, imagesc(shown_right) + end + + function update(M) + % change the state of the segmentation by updating LABELING with a + % mask M, and updating PROBABILITY + labcol = zeros(1,nr_labels); + if LABEL>0 % not unlabeling + labcol(LABEL) = 1; + end + LABELING(M(:),:) = repmat(labcol,[sum(M(:)),1]); + if LIVE_UPDATE + PROBABILITY = METHOD(LABELING); % PROBABILITY computed + labeling_overwrite + regularize + compute_overlays(true) % compute both overlays + else + compute_overlays(false) % comput only left overlay + end + % computing overlay images + show_overlays(get_pixel); % showing overlay and pointer + end + + function compute_overlays(compute_both) + if nargin<1 + compute_both = true; % default computes overlay for both images + end + % computes overlays but not pointer overalay + % TODO: a lot of this does not need to be recalculated + LABELING_OVERLAY = reshape(LABELING*COLORS(2:end,:),size(image_rgb)).*... + (COLOR_WEIGHT+(1-COLOR_WEIGHT)*image_gray); + unlabeled = repmat(~any(LABELING,2),[3,1]); % pixels not labeled + LABELING_OVERLAY(unlabeled) = image_rgb(unlabeled); + if compute_both + if SHOW_INDEX<3 % showing segmentation or overlay + maxprob = max(PROBABILITY,[],2); + maxprobloc = PROBABILITY == maxprob(:,ones(nr_labels,1)); + uncertain = sum(maxprobloc,2)>1; % pixels with max probability at two or more classes + maxprobloc(uncertain,:) = 0; + if SHOW_INDEX==1 % segmentation + SEGMENTATION_OVERLAY = reshape([uncertain,maxprobloc]*COLORS,size(image_rgb)); + else % SHOW_INDEX==2 overlay + SEGMENTATION_OVERLAY = reshape([uncertain,maxprobloc]*COLORS,... + size(image_rgb)).*(COLOR_WEIGHT+(1-COLOR_WEIGHT)*image_gray); + end + else % 'probability x' + pw = SHOW_INDEX-2; % probability to show + minpw = min(PROBABILITY(:,pw)); + maxpw = max(PROBABILITY(:,pw)); + % TODO scaling should be better, relative to 1/nr_labels + SEGMENTATION_OVERLAY = reshape((PROBABILITY(:,pw)-minpw)/(maxpw-minpw)*[1,1,1],size(image_rgb)); + end + end + end + +% TODO disc shold be saved as a list of index shifts with respect to +% the central pixel, and change only when thickness changes + function M = disc(x,r,N,dim) + % disc shaped mask in the image + angles = (0:2*pi/N:2*pi*(1-1/N)); + X = x(1)+r*cos(angles); + Y = x(2)+r*sin(angles); + M = poly2mask(X,Y,dim(1),dim(2)); + end + + function M = stadium(x1,x2,r,N,dim) + % stadium shaped mask in the image + angles = (0:2*pi/N:pi)-atan2(x1(1)-x2(1),x1(2)-x2(2)); + X = [x1(1)+r*cos(angles), x2(1)+r*cos(angles+pi)]; + Y = [x1(2)+r*sin(angles), x2(2)+r*sin(angles+pi)]; + M = poly2mask(X,Y,dim(1),dim(2)); + end + + function M = fill(x) + [maxL,labL] = max(LABELING,[],2); + label_image = reshape(maxL.*labL,[r,c]); + M = bwselect(label_image==label_image(x(2),x(1)),x(1),x(2),4); + end + + function [I,I_rgb,I_gray] = normalize_image(I) + % initialization: normalize image + if isa(I,'uint8') + I = double(I)/255; + end + if isa(I,'uint16') + I = double(I)/65535; + end + if size(I,3)==3 % rgb image + I_gray = repmat(rgb2gray(I),[1 1 3]); + I_rgb = I; + else % assuming grayscale image + I_gray = repmat(I,[1,1,3]); + I_rgb = I_gray; + end + end + + function name = method_name_str + method_str = func2str(METHOD); + s1 = find(method_str==')',1,'first')+1; + s2 = find(method_str=='(',1,'last')-1; + name = method_str(s1:s2); + end + + function n = move_once(n,total,reverse) + % moves option index once, respecting total number of options + if ~reverse + n = mod(n,total)+1; + else + n = mod(n+total-2,total)+1; + end + end + + function labeling_overwrite + % labeled areas get assigned probability 1 + if OVERWRITE + labeled = any(LABELING,2); + PROBABILITY(labeled,:) = LABELING(labeled,:); % overwritting labeled + end + end + + function regularize + sigma = regularize_options(REGULARIZE_INDEX); + if sigma>0 + filter = fspecial('gaussian',[2*ceil(sigma)+1,1],sigma); + PROBABILITY = reshape(PROBABILITY,[r,c,nr_labels]); + PROBABILITY = imfilter(PROBABILITY,filter,'replicate'); + PROBABILITY = imfilter(PROBABILITY,filter','replicate'); + PROBABILITY = reshape(PROBABILITY,[r*c,nr_labels]); + end + end + + function r = thickness2radius(t) + r = t/2+0.4; + end + + function clean_toolbar + set(fig,'MenuBar','none','Toolbar','figure'); + toolbar = findall(fig,'Type','uitoolbar'); + all_tools = allchild(toolbar); + % removing tools + for i=1:numel(all_tools) + if isempty(strfind(all_tools(i).Tag,'Pan'))&&... + isempty(strfind(all_tools(i).Tag,'Zoom'))&&... + isempty(strfind(all_tools(i).Tag,'SaveFigure'))&&... + isempty(strfind(all_tools(i).Tag,'PrintFigure'))&&... + isempty(strfind(all_tools(i).Tag,'DataCursor')) + delete(all_tools(i)) % keeping only Pan, Zoom, Save and Print + end + end + % adding a tool + [icon,~,alpha] = imread('linkaxesicon.png'); + icon = double(icon)/255; + icon(alpha==0)=NaN; + uitoggletool(toolbar,'CData',icon,... + 'TooltipString','Link Axes','Tag','LinkAxes',... + 'OnCallback',{@link_axes,'xy'},... + 'OffCallback',{@link_axes,'off'}); + % changing the order of tools + all_tools = allchild(toolbar); + set(toolbar,'children',all_tools([2,1,3:end])); + end + + function link_axes(~,~,flag) + linkaxes([labeling_axes,segmentation_axes],flag) + end + + function parsing_dictionary_input + % either dictionary_options, dictionary or mappings should be given + if isfield(dict,'patch_size') % dictionary options given + dictionary = build_dictionary(image,dict); + mappings = compute_mappings(image,dictionary); + T1 = mappings.T1; + T2 = mappings.T2; + elseif isfield(dict,'tree')% dictinary given + mappings = compute_mappings(image,dict); + T1 = mappings.T1; + T2 = mappings.T2; + elseif isfield(dict,'T1')% mapping given + T1 = dict.T1; + T2 = dict.T2; + else + error('Could not parse dictionary input.') + end + end + + function parse_labeling_input(labeling) + % parsing labeling input, either labeling or labeling image + dim_l = size(labeling); + labeling = double(labeling); + if numel(dim_l)==3 %% rgb labeling image, to be turned to 2D image + max_l = max(labeling(:))+1; + labeling = labeling(:,:,1)+max_l*labeling(:,:,2)+max_l^2*labeling(:,:,3); + dim_l = size(labeling); + end + if numel(dim_l)==2 % either LABELING or 2D labeling image + if all(dim_l==[r*c,nr_labels]) % LABELING + LABELING = labeling; + elseif all(dim_l==[r,c]) % labeling image + l = unique(labeling(:)); % present labels + if numel(union(l,0:nr_labels))~=nr_labels+1 + labeling_old = labeling; + for li = 1:numel(l) + labeling(labeling_old==l(li)) = li-1; + end + end + % now we have labels from 0 to nr_labels + i = (labeling(:)-1)*r*c + (1:r*c)'; % indices in LABELINGS + i = i(labeling(:)>0); % only labeled parts + LABELING = zeros(r*c,nr_labels); + LABELING(i) = 1; + else + error('Could not parse labeling input.') + end + else + error('Could not parse labeling input.') + end + end + +%%%%%%%%%% LABELINGS TO PROBABILITIES METHODS %%%%%%%%%% + + function [probabilities,dictprob] = distributed(labelings) + % unlabeled pixels have label weights DISTRIBUTED equally + labelings(~any(labelings,2),:) = 1/nr_labels; % distributing + dictprob = T1*labelings; + probabilities = T2*dictprob; % computing probabilities + end + + function [probabilities,dictprob] = two_max(labelings) + % DISTRIBUTED tresholded and repeated + labelings(~any(labelings,2),:) = 1/nr_labels; % distributing + probabilities = T2*(T1*labelings); % probabilities + maxprob = max(probabilities,[],2); % finding max probabilities + labelings_new = probabilities == maxprob(:,ones(nr_labels,1)); % new labeling is where max prob was + uncertain = sum(labelings_new,2)>1; % pixels with max probability at two or more classes + labelings_new(uncertain,:) = 1/nr_labels; % distributing at uncertain + dictprob = T1*labelings_new; + probabilities = T2*dictprob; + end + + function [probabilities,dictprob] = two_max_over(labelings) + % DISTRIBUTED tresholded, overwriten and repeated + known_labels = any(labelings,2); + labelings(~known_labels,:) = 1/nr_labels; % distributing + probabilities = T2*(T1*labelings); % probabilities + maxprob = max(probabilities,[],2); % finding max probabilities + labelings_new = probabilities == maxprob(:,ones(nr_labels,1)); % new labeling is where max prob was + uncertain = sum(labelings_new,2)>1; % pixels with max probability at two or more classes + labelings_new(uncertain,:) = 1/nr_labels; % distributing at uncertain + labelings_new(known_labels,:) = labelings(known_labels,:); % OVERWRITING + dictprob = T1*labelings_new; + probabilities = T2*dictprob; + end + + function [probabilities,dictprob] = two_cont(labelings) + % DISTRIBUTED repeated + labelings(~any(labelings,2),:) = 1/nr_labels; % distributing + labelings_new = T2*(T1*labelings); % probabilities + dictprob = T1*labelings_new; + probabilities = T2*dictprob; + end + + function [probabilities,dictprob] = two_cont_over(labelings) + % DISTRIBUTED overwriten and repeated + known_labels = any(labelings,2); + labelings(~known_labels,:) = 1/nr_labels; % distributing + labelings_new = T2*(T1*labelings); % probabilities + labelings_new(known_labels,:) = labelings(known_labels,:); % OVERWRITING + dictprob = T1*labelings_new; + probabilities = T2*dictprob; + end + +% function probabilities = normalized(labelings) +% probabilities = T2*(T1*labelings); % computing probabilities +% % normalizing probabilities so that they sum to 1 +% probabilities = probabilities./repmat(sum(probabilities,2),[1,size(probabilities,2)]); +% probabilities(isnan(probabilities)) = 0; +% end +% +% function probabilities = normalized_2_max(labelings) +% probabilities = T2*(T1*labelings); % probabilities +% maxprob = max(probabilities,[],2); % finding max probabilities +% labelings_new = probabilities == maxprob(:,ones(nr_labels,1)); % new labeling is where max prob was +% uncertain = sum(labelings_new,2)>1; % pixels with max probability at two or more classes +% labelings_new(uncertain,:) = 0; % zero at uncertain +% probabilities = T2*(T1*labelings_new); +% probabilities = probabilities./repmat(sum(probabilities,2),[1,size(probabilities,2)]); +% probabilities(isnan(probabilities)) = 0; +% end +% +% function probabilities = normalized_2_cont(labelings) +% labelings_new = T2*(T1*labelings); % probabilities +% probabilities = T2*(T1*labelings_new); +% probabilities = probabilities./repmat(sum(probabilities,2),[1,size(probabilities,2)]); +% probabilities(isnan(probabilities)) = 0; +% end + +end diff --git a/code/texture_gui/code/functions/linkaxesicon.png b/code/texture_gui/code/functions/linkaxesicon.png new file mode 100755 index 0000000000000000000000000000000000000000..f1c1759a401c8c3cc0dbbb158860bcb9e18ab4ae GIT binary patch literal 568 zcmV-80>}M{P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00007bV*G`2j2`A z3>^ngkcOrJ00HbtL_t(Ijm4ACOOtUN$6wEWF{u3rnbufsb5pmKtY#DiA*>?FLOVtm z4`P>Yb&BZFq5r{%@X`*uSa|EQ5t%ma>Cr?SUV5-*Wy$kAKIOKW-wvaNphkG;bNayR z&HMA_4IfElvg~GTT_dvC4!#~pNq^%ufQhkt)HVb{2n1laS~zw2ZbP^ew*WXCcFb}O z{d<iSy^6zO-*jmwZu`C19agLxV6|AbjJqA*TKy06{|y=)yjM%STh{sb<r_vt=f+T) ze849_MxHIQ^vUG&>N-Z*!0m+lxBH;9R5zF_s?4h<9q|yC(jN2*<Q0|N)Fd~rpJ(wy zl~^K5U+@nbFTA80@d-&}vWzxGMJn0K=-6|zPoLrUyXd*ti`}^w%HZ{SG1qiP9u4Da za&e*iEKXN5GgG;Gg5?#HV2g)`kDqXBs0WWP!Ig7|(E&{dO#`j06OTnnbq4ra{Xr}i z;=%Aj-N5aUm?^3ZUh79zSLu*S?2F$4v_@Gcx{|adlB@|g`+aVdNriCuAd<*rDI|`F z7qcpn<^zO6Av8@RuPDqFWV+9EQnBnI+T!2D-oBcqH+{5j05BeI7ZdUV1$iDrH)v~b z<@E6=SIef93j{^s%_7rE5#2C|L=JKCSeOCvQ<8T3&c6Wui`nF!Ww&Pl0000<MNUMn GLSTZcSO^UO literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/normalize_image.m b/code/texture_gui/code/functions/normalize_image.m new file mode 100755 index 0000000..8dd729f --- /dev/null +++ b/code/texture_gui/code/functions/normalize_image.m @@ -0,0 +1,9 @@ +function I = normalize_image(I) +% we work with [0 1] images +if isa(I,'uint8') + I = double(I)/255; +end +if isa(I,'uint16') + I = double(I)/65535; +end +end \ No newline at end of file diff --git a/code/texture_gui/code/functions/probability_search_km_tree.cpp b/code/texture_gui/code/functions/probability_search_km_tree.cpp new file mode 100755 index 0000000..489281b --- /dev/null +++ b/code/texture_gui/code/functions/probability_search_km_tree.cpp @@ -0,0 +1,305 @@ +/*================================================================= +* syntax: Q = probability_search_km_tree(I, T, P, b, n); OR Q = search_km_tree(I, T, P, b); +* +* serach_km_tree - build probability image from dictionary and intensity image +* +* Input: - I: X-by-Y image +* - T: MMl-by-K tree matrix where l is the number of layers +* in the image (1 for grayscale and 3 for RGB) + - P: MMc-by-K probability matrix where c is the number of + classes +* - b: branching factor +* - n: normalization (true or false), defaults to false +* +* Output: - Q: X-by-Y-by-c probability image +* +* Author: Anders Dahl, abda@dtu.dk, February 2016. +*=================================================================*/ + +#include "mex.h" +#include <stdio.h> +#include <math.h> +#include "matrix.h" +#include <vector> + +#include <iostream> +using namespace std; + +// struct for the tree +struct tree_st +{ + double *tree_data; + double *probability_data; + int n_dim, n_dprob, n_nodes, branch_fac, M, Mh, n_cls; + vector<int> idx_disp; +}; + +// struct for image +struct im_st +{ + double *im_data; + int rows, cols, layers, n_pix; +}; + +// estimate the distance between a vector in tree given by the node and a +// patch in the image given by the row and column +double get_dist(vector<double>& patch, tree_st& tree, int& node) +{ + double d = 0, tmp; + int id_t = tree.n_dim*node; + + for ( int i = 0; i < tree.n_dim; i++ ){ + tmp = patch[i] - *(tree.tree_data + id_t); + id_t++; + d += tmp*tmp; + } + + return d; +} + + +// Function for sampling patches from the image into the patch arrays +// inputs reference to the image struct, tree struct, patch struct and position of the sampling coordinate. +// There is no check if the sampling is outside the image +// vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im, bool& normalize) +vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im, bool& normalize) +{ + int id_l, id_r, id_i; // iterators for looking up image data + int id_p = 0; // iterator for looking up patch data + double sum_sq = 0, pix_val; // variables for normalization + int n_dim = M*M*im.layers; // number of dimensions computed here, becasue tree is not included + vector<double> patch(n_dim); + int Mh = (M-1)/2; + + for ( int l = 0; l < im.layers; l++ ){ // image is sampled by three nested loops (layers, columns, rows) + id_l = im.n_pix*l; + for ( int i = c_im-Mh; i <= c_im+Mh; i++ ){ + id_r = id_l + i*im.rows; + for ( int j = r_im-Mh; j <= r_im+Mh; j++ ){ + id_i = id_r + j; + pix_val = *(im.im_data + id_i); + patch[id_p] = pix_val; + sum_sq += pix_val*pix_val; // sum of squares for normalization + id_p++; + } + } + } + if ( normalize ){ // if the patch should be normalized to unit length + double inv_sq = 1; + if ( sum_sq > 0 ){ + inv_sq = 1/sqrt(sum_sq); // inverse sum of squares + } + for ( int i = 0; i < n_dim; i++ ){ + patch[i] = patch[i]*inv_sq; + } + } + return patch; +} + + +// The tree search function +int search_tree(im_st& im, tree_st& tree, int& r, int& c, bool& normalize) +{ + int node = 0, node_min = -1, node_min_level, next_node; // variables for searching the tree + double d_min = 10e100, d, d_min_level; + + vector<double> patch = sample_patch(im, tree.M, c, r, normalize); // get the pixel values in a patch + while ( node < tree.n_nodes ){ // go through the tree + if ( *(tree.tree_data + node*tree.n_dim) == -1 ){ // check if node is a leaf-node + return node_min; + } + + d_min_level = 10e100; // set minimum distance to high value + for ( int i = 0; i < tree.branch_fac; i++ ){ // go through nodes at level + next_node = node + i; + d = get_dist(patch, tree, next_node); + + if ( d < d_min_level ){ // set current node to the minimum distance + d_min_level = d; + node_min_level = next_node; + } + } + if ( d_min_level < d_min ){ // set overall minimum distance and minimum node + d_min = d_min_level; + node_min = node_min_level; + } + node = (node_min_level+1)*tree.branch_fac; // go to the child node + } + return node_min; +} + +void add_probability(tree_st& tree, double *Q, int& idx, int& node){ + int id_node = node*tree.n_dprob; + for ( int i = 0; i < tree.idx_disp.size(); i++ ){ + *(Q + idx + tree.idx_disp[i]) += *(tree.probability_data + id_node + i); + } +} + + +void add_count(vector<int>& ct_im, int& col, int& row, int& rows, tree_st& tree){ + for ( int i = col-tree.Mh; i <= col+tree.Mh; i++ ){ + for ( int j = row-tree.Mh; j <= row+tree.Mh; j++ ){ + ct_im[i*rows+j]++; + } + } +} + +// The tree search function applied to the entire image - border is zero and interior is in 1,...,n +void probability_search_image(im_st& im, tree_st& tree, double *Q, bool& normalize) +{ + vector<int> ct_im; + ct_im.resize(im.rows*im.cols); + int node; + int idx = tree.Mh*im.rows; // increase with empty rows at border + for ( int i = tree.Mh; i < im.cols-tree.Mh; i++ ){ + idx += tree.Mh; // first Mh pixels are border + for ( int j = tree.Mh; j < im.rows-tree.Mh; j++ ){ + node = search_tree(im, tree, i, j, normalize); // find assignment + add_probability(tree, Q, idx, node); + idx++; + add_count(ct_im, i, j, im.rows, tree); + } + idx += tree.Mh; // last Mh pixels are border + } + + int l_id; + for ( int j = 0; j < tree.n_cls; j++) { + l_id = j*im.rows*im.cols; + for ( int i = 0; i < ct_im.size(); i++ ){ + *(Q+i+l_id) = *(Q+i+l_id)/(double)ct_im[i]; + } + } +} + + +// The gateway routine +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + // input image (I), tree (tree), probabilities (P) and output probabilities (Q) + double *I, *tree, *P, *Q; + int b, M, ndim, ndtree, ndprob; + const int *dim, *dtree, *dprob; + bool normalize = false; + /* Check for proper number of arguments. */ + /* NOTE: You do not need an else statement when using + mexErrMsgTxt within an if statement. It will never + get to the else statement if mexErrMsgTxt is executed. + (mexErrMsgTxt breaks you out of the MEX-file.) + */ + if(nrhs < 4 || nrhs > 5) + mexErrMsgTxt("Four or five inputs required."); + if(nlhs != 1) + mexErrMsgTxt("One output required."); + + // Create a pointer to the input matrix. + I = mxGetPr(prhs[0]); + tree = mxGetPr(prhs[1]); + P = mxGetPr(prhs[2]); + + double *bd; + bd = mxGetPr(prhs[3]); + b = (int)bd[0]; + + if ( nrhs == 5 ){ + bool *normalize_d; + normalize_d = (bool *)mxGetData(prhs[4]); + normalize = normalize_d[0]; + } + + if ( b < 1 ) + mexErrMsgTxt("b must be positive."); + + // Get the dimensions of the matrix input. + ndim = mxGetNumberOfDimensions(prhs[0]); + if (ndim != 2 && ndim != 3) + mexErrMsgTxt("probability_search_km_tree only works for 2-dimensional or 3-dimensional images."); + + ndtree = mxGetNumberOfDimensions(prhs[1]); + if (ndtree != 2) + mexErrMsgTxt("probability_search_km_tree requires tree to be a matrix (2-dimensional)."); + + ndprob = mxGetNumberOfDimensions(prhs[2]); + if (ndprob != 2) + mexErrMsgTxt("probability_search_km_tree requires probability matrix to be a matrix (2-dimensional)."); + + dim = mxGetDimensions(prhs[0]); + dtree = mxGetDimensions(prhs[1]); + dprob = mxGetDimensions(prhs[2]); + + if ( dtree[1] != dprob[1] ) + mexErrMsgTxt("Tree matrix and probability matrix must have the same number of columns."); + + if ( ndim == 3 ) + { + M = (int)sqrt((double)dtree[0]/(double)dim[2]); + } + else + { + M = (int)sqrt((double)dtree[0]); + } + + if ( 1 - (M % 2) || M < 1) + mexErrMsgTxt("M must be odd and positive."); + + + // image struct + im_st Im; + Im.im_data = I; + Im.rows = dim[0]; + Im.cols = dim[1]; + if ( ndim == 3 ) + { + Im.layers = dim[2]; + } + else + { + Im.layers = 1; + } + Im.n_pix = Im.rows*Im.cols; + + // tree struct + tree_st Tree; + Tree.tree_data = tree; + Tree.probability_data = P; + Tree.n_dim = dtree[0]; + Tree.n_nodes = dtree[1]; + Tree.n_dprob = dprob[0]; + Tree.branch_fac = b; + Tree.M = M; + Tree.Mh = (int)(0.5*(double)(M-1.0)); + // Number of classes + Tree.n_cls = dprob[0]/(M*M); + // Compute relative displacement vector + Tree.idx_disp.reserve(dprob[0]); + int id; + for ( int k = 0; k < Tree.n_cls; k++ ){ + id = -Tree.Mh*Im.rows - Tree.Mh + k*Im.rows*Im.cols; + for ( int i = 0; i < M; i++ ){ + for ( int j = 0; j < M; j++ ){ + Tree.idx_disp.push_back(id); + id++; + } + id+=Im.rows-M; + } + } +// for ( int i = 0; i < Tree.idx_disp.size(); i++ ){ +// cout << Tree.idx_disp[i] << " "; +// } +// cout << endl; + + + if ( M*M*Im.layers != Tree.n_dim ) + mexErrMsgTxt("Dimensions of the tree and the image does not fit."); + + int ndpout[3] = {Im.rows, Im.cols, Tree.n_cls}; + + // Set the output pointer to the output matrix. Array initialized to zero. + plhs[0] = mxCreateNumericArray(3, ndpout, mxDOUBLE_CLASS, mxREAL); + + // Create a C pointer to a copy of the output matrix. + Q = mxGetPr(plhs[0]); + // Search the tree using the C++ subroutine + probability_search_image(Im, Tree, Q, normalize); +} + diff --git a/code/texture_gui/code/functions/probability_search_km_tree.mexa64 b/code/texture_gui/code/functions/probability_search_km_tree.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..80a9ec4cb9d02af3b31b2d1b6f0df54720cd8d27 GIT binary patch literal 18140 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 K{38arE&6ZNnPP+h literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/probability_search_km_tree.mexmaci64 b/code/texture_gui/code/functions/probability_search_km_tree.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..86ce9e57e1823c0b786e2bedbfb5a8fbdd0aada4 GIT binary patch literal 14168 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 Co7S}e literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/probability_search_km_tree.mexw64 b/code/texture_gui/code/functions/probability_search_km_tree.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..14ed3e6c6fea7689c9c02ffc4a86221931b35546 GIT binary patch literal 20992 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 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/process_image.m b/code/texture_gui/code/functions/process_image.m new file mode 100755 index 0000000..6dce460 --- /dev/null +++ b/code/texture_gui/code/functions/process_image.m @@ -0,0 +1,16 @@ +function [S,P] = process_image(im,dictionary) +% segmentation and probabilities from and image and a dictionary + +im = normalize_image(im); +P = probability_search_km_tree(im, ... + dictionary.tree, ... + dictionary.dictprob, ... + dictionary.options.branching_factor, ... + dictionary.options.normalization); + +[maxprob,S] = max(P,[],3); +uncertain = sum(P == maxprob(:,:,ones(size(P,3),1)),3)>1; +S(uncertain) = 0; + +end + diff --git a/code/texture_gui/code/functions/search_km_tree.cpp b/code/texture_gui/code/functions/search_km_tree.cpp new file mode 100755 index 0000000..3b9fcbc --- /dev/null +++ b/code/texture_gui/code/functions/search_km_tree.cpp @@ -0,0 +1,239 @@ +/*================================================================= +* syntax: A = search_km_tree(I, T, b, n); OR A = search_km_tree(I, T, b); +* +* serach_km_tree - build assignment image from intensity image +* +* Input: - I: X-by-Y image +* - T: MMl-by-K tree matrix where l is the number of layers +* in the image (1 for grayscale and 3 for RGB) +* - b: brancing factor +* - n: normalization (true or false), defaults to false +* +* Output: - A: X-by-Y assignment matrix +* +* Author: Anders Dahl, abda@dtu.dk, december 2015. +*=================================================================*/ + +#include "mex.h" +#include <stdio.h> +#include <math.h> +#include "matrix.h" +#include <vector> + +#include <iostream> +using namespace std; + +// struct for the tree +struct tree_st +{ + double *tree_data; + int n_dim, n_nodes, branch_fac, M, Mh; +}; + +// struct for image +struct im_st +{ + double *im_data; + int rows, cols, layers, n_pix; +}; + +// estimate the distance between a vector in tree given by the node and a +// patch in the image given by the row and column +double get_dist(vector<double>& patch, tree_st& tree, int& node) +{ + double d = 0, tmp; + int id_t = tree.n_dim*node; + + for ( int i = 0; i < tree.n_dim; i++ ){ + tmp = patch[i] - *(tree.tree_data + id_t); + id_t++; + d += tmp*tmp; + } + + return d; +} + + +// Function for sampling patches from the image into the patch arrays +// inputs reference to the image struct, tree struct, patch struct and position of the sampling coordinate. +// There is no check if the sampling is outside the image +// vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im, bool& normalize) +vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im, bool& normalize) +{ + int id_l, id_r, id_i; // iterators for looking up image data + int id_p = 0; // iterator for looking up patch data + double sum_sq = 0, pix_val; // variables for normalization + int n_dim = M*M*im.layers; // number of dimensions computed here, becasue tree is not included + vector<double> patch(n_dim); + int Mh = (M-1)/2; + + for ( int l = 0; l < im.layers; l++ ){ // image is sampled by three nested loops (layers, columns, rows) + id_l = im.n_pix*l; + for ( int i = c_im-Mh; i <= c_im+Mh; i++ ){ + id_r = id_l + i*im.rows; + for ( int j = r_im-Mh; j <= r_im+Mh; j++ ){ + id_i = id_r + j; + pix_val = *(im.im_data + id_i); + patch[id_p] = pix_val; + sum_sq += pix_val*pix_val; // sum of squares for normalization + id_p++; + } + } + } + if ( normalize ){ // if the patch should be normalized to unit length + double inv_sq = 1; + if ( sum_sq > 0 ){ + inv_sq = 1/sqrt(sum_sq); // inverse sum of squares + } + for ( int i = 0; i < n_dim; i++ ){ + patch[i] = patch[i]*inv_sq; + } + } + return patch; +} + + +// The tree search function +int search_tree(im_st& im, tree_st& tree, int& r, int& c, bool& normalize) +{ + int node = 0, node_min = -1, node_min_level, next_node; // variables for searching the tree + double d_min = 10e100, d, d_min_level; + + vector<double> patch = sample_patch(im, tree.M, c, r, normalize); // get the pixel values in a patch + while ( node < tree.n_nodes ){ // go through the tree + if ( *(tree.tree_data + node*tree.n_dim) == -1 ){ // check if node is a leaf-node + return node_min; + } + + d_min_level = 10e100; // set minimum distance to high value + for ( int i = 0; i < tree.branch_fac; i++ ){ // go through nodes at level + next_node = node + i; + d = get_dist(patch, tree, next_node); + + if ( d < d_min_level ){ // set current node to the minimum distance + d_min_level = d; + node_min_level = next_node; + } + } + if ( d_min_level < d_min ){ // set overall minimum distance and minimum node + d_min = d_min_level; + node_min = node_min_level; + } + node = (node_min_level+1)*tree.branch_fac; // go to the child node + } + return node_min; +} + +// The tree search function applied to the entire image - border is zero and interior is in 1,...,n +void search_image(im_st& im, tree_st& tree, double *A, bool& normalize) +{ + int idx = tree.Mh*im.rows; // increase with empty rows at border + for ( int i = tree.Mh; i < im.cols-tree.Mh; i++ ){ + idx += tree.Mh; // first Mh pixels are border + for ( int j = tree.Mh; j < im.rows-tree.Mh; j++ ){ + *(A + idx) = search_tree(im, tree, i, j, normalize) + 1; // find assignment + idx++; + } + idx += tree.Mh; // last Mh pixels are border + } +} + + +// The gateway routine +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + // input image (I), tree (tree) and output assignment (A) + double *I, *A, *tree; + int b, M, ndim, ndtree; + const int *dim, *dtree; + bool normalize = false; + /* Check for proper number of arguments. */ + /* NOTE: You do not need an else statement when using + mexErrMsgTxt within an if statement. It will never + get to the else statement if mexErrMsgTxt is executed. + (mexErrMsgTxt breaks you out of the MEX-file.) + */ + if(nrhs < 3 || nrhs > 4) + mexErrMsgTxt("Three or four inputs required."); + if(nlhs != 1) + mexErrMsgTxt("One output required."); + + // Create a pointer to the input matrix. + I = mxGetPr(prhs[0]); + tree = mxGetPr(prhs[1]); + + double *bd; + bd = mxGetPr(prhs[2]); + b = (int)bd[0]; + + if ( nrhs == 4 ){ + bool *normalize_d; + normalize_d = (bool *)mxGetData(prhs[3]); + normalize = normalize_d[0]; + } + + if ( b < 1 ) + mexErrMsgTxt("b must be positive."); + + // Get the dimensions of the matrix input. + ndim = mxGetNumberOfDimensions(prhs[0]); + if (ndim != 2 && ndim != 3) + mexErrMsgTxt("search_km_tree only works for 2-dimensional or 3-dimensional images."); + + ndtree = mxGetNumberOfDimensions(prhs[1]); + if (ndtree != 2) + mexErrMsgTxt("search_km_tree only works for 2-dimensional tree."); + + dim = mxGetDimensions(prhs[0]); + dtree = mxGetDimensions(prhs[1]); + + if ( ndim == 3 ) + { + M = (int)sqrt((double)dtree[0]/(double)dim[2]); + } + else + { + M = (int)sqrt((double)dtree[0]); + } + + if ( 1 - (M % 2) || M < 1) + mexErrMsgTxt("M must be odd and positive."); + + + // tree struct + tree_st Tree; + Tree.tree_data = tree; + Tree.n_dim = dtree[0]; + Tree.n_nodes = dtree[1]; + Tree.branch_fac = b; + Tree.M = M; + Tree.Mh = (int)(0.5*(double)(M-1.0)); + + // image struct + im_st Im; + Im.im_data = I; + Im.rows = dim[0]; + Im.cols = dim[1]; + if ( ndim == 3 ) + { + Im.layers = dim[2]; + } + else + { + Im.layers = 1; + } + Im.n_pix = Im.rows*Im.cols; + + if ( M*M*Im.layers != Tree.n_dim ) + mexErrMsgTxt("Dimensions of the tree and the image does not fit."); + + // Set the output pointer to the output matrix. Array initialized to zero. + plhs[0] = mxCreateNumericArray(ndtree, dim, mxDOUBLE_CLASS, mxREAL); + + // Create a C pointer to a copy of the output matrix. + A = mxGetPr(plhs[0]); + // Search the tree using the C++ subroutine + search_image(Im, Tree, A, normalize); +} + diff --git a/code/texture_gui/code/functions/search_km_tree.mexa64 b/code/texture_gui/code/functions/search_km_tree.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..1c413daea71625531c3473517f3c177df8cadac0 GIT binary patch literal 13522 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 ZiNJCSGB5V80`d;c|3`u10rC0H|2J1ZN#Fng literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/search_km_tree.mexmaci64 b/code/texture_gui/code/functions/search_km_tree.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..f6786b110627679ad91fbfd91cb9022bdf2b1a71 GIT binary patch literal 13604 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 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/search_km_tree.mexw64 b/code/texture_gui/code/functions/search_km_tree.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..cbe6d41e8ffdb3c5057845234410e9daeeebe734 GIT binary patch literal 16896 zcmeHu4Rlk-weHB4jIeQzo!G=AB?uyl6N53xO-*cq1X36!GN@p{O#`;EWvquSsrmtv zOB$$fgE)xt67qA~bX}ToQ+m^0xNTlaNY=ZuO|bb(9P(3W(!|Ma)8j&0hvb4oe)PUQ z=SavT+`hN&TW{Ufm+?Ah&+NTt&z?Oqd-jZuYwz00vKeD~Jjo<u2LLHq#rr>gjevaB zq=Q$nr^lX{d_d<qF}beI8!`og{x!j-4pVcJ&*u-DR(njrh|lEpne2;~nmYU~p7Q+s zT(c(ny|GhYc)ab-hf~i}1*aa~gYt0;f8+@P7d`TG0T(~=PXgZY$diEgJ+kTH-GFZw zKK$@c03UC=<KYJZ1C~XiUchpv2Au*f@iw<n8>t?-9gMYnZw#xhW-C&-VK&8-mvt3m zUjb%{hMfdhAo81Z1Sp@ySdKtlf^0VsFrsA9f!qjIwNeUvzN}&ra~Z2c)uP)Ndzxf! z0;Gl-8QX<+(rG7SRVZw#RMku4FU#x(eOD%~g0a0q@l52Ue0kW@8Afr<^>~rW)OVT; z8Oa1~%7ZOU;U>mDD*^#B*?2r4WF{-7O}WY@EWq5LQXg)~<N_08iSmHTDEin4I<-k1 z%t%&}x2DzG5@KvR;RAR`UJVs4B`fg-L&0XCgzYvVLwvAlX0i%CPrDzCqN$yr5+4Ye z3B~!D<-g@TkGago6+C*jg7?|Yrq7beB#+%~<~$ZQySaRh%cm^~9@Fud&4{OfcbyEo z`2ny|n#8LI)?LNp`73XP`zW4fV(hpQgS~hxo5vOydA#V~$$DJQ=J9b^Ja#7poDb)5 zc?44B7eC-naotiQmwUNPjmp$$^iV}Z?{O_zmyXs=KF#GqQsM@eY%6e;^*FmnvK3m^ zJ4sKq)!2dplJYG`LG4noN5+5}JyhB|TJ<r>;<Z@d$D!P7LeI?~(IN;|@wjaoukKzC zhC2j<+^@{arH=ITu0#arYfMxxnwZ9;gM&QAM}&A~95I38fn@Rfq9V1QHkYe9u?}^A zO_V$0`WHl-b48Oi=X)lFL3>VZZvF_b?g^s&lqi=C@aRQj2<0EDQZG&;K|56D0WKe< z=7)}sh2T+%%ZKbSZi0fwWy@`Kw&i^D*+<c=O|EcQeka`&*z1ztw@Wu!w>qR1Fkt;D zn^Zc?W1Ry|`4pEgO8d^+V=ZR0v@ck|oBzz^*Dx_4uH9UvBtA_h<?Lkt-<S2gJi?m? zZ1SPl7IOm9=PWFccDw|)BX2hkiq_7$ns=E`A#0b9xMDvuZ=!73QG0B=c>r0L++&YV z&vnH%nmc){&Rkk6zbj3$6Z?Hd4*9fQ+Bf2;J|TU7F@!qh59i1AS3ApwdGjfUe9R@k z4*FuFBl{&@{ekp-1|FJHWrukZqduaSw*M4uIOXxcI?E0@n~z}NKIPG#0#|l|XTRv| zR<fL6?S2Q-wCIl*nJ(}%SG*{PXDiYqD{GY|Z6OrojyWe4=ym6$!ui9Z@XZ7eE}apW zJ4OVie)yd9%_G3tbp^m?smS+%R9Q=SwA)1NMyP-c@lu`o_nlEi7kSK8{mOmwoN|s! z?ybcHZ$9G8K6*(##hI<7w16OLHw$tANqqgVknxsFwwp_B$zDhGZ>1flu&iZuWNh+) z9fLwPx6A5?Boo@GRb$k)%fyQz#_ZrSM%g7}NL;cyVjNNwVTI?-TLpZ~OipxD6_d6U z!tAX!Y38tW(?+u!-JiP#({tT8sr0b&--{VLC#@Q3m5P#5=}Q1dy3T)RQS>u?(1~%8 z4^Pd8lzYb{`=kAzBok%BDSJDc55e3$t<pEUr9yp=Ep9hw*`kN^ytyCvqZ<CGz52b4 zuR5Dw<<+m<yOzr@@Uk8n_>J?O@>Qu~H=Kf^=NN067V&W}W30a`71)M(+-ZWKKB>?) zfbyW09|XHD@)n6K!DvXOeK4_p0QS}o@MI708_!6k=&DmbER}}MjdrQjZnpBxhr|L~ zWxIRT%3jdUyqmEiu^wGmkFJ=tgvZ<kv@Vw}TfPD-bD3?qZ3R~6Ua~8EF(+S43skzP z^HH%vYsHG>^2>Jl8%Ddlu*4p-n`<2Mh(p?UQMQe<l_l)e2`PRpY)R%Ho(O1{3JiZi z#x6f?9)W1tUxLM3Y0v)Dj>T4ClY7cus8*!-CTxVx<~|-@TvQu3_qwp?t<ofBc_aE_ z5=*{HD!t&sUQtSIn(Xokm)y<cF0)(t!N<wuPP^<M=aTEpZb#Rjq#bK96YO$>v9|eR zmpqL(_d2D0edlZ#utS(2<%xT8q%F@u7pe5u4ogD5ub?X_?bry!9r5X(0>C@<O8a{G zqg?ZmwefL!r+k55c#C(xm+h2KI?G<=WdqPpT5-hD^~QH@bjsaM`B>{t^jlwWPP&_6 zg{PK8KaURSBEN5yX1=^`j8yuvBVJizOD0^|CwTVj_UgWk2QlHRKt8uFPbxiUli~7! zu*FyDq<wF5`FUYD8&*<03gw2%Ffg^)g1j7ytkH?7hCPTsOB*5*_QY=KelPi;(H?%5 z4K7~l;-EO?`6JHi9w}Ohf~#!63Fj_-*F==)#c9(05}{?d;G@a~4CYQO#7a9BVuee_ zu$<XAy8PF45&rNCb$J`zN$YY9g|sfeB8t%ENkXf-tR-FIx9eIZU*ZGly#$7|P;a&+ z`@~doMh_Xqv`M}|GmOK%45O}HUSgzKwy>mHvBO|BG{s!-K}K{^+WtdqA};wcGbw4$ zzK;e*Olx`mI8?!WzW2@{4xVK3k4vz*C5wNcf(KQwT?JhLJCnuVQA_Jp5Kw_v1*-ve zN~KQ=hb5IhZ{9@8(UJ{d$<nH`HP}|!R@&}vShbQu2azZk!b+_(7i6x|=-EbF-1_O^ zE#}>*2NRyQ$s_Xn(GlIcafpT{O<+v9hz><R(}f99PKY5OxSXfFCg7b$m#ip9)I9va z7V`l}QVsxtH9OrDP3j}paQV&><sX2Oo-uCSZl=a;(NA>ulFW1HkMfYHpQrc%WhKr( zi+-f4@F7vx3+kOl#f^+z9*K@*33Y^icWG5`)tI<Etdt|~l7}(AjK(+mX?7$Zg+)e` zut}}zR{ACKEz?Kjmo~oxW^&m_qDMYUc0Y#_t3DmkSAQ(j`+yWU4;<|o)HR!Y6*gda zk1CsdyB^RE$$fAsRthjUoRQ0gd1Ip8Ura9PZo^k$H#l9xcmby;P|K}Wo=&=D%d3nS zp9-u}X<rZCpjTF(7Ja1op{zr@4$BcPziyLXa>Q*p$||AJcC+xZ2x}47Ie>D-RqMKB z><o2g#D6ZcNm+am(J<!beyS8b)R-|rdXFck){jTfJM|9nt`YBY@ivKfF5btFi}=-q zDEiD~E+13AkJBZh!>f7p<78xvQf^@Ej6oR#K-Bf1B6tkHhQ>_a>K*Dp8MBaLSh$-x z(Ld_KrSuvfy{HT4(o`X}N_&-OR5|wpNY5k$&M6~gL`aD7Vzpff>7i~>oQ_H~rxDDD z&6su@%_hWz(X&wT93nx>5#?qaZMPuAwIqfV)K&f`PG;LqZN<)J{go~{_ZK)|vT*LR zV~Dm5@n)alF`!5n<u72}k_fL+4pI~x&rbp<<wm>;!3e~40Lnx{Oc#jaTEqpw-v*$_ z`NB3gDO*)BCLS-IsM4<@x(D<=!%Q@Wt%_M^C$}9_W<d?>mZa3R2p&TD0FpU2$!HY| zn`vm*iN1tOxO_d?_c`d}k`EuG`cC=9<SR+Tn-<)82t(okQb7lhDh@}>VJwqvRqye% ztxD*SWU_91UpDMT9m^YCiTm`H#2I(8KW2Cs808^&N9(o`SgI;IR|Y04Wu|88@ecGV zHZe?`zeLu$?Ik4y^(-f?+g?=SLQ(0VUiBm-6tR*HbQ0q7Aq3_kX3#<g1PnB2)>?U3 zzR2bG<MWg7J?qPr)99Ie3d^7ZL%!vgDAme`<zM5dr94B?dYbeoNYSJ&g0t6)%8|Sb zzWFez%187{%+_g;VL3^RUj;2)@IYX?(5T7NcfAqTcO@g^h<!ceb|u4xXfiu;b6SxZ z8H$9jNuzIo1hw5WIhHqQS*y0_gc+>1RSm0pwXtZYc^fvX1H$Er6EHcPaKAXK4)Lxx zB8K<^-I=RtypO@|F1b&Boy#YZuh0<_hDA(E!>n01J-!8Ed`Zr4U{EZuh(VFDo~S;m zzNpov`9Z@nv<BfB)@FKrbGZi-`A4`O;jug(UuuNgiRJPbg8Vo)DxG3Q7=AA3*>pM) zOZ+$;T5+n58=epp9J1qvts)EG)ZKfz{JB?>U!uy7Tv7hoOY+@UmjBypuAu(`TCZuX zF(>1Oe-XSoI)e=T1IX%H5oQGSL88hZ;pzYe1&gu~Ea!W5;rxT}%pkp_RAM%9xlgrh zYOY7ma@G013j@Og+=vS)6>+U-BTb)z6%V?creDBl!&h!Fg5O}K<|T*CsNDf5{|YKx z>tqj?^;0W^2HbeYz^nViMV7=cm;NPxa3*7sKhIYE=YHLp3!3j$qH5hS$}?D|@YjDr zc|ST%_X+ULaGEx_kS%bsYTZ=5zkm;}R6jd$=4$0#wJ=6Gjm(*|8TFMuV3Z!bG$hqm zee|fg?vy`rV!>m64k^R19hT;`(T{c5O<>Ho#atEZ^h6R1zRp}PBRv12Nk23VO{H}F zi4YXV6wFu3VKdq@CXqh3;Dr_p!e3|hbpi;2tpFKy{!7mo-azH9BO(fr?7dqJ$52+j z2c?uDw4hm`NO@B&jD^i!M)u4fx20Bs*5_qhK1;4mK8;XDy8lUxl#;FLScb(t4SHHh zv|q)MZSMijPPlIgoU|)(W(}8%=ggoGwm&KNJZpuN=-|Av{{1@VEB!7@k1OUF#G^<B z0uJNYP&+IY8h(#DpQjb({}%WZ&uNh_%zp=v%<G0RtSft;MPHfHiM<7;r9Ha@N2CL_ zci~Vx$MDdb$z)_BZ#MKJgIU`pz_^0VbLb*Evid+C>b1_Xl|)wHPD}cgt%%3JQNpta zkj=qab(1ST?kJBHnVr=yhfCx;#<{AygSqm2V|F+0l!A1TP<rNVIB8sUkx8z-Fy9Py zHifB>i@BTwm?frmNv}F)+fFM_L1oKHtctuXZ$!rM`-|VgH7k09*hA&nG&dWaWZO=M z^;=FxuEK`<6|4Y&VgNB&!J76_#eHRDk<+-0vK0RiqN4B2<FDXm>S8jIOaAx-+B+k0 z_|tg)-+`{|Ck2jlCAefi26v}6O|~mlenQLp!l~;M7fvN{fWuM~Yr>?>4-=1iN*I4` z_Tz8Q_<PZ;!AYRHH{$2<+LC1PYHU7Sb|83n6eNr9MhUUqEXvob`FO2KExW1gK)6v` zz`F-c+=9HtQNS(TsY4wk-H3uE)mU{eH16gx!)rHFBXo6nVF1M!sR;Y66n=p=5!^Y< zrLc}ga+1Y;aMG8eHu%u|pO>LRa_W;HkWbD35z0gL2oK=GevEu?8j?-^KsJdQ^~!r{ z2KPR~E^$L#@RHA1#^XOTTX81GRYC&hl}|r=Eltdo5Q>r3%g0XK6rEZJezZ|LY&mH= zFaVM=gy@A3$>KsN!{r|-g5#l6q>OwF=k#RpOrk(Y4Ue{%tH2%~6jZ_F!qq6Ec^pxp zbX*|<-4~Ed%9t!ilEwclc9E`rBGTmnk9AQqn746(v>l+hgpTf@Y(-XCLTbrs4TLp~ zxEOI`zqbO`QP`F&UILj2emHK1AP-?Gk2{8?U5FNNheC}`rbf}4HCcQEvhZoS=ij(K zG1=h0nPfj^9)Ou}1SD&%q;ub8F#(TH^j%FNzP1M73@#C?IiecO=$8~Psqkwa?KSf1 zw}QXdylpB-puHy@Yik?h#M!E>vd_uFCCy)vm7MzT=)m!zfBPUa_J|wyjiZj7#$J2A z2L}VO!78uQ97*rkq(?z&?L<8amYpThXE2ni(X{lqUGDBPoSUXb^0v*xzObj=E+4gR ze&6f!IPLODM_#}D%I0$mJ-&!tK4s54DrT|`-gfi*&9++m<_~O<aGP!OIgc;wwdMUL z?=)yVmLnwkNX#+p*n)$7?TB4AyopBT{@BV7W6M4XFX7Sr@1fbGE!wZ2Iy^5a*<*%- z;Fz1p(Xnvp+{T<N7M?seK;SyV{*OLEsB2n(J;zo<f9SdNL~ke$)pe-32{j#CPKGCJ zeuqiJ67KV*Mz)ctdQew`dH|aQi!B?3GQYD|pW0|96`>#LQ#%5cVk;Ht;J=L(BC}9w z1}fR4gYOiDe0_$2uc`*?GaN+@UQm#(4=p8*uY_C>rW@eh@0xfX9Kx~!c%TAvPEAf^ zwrqF|HKP5-K7))5<~2L-P)spH7fRu4qWz{mLl9ZaLKZUwkV9+;*COIgF)uBNIfjkE zBh2XSQMVbKLbK4H<kYh{^nOdcN5uQ6c)uy$6t^U&?iKF`#Cw-`e<t3Fc%KySSH+v+ z{N&UU@y-?RPXzwY;%yR`--x_k<PVEHdGr0cUc|JtiZWAc`u;pQn2D$LL02oZHcGkt zM=sxM<?^-k`_9cSd131J9a?F>gwNxBXbl72x6r7;0)m#4h$cQlxVV#i=r%V{Lx+B! zOb)#b+|Y0EvMxgdCN!|E)++q-(8vguP#UdFzK~3At5r=s^k$ld;{tTj$pyldK~<A0 zp5HM{H&%{-ysVAOwT;R*&ZA{Wt-v5bqNrgxvbhS@NJzW7<!<0f&Hs|=RlBOzGaCGX z2EV7lpa$(4EZ5-G8XQ@m)_+%n$2Is14c2RXsm_05RdeHQDU1rQp0C2$8XVE+gBpBN zgFX$`Yp`E~w`*{V22;|vYVsb?;6@Fm=;6!Cw`+QvG^l9w!y0^AgHLMk2O6x>_!ntq zy;e@OGiXiaRCrJ;@7LfS4erojK!Z$62X11UbjSy8R$&VNpM_Hl4^GH}kij{s=N|?n z4^(n1V<t^FwM%?jF%1acmcd8$h%Xzkv^vek5H&C>%3MQHy}Gub$7AvbO|AY&5FdvG zBH@rJ==pZU8}zgwmS5}xH4+9o6U*3YQ%58uJ}L?LL*B4=od?VrDdcGiHn%mb?Pv&# z27K)sOzZu@wIMVVG|isT;_dMGLSDbGshwK4j6!%jn$~ziLg4?29BL`0MQvI?e@lz0 z$=6~Ut*1TJe#qo+HHF(eCZQ)WQbA~7YVmtQCi-@!)f?6Xuk$pA{lS{L+e~4<-_-8+ zt;y=R+>hUeY=;YYrbH{Yw%%Kw_;s`{EbZ(Jr74rp+hZ>`V2-e>Z3HhThL*hQ<b z{|fx8o6xIvd_vKrGFRO4#c~B-Fxa%g<nydIwR?PP!fhn_fX4s)v`h0i^$kuw=q;Fx z)4z^qnySby75ra+m$)yrpDBFrEET_BgPAmur^$<?0g-RSj??%xd{ctwN{RS8Gw540 z@>GXpq!I$RB;~DmNN#4zY_|$^PJxcGx?-ZWJ)A)|-on`BZBu2Em6Dy=KFQBaDIUUY zMV)%c!yLF`B3}^5pzlQfN;;4bl1VyK%G86}$xNAg96+5x&@s2Jn23KQgKoqe%S<FA zl``8Uxua46V=Jy>Onf(G?hwnR5$6a*^J$#9qJnJ#?hk2Pr}$=yzQZc_(sxg0VO7jE z`t*a9S5!2&Vm%)~nF5>g1xs*yv%rQdON_?hHi5DJQ8=gI8yJlfe5Xd?+=6d#G*0j- zqi}VCZ+J9L@Uhv9RboI>--6M6|4^4In}ehqtzBBfDv$Y|d+upjO|EJu_7%~$V7S@r zw23wz9fi9~w6QxASEusr9fec*_5)W$x^$}Fk@e%7u(N1KEt_F**lw=+ZC$xX<)Lrv zb`y^}2IvjG7XuF0p(&h&aQ(n#j8!G!0#?T824AbI&I-&KBBs81qcyZURfahRQQk!5 z)u9jo7PI<IU+UX8`krk%(CRmC<_*O1*OG2RE}Kv}o=x~g@$fZ+g#%I|KQOK_&plyc z4x8AL%StN8vfL#(f#Wwp5_*`L%PE+Wk6{z*U4N_UXHs~Yw_=|zQ0smhkW!acCOV}Z zfFxr=UN*~{KPHgl#(_j@bApt`a+YLOP`OlC8Fta?>u%0vH{%DMDQ!8dM0X7<97GQq z$GInu)3b5mLZ+|0nhh4>yV9#+bLcjClAcZaP-4bc3j{6<{8qxFZQ{u+kE&<LVunrw zTa3Z2!_%+T$t)Mv6tJ4DW7yX_jm*+EmVH%M%q9&_9K3#DLgL!M_{L&)k?4aPeIWY{ zpw9%~)ZjaSfnpW6a6DNBeiWB2WS1uaiT4>mN{0Z+ugxw|@jnG5zT<#||BHq%EH<*@ z@K{#dGKLjb=CI<~g@bRwj>J0?SKZI!c@OPUDwJ}V)M8{3_@vo+ffV0z+$4nYe5 zaCFX@o#oaQ8Cendi|WR(BGf6GT@V<1T@JghQexKy#}5_-u4zokIE?xa-+=MOMG-+E zV+myB>RB%8=FZLu==3_K|1M)=2~J@wRkz_r#{Pt7G3pZ3W#OAx%n!joUz0UEt3sEr zXZe-6EI*jzR`X+7{%qun4e0wpR;)X~XkO_=KgXo|sq&6dc~w)0ca=#kDDVZBC$cev zKPbhMnJ_KI-bXkZFM=2fW}QU2(J8e@XYw)AXdWu1^axzm7SXct>?Hgb=t6W#v<Fhk zY-jfu=%aGsS9k3j=5vrq^Ca~sD4)eMjK?$q{uh3YvC)xjd55QS{VkOY;P!~GSp;=f zEAgr6bt~c)>;)BosWgmy8*o+gQW)fISPK=5xifGz80)RToyx$u7humr-Tn+*5I6?h z-VEFWzzw1<`QlU>rn4k)UzEN)$Bwqjf7NsB217^ZLQi;6q+_)wxVZJwaj4<zh?-Wa z)XT8n(y;Ua`errn4zeob76d&_VGo3Pg5G8u9XEs+*6v;1(OKTo-j3;lne7M$YeQ@5 z=tFm+cvKRTv*tC_<Dk+Y4m8$KxW#JyrmepIF1CXx>Y=5<6Ab!;X`<UP-)_ANzk)Aa zzQ8@JvO+Y%Ze$G&sNN9v1UtMwsDw4xu%NROr(FMf$EBmyRp0~qcKmyUhvUrG?g=8| zC<PX`t)31~IJlvqqsi-Q@cP#I*LrY<^{!dt39_3$&k^*5BSBx9!$^`FLIF>+x7FL+ z(54EfeNxpdeZFgG_6HDYO!*wX#S;n#{TtX+jS}7v@Ia4Ne}hJ8X!nL-!P{Wbj*thr z<qMW9TU6()b)@Yuhmqz@?cSD#KofNm25Ip5g%qD(RT=*}pbLFFi2BgE8K%bpv5ie& z9by#7`?lgdP5(3iS>7<2lzshDzQM!hv4)VRt)bQ1PU2~lEh^oMmhbU2_?j?e>l-}2 zb>5)ghx0z#svO1P_jx+K;f8S2>UOkL!y1B~HRv3qX_VHKFNB>uou1}M*pnium2QCU z!EghH8iMf85Dm>D!<qu#86BY+>%G1ipwGa6zJzh&pV2;R#;h6YWVwu^qbb~$Nxob{ zo2Mx-ie91rM#K}`;PwP@qtVgiYxXSgqZQ1Of-J181(9G7J$48E&7M%m*}{r4uypIv z)B+^?En$~#K%YD6pr9>uySLp#Id-ki?hTP)RA!fVb+9S8VF5-b#Oku07!uXYOI@%S zhdB1n`X%A^1x*1=NazApu+aVo;aq<I+DIU^uQ+^|-iTm`vfLXCN1ED~`Cuq$^WU<a zA-iXFgeJQ?=n2t~Jz!YoQ>SrDdIdpA>L^=cSn3I1ksN?Dt^fYu?eGxLD<$<#$C5=3 zmu0rPZb$pf{~t?{N7XokVg`zlW|~whq<&XDvC?$1^;I=4A`0<Q>Z}}ne^Nfrw)`?z zdPTmpFH*hg75Rib;nd+G8dP_js`5qcr#)6?03)P-`MRP73K)A7PyZbCjP@4tbvP>x z0yY+6zeFre@T8W{JY!{^zvvuRUIZDaOOVc42awM^Z_znyA<j#ufG6m|a~63zLrp+@ z`U&zBr_Q+^cUsV=5%4iQVd$_2@IySL55Y+j5l2^pAMkNJ&m+GF@Mk!y(Eo=CI0^EB zXMl6@>;azQ`up&ZOp2-B*7D56*w^q79(SZ{2c8P#cLMImQ-eIg0WJSKz=@MpeN2E> zEl+Tnmahlgq~!@dq~#w4{0I-}_6gwhDXJWTRxM93hKKao4!G<_#x^2f5BMP-JMsie z%qkDT1zO$?_@I{G1^5&mvfIx9>!%_nCpmzBhllWc0eiLlalk^{z3u|O2yhi1vRfnI zKWTZ~-LgO6A>9<fpW=M_H1hauf(7v;kf(bB`WnKDF{Se=okf3#{t~1!CFKdy8L<@n z1lQncLH^71|IPw*k>$kW!m|jETZ1b!xDpVb(y=@|U#7oZ3+P4HbmHy<_aB>5Jx$@W zfT;Ka{8%v1ePKOn+@-_1Rx7ZYrclV!u^PX=b#}D-LUX6!uySjtxy{qj6q?cDZ4UZF z{?_n}W`D=6O`(qRb+e|J5X*X7aR^!N3DOUAbEnKIub6UMey+(>6At1>KPS#2|C`b* zYCY5pA$AXX!yAMg6H*QR$cR=vE$*Oq9fDr`;+H07k{u%KhaxV|I!`-(!oqv*l%|kV z9L<7LOcAfGnPTj@Q(BwaL!K$O)yz!G9$h&tKZE0Qt=G&PrEAU1bf;0hW@gHsx4CU@ zo89hQw6MNj%lv1`G^nK*zr8=+|6}Ee;U`9(DA{A$Q@Y2xhwpLk3GC_IGqR`PA4~p` Y976&4s&+T--n#q1Ze+hqe*+fyUw8Et5dZ)H literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/search_km_tree_xcorr.cpp b/code/texture_gui/code/functions/search_km_tree_xcorr.cpp new file mode 100755 index 0000000..20c722b --- /dev/null +++ b/code/texture_gui/code/functions/search_km_tree_xcorr.cpp @@ -0,0 +1,239 @@ +#include "mex.h" +#include <stdio.h> +#include <math.h> +#include "matrix.h" +#include <vector> + +#include <iostream> +using namespace std; + +/* + * This is a MEX-file for MATLAB. + * + * Anders Bjorholm Dahl, 8/12-2015 + * + * + */ + +// struct for the tree +struct tree_st +{ + double *tree_data; + int n_dim, inv_ndim, n_nodes, branch_fac, M, Mh; +}; + +// struct for image +struct im_st +{ + double *im_data; + int rows, cols, layers, n_pix; +}; + +// estimate the distance between a vector in tree given by the node and a +// patch in the image given by the row and column +double get_corr(vector<double>& patch, tree_st& tree, int& node) +{ + double corr = 0, tmp; + int id_t = tree.n_dim*node; + + for ( int i = 0; i < tree.n_dim; i++ ){ + corr += patch[i]*(*(tree.tree_data + id_t)); + id_t++; + } + + return corr; +} + +// nomalize patch vectors to zero mean and unit Euclidean length +void norm_patch(vector<double>& patch, double& mu, int n_dim){ + + double sum_sq = 0; + + for ( int i = 0; i < n_dim; i++ ){ + patch[i] = patch[i] - mu; + sum_sq += patch[i]*patch[i]; + } + + double inv_sq = 1; + if ( sum_sq > 0 ){ + inv_sq = 1.0/sqrt(sum_sq); // divide by sum of squares + } + + for ( int i = 0; i < n_dim; i++ ){ + patch[i] = patch[i]*inv_sq; + } +} + + +// Function for sampling patches from the image into the patch arrays +// inputs reference to the image struct, tree struct, patch struct and position of the sampling coordinate. +// There is no check if the sampling is outside the image +// vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im, bool& normalize) +vector<double> sample_patch(im_st& im, int& M, int r_im, int c_im) +{ + int id_l, id_r, id_i; // iterators for looking up image data + int id_p = 0; // iterator for looking up patch data + double sum = 0, pix_val, mu; // variables for normalization + int n_dim = M*M*im.layers; // number of dimensions computed here, becasue tree is not included + vector<double> patch(n_dim); + int Mh = (M-1)/2; + + for ( int l = 0; l < im.layers; l++ ){ // image is sampled by three nested loops (layers, columns, rows) + id_l = im.n_pix*l; + for ( int i = c_im-Mh; i <= c_im+Mh; i++ ){ + id_r = id_l + i*im.rows; + for ( int j = r_im-Mh; j <= r_im+Mh; j++ ){ + id_i = id_r + j; + pix_val = *(im.im_data + id_i); + patch[id_p] = pix_val; + sum += pix_val; // sum for estimating the mean + id_p++; + } + } + } + + mu = sum/((double)n_dim); // mean value + norm_patch(patch, mu, n_dim); // nomalization + + return patch; +} + + +// The tree search function +int search_tree(im_st& im, tree_st& tree, int& r, int& c) +{ + int node = 0, node_max = -1, node_max_level, next_node; // variables for searching the tree + double corr_max = -1, corr, corr_max_level; + + vector<double> patch = sample_patch(im, tree.M, c, r); // get the pixel values in a patch + while ( node < tree.n_nodes ){ // go through the tree + if ( *(tree.tree_data + node*tree.n_dim) == -1 ){ // check if node is a leaf-node + return node_max; + } + + corr_max_level = -1; // set correlation to low value + for ( int i = 0; i < tree.branch_fac; i++ ){ // go through nodes at level + next_node = node + i; + corr = get_corr(patch, tree, next_node); + + if ( corr > corr_max_level ){ // set current node to the maximum correlation + corr_max_level = corr; + node_max_level = next_node; + } + } + if ( corr_max_level > corr_max ){ // set overall maximum correlation and corresponding node + corr_max = corr_max_level; + node_max = node_max_level; + } + node = (node_max_level+1)*tree.branch_fac; // go to the child node + } + return node_max; +} + +// The tree search function applied to the entire image - border is zero and interior is in 1,...,n +void search_image(im_st& im, tree_st& tree, double *A) +{ + int idx = tree.Mh*im.rows; // increase with empty rows at border + for ( int i = tree.Mh; i < im.cols-tree.Mh; i++ ){ + idx += tree.Mh; // first Mh pixels are border + for ( int j = tree.Mh; j < im.rows-tree.Mh; j++ ){ + *(A + idx) = search_tree(im, tree, i, j) + 1; // find assignment + idx++; + } + idx += tree.Mh; // last Mh pixels are border + } +} + + +// The gateway routine +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + // input image (I), tree (tree) and output assignment (A) + double *I, *A, *tree; + int b, M, ndim, ndtree; + const int *dim, *dtree; + /* Check for proper number of arguments. */ + /* NOTE: You do not need an else statement when using + mexErrMsgTxt within an if statement. It will never + get to the else statement if mexErrMsgTxt is executed. + (mexErrMsgTxt breaks you out of the MEX-file.) + */ + if(nrhs != 3 ) + mexErrMsgTxt("Three inputs required."); + if(nlhs != 1) + mexErrMsgTxt("One output required."); + + // Create a pointer to the input matrix. + I = mxGetPr(prhs[0]); + tree = mxGetPr(prhs[1]); + + double *bd; + bd = mxGetPr(prhs[2]); + b = (int)bd[0]; + + if ( b < 1 ) + mexErrMsgTxt("b must be positive."); + + // Get the dimensions of the matrix input. + ndim = mxGetNumberOfDimensions(prhs[0]); + if (ndim != 2 && ndim != 3) + mexErrMsgTxt("search_km_tree only works for 2-dimensional or 3-dimensional images."); + + ndtree = mxGetNumberOfDimensions(prhs[1]); + if (ndtree != 2) + mexErrMsgTxt("search_km_tree only works for 2-dimensional tree."); + + dim = mxGetDimensions(prhs[0]); + dtree = mxGetDimensions(prhs[1]); + + if ( ndim == 3 ) + { + M = (int)sqrt((double)dtree[0]/(double)dim[2]); + } + else + { + M = (int)sqrt((double)dtree[0]); + } + + if ( 1 - (M % 2) || M < 1) + mexErrMsgTxt("M must be odd and positive."); + + + // tree struct + tree_st Tree; + Tree.tree_data = tree; + Tree.n_dim = dtree[0]; + Tree.inv_ndim = 1/((double)Tree.n_dim); + Tree.n_nodes = dtree[1]; + Tree.branch_fac = b; + Tree.M = M; + Tree.Mh = (int)(0.5*(double)(M-1.0)); + + // image struct + im_st Im; + Im.im_data = I; + Im.rows = dim[0]; + Im.cols = dim[1]; + if ( ndim == 3 ) + { + Im.layers = dim[2]; + } + else + { + Im.layers = 1; + } + Im.n_pix = Im.rows*Im.cols; + + if ( M*M*Im.layers != Tree.n_dim ) + mexErrMsgTxt("Dimensions of the tree and the image does not fit."); + + // Set the output pointer to the output matrix. Array initialized to zero. + plhs[0] = mxCreateNumericArray(ndtree, dim, mxDOUBLE_CLASS, mxREAL); + + // Create a C pointer to a copy of the output matrix. + A = mxGetPr(plhs[0]); + // Search the tree using the C++ subroutine + search_image(Im, Tree, A); +} + diff --git a/code/texture_gui/code/functions/search_km_tree_xcorr.mexa64 b/code/texture_gui/code/functions/search_km_tree_xcorr.mexa64 new file mode 100755 index 0000000000000000000000000000000000000000..0ac82cdef7ccd8a4ca18fcd4b65ed8df7fd7ec13 GIT binary patch literal 13546 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 I>9bw_Us=^@WdHyG literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/search_km_tree_xcorr.mexmaci64 b/code/texture_gui/code/functions/search_km_tree_xcorr.mexmaci64 new file mode 100755 index 0000000000000000000000000000000000000000..5f224caefe0987623c73d07353e150a8b390ada6 GIT binary patch literal 13628 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 R-r;DlD{&~?cACL2{uf3;V@Ci0 literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/search_km_tree_xcorr.mexw64 b/code/texture_gui/code/functions/search_km_tree_xcorr.mexw64 new file mode 100755 index 0000000000000000000000000000000000000000..8d771be06e99b9fa7ee3d64aa69f4f7669f16de2 GIT binary patch literal 17408 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 a?WKjm3cfje8uo18vwsh=U#33;3;ZiPLSfnf literal 0 HcmV?d00001 diff --git a/code/texture_gui/code/functions/segmentation_correction_gui.m b/code/texture_gui/code/functions/segmentation_correction_gui.m new file mode 100755 index 0000000..6df8c87 --- /dev/null +++ b/code/texture_gui/code/functions/segmentation_correction_gui.m @@ -0,0 +1,406 @@ +function segmentation_correction_gui(image,segmentation,nr_labels,labeling) +%IMAGE_TEXTURE_GUI Interactive segmentation correction + +%%%%%%%%%% DEFAULTS %%%%%%%%%% + +if nargin<1 % default example image + %image = imread('bag.png'); + image = imread('football.jpg'); +end +[r,c,~] = size(image); +if nargin<2 || isempty(segmentation)% default segmentation + segmentation = zeros(r,c,'uint8'); +else + segmentation = uint8(segmentation); +end +if nargin<3 || isempty(nr_labels) % defalult number of labels + nr_labels = double(max(2,max(segmentation(:)))); +end +if nargin<4 || isempty(labeling) % default, unlabeled initial correction + % TODO allow corrections to be inferred from labelings + DRAWING = zeros(r,c,'uint8'); +else + DRAWING = uint8(labeling); +end + +%%%%%%%%%% SETTINGS %%%%%%%%%% + +% labels +LABEL = 1; % initial label is 1 + +% thickness +thickness_options = [1 2 3 4 5 10 20 30 40 50 100 -1]; % last option (value -1) is 'fill' +thickness_options_string = num2cell(thickness_options); +thickness_options_string{end} = 'fill'; +THICKNESS_INDEX = 5; % initial pencil thickness is the fifth option +RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); % pencil radius + +% show +show_options(1:2) = {'segmentation','overlay'}; +SHOW_INDEX = 1; + +% colormap +colormap_options = {@(x)[0.5,0.5,0.5;cool(x)], @(x)[0.5,0.5,0.5;spring(x)],... + @(x)[0.5,0.5,0.5;parula(x)]}; % gray color for unlabeled +COLORMAP_INDEX = 1; % current colormap +COLORS = colormap_options{COLORMAP_INDEX}(nr_labels); % visualization colors for label overlay +% other settings +color_weight = 0.4; % color weight for label overlay +nr_circle_pts = 16; % number of points defining a circular pencil + +%%%%%%%%%% INITIALIZATION AND LAYOUT %%%%%%%%%% +[image,gray] = normalize_image(image); % impose 0-to-1 rgb +CORRECTED = uint8(segmentation); +CORRECTED(DRAWING~=0) = DRAWING(DRAWING~=0); % if initial corrections given +DRAWING_OVERLAY = image; % image overlaid labeling +CORRECTED_OVERLAY = ind2rgb(CORRECTED,COLORS); + +fmar = [0.2 0.2]; % discance from screen edge to figure (x and y) +amar = [0.05 0.05]; % margin around axes, relative to figure +my = 0.8:0.05:0.9; % menu items y position +mh = 0.03; % menu items height +cw = (0.4-0.3)/(nr_labels+1); % colorcube width +cx = 0.3:cw:0.4; % colorcubes x position + +fig = figure('Units','Normalized','Position',[fmar,1-2*fmar],... + 'Pointer','watch','KeyPressFcn',@key_press,'InvertHardCopy', 'off',... + 'Name','Segmentation correction GUI'); +clean_toolbar + +labeling_axes = axes('Units','Normalized','Position',[0,0,0.5,0.8]+[amar,-2*amar]); +imagesc(DRAWING_OVERLAY), axis image off, hold on +segmentation_axes = axes('Units','Normalized','Position',[0.5,0,0.5,0.8]+[amar,-2*amar]); +imagesc(CORRECTED_OVERLAY,[0,nr_labels]), axis image off, hold on + +uicontrol('String','Label [L] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[0,my(3),0.25,mh]); +labels_text = uicontrol('String',num2str(LABEL),... + 'BackgroundColor',COLORS(LABEL+1,:),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[0.25,my(3),0.03,mh]); +label_pointer = cell(nr_labels+1,1); +label_colorcubes = cell(nr_labels+1,1); +label_char = repmat(' ',[1,nr_labels+1]); +label_char(LABEL+1)='|'; +for k = 1:nr_labels+1 + label_colorcubes{k} = uicontrol('String',' ','Style','text',... + 'BackgroundColor',COLORS(k,:),... + 'Units','Normalized','Position',[cx(k),my(3),cw,mh/2]); + label_pointer{k} = uicontrol('String',label_char(k),'Style','text',... + 'HorizontalAlignment','center',... + 'Units','Normalized','Position',[cx(k),my(3)+mh/2,cw,mh/2]); +end + +uicontrol('String','Thickness [T] : ','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[0,my(2),0.25,mh]); +thickness_text = uicontrol('String',thickness_options_string(THICKNESS_INDEX),... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[0.25,my(2),0.25,mh]); + +uicontrol('String','Show [W] :','Style','text','HorizontalAlignment','right',... + 'Units','Normalized','Position',[0,my(1),0.25,mh]); +show_text = uicontrol('String',show_options{SHOW_INDEX},... + 'Style','text','HorizontalAlignment','left',... + 'Units','Normalized','Position',[0.25,my(1),0.25,mh]); + +drawnow % pointer shows busy system + +LIMITS = [1,c-0.5,1,r+0.5]; % to capture zoom +zoom_handle = zoom(fig); +pan_handle = pan(fig); +set(zoom_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); +set(pan_handle,'ActionPostCallback',@adjust_limits,... + 'ActionPreCallback',@force_keep_key_press); + +compute_overlays + +% ready to draw +set(fig,'Pointer','arrow','WindowButtonDownFcn',@start_draw,... + 'WindowButtonMotionFcn',@pointer_motion); +XO = []; % current drawing point +uiwait % waits with assigning output until a figure is closed + +%%%%%%%%%% CALLBACK FUNCTIONS %%%%%%%%%% + function key_press(~,object) + % keyboard commands + key = object.Key; + numkey = str2double(key); + if ~isempty(numkey) && numkey<=nr_labels; + label_char(LABEL+1)=' '; + LABEL = numkey; + set(labels_text,'String',num2str(LABEL),... + 'BackgroundColor',COLORS(LABEL+1,:)); + label_char(LABEL+1)='|'; + for kk = 1:nr_labels+1 + set(label_pointer{kk},'String',label_char(kk)); + end + else + switch key + case 'l' + label_char(LABEL+1)=' '; + LABEL = move_once(LABEL+1,nr_labels+1,... + any(strcmp(object.Modifier,'shift')))-1; + set(labels_text,'String',num2str(LABEL),... + 'BackgroundColor',COLORS(LABEL+1,:)); + label_char(LABEL+1)='|'; + for kk = 1:nr_labels+1 + set(label_pointer{kk},'String',label_char(kk)); + end + case 'uparrow' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),false); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 'downarrow' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),true); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 't' + THICKNESS_INDEX = move_once(THICKNESS_INDEX,... + numel(thickness_options),any(strcmp(object.Modifier,'shift'))); + RADIUS = thickness2radius(thickness_options(THICKNESS_INDEX)); + set(thickness_text,'String',... + thickness_options_string(THICKNESS_INDEX)); + show_overlays(get_pixel); + case 'w' + SHOW_INDEX = move_once(SHOW_INDEX,numel(show_options),... + any(strcmp(object.Modifier,'shift'))); + set(show_text,'String',show_options{SHOW_INDEX}) + compute_overlays + show_overlays(get_pixel); + case 'c' + COLORMAP_INDEX = move_once(COLORMAP_INDEX,... + length(colormap_options),... + any(strcmp(object.Modifier,'shift'))); + COLORS = colormap_options{COLORMAP_INDEX}(nr_labels); + set(labels_text,'BackgroundColor',COLORS(LABEL+1,:)); + for kk = 1:nr_labels+1 + set(label_colorcubes{kk},'BackgroundColor',COLORS(kk,:)) + end + compute_overlays + show_overlays(get_pixel); + case 's' + save_project + case 'e' + export_DC + case 'q' + close(fig) + end + end + end + + function adjust_limits(~,~) + % response to zooming and panning + LIMITS([1,2]) = get(labeling_axes,'XLim'); + LIMITS([3,4]) = get(labeling_axes,'YLim'); + end + + function force_keep_key_press(~,~) + % a hack to maintain my key_press while in zoom and pan mode + % http://undocumentedmatlab.com/blog/enabling-user-callbacks-during-zoom-pan + hManager = uigetmodemanager(fig); + [hManager.WindowListenerHandles.Enabled] = deal(false); + set(fig, 'KeyPressFcn', @key_press); + end + + function start_draw(~,~) + % click in the image + x = get_pixel; + if is_inside(x) + if RADIUS>0 % thickness>0 + XO = x; + M = disc(XO,RADIUS,nr_circle_pts,[r,c]); + set(fig,'WindowButtonMotionFcn',@drag_and_draw,... + 'WindowButtonUpFcn',@end_draw) + else % fill + M = fill(x); + end + update(M); + end + end + + function drag_and_draw(~,~) + % drag after clicking in the image + x = get_pixel; + M = stadium(XO,x,RADIUS,nr_circle_pts,[r,c]); + update(M); + XO = x; + end + + function end_draw(~,~) + % release click after clicking in the image + M = stadium(XO,get_pixel,RADIUS,nr_circle_pts,[r,c]); + update(M); + XO = []; + set(fig,'WindowButtonMotionFcn',@pointer_motion,... + 'WindowButtonUpFcn','') + end + + function pointer_motion(~,~) + % move around without clicking + if strcmp(zoom_handle.Enable,'off') && ... + strcmp(pan_handle.Enable,'off') % not zooming or panning + x = get_pixel; + if is_inside(x) + set(fig,'Pointer','crosshair') + else + set(fig,'Pointer','arrow') + end + show_overlays(x); + end + end + +%%%%%%%%%% HELPING FUNCTIONS %%%%%%%%%% + + function save_project + % save mat file with user input and setting + % save images as separate files + [file,path] = uiputfile('correction.mat','Save project as'); + if ~isequal(file,0) && ~isequal(path,0) + matfile = fullfile(path,file); + roothname = matfile(1:find(matfile=='.',1,'last')-1); + current_settings.show = show_options{SHOW_INDEX}; + save(matfile,'DRAWINGS','CORRECTED',... + 'dictopt','nr_labels','current_settings') + imwrite(DRAWING_OVERLAY,[roothname,'_drawing.png']) + imwrite(CORRECTED_OVERLAY,[roothname,'_corrected.png']) + end + end + + function export_DC + button = questdlg({'Exporting variables to the base workspace',... + 'might overwrite existing variables'},... + 'Exporting variables','OK','Cancel','OK'); + if strcmp(button,'OK') + assignin('base','gui_D',DRAWING) + assignin('base','gui_C',CORRECTED) + end + + end + + function a = is_inside(x) + % check if x is inside image limits + a = inpolygon(x(1),x(2),LIMITS([1,2,2,1]),LIMITS([3,3,4,4])); + end + + function p = get_pixel + % get cursor position + p = get(labeling_axes,'CurrentPoint'); + p = round(p(1,[1,2])); + end + + function show_overlays(x) + % overlay a circular region where the pointer and show + shown_left = DRAWING_OVERLAY; + shown_right = CORRECTED_OVERLAY; + if RADIUS>0 % thickness>0 + P = repmat(disc(x,RADIUS,nr_circle_pts,[r,c]),[1,1,3]); + shown_left(P(:)) = 0.5+0.5*DRAWING_OVERLAY(P(:)); + shown_right(P(:)) = 0.5+0.5*CORRECTED_OVERLAY(P(:)); + end + % we have to imagesc(shown) to remove overlay if needed + axes(labeling_axes), cla, imagesc(shown_left) + axes(segmentation_axes), cla, imagesc(shown_right) + end + + function update(M) + % change the state of the segmentation by updating LABELINGS with a + % mask M, and updating PROBABILITIES + DRAWING(M(:)) = LABEL; + if LABEL>0 + CORRECTED(M(:)) = LABEL; + else + CORRECTED(M(:)) = segmentation(M(:)); + end + compute_overlays % computing overlay images + show_overlays(get_pixel); % showing overlay and pointer + end + + function compute_overlays + % computes overlays but not pointer overalay + uncorrected = repmat(DRAWING==0,[1 1 3]); + DRAWING_OVERLAY = uncorrected.*image + ~uncorrected.*... + (color_weight.*ind2rgb(DRAWING,COLORS) + (1-color_weight).*gray); + switch show_options{SHOW_INDEX} + case 'segmentation' + CORRECTED_OVERLAY = ind2rgb(CORRECTED,COLORS); + case 'overlay' + CORRECTED_OVERLAY = color_weight*... + ind2rgb(CORRECTED,COLORS) + (1-color_weight)*gray; + end + end + +% TODO disc shold be saved as a list of index shifts with respect to +% the central pixel, and change only when thickness changes + function M = disc(x,r,N,dim) + % disc shaped mask in the image + angles = (0:2*pi/N:2*pi*(1-1/N)); + X = x(1)+r*cos(angles); + Y = x(2)+r*sin(angles); + M = poly2mask(X,Y,dim(1),dim(2)); + end + + function M = stadium(x1,x2,r,N,dim) + % stadium shaped mask in the image + angles = (0:2*pi/N:pi)-atan2(x1(1)-x2(1),x1(2)-x2(2)); + X = [x1(1)+r*cos(angles), x2(1)+r*cos(angles+pi)]; + Y = [x1(2)+r*sin(angles), x2(2)+r*sin(angles+pi)]; + M = poly2mask(X,Y,dim(1),dim(2)); + end + + function M = fill(x) + M = bwselect(DRAWING==DRAWING(x(2),x(1)),x(1),x(2),4); + end + + function [I,G] = normalize_image(I) + % initialization: normalize image + if isa(I,'uint8') + I = double(I)/255; + end + if isa(I,'uint16') + I = double(I)/65535; + end + if size(I,3)==3 % rgb image + G = repmat(rgb2gray(I),[1 1 3]); + else % assuming grayscale image + I = repmat(I,[1,1,3]); + G = I; + end + end + + function n = move_once(n,total,reverse) + % moves option index once, respecting total number of options + if ~reverse + n = mod(n,total)+1; + else + n = mod(n+total-2,total)+1; + end + end + + function r = thickness2radius(t) + r = t/2+0.4; + end + + function clean_toolbar + set(fig,'MenuBar','none','Toolbar','figure'); + all_tools = allchild(findall(fig,'Type','uitoolbar')); + %my_tool = uipushtool(toolbar,'CData',rand(16,16,3),'Separator','on',... + % 'TooltipString','my tool','Tag','my tool'); + for i=1:numel(all_tools) + if isempty(strfind(all_tools(i).Tag,'Pan'))&&... + isempty(strfind(all_tools(i).Tag,'Zoom'))&&... + isempty(strfind(all_tools(i).Tag,'SaveFigure'))&&... + isempty(strfind(all_tools(i).Tag,'PrintFigure'))&&... + isempty(strfind(all_tools(i).Tag,'DataCursor')) + delete(all_tools(i)) % keeping only Pan, Zoom, Save and Print + end + end + end + +end diff --git a/code/texture_gui/code/functions/update_dictionary.m b/code/texture_gui/code/functions/update_dictionary.m new file mode 100755 index 0000000..11627b2 --- /dev/null +++ b/code/texture_gui/code/functions/update_dictionary.m @@ -0,0 +1,9 @@ +function dictionary = update_dictionary(dictionary,gui_dictprob) + +M = dictionary.options.patch_size; +nr_dict_patches = size(dictionary.tree,2); +L = size(gui_dictprob,2); % nr labels +gui_dictprob = reshape(gui_dictprob,[M^2,nr_dict_patches,L]); +gui_dictprob = permute(gui_dictprob,[1,3,2]); +gui_dictprob = reshape(gui_dictprob,[M^2*L,nr_dict_patches]); +dictionary.dictprob = gui_dictprob; \ No newline at end of file diff --git a/code/texture_gui/code/reusing_labels_script.m b/code/texture_gui/code/reusing_labels_script.m new file mode 100755 index 0000000..f25eb92 --- /dev/null +++ b/code/texture_gui/code/reusing_labels_script.m @@ -0,0 +1,23 @@ +clear +close all +addpath functions + +% example of re-using externaly made labeling image +im = imread('../data/randen15B.png'); % randen 5 textures +labeling = double(imread('../data/randen_labels_rgb.png')); +dictopt.patch_size = 15; +dictopt.branching_factor = 4; +dictopt.number_layers = 4; +dictopt.number_training_patches = 2000; +dictopt.normalization = false; +dictopt.method = 'euclidean'; +image_texture_gui(im,dictopt,5,labeling) + +%% an image exported from gui can be re-used +image_texture_gui(im,dictopt,5) %% <- EXPORT (E) labeling here +image_texture_gui(im,dictopt,5,gui_L) %% <- use labeling here + +%% an image saved from gui can be re-used +image_texture_gui(im,dictopt,5) %% <- SAVE (S) labeling here using some filename +image_texture_gui(im,dictopt,5,imread('filename_labels_indexed.png')) %% <- use labeling here + diff --git a/code/texture_gui/code/test.m b/code/texture_gui/code/test.m new file mode 100755 index 0000000..b05b7fd --- /dev/null +++ b/code/texture_gui/code/test.m @@ -0,0 +1,31 @@ +addpath('functions') +%% +fig = figure; +imagesc(rand(200,200)); +set(fig,'MenuBar','none','Toolbar','figure'); + +%% +toolbar = findall(fig,'Type','uitoolbar'); +all_tools = allchild(toolbar); +% removing tools +for i=1:numel(all_tools) + t = get(all_tools(i),'Tag'); + if isempty(strfind(t,'Pan'))&&... + isempty(strfind(t,'Zoom'))&&... + isempty(strfind(t,'SaveFigure'))&&... + isempty(strfind(t,'PrintFigure'))&&... + isempty(strfind(t,'DataCursor')) + delete(all_tools(i)) % keeping only Pan, Zoom, Save and Print + end +end +%% adding a tool +[icon,~,alpha] = imread('linkaxesicon.png'); +icon = double(icon)/255; +icon(alpha==0)=NaN; +uitoggletool(toolbar,'CData',icon,... + 'TooltipString','Link Axes','Tag','LinkAxes',... + 'OnCallback',{@link_axes,'xy'},... + 'OffCallback',{@link_axes,'off'}); +%% changing the order of tools +all_tools = allchild(toolbar); +set(toolbar,'children',all_tools([2,1,3:end])); \ No newline at end of file diff --git a/code/texture_gui/code/texture_gui_uses_script.m b/code/texture_gui/code/texture_gui_uses_script.m new file mode 100755 index 0000000..aa2c156 --- /dev/null +++ b/code/texture_gui/code/texture_gui_uses_script.m @@ -0,0 +1,29 @@ +clear +close all + +addpath functions + +%% demo: no input +image_texture_gui + +%% usage 1: only image (default dictionary options ) +im = double(imread('bag.png'))/255; +image_texture_gui(im) + +%% usage 2: image and dictionary options +dictopt.method = 'euclidean'; +dictopt.patch_size = 11; +dictopt.branching_factor = 2; +dictopt.number_layers = 5; +dictopt.number_training_patches = 30000; +dictopt.normalization = false; +image_texture_gui(im,dictopt,5) + +%% usage 3: image and dictionary +dictionary = build_dictionary(im,dictopt); +image_texture_gui(im,dictionary,2) + +%% usage 4: image and mappings +mappings = compute_mappings(im,dictionary); +image_texture_gui(im,mappings,2) + -- GitLab