Repository: talmo/leap Branch: master Commit: c39e07b647da Files: 157 Total size: 36.8 MB Directory structure: gitextract_ix4wtaz2/ ├── .gitignore ├── LICENSE ├── analysis/ │ └── gait_analysis/ │ ├── Cluster_Velocity_Distributions.mat │ ├── GaitVectors3.mat │ ├── Gait_Densities.mat │ ├── Gait_Speed_Distributions.mat │ ├── Swing_Velocity_Over_Time.mat │ ├── Swing_and_Stance_versus_Velocity.mat │ ├── TetrapodExample.mat │ ├── TripodExample.mat │ ├── compute_gait_densities.m │ ├── gait_analysis_computation.m │ ├── gait_analysis_plotting.m │ └── plot_gait_densities.m ├── data/ │ └── readme.md ├── examples/ │ ├── batch_process_video.ipynb │ ├── hdf5tovid.m │ └── vidtohdf5.m ├── install_leap.m ├── leap/ │ ├── __init__.py │ ├── compute_errors.m │ ├── confmaps2pts.m │ ├── generate_training_set.m │ ├── graph2paf.m │ ├── guis/ │ │ ├── cluster_sample.mlapp │ │ ├── create_skeleton.mlapp │ │ └── label_joints.m │ ├── hpc/ │ │ └── python_gpu.sh │ ├── image_augmentation.py │ ├── layers.py │ ├── models.py │ ├── plot_joints_single.m │ ├── predict_box.m │ ├── predict_box.py │ ├── pts2confmaps.m │ ├── test_leap.m │ ├── toolbox/ │ │ ├── aliases/ │ │ │ ├── alims.m │ │ │ ├── ff.m │ │ │ ├── h5file.m │ │ │ ├── imgsc.m │ │ │ └── repext.m │ │ ├── graphics/ │ │ │ ├── FEX-settingsdlg/ │ │ │ │ └── settingsdlg.m │ │ │ ├── GUI Layout Toolbox/ │ │ │ │ └── layout/ │ │ │ │ └── +uix/ │ │ │ │ ├── +mixin/ │ │ │ │ │ ├── Container.m │ │ │ │ │ ├── Flex.m │ │ │ │ │ └── Panel.m │ │ │ │ ├── Box.m │ │ │ │ ├── ChildEvent.m │ │ │ │ ├── ChildObserver.m │ │ │ │ ├── Container.m │ │ │ │ ├── Divider.m │ │ │ │ ├── FigureData.m │ │ │ │ ├── FigureObserver.m │ │ │ │ ├── Grid.m │ │ │ │ ├── GridFlex.m │ │ │ │ ├── HBox.m │ │ │ │ ├── Node.m │ │ │ │ ├── Panel.m │ │ │ │ ├── PointerManager.m │ │ │ │ ├── SelectionData.m │ │ │ │ ├── VBox.m │ │ │ │ ├── calcPixelSizes.m │ │ │ │ ├── setPosition.m │ │ │ │ └── tracking.m │ │ │ ├── distributionPlot/ │ │ │ │ └── colorCode2rgb.m │ │ │ ├── draggable/ │ │ │ │ └── draggable.m │ │ │ ├── figclosekey.m │ │ │ ├── figsize.m │ │ │ ├── fontsize.m │ │ │ ├── hline.m │ │ │ ├── isax.m │ │ │ ├── isfig.m │ │ │ ├── noticks.m │ │ │ ├── pareto2.m │ │ │ ├── plotExplainedVar.m │ │ │ ├── plotpts.m │ │ │ ├── redblue.m │ │ │ ├── sc/ │ │ │ │ ├── gray.m │ │ │ │ ├── private/ │ │ │ │ │ ├── colormap_helper.m │ │ │ │ │ └── rescale.m │ │ │ │ └── real2rgb.m │ │ │ └── shortticks.m │ │ ├── hdf5/ │ │ │ ├── h5att2struct.m │ │ │ ├── h5getdatasets.m │ │ │ ├── h5readframes.m │ │ │ ├── h5readgroup.m │ │ │ ├── h5save.m │ │ │ ├── h5savegroup.m │ │ │ ├── h5size.m │ │ │ ├── h5struct2att.m │ │ │ └── hdf5prop/ │ │ │ ├── h5datacreate.m │ │ │ └── hdf5prop.m │ │ ├── imageproc/ │ │ │ └── ind2im.m │ │ ├── inputParsing/ │ │ │ ├── get_caller_name.m │ │ │ ├── nameval2struct.m │ │ │ ├── parse_params.m │ │ │ └── struct2nameval.m │ │ ├── io/ │ │ │ ├── GetFullPath.m │ │ │ ├── dir_ext.m │ │ │ ├── dir_paths.m │ │ │ ├── dir_regex.m │ │ │ ├── exists.m │ │ │ ├── ext2filter_spec.m │ │ │ ├── extrep.m │ │ │ ├── funpath.m │ │ │ ├── get_ext.m │ │ │ ├── get_filename.m │ │ │ ├── get_filesize.m │ │ │ ├── get_new_filename.m │ │ │ ├── lastdir.m │ │ │ ├── mkdirto.m │ │ │ └── uibrowse.m │ │ ├── ml/ │ │ │ └── ezpca.m │ │ ├── strings/ │ │ │ ├── bytes2str.m │ │ │ ├── instr.m │ │ │ ├── printf.m │ │ │ ├── secs2hms.m │ │ │ └── secsf.m │ │ ├── utilities/ │ │ │ ├── af.m │ │ │ ├── arange.m │ │ │ ├── areempty.m │ │ │ ├── argmin.m │ │ │ ├── arr2cell.m │ │ │ ├── cell1.m │ │ │ ├── cellcat.m │ │ │ ├── cf.m │ │ │ ├── clip.m │ │ │ ├── functional_programming/ │ │ │ │ └── wrap.m │ │ │ ├── get_new_string.m │ │ │ ├── grp2cell.m │ │ │ ├── horz.m │ │ │ ├── iseven.m │ │ │ ├── loadvar.m │ │ │ ├── nunique.m │ │ │ ├── rownorm.m │ │ │ ├── stacks/ │ │ │ │ ├── imtile.m │ │ │ │ ├── stack2cell.m │ │ │ │ ├── stack2vecs.m │ │ │ │ └── vecs2stack.m │ │ │ ├── swap.m │ │ │ ├── time/ │ │ │ │ ├── GetSystemTimePreciseAsFileTime.m │ │ │ │ ├── GetSystemTimePreciseAsFileTime.mexw64 │ │ │ │ ├── stic.m │ │ │ │ ├── stoc.m │ │ │ │ ├── stocf.m │ │ │ │ └── systime.m │ │ │ ├── varsize.m │ │ │ ├── varstruct.m │ │ │ ├── vert.m │ │ │ └── vplay.m │ │ └── video/ │ │ ├── validate_stack.m │ │ └── vplayer.m │ ├── training.py │ ├── utils.py │ └── viz.py ├── readme.md ├── setup.py └── uninstall_leap.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # MATLAB *.asv # LEAP-specific data/* !data/readme.md models/* leap/toolbox/io/.lastdir ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: analysis/gait_analysis/GaitVectors3.mat ================================================ [File too large to display: 25.2 MB] ================================================ FILE: analysis/gait_analysis/Gait_Densities.mat ================================================ [File too large to display: 11.2 MB] ================================================ FILE: analysis/gait_analysis/compute_gait_densities.m ================================================ %% Pathing embed_path = 'Z:\code\2018-05-05_joints_tsne_FlyAging_talmo-labels\results\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03.mat'; density_path = 'Z:\code\2018-05-05_joints_tsne_FlyAging_talmo-labels\viz\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03\density.mat'; density = load(density_path); embed = load(embed_path); Y = embed.Y; Ld = density.Ld; gait_path = 'C:\code\murthylab\JointTracker\LabelPostProcessing\GaitVectors3.mat'; gait = load(gait_path); %% Compute density and segment sigma = 20/30; numGridPoints = 500; gridRange = [-20 20]; % Setup grid gv = linspace(gridRange(1), gridRange(2), numGridPoints); xv = gv; yv = gv; D_tripod = getDensity(Y(gait.hmm.most_likely_seq == 3 & gait.moving_forward',:),sigma, numGridPoints, gridRange); D_tetrapod = getDensity(Y(gait.hmm.most_likely_seq == 4 & gait.moving_forward',:),sigma, numGridPoints, gridRange); D_NC = getDensity(Y(gait.hmm.most_likely_seq == 5 & gait.moving_forward',:),sigma, numGridPoints, gridRange); gait_density = zeros([size(D_tripod),3]); gait_density(:,:,1) = D_tripod; gait_density(:,:,2) = D_tetrapod; gait_density(:,:,3) = D_NC; % Saving save_path = 'Gait_Densities'; save(save_path,'gait_density','D_tripod','D_tetrapod','D_NC','Ld'); ================================================ FILE: analysis/gait_analysis/gait_analysis_computation.m ================================================ clear all; %% Pathing % addpath(genpath('deps')); joints_dir = 'Z:\data\JointTracker\2018-02_FlyAging_boxes\expts\preds\talmo-labels\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03'; data_dir = 'Z:\data\Fly_Aging\male_data'; % Get the paths from the directories data_fns = dir([data_dir,'/*_*']); exptnames = {data_fns(:).name}; joint_fns = dir(strcat(joints_dir,'\*.h5')); joint_exptnames = {joint_fns(:).name}; joint_exptnames = cf(@(x) x(1:end-3),joint_exptnames); joint_expt_2_data = zeros(size(joint_exptnames)); for i = 1:numel(joint_exptnames) for j = 1:numel(exptnames) if strcmp(exptnames{j},joint_exptnames{i}) joint_expt_2_data(i) = j; end end end joint_paths = cell(size(joint_fns)); for i = 1:numel(joint_fns) joint_paths{i} = fullfile(joint_fns(i).folder,joint_fns(i).name); end data_paths = cell(size(data_fns)); for i = 1:numel(data_fns) data_paths{i} = [fullfile(data_fns(i).folder,data_fns(i).name),'/Positions.dat']; end %% Get all of the positions for all of the videos. pos = cell(size(joint_paths)); % Loads the joint positions and adds the thorax back in to the fifth % feature as all zeros. parfor i = 1:numel(joint_paths) joints = h5read(joint_paths{i},'/positions_pred'); joints = joints - joints(5,:,:); joints = reshape(joints,[],size(joints,3)); pos{i} = joints; end pos = cat(2,pos{:}); %% Get the ids of all of the walking bouts according to the speed of centroids moving_forward = cell(size(data_paths)); forward_velocity = cell(size(data_paths)); speed = cell(size(data_paths)); smoothing_window = 5; % Velocity thresholds reconversion_constant = (1/(24.40/1088))./35; % This corrects a previous measurement error conversion_to_mm = 31.0857/1088; forward_motion_thresh = .02; % 2 mm/s % For each video, get all velocity and speed stats; for i = 1:numel(data_paths) % Load centers from ellipse data and smooth frames = h5read(['Z:\data\JointTracker\2018-02_FlyAging_boxes\expts\' exptnames{i} '.h5'],'/framesIdx'); % ell = h5read(data_paths{i},'/ell'); [X,Y,numLines] = positionReader(data_paths{i}); X = X.*reconversion_constant; Y = Y.*reconversion_constant;% reconversion constants. ctr = [X(frames),Y(frames)]; ctr = smoothdata(ctr,1,'movmean',smoothing_window); ell = h5read(['Z:\data\JointTracker\2018-02_FlyAging_boxes\expts\' exptnames{i} '.h5'],'/ell'); % Get the velocity, direction of motion, and orientation of the fly vel_ctr = diffpad(ctr); % vel_ctr = smoothdata(vel_ctr,1,'movmean',smoothing_window*10); direction_of_motion = smoothdata(mod(unwrap(atan2(vel_ctr(:,2),vel_ctr(:,1))),2*pi),'movmean',smoothing_window); orientation = mod(unwrap((ell(frames,5)*2*pi/360)),2*pi); difference_dir = abs(direction_of_motion-orientation); % Get the component of the velocity in the forward direction speed_ctr = sqrt(sum(vel_ctr.^2,2)); speed{i} = speed_ctr; forward_velocity{i} = cos(difference_dir).*speed{i}; moving_forward{i} = forward_velocity{i} > forward_motion_thresh; end lengths = cellfun(@(x) numel(x),moving_forward); speed = cat(1,speed{:}); moving_forward = cat(1,moving_forward{:}); forward_velocity = cat(1,forward_velocity{:}); fv = forward_velocity; %% Get rasters of when legs are moving in the forward direction % This is meant to replicate "Quantification of % gait parameters in freely walking wild type and sensory deprived % Drosophila melanogaster" Figure 4 Fs = 100; % We want to look at only the leg tips tips = [22 26 30 10 14 18]; % (The order matches the paper) pos_tips = reshape(pos,[],2,size(pos,2)); pos_tips = pos_tips(tips,:,:); dim = 1; % Only look in the x direction traj = squeeze(pos_tips(:,dim,:)); % Get the velocity relative to the center of the fly (egocentric vel) vel = diffpad(traj,2); vel = smoothdata(vel,2,'gauss',smoothing_window); % Define stance to be when the legs move in the negative x direction. stance = vel<0; %% Get the contiguous bouts that will be used for HMM fitting seq = sum(stance)+1; TRGuess = rand(3); EMITGuess = rand(3,7); mf = find(moving_forward); % Get the frames of contiguous bouts endFrames = cumsum(lengths); startFrames = [1 endFrames(1:end-1)'+1]; samples = cell1(numel(lengths)); samples_per_video = 3000; duration_thresh = 50; for i = 1:numel(lengths) bw = bwconncomp(moving_forward(startFrames(i):endFrames(i))); duration = cellfun(@(x) numel(x),bw.PixelIdxList); bw.PixelIdxList(duration < duration_thresh) = []; ids = cat(1,bw.PixelIdxList{:}); ids = ids(1:(min(samples_per_video,numel(ids)))); samples{i} = startFrames(i) + ids - 1; end num_samples = cellfun(@(x) numel(x),samples); samples = cat(1,samples{num_samples>1}); %% Train HMM and get most likely sequence tic;[ESTTR,ESTEMIT] = hmmtrain(seq(samples),TRGuess,EMITGuess);toc mls = hmmviterbi(seq,ESTTR,ESTEMIT); %% Here you should look at the emission probabilities figure; imagesc(ESTEMIT); %% Reorder the labels to 3 = tripod, 4 = tetrapod, 5 = non-canonical % Note: this can be automated, but since initialization is random % and it doesn't take that long to train, I prefer checking manually. mls(mls == 3) = 4; mls(mls == 2) = 5; mls(mls == 1) = 3; %% Save results into a structure hmm.TRGUESS = TRGuess; hmm.EMITGUESS = EMITGuess; hmm.TrainingSamples = uint8(seq(samples)); hmm.ESTTR = ESTTR; hmm.ESTEMIT = ESTEMIT; hmm.most_likely_seq = uint8(mls); % Saving % save_path = 'GaitVector3'; % save(save_path,'hmm'); %% Look at particular section % Limit the window to a particular region % Tripod = 17230751; % Tetrapod = 4781582; % Tetrapod = 8472800 start = 8472800; ids = start:start+100; % Save example gait vectors example_vel = vel(:,ids); example_stance = stance(:,ids); example_fv = fv(ids); % Saving % save_path = 'TripodExample'; % save_path = 'TetrapodExample'; % save(save_path,'example_vel','example_stance','example_fv','Fs'); %% Calculate the distribution of speeds for each hidden state and save mls = hmm.most_likely_seq; speed_lim = [2 35]; % mm/s tri_ids = moving_forward & (mls == 3)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1)); tetra_ids = moving_forward & (mls == 4)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1)); NC_ids = moving_forward & (mls == 5)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1)); [N1,edges1] = histcounts(fv(tri_ids).*Fs,166); [N2,edges2] = histcounts(fv(tetra_ids).*Fs,166); [N3,edges3] = histcounts(fv(NC_ids).*Fs,166); % Saving % save_path = 'Gait_Speed_Distributions'; % save(save_path,'N1','N2','N3','edges1','edges2','edges3','speed_lim') %% Look at the Velocity statistics per Tsne locomotor state density_path = 'Z:\code\2018-05-05_joints_tsne_FlyAging_talmo-labels\viz\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03\density.mat'; density = load(density_path); embedding_ordered_locomotor_states = [7 11 13 10 8 9]; num_states = numel(embedding_ordered_locomotor_states); %% Calculate the velocity distributions. speed_ids = fv > 0 & fv < .4; h = cell1(num_states); N = cell1(num_states); edges = cell1(num_states); for i = 1:num_states state_ids = density.YL == embedding_ordered_locomotor_states(i); [N{i},edges{i}] = histcounts(fv(state_ids & speed_ids)*100,'Normalization','pdf'); end % Saving % save_path = 'Cluster_Velocity_Distributions'; % save(save_path,'N','edges','num_states'); %% Calculate the velocity of legs during swing as you bin velocities differently win = -1:5; speed_levels = [2 5:5:45]; leg_vel_at_speed = zeros(numel(speed_levels)-1,numel(win)); leg_vel_std_at_speed = zeros(numel(speed_levels)-1,numel(win)); % For each body speed level get the velocity of the swings over the window for s = 1:numel(speed_levels)-1 swings = cell([size(vel,1) 1]); inSpeed = (speed'*Fs >= speed_levels(s) & speed'*Fs < speed_levels(s+1)); % Get the swings in which the fly was at the speed level and moving % forward. parfor i = 1:size(vel,1) % First get the swing starts bw = bwconncomp(~stance(i,:)); ids = cell2mat(cf(@(x) x(1),bw.PixelIdxList)); swing_start = false(size(stance(i,:))); swing_start(ids) = true; % Then get the swing starts where the fly is moving forward within % the speed threshold. bw = bwconncomp(swing_start & inSpeed); ids = cell2mat(cf(@(x) x(1),bw.PixelIdxList)); ids = ids' + win; vel_i = vel(i,:); swings{i} = indpad(vel_i,ids); end swings = cat(1,swings{:}); leg_vel_at_speed(s,:) = nanmean(swings,1); leg_vel_std_at_speed(s,:) = nanstd(swings,1); end % Saving % save_path = 'Swing_Velocity_Over_Time'; % save(save_path,'leg_vel_at_speed','leg_vel_std_at_speed','speed_levels','win'); %% Stance / Swing Duration swing_durations = cell([1,size(stance,1)]); stance_durations = cell([1,size(stance,1)]); swing_body_velocities = cell([1,size(stance,1)]); stance_body_velocities = cell([1,size(stance,1)]); period = cell([1,size(stance,1)]); period_velocities = cell([1,size(stance,1)]); % Get the swing, stance, and period duration and velocities parfor i = 1:size(stance,1) % Swing bw = bwconncomp(~stance(i,:)); swing_durations{i} = cellfun(@(x) numel(x),bw.PixelIdxList); swing_body_velocities{i} = cellfun(@(x) mean(fv(x)),bw.PixelIdxList); % Stance bw = bwconncomp(stance(i,:)); stance_durations{i} = cellfun(@(x) numel(x),bw.PixelIdxList); stance_body_velocities{i} = cellfun(@(x) mean(fv(x)),bw.PixelIdxList); % Period % bw = bwconncomp(stance(i,:)); period{i} = cellfun(@(x,y) numel(x(1):y(end)),{bw.PixelIdxList{1:end-1}},{bw.PixelIdxList{2:end}}); period_velocities{i} = cellfun(@(x,y) mean(fv(x(1):y(end))),{bw.PixelIdxList{1:end-1}},{bw.PixelIdxList{2:end}}); end swing_durations = cat(2,swing_durations{:}); stance_durations = cat(2,stance_durations{:}); swing_body_velocities = cat(2,swing_body_velocities{:}); stance_body_velocities = cat(2,stance_body_velocities{:}); period = cat(2,period{:}); period_velocities = cat(2,period_velocities{:}); %% Plot as a line plot stance_vel_thresh = 7.2; % This number is taken from Mendes et al ids = stance_body_velocities*Fs > stance_vel_thresh; xranges = [stance_vel_thresh 50]; yranges = [0 prctile(stance_durations(ids),99)]; stance_edges = xranges(1):1:xranges(2); % Stance Duration vs Velocity X1 = stance_body_velocities(ids)*Fs; Y1 = stance_durations(ids); [X1ids] = discretize(X1,stance_edges); [stance_dur_mu, stance_dur_std] = grpstats(Y1, categorical(X1ids),{'mean','std'}); swing_vel_thresh = 7.2; % This number is taken from Mendes et al ids = swing_body_velocities*Fs > swing_vel_thresh; xranges = [swing_vel_thresh 50]; swing_edges = xranges(1):1:xranges(2); % Stance Duration vs Velocity X2 = swing_body_velocities(ids)*Fs; Y2 = swing_durations(ids); [X2ids] = discretize(X2,swing_edges); [swing_dur_mu, swing_dur_std] = grpstats(Y2, categorical(X2ids),{'mean','std'}); % Saving % save_path = 'Swing_and_Stance_versus_Velocity'; % save(save_path,'stance_dur_mu','swing_dur_mu','stance_dur_std','swing_dur_std','stance_edges','swing_edges') ================================================ FILE: analysis/gait_analysis/gait_analysis_plotting.m ================================================ clear all; %% Look at particular section % Pick the gait example to observe % load('TetrapodExample'); load('TripodExample'); % Plot the velocity of the leg tips figure('pos',[153, 427, 560, 420]); hold on; axis tight; set(gcf,'color','w'); fontsize(16) imagesc(example_vel); xlabel('Time (seconds)') xticklabels(xticks/Fs); ylabel('Leg Tip') yticks([1:6]) yticklabels({'RF','RM','RH','LF','LM','LH'}) ax1 = gca; caxis([-10 10]); % Plot the rasters figure('pos',[850, 634, 848, 334]); hold on; axis tight; set(gcf,'color','w'); fontsize(16) imagesc(example_stance); colormap('gray'); xlabel('Time (seconds)') xticklabels(xticks/Fs); ylabel('Leg Tip') yticks([1:6]) yticklabels({'LF','LM','LH','RF','RM','RH'}) ax2 = gca; % plot the forward velocity of the fly figure('pos',[850, 359, 854, 186]); hold on; axis tight; set(gcf,'color','w'); fontsize(16) plot(example_fv.*Fs) xlabel('Time (seconds)') xticklabels(xticks/Fs); ylabel('Forward Velocity (mm/s)') ax3 = gca; ylim([0 40]); % plot the raster of tripod, tetrapod, or non-canonical figure('pos',[849, 114, 931, 150]); hold on; axis tight; set(gcf,'color','w'); fontsize(16) example_gait = sum(example_stance,1); example_gait(~(example_gait == 3 | example_gait == 4)) = 5; imagesc(example_gait);colormap('jet');h = colorbar; xlabel('Time (seconds)') xticklabels(xticks/Fs); yticks([]) ylabel(h,'Number of legs in stance') ax4 = gca; linkaxes([ax1,ax2,ax3,ax4],'x') %% Plot the emission probabilities for each hidden states load('GaitVectors3.mat'); emissions = hmm.ESTEMIT; temp = emissions(2,:); emissions(2,:) = emissions(3,:); emissions(3,:) = temp; figure; hold on; imagesc(emissions); for i = 1:size(emissions,1) for j = 1:size(emissions,2) caption = sprintf('%.2f',emissions(i,j)); text(j,i,caption,'Fontsize',10,'FontWeight','bold','HorizontalAlignment','center','Color',[0 0 0]); end end axis ij; axis tight xlabel('Number of Legs in Stance'); yticks([1 2 3]); yticklabels({'Tripod','Tetrapod','Non-canonical'}) xticklabels({'0','1','2','3','4','5','6'}) fontsize(16) figure; hold on; plot(emissions','LineWidth',3); xlabel('Number of Legs in Stance'); xticklabels({'0','1','2','3','4','5','6'}) legend({'Tripod','Tetrapod','Non-canonical'}) ylabel('Emission Probability') %% Plot the distribution of speeds load('Gait_Speed_Distributions'); figure; hold on; plot(edges1(1:end-1),N1,'LineWidth',3) plot(edges2(1:end-1),N2,'LineWidth',3) plot(edges3(1:end-1),N3,'LineWidth',3) xlabel('Forward Velocity (ms)') ylabel('Count') xlim(speed_lim) legend({'Tripod','Tetrapod','Non-canonical'}) fontsize(16) %% Plot the velocity Distributions load('Cluster_Velocity_Distributions'); figure; hold on; cmap = spring(num_states); for i = 1:num_states plot(edges{i}(1:end-1),N{i},'Color',cmap(num_states + 1 -i,:),'LineWidth',3); end grid on; xlabel('Forward Velocity') ylabel('Probability') axis tight; ylim([0 .2]) %% Plotting the mean and std with bounded lines load('Swing_Velocity_Over_Time'); cmap = parula(numel(speed_levels)); p_lines = cell1(numel(speed_levels)-1); figure('pos',[568, 186, 1036, 798]); figclosekey; set(gcf,'color','w'); hold on; for i = 1:size(leg_vel_at_speed,1) yci = zeros(2,size(leg_vel_at_speed,2)); yci(1,:) = leg_vel_std_at_speed(i,:); yci(2,:) = leg_vel_std_at_speed(i,:); [p_lines{i},~] = boundedline(win*10,leg_vel_at_speed(i,:),yci','alpha','cmap',cmap(i,:)); p_lines{i}.LineWidth = 3; end % Legend leg = cell([1 numel(speed_levels)-1]); for i = 1:numel(speed_levels)-1 leg{i} = sprintf('%d - %d mm/s',speed_levels(i),speed_levels(i+1)); end l = legend([p_lines{:}],leg); l.Position = [0.7503 0.6488 0.1573 0.3239]; fontsize(16) xlabel('Time from swing onset (ms)') ylabel('Swing velocity (mm/s)') % export_fig('figs/Swing_Velocity_vs_Time_Confidences.png','-r300') %% Plot the Swing_and_Stance_versus_Velocity load('Swing_and_Stance_versus_Velocity') figure; figclosekey, hold on; yci = zeros(2,numel(stance_dur_std)); yci(1,:) = stance_dur_std; yci(2,:) = stance_dur_std; [bl1,~] = boundedline(stance_edges(2:end),stance_dur_mu',yci','alpha'); yci = zeros(2,numel(swing_dur_std)); yci(1,:) = swing_dur_std; yci(2,:) = swing_dur_std; [bl2,~] = boundedline(swing_edges(2:end),swing_dur_mu',yci','alpha','r'); yticklabels(round(yticks*10)) ylabel('Durations (ms)'); xlabel('Average Body Speed (mm/s)'); axis tight legend([bl1,bl2],{'Stance','Swing'}) fontsize(16); ================================================ FILE: analysis/gait_analysis/plot_gait_densities.m ================================================ clear all; %% Plot all three densities overlayed ontop of one another load('Gait_Densities'); figure; hold on; h = imagesc(gait_density./(max(max(max(gait_density))))); axis equal; axis xy; axis off %% Plot the distributions for each mode in the same scale Locomotor_states = [7 11 13 9 10 8]; [cropped,~,crop_mask] = bwcrop(ismember(Ld,Locomotor_states)); figure; hold on; axis xy; axis equal; axis off; cropped_density = reshape(D_tripod(crop_mask),size(cropped,1),[]); h = imagesc(cropped_density); c1 = colorbar; peak = max(c1.Limits); colormap('viridis') figure; hold on; axis xy; axis equal; axis off; cropped_density = reshape(D_tetrapod(crop_mask),size(cropped,1),[]); h = imagesc(cropped_density); % h.AlphaData = 1 .* ~reshape(density.Lbnds(crop_mask),size(cropped,1),[]); c2 = colorbar; colormap('viridis') caxis([0 peak]); figure; hold on; axis xy; axis equal; axis off; cropped_density = reshape(D_NC(crop_mask),size(cropped,1),[]); h = imagesc(cropped_density); % h.AlphaData = 1 .* ~reshape(density.Lbnds(crop_mask),size(cropped,1),[]); colormap('viridis') c3 = colorbar; caxis([0 peak]); ================================================ FILE: data/readme.md ================================================ The full fly dataset and all trained networks used in our paper can be downloaded from: [`http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z`](http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z) Additional datasets: ## BermanFlies _We recommend using this for testing._ - [Cluster sampled images](https://1drv.ms/u/s!AnmpIqqfwz3zgbUg2M8Sa_0NcLhrMg) (**68.7 MiB**) - [Labels for n = 1500 images](https://1drv.ms/u/s!AnmpIqqfwz3zgbUeIuYYDj_A1pCr9Q) (**291 KiB**) - [Trained network model](https://1drv.ms/u/s!AnmpIqqfwz3zgcwUvbqPUn7mXIMLLg) (**53.0 MiB**) - [Full dataset](http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z) (**~168 GiB**) ## CatNect This is the dataset used for the tutorial. It contains a clip of a cat chasing a laser pointer recorded with a Kinect. - [Full clip](https://1drv.ms/u/s!AnmpIqqfwz3zgcwS_3gAJFU0sANBcA) (**105 MiB**) - [Cluster sampled images](https://1drv.ms/u/s!AnmpIqqfwz3zgcwR_9mmNJz8ALW3Hw) (**42.2 MiB**) - [Labels for n = 40 images](https://1drv.ms/u/s!AnmpIqqfwz3zgcwQlKSXXDy9KvIPVg) (**82.3 KiB**) - [Trained network model](https://1drv.ms/u/s!AnmpIqqfwz3zgc0H-v6qbnc3vak2Lw) (**64.7 MiB**) ================================================ FILE: examples/batch_process_video.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Example: Predict body part positions from an MP4 file\n", "This notebook presents an example pipeline for applying a trained LEAP network to ~360k frames read from an MP4 video.\n", "\n", "You can download the data to reproduce the benchmarking results below.\n", "\n", "**Input data:** [072212_163153.mp4](https://1drv.ms/v/s!AnmpIqqfwz3zgcgekCxNp-MN76p1UQ) (254 MiB)\n", "\n", "**Output data:** [072212_163153.preds.h5](https://1drv.ms/u/s!AnmpIqqfwz3zgcgdDhQrKRsBaxvCXQ) (46.9 MiB)\n", "\n", "The trained network can be [downloaded here](https://1drv.ms/u/s!AnmpIqqfwz3zgdpIOzsqojhEmr0J0w) or from the links in the [repository data folder](https://github.com/talmo/leap/tree/master/data)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Anaconda3\\lib\\site-packages\\h5py\\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", " from ._conv import register_converters as _register_converters\n", "Using TensorFlow backend.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Platform: Windows-10-10.0.16299-SP0\n", "h5py:\n", "Summary of the h5py configuration\n", "---------------------------------\n", "\n", "h5py 2.7.1\n", "HDF5 1.10.1\n", "Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]\n", "sys.platform win32\n", "sys.maxsize 9223372036854775807\n", "numpy 1.14.1\n", "\n", "Keras: 2.2.0\n", "Tensorflow: 1.5.0\n", "Devices:\n", "[name: \"/device:CPU:0\"\n", "device_type: \"CPU\"\n", "memory_limit: 268435456\n", "locality {\n", "}\n", "incarnation: 12947954769568633288\n", ", name: \"/device:GPU:0\"\n", "device_type: \"GPU\"\n", "memory_limit: 9143884186\n", "locality {\n", " bus_id: 1\n", "}\n", "incarnation: 16757352010506659625\n", "physical_device_desc: \"device: 0, name: GeForce GTX 1080 Ti, pci bus id: 0000:01:00.0, compute capability: 6.1\"\n", ", name: \"/device:GPU:1\"\n", "device_type: \"GPU\"\n", "memory_limit: 9143884186\n", "locality {\n", " bus_id: 1\n", "}\n", "incarnation: 16188733693954377295\n", "physical_device_desc: \"device: 1, name: GeForce GTX 1080 Ti, pci bus id: 0000:02:00.0, compute capability: 6.1\"\n", "]\n" ] } ], "source": [ "import os\n", "import numpy as np\n", "import cv2\n", "import h5py\n", "from time import time\n", "\n", "import keras\n", "import keras.models\n", "from leap.predict_box import convert_to_peak_outputs\n", "from leap.utils import versions\n", "\n", "versions(list_devices=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Parameters" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Media file path\n", "video_path = \"D:/tmp/072212_163153.mp4\"\n", "\n", "# Trained network path\n", "model_path = \"D:/OneDrive/code/leap/data/BermanFlies/models/180615_025354-n=1500/final_model.h5\"\n", "\n", "# Predictions output path\n", "save_path = \"D:/tmp/072212_163153.preds.h5\"\n", "\n", "# Number of frames to read before predicting (higher = faster, but limited by RAM)\n", "chunk_size = 10000\n", "\n", "# Number of frames to evaluate at once on the GPU (higher = faster, but limited by GPU memory)\n", "batch_size = 64" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Processing" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: D:/OneDrive/code/leap/data/BermanFlies/models/180615_025354-n=1500/final_model.h5\n", " Input: (None, 192, 192, 1)\n", " Output: (None, 3, 32)\n", "Predicted: 10000/361000 frames | Elapsed: 0.4 min / 391.1 FPS / ETA: 15.0 min\n", "Predicted: 20000/361000 frames | Elapsed: 0.8 min / 435.0 FPS / ETA: 13.1 min\n", "Predicted: 30000/361000 frames | Elapsed: 1.1 min / 451.0 FPS / ETA: 12.2 min\n", "Predicted: 40000/361000 frames | Elapsed: 1.5 min / 459.0 FPS / ETA: 11.7 min\n", "Predicted: 50000/361000 frames | Elapsed: 1.8 min / 464.7 FPS / ETA: 11.2 min\n", "Predicted: 60000/361000 frames | Elapsed: 2.1 min / 468.7 FPS / ETA: 10.7 min\n", "Predicted: 70000/361000 frames | Elapsed: 2.5 min / 471.4 FPS / ETA: 10.3 min\n", "Predicted: 80000/361000 frames | Elapsed: 2.8 min / 473.5 FPS / ETA: 9.9 min\n", "Predicted: 90000/361000 frames | Elapsed: 3.2 min / 474.8 FPS / ETA: 9.5 min\n", "Predicted: 100000/361000 frames | Elapsed: 3.5 min / 476.0 FPS / ETA: 9.1 min\n", "Predicted: 110000/361000 frames | Elapsed: 3.8 min / 477.4 FPS / ETA: 8.8 min\n", "Predicted: 120000/361000 frames | Elapsed: 4.2 min / 478.4 FPS / ETA: 8.4 min\n", "Predicted: 130000/361000 frames | Elapsed: 4.5 min / 479.5 FPS / ETA: 8.0 min\n", "Predicted: 140000/361000 frames | Elapsed: 4.8 min / 481.4 FPS / ETA: 7.7 min\n", "Predicted: 150000/361000 frames | Elapsed: 5.2 min / 483.0 FPS / ETA: 7.3 min\n", "Predicted: 160000/361000 frames | Elapsed: 5.5 min / 483.7 FPS / ETA: 6.9 min\n", "Predicted: 170000/361000 frames | Elapsed: 5.9 min / 484.0 FPS / ETA: 6.6 min\n", "Predicted: 180000/361000 frames | Elapsed: 6.2 min / 484.1 FPS / ETA: 6.2 min\n", "Predicted: 190000/361000 frames | Elapsed: 6.5 min / 484.3 FPS / ETA: 5.9 min\n", "Predicted: 200000/361000 frames | Elapsed: 6.9 min / 484.6 FPS / ETA: 5.5 min\n", "Predicted: 210000/361000 frames | Elapsed: 7.2 min / 484.7 FPS / ETA: 5.2 min\n", "Predicted: 220000/361000 frames | Elapsed: 7.6 min / 484.6 FPS / ETA: 4.8 min\n", "Predicted: 230000/361000 frames | Elapsed: 7.9 min / 484.7 FPS / ETA: 4.5 min\n", "Predicted: 240000/361000 frames | Elapsed: 8.3 min / 484.7 FPS / ETA: 4.2 min\n", "Predicted: 250000/361000 frames | Elapsed: 8.6 min / 484.9 FPS / ETA: 3.8 min\n", "Predicted: 260000/361000 frames | Elapsed: 8.9 min / 485.0 FPS / ETA: 3.5 min\n", "Predicted: 270000/361000 frames | Elapsed: 9.3 min / 485.2 FPS / ETA: 3.1 min\n", "Predicted: 280000/361000 frames | Elapsed: 9.6 min / 485.3 FPS / ETA: 2.8 min\n", "Predicted: 290000/361000 frames | Elapsed: 10.0 min / 485.1 FPS / ETA: 2.4 min\n", "Predicted: 300000/361000 frames | Elapsed: 10.3 min / 485.1 FPS / ETA: 2.1 min\n", "Predicted: 310000/361000 frames | Elapsed: 10.7 min / 485.1 FPS / ETA: 1.8 min\n", "Predicted: 320000/361000 frames | Elapsed: 11.0 min / 485.3 FPS / ETA: 1.4 min\n", "Predicted: 330000/361000 frames | Elapsed: 11.3 min / 485.1 FPS / ETA: 1.1 min\n", "Predicted: 340000/361000 frames | Elapsed: 11.7 min / 485.2 FPS / ETA: 0.7 min\n", "Predicted: 350000/361000 frames | Elapsed: 12.0 min / 485.4 FPS / ETA: 0.4 min\n", "Predicted: 360000/361000 frames | Elapsed: 12.4 min / 485.6 FPS / ETA: 0.0 min\n", "Predicted: 361000/361000 frames | Elapsed: 12.4 min / 485.4 FPS / ETA: 0.0 min\n", "Finished predicting 361000 frames.\n", " Prediction | Runtime: 11.55 min / 520.921 FPS\n", " Reading | Runtime: 0.78 min / 7726.516 FPS\n", "Saved: D:/tmp/072212_163153.preds.h5\n", "Total runtime: 12.4 mins\n", "Total performance: 483.585 FPS\n" ] } ], "source": [ "t0_all = time()\n", "\n", "# Load model and convert to peak-coordinate output\n", "model = convert_to_peak_outputs(keras.models.load_model(model_path))\n", "print(\"Model:\", model_path)\n", "print(\" Input:\", str(model.input_shape))\n", "print(\" Output:\", str(model.output_shape))\n", "\n", "# model = keras.utils.multi_gpu_model(model, gpus=2)\n", "\n", "# Open video for reading\n", "reader = cv2.VideoCapture(video_path)\n", "num_samples = int(reader.get(cv2.CAP_PROP_FRAME_COUNT))\n", "\n", "# Initialize\n", "positions_pred = []\n", "conf_pred = []\n", "buffer = []\n", "samples_predicted = 0\n", "reading_runtime = 0\n", "prediction_runtime = 0\n", "done = False\n", "\n", "# Process video chunk-by-chunk\n", "while not done:\n", " t0_reading = time()\n", " # Read and finish if no frame was retrieved\n", " returned_frame, I = reader.read()\n", " done = not returned_frame\n", " reading_runtime += time() - t0_reading\n", " \n", " # Add current frame to buffer\n", " if not done:\n", " buffer.append(I[...,0])\n", " \n", " # Do we have anything to predict?\n", " if len(buffer) >= chunk_size or (done and len(buffer) > 0):\n", " t0_prediction = time()\n", " \n", " # Predict on buffer\n", " Y = model.predict(np.stack(buffer, axis=0)[...,None], batch_size=batch_size)\n", " \n", " # Save\n", " positions_pred.append(Y[:,:2,:].astype(\"int32\"))\n", " conf_pred.append(Y[:,2,:].squeeze())\n", " \n", " # Empty out buffer container\n", " buffer = []\n", " \n", " # Performance stats\n", " samples_predicted += len(Y)\n", " prediction_runtime += time() - t0_prediction\n", " elapsed = time() - t0_all\n", " fps = samples_predicted / elapsed\n", " print(\"Predicted: %d/%d frames | Elapsed: %.1f min / %.1f FPS / ETA: %.1f min\" %\n", " (samples_predicted, num_samples, elapsed / 60, fps, (num_samples - samples_predicted) / fps / 60))\n", " \n", "# Close video reader\n", "reader.release()\n", "\n", "# Merge arrays\n", "positions_pred = np.concatenate(positions_pred, axis=0)\n", "conf_pred = np.concatenate(conf_pred, axis=0)\n", "\n", "# Report performance stats\n", "print(\"Finished predicting %d frames.\" % samples_predicted)\n", "print(\" Prediction | Runtime: %.2f min / %.3f FPS\" % (prediction_runtime / 60, samples_predicted / prediction_runtime))\n", "print(\" Reading | Runtime: %.2f min / %.3f FPS\" % (reading_runtime / 60, samples_predicted / reading_runtime))\n", "\n", "# Save\n", "if os.path.exists(save_path):\n", " os.remove(save_path)\n", "with h5py.File(save_path, \"w\") as f:\n", " f.attrs[\"num_samples\"] = num_samples\n", " f.attrs[\"video_path\"] = video_path\n", " f.attrs[\"model_path\"] = model_path\n", "\n", " ds_pos = f.create_dataset(\"positions_pred\", data=positions_pred, compression=\"gzip\", compression_opts=1)\n", " ds_pos.attrs[\"description\"] = \"coordinate of peak at each sample\"\n", " ds_pos.attrs[\"dims\"] = \"(sample, [x, y], joint) === (sample, [column, row], joint)\"\n", "\n", " ds_conf = f.create_dataset(\"conf_pred\", data=conf_pred, compression=\"gzip\", compression_opts=1)\n", " ds_conf.attrs[\"description\"] = \"confidence map value in [0, 1.0] at peak\"\n", " ds_conf.attrs[\"dims\"] = \"(sample, joint)\"\n", "\n", " total_runtime = time() - t0_all\n", " f.attrs[\"reading_runtime_secs\"] = reading_runtime\n", " f.attrs[\"prediction_runtime_secs\"] = prediction_runtime\n", " f.attrs[\"total_runtime_secs\"] = total_runtime\n", " \n", " \n", "print(\"Saved:\", save_path)\n", "\n", "print(\"Total runtime: %.1f mins\" % (total_runtime / 60))\n", "print(\"Total performance: %.3f FPS\" % (samples_predicted / total_runtime))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" }, "toc": { "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "toc_cell": false, "toc_position": {}, "toc_section_display": "block", "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: examples/hdf5tovid.m ================================================ % Clean start! clear all, clc %% Parameters % Path to input file dataPath = '../data/examples/072212_163153.clip.h5'; % Dataset name dset = '/box'; % Path to output file savePath = 'C:\tmp\072212_163153.clip.mp4'; % Frames to convert at a time (lower this if your memory is limited) chunkSize = 1000; % Framerate for playback of the video file fps = 25; %% Initialize % Get dataset info info = h5info(dataPath, dset); shape = info.Dataspace.Size; numFrames = shape(end); % Check if file already exists if exist(savePath,'file') > 0 warning(['Overwriting existing video file: ' savePath]) delete(savePath) end % Open video for writing writer = VideoWriter(savePath,'MPEG-4'); % use this for MP4s % writer = VideoWriter(savePath,'Motion JPEG AVI'); % use this for AVIs % Set compression quality (higher = bigger file, better quality) writer.Quality = 100; % Set playback speed in frames/second writer.FrameRate = fps; % Open file for writing writer.open(); %% Save framesWritten = 0; done = false; t0 = tic; while ~done % Check how many frames to read chunkFrames = min(chunkSize, numFrames - framesWritten); % Read chunk chunk = h5read(dataPath,dset,[1 1 1 framesWritten+1], [inf inf inf chunkFrames]); % Check for datatype/range concordance (floats must be in [0,1]) if isfloat(chunk) && max(chunk(:)) > 1 chunk = chunk / 255; end % Write frames writer.writeVideo(chunk); % Increment frames written counter framesWritten = framesWritten + size(chunk,4); % Check if we're done done = framesWritten >= numFrames; end elapsed = toc(t0); fprintf('Finished writing %d frames in %.2f mins:\n\t%s\n', framesWritten, elapsed/60, savePath) % Close file writer.close(); ================================================ FILE: examples/vidtohdf5.m ================================================ % Clean start! clear all, clc %% Parameters % Path to input file videoPath = '..\..\leap\data\examples\072212_163153.clip.mp4'; % Path to output file % savePath = '..\..\leap\data\examples\072212_163153.clip.h5'; savePath = 'C:\tmp\072212_163153.clip.h5'; % Frames to convert at a time (lower this if your memory is limited) chunkSize = 1000; % Convert frames to single channel grayscale images (instead of 3 channel RGB) grayscale = true; %% Initialize % Open video for reading vr = VideoReader(videoPath); % Check size from first frame I0 = vr.readFrame(); if grayscale; I0 = rgb2gray(I0); end frameSize = size(I0); if numel(frameSize) == 2; frameSize = [frameSize 1]; end % Reset VideoReader delete(vr); vr = VideoReader(videoPath); % Check if file already exists if exist(savePath,'file') > 0 warning(['Overwriting existing HDF5 file: ' savePath]) delete(savePath) end % Create HDF5 file with infinite number of frames and GZIP compression h5create(savePath,'/box',[frameSize inf],'ChunkSize',[frameSize 1],'Deflate',1,'Datatype','uint8') %% Save buffer = cell(chunkSize,1); done = false; framesRead = 0; framesWritten = 0; t0 = tic; while ~done % Read next frame I = vr.readFrame(); if grayscale; I = rgb2gray(I); end % Check if there are any frames left done = ~vr.hasFrame(); % Increment frames read counter and add to the write buffer framesRead = framesRead + 1; buffer{mod(framesRead-1, chunkSize)+1} = I; % Have we filled the buffer or are there no frames left? if mod(framesRead, chunkSize) == 0 || done % Concatenate the buffer into an array chunk = cat(4, buffer{:}); % Extend the dataset and save to disk h5write(savePath, '/box', chunk, [1 1 1 framesWritten+1], size(chunk)) % Increment frames written counter framesWritten = framesWritten + size(chunk,4); end end elapsed = toc(t0); fprintf('Finished writing %d frames in %.2f mins.\n', framesWritten, elapsed/60) h5disp(savePath) ================================================ FILE: install_leap.m ================================================ function install_leap() %INSTALL_LEAP Installs the Python package and adds MATLAB scripts to path. % Usage: % install_leap % % See also: uninstall_leap, test_leap % Find base repository path (where this file is contained) basePath = fileparts(which('install_leap')); % Add to MATLAB path addpath(genpath(fullfile(basePath,'leap'))); % Check if Python package is importable canImportPython = test_leap(); if ~canImportPython [status,msg] = system(['pip install -e "' basePath '"']); disp(msg) end % Check again test_leap; end ================================================ FILE: leap/__init__.py ================================================ from . import image_augmentation from . import layers from . import models from . import predict_box from . import training from . import utils from . import viz ================================================ FILE: leap/compute_errors.m ================================================ function err = compute_errors(pos_pred, pos_gt) %COMPUTE_ERRORS Computes error rates given predicted and ground truth positions. % Usage: % err = compute_errors(pos_pred, pos_gt) % % Args: % pos_pred: predicted positions (J x 2 x N) % pos_gt: ground truth predictions (J x 2 x N) % % Returns: % err: struct with error metrics % % See also: if isstruct(pos_pred) pos_pred = pos_pred.positions_pred; end if isstruct(pos_gt) pos_gt = pos_gt.positions_pred; end % Find the difference between predicted and ground truth delta = pos_pred - pos_gt; % Find Euclidean distance between each pair of points euclidean = squeeze(sqrt(sum(delta .^ 2, 2)))'; % Compute metrics overall mae_all = mean(abs(delta(:))); mse_all = mean(delta(:) .^ 2); rmse_all = sqrt(mse_all); % Compute metrics per joint delta_rows = reshape(permute(delta,[2 3 1]),[],size(delta,1)); mae = mean(abs(delta_rows)); mse = mean(delta_rows .^ 2); rmse = sqrt(mse); % Return everything err = varstruct(delta, euclidean, ... mae_all, mse_all, rmse_all, ... delta_rows, mae, mse, rmse); end ================================================ FILE: leap/confmaps2pts.m ================================================ function [pts, confvals] = confmaps2pts(C) %CONFMAPS2PTS Convert a set of confidence maps into a set of points. % Usage: % [pts, confvals] = confmaps2pts(C) % % See also: pts2confmaps numChannels = size(C,3); pts = zeros(numChannels,2,'single'); confvals = zeros(numChannels,1,'like',C); for i = 1:numChannels [confvals(i), ind] = max(vert(C(:,:,i))); [r,c] = ind2sub(size(C(:,:,i)),ind); pts(i,:) = [c r]; end end ================================================ FILE: leap/generate_training_set.m ================================================ function savePath = generate_training_set(boxPath, varargin) %GENERATE_TRAINING_SET Creates a dataset for training. % Usage: generate_training_set(boxPath, ...) t0_all = stic; %% Setup defaults = struct(); defaults.savePath = []; defaults.scale = 1; defaults.mirroring = true; % flip images and adjust confidence maps to augment dataset defaults.horizontalOrientation = true; % animal is facing right/left if true (for mirroring) defaults.sigma = 5; % kernel size for confidence maps defaults.normalizeConfmaps = true; % scale maps to [0,1] range defaults.postShuffle = true; % shuffle data before saving (useful for reproducible dataset order) defaults.testFraction = 0; % separate these data from training and validation sets defaults.compress = false; % use GZIP compression to save the outputs params = parse_params(varargin,defaults); % Paths labelsPath = repext(boxPath,'labels.mat'); % Output savePath = params.savePath; if isempty(savePath) savePath = ff(fileparts(boxPath), 'training', [get_filename(boxPath,true) '.h5']); savePath = get_new_filename(savePath,true); end mkdirto(savePath) %% Labels labels = load(labelsPath); % Check for complete frames labeledIdx = find(squeeze(all(all(~isnan(labels.positions),2),1))); numFrames = numel(labeledIdx); printf('Found %d/%d labeled frames.', numFrames, size(labels.positions,3)) % Pull out label data joints = labels.positions(:,:,labeledIdx); joints = joints * params.scale; numJoints = size(joints,1); % Pull out other info jointNames = labels.skeleton.nodes; skeleton = struct(); skeleton.edges = labels.skeleton.edges; skeleton.pos = labels.skeleton.pos; %% Load images stic; box = h5readframes(boxPath,'/box',labeledIdx); if params.scale ~= 1; box = imresize(box,params.scale); end boxSize = size(box(:,:,:,1)); stocf('Loaded %d images', size(box,4)) % Load metadata try exptID = h5read(boxPath, '/exptID'); exptID = exptID(labeledIdx); catch; end try framesIdx = h5read(boxPath, '/framesIdx'); framesIdx = framesIdx(labeledIdx); catch; end try idxs = h5read(boxPath, '/idxs'); idxs = idxs(labeledIdx); catch; end try L = h5read(boxPath, '/L'); L = L(labeledIdx); catch; end try box_no_seg = imresize(h5readframes(boxPath,'/box_no_seg',labeledIdx),params.scale); catch; end try box_raw = imresize(h5readframes(boxPath,'/box_raw',labeledIdx),params.scale); catch; end attrs = h5att2struct(boxPath); %% Generate confidence maps stic; confmaps = NaN([boxSize(1:2), numJoints, numFrames],'single'); parfor i = 1:numFrames pts = joints(:,:,i); confmaps(:,:,:,i) = pts2confmaps(pts,boxSize(1:2),params.sigma,params.normalizeConfmaps); end stocf('Generated confidence maps') % 15 sec for 192x192x32x500 varsize(confmaps) %% Augment by mirroring if params.mirroring % Flip images if params.horizontalOrientation box_flip = flipud(box); try box_no_seg_flip = flipud(box_no_seg); catch; end try box_raw_flip = flipud(box_raw); catch; end confmaps_flip = flipud(confmaps); joints_flip = joints; joints_flip(:,2,:) = size(box,1) - joints_flip(:,2,:); else box_flip = fliplr(box); try box_no_seg_flip = fliplr(box_no_seg); catch; end try box_raw_flip = fliplr(box_raw); catch; end confmaps_flip = fliplr(confmaps); joints_flip = joints; joints_flip(:,1,:) = size(box,2) - joints_flip(:,1,:); end % Check for *L/*R naming pattern (e.g., {{'wingL','wingR'}, {'legR1','legL1'}}) swap_names = {}; baseNames = regexp(jointNames,'(.*)L([0-9]*)$','tokens'); isSymmetric = ~cellfun(@isempty,baseNames); for i = horz(find(isSymmetric)) nameR = [baseNames{i}{1}{1} 'R' baseNames{i}{1}{2}]; if ismember(nameR,jointNames) swap_names{end+1} = {jointNames{i}, nameR}; end end % Swap channels accordingly printf('Symmetric channels:') for i = 1:numel(swap_names) [~,swap_idx] = ismember(swap_names{i}, jointNames); if any(swap_idx == 0); continue; end printf(' %s (%d) <-> %s (%d)', jointNames{swap_idx(1)}, swap_idx(1), ... jointNames{swap_idx(2)}, swap_idx(2)) joints_flip(swap_idx,:,:) = joints_flip(fliplr(horz(swap_idx)),:,:); confmaps_flip(:,:,swap_idx,:) = confmaps_flip(:,:,fliplr(horz(swap_idx)),:); end % Merge [box,flipped] = cellcat({box,box_flip},4); joints = cat(3, joints, joints_flip); try box_raw = cat(4,box_raw,box_raw_flip); catch; end try box_no_seg = cat(4,box_no_seg,box_no_seg_flip); catch; end confmaps = cat(4, confmaps, confmaps_flip); labeledIdx = [labeledIdx(:); labeledIdx(:)]; try exptID = [exptID(:); exptID(:)]; catch; end try framesIdx = [framesIdx(:); framesIdx(:)]; catch; end try idxs = [idxs(:); idxs(:)]; catch; end % Update frame count numFrames = size(box,4); end %% Post-shuffle shuffleIdx = vert(1:numFrames); if params.postShuffle shuffleIdx = randperm(numFrames); box = box(:,:,:,shuffleIdx); labeledIdx = labeledIdx(shuffleIdx); try box_no_seg = box_no_seg(:,:,:,shuffleIdx); catch; end try box_raw = box_raw(:,:,:,shuffleIdx); catch; end try exptID = exptID(shuffleIdx); catch; end try framesIdx = framesIdx(shuffleIdx); catch; end joints = joints(:,:,shuffleIdx); confmaps = confmaps(:,:,:,shuffleIdx); end %% Separate testing set numTestFrames = round(numel(shuffleIdx) * params.testFraction); if numTestFrames > 0 testIdx = randperm(numel(shuffleIdx),numTestFrames); trainIdx = setdiff(shuffleIdx, testIdx); % Test set testing = struct(); testing.shuffleIdx = shuffleIdx(testIdx); testing.box = box(:,:,:,testIdx); testing.labeledIdx = labeledIdx(testIdx); try testing.box_no_seg = box_no_seg(:,:,:,testIdx); catch; end try testing.box_raw = box_raw(:,:,:,testIdx); catch; end try testing.exptID = exptID(testIdx); catch; end try testing.framesIdx = framesIdx(testIdx); catch; end testing.joints = joints(:,:,testIdx); testing.confmaps = confmaps(:,:,:,testIdx); testing.testIdx = testIdx; % Training set shuffleIdx = shuffleIdx(trainIdx); box = box(:,:,:,trainIdx); labeledIdx = labeledIdx(trainIdx); try box_no_seg = box_no_seg(:,:,:,trainIdx); catch; end try box_raw = box_raw(:,:,:,trainIdx); catch; end try exptID = exptID(trainIdx); catch; end try framesIdx = framesIdx(trainIdx); catch; end joints = joints(:,:,trainIdx); confmaps = confmaps(:,:,:,trainIdx); end %% Save % Augment metadata attrs.createdOn = datestr(now); attrs.boxPath = boxPath; attrs.labelsPath = labelsPath; attrs.scale = params.scale; attrs.postShuffle = uint8(params.postShuffle); attrs.horizontalOrientation = uint8(params.horizontalOrientation); % Write stic; if exists(savePath); delete(savePath); end % Training data h5save(savePath,box,[],'compress',params.compress) h5save(savePath,labeledIdx) h5save(savePath,shuffleIdx) try h5save(savePath,box_no_seg,[],'compress',params.compress); catch; end try h5save(savePath,box_raw,[],'compress',params.compress); catch; end try h5save(savePath,exptID); catch; end try h5save(savePath,framesIdx); catch; end h5save(savePath,joints,[],'compress',params.compress) h5save(savePath,confmaps,[],'compress',params.compress) % Testing data if numTestFrames > 0 h5save(savePath,trainIdx) h5savegroup(savePath,testing,'/testing','compress',params.compress) end % Metadata h5writeatt(savePath,'/confmaps','sigma',params.sigma) h5writeatt(savePath,'/confmaps','normalize',uint8(params.normalizeConfmaps)) h5struct2att(savePath,'/',attrs) h5savegroup(savePath,skeleton,'/skeleton') h5writeatt(savePath,'/skeleton','jointNames',strjoin(jointNames,'\n')) stocf('Saved:\n%s', savePath) get_filesize(savePath) stocf(t0_all, 'Finished generating training set.'); end ================================================ FILE: leap/graph2paf.m ================================================ function paf = graph2paf(nodes, edges, sz, channelsOnly, sigma) %GRAPH2PAF Converts a set of edges into part affinity fields. % Usage: % graph2paf(nodes, edges, sz, sigma) % % Args: % nodes: set of points (N x 2) % edges: indices into nodes defining directed edges (E x 2) % sz: grid/image size (1 x 2) % channelsOnly: stack all PAFs along channels (dim 3) instead of dim 4 (default: true) % sigma: maximum distance from edge to keep (default: 5) % % Returns: % paf: part affinity fields (sz(1) x sz(2) x 2E) or (sz(1) x sz(2) x 2 x E) % % See also: pts2confmaps if nargin < 4 || isempty(channelsOnly); channelsOnly = true; end if nargin < 5 || isempty(sigma); sigma = 5; end % Create image coordinate grid [XX,YY] = meshgrid(1:sz(2), 1:sz(1)); % Create PAFs for each edge E = size(edges,1); paf = cell(E,1); for i = 1:E % Pull out edge points src = nodes(edges(i,2),:); dst = nodes(edges(i,1),:); % Edge length L = norm(dst - src, 2); % Unit vectors V = (dst - src) ./ L; % pointing along edge Vp = [-V(:,2), V(:,1)]; % perpendicular % Signed distance along edge D1 = sum(V .* ([XX(:) YY(:)] - src),2); % Absolute distance orthogonal to edge D2 = abs(sum(Vp .* ([XX(:) YY(:)] - src),2)); % Vector field mask paf_mask = reshape(D1 >= 0 & D1 <= L & D2 <= sigma, sz); % Create vector field along channels (X and Y) paf{i} = paf_mask .* permute(V, [1 3 2]); end % Merge all edge PAFs if channelsOnly paf = cat(3, paf{:}); else paf = cat(4, paf{:}); end end ================================================ FILE: leap/guis/label_joints.m ================================================ function label_joints(boxPath, skeletonPath) %LABEL_JOINTS GUI to click on images to yield a graph. % Usage: % label_joints(boxPath) % label_joints(boxPath, skeletonPath) % % See also: make_template_skeleton %% Startup % addpath(genpath('deps')) % Ask for path to data file if nargin < 1 || isempty(boxPath); boxPath = uibrowse('*.h5',[],'Select box HDF5 file'); end % Params if nargin < 2; skeletonPath = []; end recreate_labels = nargin > 1; % force recreate labels file % Settings (saved in *.labels.mat file) global config; config = struct(); config.dsetName = '/box'; config.nodeSize = 10; % size of draggable markers config.defaultNodeColor = [1 0 0]; % default color of movable nodes config.initializedNodeColor = [1 1 0]; % color of initialized nodes config.labeledNodeColor = [0 1 0]; % color of movable nodes with user input config.initialFrame = 1; % first frame displayed config.shuffleFrames = false; % shuffle frame order config.autoSave = true; % save before going to a new frame config.clickNearest = false; % true = click moves nearest node; false = selected node config.draggable = true; % false = cannot drag joint markers config.altArrowsToMoveNodes = true; % false = arrow keys move nodes, alt+arrows changes frames config.zoomBoxFrames = [-250, 250]; % number of frames in the status zoomed in box (pre, post) config.imgFigPos = [835 341 709 709]; % main labeling figure window config.ctrlFigPos = [1545 342 374 708]; % control/reference window config.statusFigPos = [836 33 1081 277]; % status bars and settings window %% % Initialize labeling session box = []; numNodes = []; numFrames = []; numLabeled = []; global labels; % Loads or creates *.labels.mat and populates config initializeLabels(); % Pre-shuffle frames for shuffle mode shuffleIdx = randperm(numFrames); % Set status colormap colors statusCmap = { config.defaultNodeColor config.initializedNodeColor config.labeledNodeColor }; for k = 1:numel(statusCmap) if ischar(statusCmap{k}); statusCmap{k} = colorCode2rgb(statusCmap{k}); end end statusCmap = cellcat(statusCmap,1); % Zoom box convenience (compute window) if isscalar(config.zoomBoxFrames); config.zoomBoxFrames = round([-0.5 0.5] .* config.zoomBoxFrames); end zoomBoxWindow = config.zoomBoxFrames(1):config.zoomBoxFrames(2); function initializeLabels() labels = struct(); % Metadata labels.boxPath = boxPath; labels.savePath = repext(boxPath, '.labels.mat'); % Ask for path to skeleton file if isequal(skeletonPath,true) || ~exists(labels.savePath) skeletonPath = uibrowse('*.mat',[],'Select skeleton MAT file'); end % Open box file box = h5file(boxPath, config.dsetName); numFrames = size(box,4); stic; if ~exists(labels.savePath) || recreate_labels % Load template skeleton labels.skeletonPath = skeletonPath; labels.skeleton = load(skeletonPath); % Initialize custom defaults container labels.initialization = NaN(numel(labels.skeleton.nodes), 2, numFrames, 'single'); % Try using initialization built into the HDF5 file try labels.initialization = h5read(boxPath, '/initialization'); labels.initialization_metadata = h5att2struct(boxPath, '/initialization'); printf('Using pre-initialized joint predictions.') catch end % Initialize user labels labels.positions = NaN(numel(labels.skeleton.nodes), 2, numFrames, 'single'); % Settings labels.config = config; % Timestamps labels.createdOn = datestr(now); labels.lastModified = datestr(now); % Initialize history labels.session = 1; addToHistory("Created labels file."); % Create labels file save(labels.savePath, '-struct', 'labels', '-v7.3') stocf('Created labels file: %s', labels.savePath) else % Load labels = load(labels.savePath); % Update paths labels.boxPath = boxPath; labels.savePath = repext(boxPath, '.labels.mat'); % Update config if isfield(labels,'config') config = parse_params(labels.config,config); else labels.config = config; end if ~isfield(labels,'session') labels.session = 1; else labels.session = labels.session + 1; end stocf('Loaded existing labels file: %s', labels.savePath) end addToHistory('Started session.') % Convenience numNodes = numel(labels.skeleton.nodes); end function addToHistory(message) % Utility for adding a timestamped message to the history log session = labels.session; timestamp = datetime(); message = string(message); historyItem = table(session, timestamp, message); disp(historyItem) if ~isfield(labels,'history') || isempty(labels.history) labels.history = historyItem; else labels.history = [labels.history; historyItem]; end end %% GUI % Build GUI global ui; initializeGUI(); function initializeGUI() ui = struct(); % %%%% Controls figure %%%% ui.ctrl = struct(); ui.ctrl.fig = figure('NumberTitle','off','MenuBar','none', ... 'Name','LEAP Label GUI', 'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ... 'Position', config.ctrlFigPos); ui.ctrl.hbox = uix.HBox('Parent', ui.ctrl.fig); % Joints panel ui.ctrl.jointsPanel = uix.Panel('Parent',ui.ctrl.hbox, 'Title', 'Joints', 'Padding',5); ui.ctrl.jointsList = uicontrol(ui.ctrl.jointsPanel, 'Style', 'listbox', 'String', labels.skeleton.joints.name, ... 'Callback',@(h,~,~)selectNode(h.Value)); % Reference image ui.ctrl.refPanel = uix.Panel('Parent',ui.ctrl.hbox, 'Title', 'Reference', 'Padding',5); ui.ctrl.refAx = axes(uicontainer('Parent',ui.ctrl.refPanel)); ui.ctrl.refImg = imagesc(labels.skeleton.refI); % Style ui.ctrl.refAx.Units = 'normalized'; ui.ctrl.refAx.Position = [0 0 1 1]; axis(ui.ctrl.refAx,'equal','tight','ij') colormap(ui.ctrl.refAx,'gray') noticks(ui.ctrl.refAx) hold(ui.ctrl.refAx,'on') % Plot reference skeleton for i = 1:size(labels.skeleton.segments,1) % Find default position of each nodes in the segment pos = labels.skeleton.pos(labels.skeleton.segments.joints_idx{i},:); % Plot plot(ui.ctrl.refAx, pos(:,1), pos(:,2), '.-', 'Color',labels.skeleton.segments.color{i}, 'LineWidth', 1); end % Draw each joint node ui.ctrl.refNodes = gobjects(height(labels.skeleton.joints),1); for i = 1:numel(ui.ctrl.refNodes) pos = labels.skeleton.joints.pos(i,:); ui.ctrl.refNodes(i) = plot(ui.ctrl.refAx, pos(1),pos(2),'o', 'Color','r'); end % Set box widths ui.ctrl.hbox.Widths = [-1 -3]; %%%% % %%%% Image figure %%%% ui.img = struct(); ui.img.fig = figure('NumberTitle','off','MenuBar','none','ToolBar','none', ... 'Name',sprintf('Frame %d/%d', config.initialFrame, numFrames), 'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ... 'Position', config.imgFigPos); ui.img.ax = axes(ui.img.fig); ui.img.img = imagesc(ui.img.ax, box(:,:,:,1)); ui.img.img.ButtonDownFcn = @(~,~) clickImage(); % Full figure image axes ui.img.ax.Units = 'normalized'; ui.img.ax.Position = [0 0 1 1]; % Style axis(ui.img.ax,'equal','tight','ij') colormap(ui.img.ax,'gray') noticks(ui.img.ax) hold(ui.img.ax,'on') % Initialize skeleton drawing container ui.skel = struct(); ui.skel.segs = []; ui.skel.nodes = []; %%%% % %%%% Status figure %%%% % Initialize status container ui.status = struct(); ui.status.selectedNode = []; ui.status.movedNodes = false(numNodes,1); ui.status.currentFrame = config.initialFrame; ui.status.unsavedChanges = false(numFrames,1); ui.status.initialPos = []; % Get full status indicators for all frames status = getStatus(); numInitialized = sum(all(status == 1,1)); numLabeled = sum(all(status == 2,1)); % Create figure window ui.status.fig = figure('NumberTitle','off','MenuBar','none','ToolBar','none', ... 'Name',sprintf('Status: %d/%d (%.2f%%) labeled', numLabeled, numFrames, numLabeled/numFrames*100), ... 'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ... 'Position', config.statusFigPos); ui.status.hbox = uix.HBox('Parent', ui.status.fig, 'Padding',3); % Status panel (left) ui.status.statusPanel = uix.Panel('Parent',ui.status.hbox, 'Title','Status', 'Padding',5); ui.status.statusBoxes = uix.VBox('Parent', ui.status.statusPanel); % Status text ui.status.stats = uix.VBox('Parent',ui.status.statusBoxes); ui.status.framesInitialized = uicontrol(ui.status.stats,'Style','text','HorizontalAlignment','left',... 'String',sprintf('Initialized: %d/%d (%.3f%%)', numInitialized, numFrames, numInitialized/numFrames*100)); ui.status.framesLabeled = uicontrol(ui.status.stats,'Style','text','HorizontalAlignment','left',... 'String',sprintf('Labeled: %d/%d (%.3f%%)', numLabeled, numFrames, numLabeled/numFrames*100)); ui.status.stats.Heights = ones(1, numel(ui.status.stats.Children)) * 15; % Status bars ui.status.fullAx = axes(uicontainer('Parent',ui.status.statusBoxes)); ui.status.fullImg = imagesc(ui.status.fullAx, 1:numFrames, 1:numNodes, status, 'ButtonDownFcn', @clickStatusbar); axis(ui.status.fullAx,'tight','ij') hold(ui.status.fullAx,'on'); zoomBoxIdx = zoomBoxWindow + ui.status.currentFrame; zoomBoxPts = [ zoomBoxIdx(1) 0 zoomBoxIdx(end) 0 zoomBoxIdx(end) numNodes zoomBoxIdx(1) numNodes zoomBoxIdx(1) 0 ]; ui.status.fullZoomBox = patch(ui.status.fullAx, zoomBoxPts(:,1),zoomBoxPts(:,2),'w','PickableParts','none'); ui.status.fullZoomBox.FaceAlpha = 0.25; ui.status.fullZoomBox.EdgeColor = 'w'; colormap(ui.status.fullAx, statusCmap) caxis(ui.status.fullAx,[0 2]) ui.status.fullAx.XLim = [-0.5 0.5] + [1 numFrames]; ui.status.fullAx.YLim = [-0.5 0.5] + [1 numNodes]; % ui.status.fullAx.YTick = 1:numNodes; % ui.status.fullAx.YTickLabel = labels.skeleton.nodes; % ui.status.fullAx.YAxis.TickLabelInterpreter = 'none'; % Status bars (zoomed) ui.status.zoomAx = axes(uicontainer('Parent',ui.status.statusBoxes)); ui.status.zoomImg = imagesc(ui.status.zoomAx, zoomBoxIdx, 1:numNodes, zeros(numNodes,numel(zoomBoxIdx)), 'ButtonDownFcn', @clickStatusbar); axis(ui.status.zoomAx,'tight','ij') colormap(ui.status.zoomAx, statusCmap) caxis(ui.status.zoomAx,[0 2]) ui.status.zoomAx.YLim = [-0.5 0.5] + [1 numNodes]; % ui.status.zoomAx.YTick = 1:numNodes; % ui.status.zoomAx.YTickLabel = labels.skeleton.nodes; % ui.status.zoomAx.YAxis.TickLabelInterpreter = 'none'; % Set UI heights ui.status.statusBoxes.Heights = [sum(ui.status.stats.Heights)+5 -1 -1]; % Settings panel (right) ui.status.configPanel = uix.Panel('Parent',ui.status.hbox, 'Title','Settings','Padding',5); ui.status.configButtons = uix.VBox('Parent',ui.status.configPanel); % Auto-save uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.autoSave, ... 'Callback',@(h,~)setConfig('autoSave',h.Value), ... 'String','Autosave labels','TooltipString','Automatically saves changes to disk when changing frames or exiting.'); % Shuffle frame order uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.shuffleFrames, ... 'Callback',@(h,~)setConfig('shuffleFrames',h.Value), ... 'String','Shuffle frame order','TooltipString','Shuffled order is fixed within this session. Uncheck to use file ordering.'); % Click nearest uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.clickNearest, ... 'Callback',@(h,~)setConfig('clickNearest',h.Value), ... 'String','Click to move nearest joint','TooltipString','If unchecked, clicking on the image moves the currently selected joint.'); % Draggable markers uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.draggable, ... 'Callback', @(h,~)toggleDraggableMarkers(h.Value), ... 'String','Draggable markers','TooltipString','If unchecked, joint markers can only be moved by clicking or keyboard.'); % Alt + arrows to move nodes uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.altArrowsToMoveNodes, ... 'Callback', @(h,~)setConfig('altArrowsToMoveNodes',h.Value), ... 'String','Alt + arrow keys move markers','TooltipString','If unchecked, move markers with Alt + arrow keys, change frames with arrow keys.'); % Export confidence maps uicontrol(ui.status.configButtons,'Style','pushbutton', ... 'Callback', @(h,~)generateTrainingSet(), ... 'String','Generate training set','TooltipString','Creates a test set with confidence maps for training a network.'); % Fast training uicontrol(ui.status.configButtons,'Style','pushbutton', ... 'Callback', @(h,~)fastTrain(), ... 'String','Fast train network','TooltipString','Trains a network for initialization using fast presets.'); % Initialization from predictions uicontrol(ui.status.configButtons,'Style','pushbutton', ... 'Callback', @(h,~)predictInitializations(), ... 'String','Initialize with trained model','TooltipString','Generates predictions for all frames and uses it as initialization.'); % Set UI sizes ui.status.configButtons.Heights = ones(1, numel(ui.status.configButtons.Children)) * 25; ui.status.hbox.Widths = [-1 175]; % Give focus back to main image window figure(ui.img.fig); end function toggleDraggableMarkers(TF) % Sets whether joint markers are draggable using the mouse if TF set(ui.skel.nodes,'PickableParts','visible'); draggable(ui.skel.nodes, @nodesMoved, 'endFcn', @nodesMoveEnd); else draggable(ui.skel.nodes, 'off'); set(ui.skel.nodes,'PickableParts','none'); end setConfig('draggable',TF); end function setConfig(configField, value) % Helper to set config fields to specified value config.(configField) = value; end function quit(h,~) % Quit callback to close all windows simultaneously % Log to history addToHistory("Finished session.") % Save if config.autoSave && isequal(h, ui.img.fig) saveLabels(); end % Delete figs delete(ui.img.fig) delete(ui.ctrl.fig) delete(ui.status.fig) end function keyPress(~,evt) % Hotkeys % exclusive modifier flags: noModifier = isempty(evt.Modifier); shiftOnly = isequal(evt.Modifier, {'shift'}); ctrlOnly = isequal(evt.Modifier, {'control'}); altOnly = isequal(evt.Modifier, {'alt'}); % non-exclusive: altPressed = ismember({'alt'}, evt.Modifier); ctrlPressed = ismember({'control'}, evt.Modifier); shiftPressed = ismember({'shift'}, evt.Modifier); switch evt.Key case 'q' delete(ui.img.fig) case 's' saveLabels() case 'r' if noModifier % current node resetNodes(ui.status.selectedNode); elseif shiftOnly % all nodes resetNodes(); end case 'd' if noModifier % current node setNodesToDefault(ui.status.selectedNode); elseif shiftOnly % all nodes setNodesToDefault(); end case 'tab' if noModifier selectNode(mod(ui.status.selectedNode-1+1, numNodes) + 1); elseif shiftOnly selectNode(mod(ui.status.selectedNode-1-1, numNodes) + 1); end case 'downarrow' dXY = [0 1]; if (config.altArrowsToMoveNodes && altPressed) || ~config.altArrowsToMoveNodes if noModifier nudgeNode(dXY) elseif shiftOnly nudgeNode(dXY * 5) elseif ctrlOnly nudgeSegment(dXY) end end case 'uparrow' dXY = [0 -1]; if (config.altArrowsToMoveNodes && altPressed) || ~config.altArrowsToMoveNodes if noModifier nudgeNode(dXY) elseif shiftOnly nudgeNode(dXY * 5) elseif ctrlOnly nudgeSegment(dXY) end end case 'leftarrow' if (config.altArrowsToMoveNodes && altPressed) || (~config.altArrowsToMoveNodes && ~altPressed) dXY = [-1 0] - (shiftPressed * 4); if ctrlPressed; nudgeSegment(dXY); else; nudgeNode(dXY); end else dt = -1 - (shiftPressed * 4); if config.shuffleFrames idx = find(shuffleIdx == ui.status.currentFrame); goToFrame(shuffleIdx(mod(idx-1+dt, numFrames) + 1)) else goToFrame(mod(ui.status.currentFrame-1+dt, numFrames) + 1) end end case 'rightarrow' if (config.altArrowsToMoveNodes && altPressed) || (~config.altArrowsToMoveNodes && ~altPressed) dXY = [1 0] + (shiftPressed * 4); if ctrlPressed; nudgeSegment(dXY); else; nudgeNode(dXY); end else dt = 1 + (shiftPressed * 4); if config.shuffleFrames idx = find(shuffleIdx == ui.status.currentFrame); goToFrame(shuffleIdx(mod(idx-1+dt, numFrames) + 1)) else goToFrame(mod(ui.status.currentFrame-1+dt, numFrames) + 1) end end case 'space' % Get labeling status for all frames labeled = getStatus() == 2; % Consider current joint only if shift is pressed if shiftPressed; labeled = labeled(ui.status.selectedNode,:); else; labeled = all(labeled,1); end % Find unlabeled frames excluding current frame unlabeledIdxs = setdiff(find(labeled), ui.status.currentFrame); if ~isempty(unlabeledIdxs) if ctrlPressed % Go to random unlabeled frame goToFrame(datasample(unlabeledIdxs,1)); else % Go to first unlabeled frame goToFrame(unlabeledIdxs(1)) end end case 'g' % go to frame dialog % if ctrlOnly % TODO: % - custom dialog box that starts focused on the textbox % and returns after pressing Enter/Esc answer = inputdlg('Skip to frame index:','Skip to frame',1,{num2str(ui.status.currentFrame)}); try idx = round(str2double(answer)); if idx >= 1 && idx <= numFrames goToFrame(idx); end catch end % end case 'f' markAllCorrect(); otherwise % evt end end function clickImage() % Callback to image clicks (but not on nodes) % Pull out clicked point coordinate pt = ui.img.ax.CurrentPoint(1,1:2); % Get current node positions pos = getNodePositions(); if config.clickNearest % Find nearest node location i = argmin(rownorm(pos - pt)); else % Use current selection i = ui.status.selectedNode; end % Update node position pos(i,:) = pt; updateSkeleton(pos); end function clickStatusbar(h,evt) % Callback for seeking via mouse-click on the status bars if evt.Button == 1 idx = clip(round(evt.IntersectionPoint(1)),[1 numFrames]); goToFrame(idx); end end function status = getStatus(idx) % Utility function that checks labels for completeness status % Returns [numJoints x numel(idx)] matrix with values: % 0: default % 1: initialized % 2: labeled % Get status for all frames by default if nargin < 1; idx = 1:numFrames; end % Initialize as default (0) status = zeros(numNodes, numel(idx)); % Check for initialization isInitialized = squeeze(all(~isnan(labels.initialization(:,:,idx)),2)); status(isInitialized) = 1; % Check for user labels isLabeled = squeeze(all(~isnan(labels.positions(:,:,idx)),2)); status(isLabeled) = 2; end %% Training and dataset generation function predictInitializations(modelPath) % Generates predictions for the entire dataset and uses those for % initialization of unlabeled frames. if nargin < 1 || isempty(modelPath) modelPath = uibrowse([],[],'Select model folder...', 'dir'); if isempty(modelPath) || ~exists(modelPath); return; end end % TODO: better system for choosing final vs best validation model % if exists(ff(modelPath, 'final_model.h5')) % numValidationSamples = numel(loadvar(ff(modelPath,'training_info.mat'),'val_idx')); % % numWeights = numel(dir_files(ff(modelPath,'weights'))); % if numValidationSamples < 500 % modelPath = ff(modelPath,'final_model.h5'); % end % end numValidationSamples = numel(loadvar(ff(modelPath,'training_info.mat'),'val_idx')); if exists(ff(modelPath, 'best_model.h5')) && numValidationSamples > 500 modelPath = ff(modelPath, 'best_model.h5'); else modelPath = ff(modelPath, 'final_model.h5'); end % Predict preds = predict_box(boxPath, modelPath, false); % Save labels.initialization = preds.positions_pred; saveLabels(); % Update status isInitialized = squeeze(all(~isnan(labels.initialization),2)); numInitialized = sum(all(isInitialized,1)); ui.status.framesInitialized.String = sprintf('Initialized: %d/%d (%.2f%%)', numInitialized, numFrames, numInitialized/numFrames*100); % Update status bars status = getStatus(); ui.status.fullImg.CData = status; zoom_idx = ui.status.zoomImg.XData > 0 & ui.status.zoomImg.XData <= size(status,2); ui.status.zoomImg.CData(:,zoom_idx) = status(:,ui.status.zoomImg.XData(zoom_idx)); % Log event addToHistory(['Initialized with model: ' modelPath]) % Calculate error rate on labels labeled = all(getStatus() == 2,1); pos_gt = labels.positions(:,:,labeled); pos_pred = labels.initialization(:,:,labeled); pred_metrics = compute_errors(pos_pred,pos_gt); % Display errors printf('Error: mean = %.2f, s.d. = %.2f', mean(pred_metrics.euclidean(:)), std(pred_metrics.euclidean(:))) prcs = [50 75 90]; prc_errs = prctile(pred_metrics.euclidean(:), prcs); for i = 1:numel(prcs) printf(' %d%% = %.3f', prcs(i), prc_errs(i)) end % Replot goToFrame(ui.status.currentFrame); end function generateTrainingSet() % Default save path defaultSavePath = ff(fileparts(boxPath), 'training', [get_filename(boxPath,true) '.h5']); defaultSavePath = get_new_filename(defaultSavePath,true); % Create dialog with parameters [params, buttonPressed] = settingsdlg(... 'WindowWidth', 400,... 'title','Generate a training set', ... 'Description','Export a dataset for training based on the current labels.',... 'separator','General options',... {'Save path';'savePath'}, defaultSavePath, ... {'Scale - for resizing images';'scale'}, 1, ... {'Sigma - kernel size for confidence maps';'sigma'}, 5, ... {'Test set fraction - held out frames';'testFraction'},0.1,... {'Shuffle - randomize saved dataset order';'postShuffle'}, true, ... {'Compress - reduce file size, but slower to load';'compress'}, true, ... 'separator','Data mirroring',... {'Mirror images - augment by flipping along the body axis';'mirroring'}, [true, false], ... {'Animal orientation';'animalOrientation'}, {'left/right','top/bottom'} ... ); % Cancel if OK was not pressed (cancel or window closed) if ~strcmpi(buttonPressed,'ok'); return; end % Convert listbox input to boolean for orientation params.horizontalOrientation = strcmpi(params.animalOrientation,'left/right'); % Check for existing save path if exists(params.savePath) answer = questdlg('Save path already exists, overwrite existing file?', 'Overwrite file', 'Overwrite', 'Cancel', 'Overwrite'); if ~strcmpi(answer, 'Overwrite'); return; end end % Run! generate_training_set(boxPath,params); % Log action addToHistory('Generated training set.') end function fastTrain() % Generate a training set for fast training from current labels % Build default output path runName = sprintf('%s-n=%d', datestr(now,'yymmdd_HHMMSS'), numLabeled); defaultModelsFolder = ff(fileparts(boxPath), 'models'); % Create dialog with parameters [params, buttonPressed] = settingsdlg(... 'WindowWidth', 500,... 'title','Fast training', ... 'Description','Quickly train a model using current labels and predict on remaining frames as initialization.',... 'separator','Dataset',... {'Scale - for resizing images';'scale'}, 1, ... {'Sigma - kernel size for confidence maps';'sigma'}, 5, ... 'separator','Data mirroring',... {'Mirror images - augment by flipping along the body axis';'mirroring'}, [true, false], ... {'Animal orientation';'animalOrientation'}, {'left/right','top/bottom'}, ... 'separator','Model',... {'Network architecture';'netName'},{'leap_cnn','hourglass','stacked_hourglass'},... {'Filters - base number of filters for model';'filters'},32,... {'Upsampling layers - use bilinear upsampling instead of transposed conv';'upsamplingLayers'},true,... 'separator','Training',... {'Model path - folder to save run data to';'modelsFolder'},defaultModelsFolder,... {'Rotate angle - augment data via random rotations';'rotateAngle'},5,... {'Validation set fraction - frames used for validation';'valSize'},0.1,... {'Epochs - number of rounds of training';'epochs'},15,... {'Batch size - number of samples per batch';'batchSize'},50,... {'Batches per epoch - number of batches of samples per round';'batchesPerEpoch'},50,... {'Validation batches per epoch - number of batches to use for validation';'valBatchesPerEpoch'},10,... {'Save every epoch - save weights from every epoch instead of just best+final';'saveEveryEpoch'},false,... 'separator','Training (advanced)',... {'Reduce LR factor - drop learning rate when loss plateaus';'reduceLRFactor'},0.1,... {'Reduce LR patience - wait after loss plateaus before reducing LR';'reduceLRPatience'},2,... {'Reduce LR cooldown - wait after reducing LR before detecting plateau';'reduceLRCooldown'},0,... {'Reduce LR min delta - minimum change in loss to not plateau';'reduceLRMinDelta'},1e-5,... {'Reduce LR min LR - minimum LR to not drop below';'reduceLRMinLR'},1e-10,... {'AMSGrad - optimizer variant for more emphasis on rare data';'amsgrad'},true ... ); % Cancel if OK was not pressed (cancel or window closed) if ~strcmpi(buttonPressed,'ok'); return; end % Convert listbox input to boolean for orientation params.horizontalOrientation = strcmpi(params.animalOrientation,'left/right'); % Generate temporary training set file dataPath = [tempname '.h5']; dataPath = generate_training_set(boxPath,'savePath',dataPath,... 'scale',params.scale,... 'mirroring',params.mirroring,... 'horizontalOrientation',params.horizontalOrientation,... 'sigma',params.sigma, ... 'normalizeConfmaps',true,... 'postShuffle',true, ... 'testFraction',0); % Log action addToHistory(sprintf('Fast training (n = %d)', numLabeled)) % Create CLI command for training basePath = fileparts(funpath(true)); cmd = { 'python' ['"' ff(basePath, 'training.py') '"'] ['"' dataPath '"'] ['--base-output-path="' params.modelsFolder '"'] ['--run-name="' runName '"'] ['--net-name="' params.netName '"'] sprintf('--filters=%d',params.filters) sprintf('--rotate-angle=%d', params.rotateAngle) sprintf('--val-size=%.5f', params.valSize) sprintf('--epochs=%d', params.epochs) sprintf('--batch-size=%d', params.batchSize) sprintf('--batches-per-epoch=%d', params.batchesPerEpoch) sprintf('--val-batches-per-epoch=%d', params.valBatchesPerEpoch) sprintf('--reduce-lr-factor=%.10f', params.reduceLRFactor) sprintf('--reduce-lr-patience=%d', params.reduceLRPatience) sprintf('--reduce-lr-cooldown=%d', params.reduceLRCooldown) sprintf('--reduce-lr-min-delta=%.10f', params.reduceLRMinDelta) sprintf('--reduce-lr-min-lr=%.10f', params.reduceLRMinLR) }; if params.upsamplingLayers; cmd{end+1} = '--upsampling-layers'; end if params.saveEveryEpoch; cmd{end+1} = '--save-every-epoch'; end if params.amsgrad; cmd{end+1} = '--amsgrad'; end cmd = strjoin(cmd); disp(cmd) % Train! try exit_code = system(cmd); % [exit_code,cmd_output] = system(cmd); catch ME delete(dataPath) rethrow(ME) end delete(dataPath) % TODO: parse this out from python output? modelPath = ff(params.modelsFolder, runName); % Run trained model on data to initialize labels if exists(ff(modelPath, 'final_model.h5')) predictInitializations(modelPath) end end %% Ploting initializeSkeleton(); function initializeSkeleton() % Creates graphics objects representing the interactive skeleton % Draw each line segment if ~isempty(ui.skel.segs); delete(ui.skel.segs); end ui.skel.segs = gobjects(size(labels.skeleton.segments,1),1); for i = 1:numel(ui.skel.segs) % Find default position of each nodes in the segment pos = labels.skeleton.pos(labels.skeleton.segments.joints_idx{i},:); % Plot ui.skel.segs(i) = plot(ui.img.ax, pos(:,1), pos(:,2), '.-', ... 'Color',labels.skeleton.segments.color{i}); % Add metadata ui.skel.segs(i).UserData.seg_idx = i; ui.skel.segs(i).UserData.seg_joints_idx = labels.skeleton.segments.joints_idx{i}; end % Clicks on the skeleton edges should pass through to the image set(ui.skel.segs, 'PickableParts', 'none'); % Draw each joint node if ~isempty(ui.skel.nodes); delete(ui.skel.nodes); end % status = getStatus(ui.status.currentFrame); statusCmap(status(i)+1,:) ui.skel.nodes = gobjects(height(labels.skeleton.joints),1); for i = 1:numel(ui.skel.nodes) ui.skel.nodes(i) = plot(ui.img.ax,labels.skeleton.joints.pos(i,1),labels.skeleton.joints.pos(i,2),'o',... 'Color','w', 'LineWidth', 1, 'PickableParts','none'); ui.skel.nodes(i).UserData.node_idx = i; end % Make movable and add callbacks if config.draggable set(ui.skel.nodes, 'PickableParts','visible'); draggable(ui.skel.nodes, @nodesMoved, 'endFcn', @nodesMoveEnd); end end function pos = getNodePositions() % Utility function that returns node positions from the corresponding graphics objects pos = NaN(numel(ui.skel.nodes),2); for i = 1:numel(ui.skel.nodes) pos(i,:) = [ui.skel.nodes(i).XData ui.skel.nodes(i).YData]; end end function updateSkeleton(pos) % Updates pre-initialiazed skeleton graphics objects if nargin < 1 % Get current node positions from graphics pos = getNodePositions(); else % Update node positions for i = 1:size(pos,1) % Check for modification to graphics positions old_pos = [ui.skel.nodes(i).XData ui.skel.nodes(i).YData]; if ~isequal(pos(i,:), old_pos) % Update graphics ui.skel.nodes(i).XData = pos(i,1); ui.skel.nodes(i).YData = pos(i,2); end end end % Check for changes for i = 1:numNodes if ~isequal(pos(i,:), ui.status.initialPos(i,:)) % Mark node as moved ui.status.movedNodes(i) = true; % Denote unsaved changes ui.status.unsavedChanges(ui.status.currentFrame) = true; end end % Set defaults set(ui.skel.nodes, 'Marker', 'o'); % Default marker (no changes) set(ui.skel.nodes, 'MarkerSize', config.nodeSize); % Default size (unselected) set(ui.ctrl.refNodes, 'MarkerSize', config.nodeSize); % Default size (unselected) % Update node colors based on status status = getStatus(ui.status.currentFrame); for i = 1:numNodes % Set status color ui.skel.nodes(i).Color = statusCmap(status(i)+1,:); % Uncommitted changes if ui.status.movedNodes(i); ui.skel.nodes(i).Marker = 's'; end % Selected node if ui.status.selectedNode == i ui.skel.nodes(i).MarkerSize = 9; ui.ctrl.refNodes(i).MarkerSize = 9; end end % Update edges for i = 1:numel(ui.skel.segs) ui.skel.segs(i).XData(:) = pos(ui.skel.segs(i).UserData.seg_joints_idx,1); ui.skel.segs(i).YData(:) = pos(ui.skel.segs(i).UserData.seg_joints_idx,2); end drawnow; end function nodesMoved(h) % Called while node is being moved to update skeleton % Get node index node_idx = h.UserData.node_idx; % Set selected node if ui.status.selectedNode ~= node_idx selectNode(node_idx) end % Update updateSkeleton() end function nodesMoveEnd(h) % Called when the node is released after moving % Get node index node_idx = h.UserData.node_idx; % Set selected node if ui.status.selectedNode ~= node_idx selectNode(node_idx) end % Update updateSkeleton() end function selectNode(i) % Utility function that sets the selected node across the entire GUI % Check for changes previousSelection = ui.status.selectedNode; if ~isequal(previousSelection, i) % Set selected node ui.status.selectedNode = i; % Update listbox ui.ctrl.jointsList.Value = i; % Update graphics updateSkeleton(); end end function nudgeNode(dXY, i) % Utility function for moving a node by a delta amount if nargin < 2; i = ui.status.selectedNode; end % Get and update node position pos = getNodePositions(); pos(i,:) = pos(i,:) + dXY; % Update updateSkeleton(pos); end function nudgeSegment(dXY, i) % Utility function for moving all segments with a node by a delta amount if nargin < 2; i = ui.status.selectedNode; end % Find each segment with the current node and pull out all nodes seg_nodes = {}; for j = 1:height(labels.skeleton.segments) idx = labels.skeleton.segments.joints_idx{j}; if any(idx == i) seg_nodes{end+1} = idx; end end % Get the union of the set to make sure we don't double move any nodes seg_nodes = unique(cellcat(seg_nodes)); % Get current positions pos = getNodePositions(); % Move all nodes for j = 1:numel(seg_nodes) pos(j,:) = pos(j,:) + dXY; end % Update edges updateSkeleton(pos); end function setNodesToDefault(node_idx) % Utility function to reset nodes to default position from the skeleton template if nargin < 1; node_idx = 1:numNodes; end % Get current positions pos = getNodePositions(); % Get default positions default_pos = labels.skeleton.joints.pos; % Update with defaults % pos(node_idx,:) = default_pos(node_idx,:); labels.positions(node_idx,:,ui.status.currentFrame) = NaN; ui.status.movedNodes(node_idx) = false; % Update updateSkeleton(); end function pos = getInitialPos(idx) % Utility to compute the initial node positions for a single frame if nargin < 1; idx = ui.status.currentFrame; end % Start off with defaults pos = labels.skeleton.joints.pos; % Update with initialized positions init_pos = labels.initialization(:,:,idx); init_nodes = find(all(~isnan(init_pos),2)); pos(init_nodes,:) = init_pos(init_nodes,:); % Update with user-labeled positions label_pos = labels.positions(:,:,idx); label_nodes = find(all(~isnan(label_pos),2)); pos(label_nodes,:) = label_pos(label_nodes,:); end function resetNodes(node_idx) % Utility function to reset nodes to their initial positions when the frame was drawn if nargin < 1; node_idx = 1:numNodes; end % Start off with what we have now pos = getNodePositions(); % Get initial postions init_pos = getInitialPos(ui.status.currentFrame); % Set positions for specified nodes pos(node_idx,:) = init_pos(node_idx); % Update updateSkeleton(pos); end %% Frame update and saving function markAllCorrect() % Helper for setting all nodes in the current frame as correct ui.status.movedNodes(:) = true; commitChanges(); end function commitChanges() % Utility function for committing changes to node positions in the % current frame to the labels structure (but does not save to disk) % Get current positions pos = getNodePositions(); % Check current status status = getStatus(ui.status.currentFrame); isLabeled = all(status == 2); for i = horz(find(ui.status.movedNodes)) % Commit to labels labels.positions(i,:,ui.status.currentFrame) = pos(i,:); % Reset moved state ui.status.movedNodes(i) = false; % Mark unsaved changes ui.status.unsavedChanges(ui.status.currentFrame) = true; end % Update status status = getStatus(ui.status.currentFrame); % Update skeleton display %updateSkeleton(); % Update stats if changed if ~all(isLabeled) && all(status == 2) addToHistory(sprintf('Labeled frame %d', ui.status.currentFrame)); numLabeled = numLabeled + 1; ui.status.framesLabeled.String = sprintf('Labeled: %d/%d (%.2f%%)', numLabeled, numFrames, numLabeled/numFrames*100); end % Update full status data ui.status.fullImg.CData(:,ui.status.currentFrame) = status; % Update zoomed status bar data zoomBoxIdx = ui.status.zoomImg.XData; if any(zoomBoxIdx == ui.status.currentFrame) ui.status.zoomImg.CData(:,zoomBoxIdx == ui.status.currentFrame) = status; end % Update status fig title savedStatus = ''; if any(ui.status.unsavedChanges); savedStatus = ' [unsaved]'; end ui.status.fig.Name = sprintf('Status: %d/%d (%.2f%%) labeled%s', numLabeled, numFrames, numLabeled/numFrames*100, savedStatus); end function goToFrame(idx) % Utility function for seeking to another frame % Commit changes to labels commitChanges(); % Autosave before anything if config.autoSave && ~isempty(ui.status.currentFrame) && ui.status.unsavedChanges(ui.status.currentFrame) saveLabels(); end % Update image ui.img.img.CData = box(:,:,:,idx); % Update status ui.status.currentFrame = idx; ui.img.fig.Name = sprintf('%d/%d', ui.status.currentFrame, numFrames); % Get initial positions ui.status.initialPos = getInitialPos(idx); % Update with initial positions updateSkeleton(ui.status.initialPos); % Update status zoom box position zoomBoxIdx = zoomBoxWindow + ui.status.currentFrame; zoomBoxPts = [ zoomBoxIdx(1) 0 zoomBoxIdx(end) 0 zoomBoxIdx(end) numNodes zoomBoxIdx(1) numNodes zoomBoxIdx(1) 0 ]; ui.status.fullZoomBox.XData = zoomBoxPts(:,1); ui.status.fullZoomBox.YData = zoomBoxPts(:,2); % Update zoomed status bar data ui.status.zoomImg.XData = zoomBoxIdx; ui.status.zoomImg.CData(:) = 0; % reset isValidIdx = zoomBoxIdx > 0 & zoomBoxIdx <= numFrames; ui.status.zoomImg.CData(:,isValidIdx) = ui.status.fullImg.CData(:,zoomBoxIdx(isValidIdx)); ui.status.zoomAx.XLim = zoomBoxIdx([1 end]) + [-0.5 0.5]; end function saveLabels() % Saves everything in the labels structure to disk stic; % Commit unsaved changes to labels commitChanges(); % Update if there were any changes if any(ui.status.unsavedChanges) % Update last modified timestamp labels.lastModified = datestr(now); end % Save current frame so we pick up where we left off config.initialFrame = ui.status.currentFrame; % Save figure positions config.imgFigPos = ui.img.fig.Position; config.ctrlFigPos = ui.ctrl.fig.Position; config.statusFigPos = ui.status.fig.Position; % Save config labels.config = config; % Save to labels file save(labels.savePath, '-struct', 'labels') % Clear modified flags ui.status.unsavedChanges(:) = false; commitChanges(); stocf('Saved labels: %s', labels.savePath) end %% Start! goToFrame(config.initialFrame); selectNode(1); updateSkeleton(); end ================================================ FILE: leap/hpc/python_gpu.sh ================================================ #!/bin/bash #SBATCH --time=4:00:00 #SBATCH --mem=128000 #SBATCH -N 1 #SBATCH --cpus-per-task=4 #SBATCH --ntasks-per-node=1 #SBATCH --ntasks-per-socket=1 #SBATCH --gres=gpu:1 echo "args: ${@:1}" python ${@:1} ================================================ FILE: leap/image_augmentation.py ================================================ import cv2 import numpy as np import keras from keras.utils import Sequence def transform_imgs(X, theta=(-180,180), scale=1.0): """ Transforms sets of images with the same random transformation across each channel. """ # Sample random rotation and scale if range specified if not np.isscalar(theta): theta = np.ptp(theta) * np.random.rand() + np.min(theta) if not np.isscalar(scale): scale = np.ptp(scale) * np.random.rand() + np.min(scale) # Standardize X input to a list single_img = type(X) == np.ndarray if single_img: X = [X,] # Find image parameters img_size = X[0].shape[:2] ctr = (img_size[0] / 2, img_size[0] / 2) # Compute affine transformation matrix T = cv2.getRotationMatrix2D(ctr, theta, scale) # Make sure we don't overwrite the inputs X = [x.copy() for x in X] # Apply to each image for i in range(len(X)): if X[i].ndim == 2: # Single channel image X[i] = cv2.warpAffine(X[i], T, img_size[::-1]) else: # Multi-channel image for c in range(X[i].shape[-1]): X[i][...,c] = cv2.warpAffine(X[i][...,c], T, img_size[::-1]) # Pull the single image back out of the list if single_img: X = X[0] return X class PairedImageAugmenter(Sequence): def __init__(self, X, Y, batch_size=32, shuffle=False, theta=(-180,180), scale=1.0): self.X = X self.Y = Y self.batch_size = batch_size self.theta = theta self.scale = scale self.num_samples = len(X) all_idx = np.arange(self.num_samples) if shuffle: np.random.shuffle(all_idx) self.batches = np.array_split(all_idx, np.ceil(self.num_samples / self.batch_size)) def __len__(self): return len(self.batches) def __getitem__(self, batch_idx): idx = self.batches[batch_idx] X = self.X[idx] Y = self.Y[idx] for i in range(len(X)): X[i], Y[i] = transform_imgs((X[i],Y[i]), theta=self.theta, scale=self.scale) return X, Y class MultiInputOutputPairedImageAugmenter(PairedImageAugmenter): def __init__(self, input_names, output_names, *args, **kwargs): if type(input_names) != list: input_names = [input_names,] if type(output_names) != list: output_names = [output_names,] self.input_names = input_names self.output_names = output_names super().__init__(*args, **kwargs) def __getitem__(self, batch_idx): X,Y = super().__getitem__(batch_idx) return ({k: X for k in self.input_names}, {k: Y for k in self.output_names}) ================================================ FILE: leap/layers.py ================================================ # -*- coding: utf-8 -*- """ Copyright 2018 Jacob M. Graving Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Modified from code written by François Chollet All contributions by François Chollet: Copyright (c) 2015 - 2018, François Chollet. All rights reserved. """ import numpy as np import keras import keras.backend as K from keras.legacy import interfaces from keras.engine import Layer from keras.engine import InputSpec from keras.utils import conv_utils from keras.backend import int_shape, permute_dimensions from keras.backend import tf from keras.layers import Conv2D, Add from packaging.version import parse as parse_version __all__ = ['UpSampling2D', 'Maxima2D'] def resize_images(x, height_factor, width_factor, interpolation, data_format): """Resizes the images contained in a 4D tensor. # Arguments x: Tensor or variable to resize. height_factor: Positive integer. width_factor: Positive integer. interpolation: string, "nearest", "bilinear" or "bicubic" data_format: string, `"channels_last"` or `"channels_first"`. # Returns A tensor. # Raises ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`. """ if interpolation == 'nearest': tf_resize = tf.image.resize_nearest_neighbor elif interpolation == 'bilinear': tf_resize = tf.image.resize_bilinear elif interpolation == 'bicubic': tf_resize = tf.image.resize_bicubic else: raise ValueError('Invalid interpolation method:', interpolation) if data_format == 'channels_first': original_shape = int_shape(x) new_shape = tf.shape(x)[2:] new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32')) x = permute_dimensions(x, [0, 2, 3, 1]) x = tf_resize(x, new_shape, align_corners=True) x = permute_dimensions(x, [0, 3, 1, 2]) x.set_shape((None, None, original_shape[2] * height_factor if original_shape[2] is not None else None, original_shape[3] * width_factor if original_shape[3] is not None else None)) return x elif data_format == 'channels_last': original_shape = int_shape(x) new_shape = tf.shape(x)[1:3] new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32')) x = tf_resize(x, new_shape, align_corners=True) x.set_shape((None, original_shape[1] * height_factor if original_shape[1] is not None else None, original_shape[2] * width_factor if original_shape[2] is not None else None, None)) return x else: raise ValueError('Invalid data_format:', data_format) class UpSampling2D(Layer): """Upsampling layer for 2D inputs. Repeats the rows and columns of the data by size[0] and size[1] respectively with bilinear interpolation. # Arguments size: int, or tuple of 2 integers. The upsampling factors for rows and columns. data_format: A string, one of `channels_last` (default) or `channels_first`. The ordering of the dimensions in the inputs. `channels_last` corresponds to inputs with shape `(batch, height, width, channels)` while `channels_first` corresponds to inputs with shape `(batch, channels, height, width)`. It defaults to the `image_data_format` value found in your Keras config file at `~/.keras/keras.json`. If you never set it, then it will be "channels_last". interpolation: A string, one of 'nearest' (default), 'bilinear', or 'bicubic' # Input shape 4D tensor with shape: - If `data_format` is `"channels_last"`: `(batch, rows, cols, channels)` - If `data_format` is `"channels_first"`: `(batch, channels, rows, cols)` # Output shape 4D tensor with shape: - If `data_format` is `"channels_last"`: `(batch, upsampled_rows, upsampled_cols, channels)` - If `data_format` is `"channels_first"`: `(batch, channels, upsampled_rows, upsampled_cols)` """ @interfaces.legacy_upsampling2d_support def __init__(self, size=(2, 2), data_format=None, interpolation='nearest', **kwargs): super(UpSampling2D, self).__init__(**kwargs) # Update to K.normalize_data_format after keras 2.2.0 if parse_version(keras.__version__) > parse_version("2.2.0"): self.data_format = K.normalize_data_format(data_format) else: self.data_format = conv_utils.normalize_data_format(data_format) self.interpolation = interpolation self.size = conv_utils.normalize_tuple(size, 2, 'size') self.input_spec = InputSpec(ndim=4) def compute_output_shape(self, input_shape): if self.data_format == 'channels_first': height = self.size[0] * input_shape[2] if input_shape[2] is not None else None width = self.size[1] * input_shape[3] if input_shape[3] is not None else None return (input_shape[0], input_shape[1], height, width) elif self.data_format == 'channels_last': height = self.size[0] * input_shape[1] if input_shape[1] is not None else None width = self.size[1] * input_shape[2] if input_shape[2] is not None else None return (input_shape[0], height, width, input_shape[3]) def call(self, inputs): return resize_images(inputs, self.size[0], self.size[1], self.interpolation, self.data_format) def get_config(self): config = {'size': self.size, 'data_format': self.data_format} base_config = super(UpSampling2D, self).get_config() return dict(list(base_config.items()) + list(config.items())) def _find_maxima(x): x = K.cast(x, K.floatx()) col_max = K.max(x, axis=1) row_max = K.max(x, axis=2) maxima = K.max(col_max, 1) maxima = K.expand_dims(maxima, -2) cols = K.cast(K.argmax(col_max, -2), K.floatx()) rows = K.cast(K.argmax(row_max, -2), K.floatx()) cols = K.expand_dims(cols, -2) rows = K.expand_dims(rows, -2) # maxima = K.concatenate([rows, cols, maxima], -2) # y, x, val maxima = K.concatenate([cols, rows, maxima], -2) # x, y, val return maxima def find_maxima(x, data_format): """Finds the 2D maxima contained in a 4D tensor. # Arguments x: Tensor or variable. data_format: string, `"channels_last"` or `"channels_first"`. # Returns A tensor. # Raises ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`. """ if data_format == 'channels_first': x = permute_dimensions(x, [0, 2, 3, 1]) x = _find_maxima(x) x = permute_dimensions(x, [0, 2, 1]) return x elif data_format == 'channels_last': x = _find_maxima(x) return x else: raise ValueError('Invalid data_format:', data_format) class Maxima2D(Layer): """Maxima layer for 2D inputs. Finds the maxima and 2D indices for the channels in the input. The output is ordered as [row, col, maximum]. # Arguments data_format: A string, one of `channels_last` (default) or `channels_first`. The ordering of the dimensions in the inputs. `channels_last` corresponds to inputs with shape `(batch, height, width, channels)` while `channels_first` corresponds to inputs with shape `(batch, channels, height, width)`. It defaults to the `image_data_format` value found in your Keras config file at `~/.keras/keras.json`. If you never set it, then it will be "channels_last". # Input shape 4D tensor with shape: - If `data_format` is `"channels_last"`: `(batch, rows, cols, channels)` - If `data_format` is `"channels_first"`: `(batch, channels, rows, cols)` # Output shape 3D tensor with shape: - If `data_format` is `"channels_last"`: `(batch, 3, channels)` - If `data_format` is `"channels_first"`: `(batch, channels, 3)` """ def __init__(self, data_format=None, **kwargs): super(Maxima2D, self).__init__(**kwargs) # Update to K.normalize_data_format after keras 2.2.0 if parse_version(keras.__version__) > parse_version("2.2.0"): self.data_format = K.normalize_data_format(data_format) else: self.data_format = conv_utils.normalize_data_format(data_format) self.input_spec = InputSpec(ndim=4) def compute_output_shape(self, input_shape): if self.data_format == 'channels_first': return (input_shape[0], input_shape[1], 3) elif self.data_format == 'channels_last': return (input_shape[0], 3, input_shape[3]) def call(self, inputs): return find_maxima(inputs, self.data_format) def get_config(self): config = {'data_format': self.data_format} base_config = super(Maxima2D, self).get_config() return dict(list(base_config.items()) + list(config.items())) def residual_bottleneck_module(x_in, output_filters=32, bottleneck_factor=2, prefix="res", activation="relu", initializer="glorot_normal"): # Get input shape and channels in_shape = K.int_shape(x_in) input_filters = in_shape[3] # Bottleneck filters are proportional to the output filters bottleneck_filters = output_filters // bottleneck_factor # Bottleneck block x = Conv2D(filters=bottleneck_filters, kernel_size=1, padding="same", activation=activation, kernel_initializer=initializer, name=prefix + "_Conv1")(x_in) x = Conv2D(filters=bottleneck_filters, kernel_size=3, padding="same", activation=activation, kernel_initializer=initializer, name=prefix + "_Conv2")(x) x = Conv2D(filters=output_filters, kernel_size=1, padding="same", activation=activation, kernel_initializer=initializer, name=prefix + "_Conv3")(x) # 1x1 conv if input channels are different from output channels if output_filters != input_filters: x_in = Conv2D(filters=output_filters, kernel_size=1, padding="same", activation=activation, kernel_initializer=initializer, name=prefix + "_ConvSkip")(x_in) # Residual connection x = Add(name=prefix + "_AddRes")([x_in, x]) return x ================================================ FILE: leap/models.py ================================================ import keras from keras import backend as K from keras.models import Model from keras.layers import Input, Conv2D, Conv2DTranspose, Add, MaxPooling2D from keras.optimizers import Adam from leap.layers import residual_bottleneck_module, UpSampling2D def leap_cnn(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False): """ Creates and compiles network model. :param img_size: shape of a single image, optionally including channels :param output_channels: number of output channels (joints being predicted) :param filters: number of baseline filters to use (more filters will be used in intermediate layers) :param summary: prints network summary after compiling """ if len(img_size) == 2: img_size = img_size + (1,) x_in = Input(img_size) x1 = Conv2D(filters, kernel_size=3, padding="same", activation="relu")(x_in) x1 = Conv2D(filters, kernel_size=3, padding="same", activation="relu")(x1) x1 = Conv2D(filters, kernel_size=3, padding="same", activation="relu")(x1) x1_pool = MaxPooling2D(pool_size=2, strides=2, padding="same")(x1) x2 = Conv2D(filters*2, kernel_size=3, padding="same", activation="relu")(x1_pool) x2 = Conv2D(filters*2, kernel_size=3, padding="same", activation="relu")(x2) x2 = Conv2D(filters*2, kernel_size=3, padding="same", activation="relu")(x2) x2_pool = MaxPooling2D(pool_size=2, strides=2, padding="same")(x2) x3 = Conv2D(filters*4, kernel_size=3, padding="same", activation="relu")(x2_pool) x3 = Conv2D(filters*4, kernel_size=3, padding="same", activation="relu")(x3) x3 = Conv2D(filters*4, kernel_size=3, padding="same", activation="relu")(x3) if upsampling_layers: x4 = UpSampling2D(interpolation="bilinear")(x3) else: x4 = Conv2DTranspose(filters*2, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal")(x3) x4 = Conv2D(filters*2, kernel_size=3, padding="same", activation="relu")(x4) x4 = Conv2D(filters*2, kernel_size=3, padding="same", activation="relu")(x4) if upsampling_layers: x_out = UpSampling2D(interpolation="bilinear")(x4) x_out = Conv2D(output_channels, kernel_size=3, padding="same", activation="linear")(x_out) else: x_out = Conv2DTranspose(output_channels, kernel_size=3, strides=2, padding="same", activation="linear", kernel_initializer="glorot_normal")(x4) # Compile net = Model(inputs=x_in, outputs=x_out, name="LeapCNN") net.compile(optimizer=Adam(amsgrad=amsgrad), loss="mean_squared_error") if summary: net.summary() return net def hourglass(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False): """ Creates and compiles network model. :param img_size: shape of a single image, optionally including channels :param output_channels: number of output channels (joints being predicted) :param filters: number of baseline filters to use (more filters will be used in intermediate layers) :param summary: prints network summary after compiling """ if len(img_size) == 2: img_size = img_size + (1,) x_in = Input(img_size, name="x_in") x1_pre = residual_bottleneck_module(x_in, prefix="x1", output_filters=filters) x1 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x1_pool")(x1_pre) x2_pre = residual_bottleneck_module(x1, prefix="x2", output_filters=filters) x2 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x2_pool")(x2_pre) x3_pre = residual_bottleneck_module(x2, prefix="x3", output_filters=filters) x3 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x3_pool")(x3_pre) x4_pre = residual_bottleneck_module(x3, prefix="x4", output_filters=filters) x4 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x4_pool")(x4_pre) x5 = residual_bottleneck_module(x4, prefix="x5", output_filters=filters) if upsampling_layers: x6_pre = UpSampling2D(interpolation="bilinear", name="x6_Upsample")(x5) else: x6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x6_ConvT")(x5) x6_add = Add(name="x6_Add")([x4_pre, x6_pre]) x6 = residual_bottleneck_module(x6_add, prefix="x6", output_filters=filters) if upsampling_layers: x7_pre = UpSampling2D(interpolation="bilinear", name="x7_Upsample")(x6) else: x7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x7_ConvT")(x6) x7_add = Add(name="x7_Add")([x3_pre, x7_pre]) x7 = residual_bottleneck_module(x7_add, prefix="x7", output_filters=filters) if upsampling_layers: x8_pre = UpSampling2D(interpolation="bilinear", name="x8_Upsample")(x7) else: x8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x8_ConvT")(x7) x8_add = Add(name="x8_Add")([x2_pre, x8_pre]) x8 = residual_bottleneck_module(x8_add, prefix="x8", output_filters=filters) if upsampling_layers: x9_pre = UpSampling2D(interpolation="bilinear", name="x9_Upsample")(x8) else: x9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x9_ConvT")(x8) x9_add = Add(name="x9_Add")([x1_pre, x9_pre]) x9 = residual_bottleneck_module(x9_add, prefix="x9", output_filters=filters) x_out = Conv2D(filters=output_channels, kernel_size=3, strides=1, padding="same", activation="linear", name="x_out")(x9) # Compile model = Model(inputs=x_in, outputs=x_out, name="hourglass") model.compile(optimizer=Adam(amsgrad=amsgrad), loss="mean_squared_error") if summary: model.summary() return model def stacked_hourglass(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False): """ Creates and compiles network model. :param img_size: shape of a single image, optionally including channels :param output_channels: number of output channels (joints being predicted) :param filters: number of baseline filters to use (more filters will be used in intermediate layers) :param summary: prints network summary after compiling """ if len(img_size) == 2: img_size = img_size + (1,) x_in = Input(img_size, name="x_in") x1_1_pre = residual_bottleneck_module(x_in, prefix="x1_1", output_filters=filters) x1_1 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x1_1_pool")(x1_1_pre) x1_2_pre = residual_bottleneck_module(x1_1, prefix="x1_2", output_filters=filters) x1_2 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x1_2_pool")(x1_2_pre) x1_3_pre = residual_bottleneck_module(x1_2, prefix="x1_3", output_filters=filters) x1_3 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x1_3_pool")(x1_3_pre) x1_4_pre = residual_bottleneck_module(x1_3, prefix="x1_4", output_filters=filters) x1_4 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x1_4_pool")(x1_4_pre) x1_5 = residual_bottleneck_module(x1_4, prefix="x1_5", output_filters=filters) if upsampling_layers: x1_6_pre = UpSampling2D(interpolation="bilinear", name="x1_6_Upsample")(x1_5) else: x1_6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x1_6_ConvT")(x1_5) x1_6_add = Add(name="x1_6_Add")([x1_4_pre, x1_6_pre]) x1_6 = residual_bottleneck_module(x1_6_add, prefix="x1_6", output_filters=filters) if upsampling_layers: x1_7_pre = UpSampling2D(interpolation="bilinear", name="x1_7_Upsample")(x1_6) else: x1_7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x1_7_ConvT")(x1_6) x1_7_add = Add(name="x1_7_Add")([x1_3_pre, x1_7_pre]) x1_7 = residual_bottleneck_module(x1_7_add, prefix="x1_7", output_filters=filters) if upsampling_layers: x1_8_pre = UpSampling2D(interpolation="bilinear", name="x1_8_Upsample")(x1_7) else: x1_8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x1_8_ConvT")(x1_7) x1_8_add = Add(name="x1_8_Add")([x1_2_pre, x1_8_pre]) x1_8 = residual_bottleneck_module(x1_8_add, prefix="x1_8", output_filters=filters) if upsampling_layers: x1_9_pre = UpSampling2D(interpolation="bilinear", name="x1_9_Upsample")(x1_8) else: x1_9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x1_9_ConvT")(x1_8) x1_9_add = Add(name="x1_9_Add")([x1_1_pre, x1_9_pre]) x1_9 = residual_bottleneck_module(x1_9_add, prefix="x1_9", output_filters=filters) ############# x2_1_pre = residual_bottleneck_module(x1_9, prefix="x2_1", output_filters=filters) x2_1 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x2_1_pool")(x2_1_pre) x2_2_pre = residual_bottleneck_module(x2_1, prefix="x2_2", output_filters=filters) x2_2 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x2_2_pool")(x2_2_pre) x2_3_pre = residual_bottleneck_module(x2_2, prefix="x2_3", output_filters=filters) x2_3 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x2_3_pool")(x2_3_pre) x2_4_pre = residual_bottleneck_module(x2_3, prefix="x2_4", output_filters=filters) x2_4 = MaxPooling2D(pool_size=2, strides=2, padding="same", name="x2_4_pool")(x2_4_pre) x2_5 = residual_bottleneck_module(x2_4, prefix="x2_5", output_filters=filters) if upsampling_layers: x2_6_pre = UpSampling2D(interpolation="bilinear", name="x2_6_Upsample")(x2_5) else: x2_6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x2_6_ConvT")(x2_5) x2_6_add = Add(name="x2_6_Add")([x2_4_pre, x2_6_pre]) x2_6 = residual_bottleneck_module(x2_6_add, prefix="x2_6", output_filters=filters) if upsampling_layers: x2_7_pre = UpSampling2D(interpolation="bilinear", name="x2_7_Upsample")(x2_6) else: x2_7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x2_7_ConvT")(x2_6) x2_7_add = Add(name="x2_7_Add")([x2_3_pre, x2_7_pre]) x2_7 = residual_bottleneck_module(x2_7_add, prefix="x2_7", output_filters=filters) if upsampling_layers: x2_8_pre = UpSampling2D(interpolation="bilinear", name="x2_8_Upsample")(x2_7) else: x2_8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x2_8_ConvT")(x2_7) x2_8_add = Add(name="x2_8_Add")([x2_2_pre, x2_8_pre]) x2_8 = residual_bottleneck_module(x2_8_add, prefix="x2_8", output_filters=filters) if upsampling_layers: x2_9_pre = UpSampling2D(interpolation="bilinear", name="x2_9_Upsample")(x2_8) else: x2_9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding="same", activation="relu", kernel_initializer="glorot_normal", name="x2_9_ConvT")(x2_8) x2_9_add = Add(name="x2_9_Add")([x2_1_pre, x2_9_pre]) x2_9 = residual_bottleneck_module(x2_9_add, prefix="x2_9", output_filters=filters) ############# x_out1 = residual_bottleneck_module(x1_9, output_filters=output_channels, bottleneck_factor=1, prefix="x_out1", activation="linear") x_out2 = residual_bottleneck_module(x2_9, output_filters=output_channels, bottleneck_factor=1, prefix="x_out2", activation="linear") # Compile model = Model(inputs=x_in, outputs=[x_out1, x_out2], name="StackedHourglass") model.compile(optimizer=Adam(amsgrad=amsgrad), loss="mean_squared_error") if summary: model.summary() return model ================================================ FILE: leap/plot_joints_single.m ================================================ function h = plot_joints_single(pts, segments, markerSize, lineWidth) %PLOT_JOINTS_SINGLE Plot joints on a single frame. % Usage: % plot_joints_single(pts, segments) % plot_joints_single(pts, segments, markerSize, lineWidth) % % Args: % pts: % segments (or skeleton): table with line segments or struct containing it % markerSize: default: 20 % lineWidth: default: 2 % % See also: if isfield(segments,'segments'); segments = segments.segments; end if nargin < 3 || isempty(markerSize); markerSize = 20; end if nargin < 4 || isempty(lineWidth); lineWidth = 2; end h = gobjects(numel(segments.joints_idx),1); for i = 1:numel(segments.joints_idx) h(i) = plotpts(pts(segments.joints_idx{i},:),'.-', ... 'Color',segments.color{i},'MarkerSize',markerSize,'LineWidth',lineWidth); end if nargout < 1; clear h; end end ================================================ FILE: leap/predict_box.m ================================================ function preds = predict_box(box, modelPath, saveConfmaps) %PREDICT_BOX Evaluates model predictions on a stack of frames. Wrapper for predict_box.py. % Usage: % preds = predict_box(box, modelPath) % preds = predict_box(box, modelPath, saveConfmaps) % % Args: % box: 4-D array or path to HDF5 file with '/box' dataset % modelPath: path to model weights % saveConfmaps: if true, returns full confidence maps in addition to % peaks (default: false). Very slow and memory intensive! % % Returns: % preds: struct containing results from model prediction % .positions_pred: 3-D array of (parts x [X Y] x frames) indicating % peak positions for each confidence map in image coordinates % .conf_pred: 2-D array of (parts x frames) with the confidence map % value at the peak pixel for detecting bad predictions % .confmaps: 4-D array of confidence maps returned if saveConfmaps % is set true if nargin < 3 || isempty(saveConfmaps); saveConfmaps = false; end % Process args delete_box = false; is_singleton = false; if ischar(box) boxPath = box; else boxPath = [tempname '.h5']; if numel(size(box)) < 4 box = repmat(box,[1 1 1 2]); is_singleton = true; end h5save(boxPath, box) delete_box = true; end % Generate temporary output filename outPath = [tempname '.h5']; % Build command line args cmd = { 'python' ['"' ff(funpath(true), 'predict_box.py') '"'] ['"' boxPath '"'] ['"' modelPath '"'] ['"' outPath '"'] }; if saveConfmaps cmd{end+1} = '--save-confmaps'; end disp(strjoin(cmd)) % Predict try exit_code = system(strjoin(cmd)); catch ME if delete_box && exists(boxPath); delete(boxPath); end rethrow(ME) end if delete_box && exists(boxPath); delete(boxPath); end % Read data back in try preds = h5readgroup(outPath); catch ME if exists(outPath); delete(outPath); end rethrow(ME) end if exists(outPath); delete(outPath); end % Adjust for 0-based indexing preds.positions_pred = single(preds.positions_pred) + 1; % Rescale confidence maps to correct range prior to quantization if saveConfmaps && isfield(preds.Attributes, 'confmaps') c_min = preds.Attributes.confmaps.range_min; c_max = preds.Attributes.confmaps.range_max; preds.confmaps = rescale(single(preds.confmaps) / 255, c_min, c_max); end % Adjust for singleton input if is_singleton preds.positions_pred = preds.positions_pred(:,:,1); preds.conf_pred = preds.conf_pred(:,1); if isfield(preds,'confmaps') preds.confmaps = preds.confmaps(:,:,:,1); end end end ================================================ FILE: leap/predict_box.py ================================================ import h5py import numpy as np import os from time import time import keras import keras.models from keras.layers import Lambda import tensorflow as tf import re from clize import run from leap.utils import find_weights, find_best_weights, preprocess from leap.layers import Maxima2D def tf_find_peaks(x): """ Finds the maximum value in each channel and returns the location and value. Args: x: rank-4 tensor (samples, height, width, channels) Returns: peaks: rank-3 tensor (samples, [x, y, val], channels) """ # Store input shape in_shape = tf.shape(x) # Flatten height/width dims flattened = tf.reshape(x, [in_shape[0], -1, in_shape[-1]]) # Find peaks in linear indices idx = tf.argmax(flattened, axis=1) # Convert linear indices to subscripts rows = tf.floor_div(tf.cast(idx,tf.int32), in_shape[1]) cols = tf.floormod(tf.cast(idx,tf.int32), in_shape[1]) # Dumb way to get actual values without indexing vals = tf.reduce_max(flattened, axis=1) # Return N x 3 x C tensor return tf.stack([ tf.cast(cols, tf.float32), tf.cast(rows, tf.float32), vals ], axis=1) def convert_to_peak_outputs(model, include_confmaps=False): """ Creates a new Keras model with a wrapper to yield channel peaks from rank-4 tensors. """ if type(model.output) == list: confmaps = model.output[-1] else: confmaps = model.output if include_confmaps: return keras.Model(model.input, [Lambda(tf_find_peaks)(confmaps), confmaps]) else: # return keras.Model(model.input, Lambda(tf_find_peaks)(confmaps)) return keras.Model(model.input, Maxima2D()(confmaps)) def predict_box(box_path, model_path, out_path, *, box_dset="/box", epoch=None, verbose=True, overwrite=False, save_confmaps=False, batch_size=32): """ Predict and save peak coordinates for a box. :param box_path: path to HDF5 file with box dataset :param model_path: path to Keras weights file or run folder with weights subfolder :param out_path: path to HDF5 file to save results to :param box_dset: name of HDF5 dataset containing box images :param epoch: epoch to use if run folder provided instead of Keras weights file :param verbose: if True, prints some info and statistics during procesing :param overwrite: if True and out_path exists, file will be overwritten :param save_confmaps: if True, saves the full confidence maps as additional datasets in the output file (very slow) :param batch_size: number of samples to evaluate at once per batch (see keras.Model API) """ if verbose: print("model_path:", model_path) # Find model weights model_name = None weights_path = model_path if os.path.isdir(model_path): model_name = os.path.basename(model_path) weights_paths, epochs, val_losses = find_weights(model_path) if epoch == None and len(val_losses) > 0: weights_path = weights_paths[np.argmin(val_losses)] elif epoch == "final" or (epoch == None and len(val_losses) == 0): weights_path = os.path.join(model_path, "final_model.h5") else: weights_path = weights_paths[epoch] # Input data box = h5py.File(box_path,"r")[box_dset] num_samples = box.shape[0] if verbose: print("Input:", box_path) print("box.shape:", box.shape) # Create output path if out_path[-3:] != ".h5": if model_name == None: out_path = os.path.join(out_path, os.path.basename(box_path)) else: out_path = os.path.join(out_path, model_name, os.path.basename(box_path)) os.makedirs(os.path.dirname(out_path), exist_ok=True) model_name = os.path.basename(model_path) if verbose: print("Output:", out_path) t0_all = time() if os.path.exists(out_path): if overwrite: os.remove(out_path) print("Deleted existing output.") else: print("Error: Output path already exists.") return # Load and prepare model model = keras.models.load_model(weights_path) model_peaks = convert_to_peak_outputs(model, include_confmaps=save_confmaps) if verbose: print("weights_path:", weights_path) print("Loaded model: %d layers, %d params" % (len(model.layers), model.count_params())) # Load data and preprocess (normalize) t0 = time() X = preprocess(box[:]) if verbose: print("Loaded [%.1fs]" % (time() - t0)) # Evaluate t0 = time() if save_confmaps: Ypk, confmaps = model_peaks.predict(X, batch_size=batch_size) # Quantize confmaps_min = confmaps.min() confmaps_max = confmaps.max() confmaps = (confmaps - confmaps_min) / (confmaps_max - confmaps_min) confmaps = (confmaps * 255).astype('uint8') # Reshape confmaps = np.transpose(confmaps, (0, 3, 2, 1)) else: Ypk = model_peaks.predict(X, batch_size=batch_size) prediction_runtime = time() - t0 if verbose: print("Predicted [%.1fs]" % prediction_runtime) print("Prediction performance: %.3f FPS" % (num_samples / prediction_runtime)) # Save t0 = time() with h5py.File(out_path, "w") as f: f.attrs["num_samples"] = num_samples f.attrs["img_size"] = X.shape[1:] f.attrs["box_path"] = box_path f.attrs["box_dset"] = box_dset f.attrs["model_path"] = model_path f.attrs["weights_path"] = weights_path f.attrs["model_name"] = model_name ds_pos = f.create_dataset("positions_pred", data=Ypk[:,:2,:].astype("int32"), compression="gzip", compression_opts=1) ds_pos.attrs["description"] = "coordinate of peak at each sample" ds_pos.attrs["dims"] = "(sample, [x, y], joint) === (sample, [column, row], joint)" ds_conf = f.create_dataset("conf_pred", data=Ypk[:,2,:].squeeze(), compression="gzip", compression_opts=1) ds_conf.attrs["description"] = "confidence map value in [0, 1.0] at peak" ds_conf.attrs["dims"] = "(sample, joint)" if save_confmaps: ds_confmaps = f.create_dataset("confmaps", data=confmaps, compression="gzip", compression_opts=1) ds_confmaps.attrs["description"] = "confidence maps" ds_confmaps.attrs["dims"] = "(sample, channel, width, height)" ds_confmaps.attrs["range_min"] = confmaps_min ds_confmaps.attrs["range_max"] = confmaps_max total_runtime = time() - t0_all f.attrs["total_runtime_secs"] = total_runtime f.attrs["prediction_runtime_secs"] = prediction_runtime if verbose: print("Saved [%.1fs]" % (time() - t0)) print("Total runtime: %.1f mins" % (total_runtime / 60)) print("Total performance: %.3f FPS" % (num_samples / total_runtime)) if __name__ == "__main__": run(predict_box) ================================================ FILE: leap/pts2confmaps.m ================================================ function confmaps = pts2confmaps(pts, sz, sigma, normalize) %PTS2CONFMAPS Generate confidence maps centered at specified points. % Usage: % confmaps = pts2confmaps(pts, sz, sigma) % % Args: % pts: N x 2 or cell array of {N1 x 2, N2 x 2, ...}, where each cell will % correspond to a single channel to create multipoint confidence maps % sz: [rows cols] % sigma: filter size (default: 5) % normalize: outputs maps in [0, 1] rather than PDF (default: true) % % See also: label_joints if ~iscell(pts); pts = arr2cell(pts,1); end if nargin < 3 || isempty(sigma); sigma = 5; end if nargin < 4 || isempty(normalize); normalize = true; end confmaps = NaN(sz(1), sz(2), numel(pts)); xv = 1:sz(2); yv = 1:sz(1); [XX,YY] = meshgrid(xv,yv); for i = 1:numel(pts) x = permute(pts{i}(:,1),[2 3 1]); y = permute(pts{i}(:,2),[2 3 1]); % confmaps(:,:,i) = sum(exp(-((YY-y).^2 + (XX-x).^2)./(2*sigma^2)),3); confmaps(:,:,i) = max(exp(-((YY-y).^2 + (XX-x).^2)./(2*sigma^2)),[],3); end if ~normalize confmaps = confmaps ./ (sigma * sqrt(2*pi)); end end ================================================ FILE: leap/test_leap.m ================================================ function works = test_leap() %TEST_LEAP Checks whether LEAP is properly installed. % Usage: % test_leap % works = test_leap % returns true/false % % See also: install_leap works = true; % Check if we can import the LEAP package from anywhere cdCmd = ['cd "' matlabroot '"']; if ispc(); cdCmd = ['cd /D "' matlabroot '"']; end [status,msg] = system([cdCmd ' && python -c "import leap"']); if status ~= 0 works = false; end if nargout == 0 if works printf('Test LEAP successful!') else disp('Unable to import LEAP python package. Make sure LEAP and its dependencies are installed.') disp('Go to the base LEAP directory containing setup.py and run from MATLAB:') disp(' !pip install -e .') disp('Or try the MATLAB installer:') disp(' install_leap') end clear works end end ================================================ FILE: leap/toolbox/aliases/alims.m ================================================ function varargout = alims(X) %ALIMS Alias for arange. % Usage: % R = alims(X) % [min_val, max_val] = alims(X) % % See also: arange varargout = wrap(@() arange(X), 1:max(1, nargout)); end ================================================ FILE: leap/toolbox/aliases/ff.m ================================================ function varargout = ff(varargin) N = max(nargout,1); varargout{1:N} = fullfile(varargin{:}); end ================================================ FILE: leap/toolbox/aliases/h5file.m ================================================ function varargout = h5file(varargin) varargout{1:nargout} = hdf5prop(varargin{:}); end ================================================ FILE: leap/toolbox/aliases/imgsc.m ================================================ function h = imgsc(I, varargin) %IMGSC Alias for imagesc for images. % Usage: % imgsc(I) % imgsc(I, ...) % % See also: imagesc, sc % figure('KeyPressFcn',@KeyPressFcn_cb) figclosekey h = imagesc(I, varargin{:}); H = size(I,1); W = size(I,2); if max([H W]) / min([H W]) < 2 || all([H W] < 25) axis image end colorbar if nargout < 1; clear h; end end ================================================ FILE: leap/toolbox/aliases/repext.m ================================================ function varargout = repext(varargin) varargout{1:max(nargout,1)} = extrep(varargin{:}); end ================================================ FILE: leap/toolbox/graphics/FEX-settingsdlg/settingsdlg.m ================================================ function [settings, button] = settingsdlg(varargin) % SETTINGSDLG Default dialog to produce a settings-structure % % settings = SETTINGSDLG('fieldname', default_value, ...) creates a modal % dialog box that returns a structure formed according to user input. The % input should be given in the form of 'fieldname', default_value - pairs, % where 'fieldname' is the fieldname in the structure [settings], and % default_value the initial value displayed in the dialog box. % % SETTINGSDLG uses UIWAIT to suspend execution until the user responds. % % settings = SETTINGSDLG(settings) uses the structure [settings] to form % the various input fields. This is the most basic (and limited) usage of % SETTINGSDLG. % % [settings, button] = SETTINGSDLG(settings) returns which button was % pressed, in addition to the (modified) structure [settings]. Either 'ok', % 'cancel' or [] are possible values. The empty output means that the % dialog was closed before either Cancel or OK were pressed. % % SETTINGSDLG('title', 'window_title') uses 'window_title' as the dialog's % title. The default is 'Adjust settings'. % % SETTINGSDLG('description', 'brief_description',...) starts the dialog box % with 'brief_description', followed by the input fields. % % SETTINGSDLG('windowposition', P, ...) positions the dialog box according to % the string or vector [P]; see movegui() for valid values. % % SETTINGSDLG( {'display_string', 'fieldname'}, default_value,...) uses the % 'display_string' in the dialog box, while assigning the corresponding % user-input to fieldname 'fieldname'. % % SETTINGSDLG(..., 'checkbox_string', true, ...) displays a checkbox in % stead of the default edit box, and SETTINGSDLG('fieldname', {'string1', % 'string2'},... ) displays a popup box with the strings given in % the second cell-array. % % Additionally, you can put [..., 'separator', 'seperator_string',...] % anywhere in the argument list, which will divide all the arguments into % sections, with section headings 'seperator_string'. % % You can also modify the display behavior in the case of checkboxes. When % defining checkboxes with a 2-element logical array, the second boolean % determines whether all fields below that checkbox are initially disabled % (true) or not (false). % % Example: % % [settings, button] = settingsdlg(... % 'Description', 'This dialog will set the parameters used by FMINCON()',... % 'title' , 'FMINCON() options',... % 'separator' , 'Unconstrained/General',... % {'This is a checkbox'; 'Check'}, [true true],... % {'Tolerance X';'TolX'}, 1e-6,... % {'Tolerance on Function';'TolFun'}, 1e-6,... % 'Algorithm' , {'active-set','interior-point'},... % 'separator' , 'Constrained',... % {'Tolerance on Constraints';'TolCon'}, 1e-6) % % See also inputdlg, dialog, errordlg, helpdlg, listdlg, msgbox, questdlg, textwrap, % uiwait, warndlg. % Please report bugs and inquiries to: % % Name : Rody P.S. Oldenhuis % E-mail : oldenhuis@gmail.com % Licence: 2-clause BSD (See Licence.txt) % If you find this work useful, please consider a donation: % https://www.paypal.me/RodyO/3.5 %% Initialize % errortraps narg = nargin; if verLessThan('MATLAB', '8.6') error(nargchk(1, inf, narg, 'struct')); %#ok else narginchk(1, inf); end % parse input (+errortrap) have_settings = 0; if isstruct(varargin{1}) settings = varargin{1}; have_settings = 1; end if (narg == 1) if isstruct(varargin{1}) parameters = fieldnames(settings); values = cellfun(@(x)settings.(x), parameters, 'UniformOutput', false); else error('settingsdlg:incorrect_input',... 'When passing a single argument, that argument must be a structure.') end else parameters = varargin(1+have_settings : 2 : end); values = varargin(2+have_settings : 2 : end); end % Initialize data button = []; fields = cell(numel(parameters),1); tags = fields; % Fill [settings] with default values & collect data for ii = 1:numel(parameters) % Extract fields & tags if iscell(parameters{ii}) tags{ii} = parameters{ii}{1}; fields{ii} = parameters{ii}{2}; else % More errortraps if ~ischar(parameters{ii}) error('settingsdlg:nonstring_parameter',... 'Arguments should be given as [''parameter'', value,...] pairs.') end tags{ii} = parameters{ii}; fields{ii} = parameters{ii}; end % More errortraps if ~ischar(fields{ii}) error('settingsdlg:fieldname_not_char',... 'Fieldname should be a string.') end if ~ischar(tags{ii}) error('settingsdlg:tag_not_char',... 'Display name should be a string.') end % NOTE: 'Separator' is now in 'fields' even though % it will not be used as a fieldname % Make sure all fieldnames are properly formatted % (alternating capitals, no whitespace) if ~strcmpi(fields{ii}, {'Separator';'Title';'Description'}) whitespace = isspace(fields{ii}); capitalize = circshift(whitespace,[0,1]); fields{ii}(capitalize) = upper(fields{ii}(capitalize)); fields{ii} = fields{ii}(~whitespace); % insert associated value in output if iscell(values{ii}) settings.(fields{ii}) = values{ii}{1}; elseif (length(values{ii}) > 1) settings.(fields{ii}) = values{ii}(1); else settings.(fields{ii}) = values{ii}; end end end % Avoid (some) confusion clear parameters % Use default colorscheme from the OS bgcolor = get(0, 'defaultUicontrolBackgroundColor'); % Default fontsize fontsize = get(0, 'defaultuicontrolfontsize'); % Edit-bgcolor is platform-dependent. % MS/Windows: white. % UNIX: same as figure bgcolor % if ispc, edit_bgcolor = 'White'; % else edit_bgcolor = bgcolor; % end % TODO: not really applicable since defaultUicontrolBackgroundColor % doesn't really seem to work on Unix... edit_bgcolor = 'White'; % Get basic window properties title = getValue('Adjust settings', 'Title'); description = getValue( [], 'Description'); total_width = getValue(325, 'WindowWidth'); control_width = getValue(100, 'ControlWidth'); % Window positioning: % Put the window in the center of the screen by default. % This will usually work fine, except on some multi-monitor setups. scz = get(0, 'ScreenSize'); scxy = round(scz(3:4)/2-control_width/2); scx = min(scz(3),max(1,scxy(1))); scy = min(scz(4),max(1,scxy(2))); % String to pass on to movegui window_position = getValue('center', 'WindowPosition'); % Calculate best height for all uicontrol() control_height = max(18, (fontsize+6)); % Calculate figure height (will be adjusted later according to description) total_height = numel(fields)*1.25*control_height + ... % to fit all controls 1.5*control_height + 20; % to fit "OK" and "Cancel" buttons % Total number of separators num_separators = nnz(strcmpi(fields,'Separator')); % Draw figure in background fighandle = figure(... 'integerhandle' , 'off',... % use non-integers for the handle (prevents accidental plots from going to the dialog) 'Handlevisibility', 'off',... % only visible from within this function 'position' , [scx, scy, total_width, total_height],...% figure position 'visible' , 'off',... % hide the dialog while it is being constructed 'backingstore' , 'off',... % DON'T save a copy in the background 'resize' , 'off', ... % but just keep it resizable 'renderer' , 'zbuffer', ... % best choice for speed vs. compatibility 'WindowStyle' ,'modal',... % window is modal 'units' , 'pixels',... % better for drawing 'DockControls' , 'off',... % force it to be non-dockable 'name' , title,... % dialog title 'menubar' ,'none', ... % no menubar of course 'toolbar' ,'none', ... % no toolbar 'NumberTitle' , 'off',... % "Figure 1.4728...:" just looks corny 'color' , bgcolor); % use default colorscheme %% Draw all required uicontrols(), and unhide window % Define X-offsets (different when separators are used) separator_offset_X = 2; if num_separators > 0 text_offset_X = 20; text_width = (total_width-control_width-text_offset_X); else text_offset_X = separator_offset_X; text_width = (total_width-control_width); end % Handle description description_offset = 0; if ~isempty(description) % create textfield (negligible height initially) description_panel = uicontrol(... 'parent' , fighandle,... 'style' , 'text',... 'Horizontalalignment', 'left',... 'position', [separator_offset_X,... total_height,total_width,1]); % wrap the description description = textwrap(description_panel, {description}); % adjust the height of the figure textheight = size(description,1)*(fontsize+6); description_offset = textheight + 20; total_height = total_height + description_offset; set(fighandle,... 'position', [scx, scy, total_width, total_height]) % adjust the position of the textfield and insert the description set(description_panel, ... 'string' , description,... 'position', [separator_offset_X, total_height-textheight, ... total_width, textheight]); end % Define Y-offsets (different when descriptions are used) control_offset_Y = total_height-control_height-description_offset; % initialize loop controls = zeros(numel(tags)-num_separators,1); ii = 1; sep_ind = 1; enable = 'on'; separators = zeros(num_separators,1); % loop through the controls if numel(tags) > 0 while true % Should we draw a separator? if strcmpi(tags{ii}, 'Separator') % Print separator uicontrol(... 'style' , 'text',... 'parent' , fighandle,... 'string' , values{ii},... 'horizontalalignment', 'left',... 'fontweight', 'bold',... 'position', [separator_offset_X,control_offset_Y-4, ... total_width, control_height]); % remove separator, but save its position fields(ii) = []; tags(ii) = []; separators(sep_ind) = ii; values(ii) = []; sep_ind = sep_ind + 1; % reset enable (when neccessary) if strcmpi(enable, 'off') enable = 'on'; end % NOTE: DON'T increase loop index % ... or a setting? else % logicals: use checkbox if islogical(values{ii}) % First draw control controls(ii) = uicontrol(... 'style' , 'checkbox',... 'parent' , fighandle,... 'enable' , enable,... 'string' , tags{ii},... 'value' , values{ii}(1),... 'position', [text_offset_X,control_offset_Y-4, ... total_width, control_height]); % Should everything below here be OFF? if (length(values{ii})>1) % turn next controls off when asked for if values{ii}(2) enable = 'off'; end % Turn on callback function set(controls(ii),... 'Callback', @(varargin) EnableDisable(ii,varargin{:})); end % doubles : use edit box % cells : use popup % cell-of-cells: use table else % First print parameter uicontrol(... 'style' , 'text',... 'parent' , fighandle,... 'string' , [tags{ii}, ':'],... 'horizontalalignment', 'left',... 'position', [text_offset_X,control_offset_Y-4, ... text_width, control_height]); % Popup, edit box or table? style = 'edit'; draw_table = false; if iscell(values{ii}) style = 'popup'; if all(cellfun('isclass', values{ii}, 'cell')) draw_table = true; end end % Draw appropriate control if ~draw_table controls(ii) = uicontrol(... 'enable' , enable,... 'style' , style,... 'Background', edit_bgcolor,... 'parent' , fighandle,... 'string' , values{ii},... 'position', [text_width,control_offset_Y,... control_width, control_height]); else % TODO % ...table? ...radio buttons? How to do this? warning(... 'settingsdlg:not_yet_implemented',... 'Treatment of cells is not yet implemented.'); end end % increase loop index ii = ii + 1; end % end loop? if ii > numel(tags) break, end % Decrease offset control_offset_Y = control_offset_Y - 1.25*control_height; end end % Draw cancel button uicontrol(... 'style' , 'pushbutton',... 'parent' , fighandle,... 'string' , 'Cancel',... 'position', [separator_offset_X,2, total_width/2.5,control_height*1.5],... 'Callback', @Cancel) % Draw OK button uicontrol(... 'style' , 'pushbutton',... 'parent' , fighandle,... 'string' , 'OK',... 'position', [total_width*(1-1/2.5)-separator_offset_X,2, ... total_width/2.5,control_height*1.5],... 'Callback', @OK) % move to center of screen and make visible movegui(fighandle, window_position); set(fighandle, 'Visible', 'on'); % WAIT until OK/Cancel is pressed uiwait(fighandle); %% Helper funcitons % Get a value from the values array: % - if it does not exist, return the default value % - if it exists, assign it and delete the appropriate entries from the % data arrays function val = getValue(default, tag) index = strcmpi(fields, tag); if any(index) val = values{index}; values(index) = []; fields(index) = []; tags(index) = []; else val = default; end end %% callback functions % Enable/disable controls associated with (some) checkboxes function EnableDisable(which, varargin) %#ok % find proper range of controls to switch if (num_separators > 1) last_control = separators(find(separators > which,1)) - 1; if isempty(last_control); last_control = numel(controls); end range = (which+1):(last_control); else range = (which+1):numel(controls); end % enable/disable these controls if strcmpi(get(controls(range(1)), 'enable'), 'off') set(controls(range), 'enable', 'on') else set(controls(range), 'enable', 'off') end end % OK button: % - update fields in [settings] % - assign [button] output argument ('ok') % - kill window function OK(varargin) %#ok % button pressed button = 'OK'; % fill settings for i = 1:numel(controls) % extract current control's string, value & type str = get(controls(i), 'string'); val = get(controls(i), 'value'); style = get(controls(i), 'style'); % popups/edits if ~strcmpi(style, 'checkbox') % extract correct string (popups only) if strcmpi(style, 'popupmenu'), str = str{val}; end % try to convert string to double val = str2double(str); % insert this double in [settings]. If it was not a % double, insert string instead if ~isnan(val), settings.(fields{i}) = val; else settings.(fields{i}) = str; end % checkboxes else % we can insert value immediately settings.(fields{i}) = val; end end % kill window delete(fighandle); end % Cancel button: % - assign [button] output argument ('cancel') % - delete figure (so: return default settings) function Cancel(varargin) %#ok button = 'cancel'; delete(fighandle); end end ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Container.m ================================================ classdef Container < handle %uix.mixin.Container Container mixin % % uix.mixin.Container is a mixin class used by containers to provide % various properties and template methods. % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1358 $ $Date: 2016-09-14 11:34:17 +0100 (Wed, 14 Sep 2016) $ properties( Dependent, Access = public ) Contents % contents in layout order end properties( Access = public, Dependent, AbortSet ) Padding % space around contents, in pixels end properties( Access = protected ) Contents_ = gobjects( [0 1] ) % backing for Contents Padding_ = 0 % backing for Padding end properties( Dependent, Access = protected ) Dirty % needs redraw end properties( Access = private ) Dirty_ = false % backing for Dirty FigureObserver % observer FigureListener % listener ChildObserver % observer ChildAddedListener % listener ChildRemovedListener % listener SizeChangedListener % listener ActivePositionPropertyListeners = cell( [0 1] ) % listeners end methods function obj = Container() %uix.mixin.Container Initialize % % c@uix.mixin.Container() initializes the container c. % Create observers and listeners figureObserver = uix.FigureObserver( obj ); figureListener = event.listener( figureObserver, ... 'FigureChanged', @obj.onFigureChanged ); childObserver = uix.ChildObserver( obj ); childAddedListener = event.listener( ... childObserver, 'ChildAdded', @obj.onChildAdded ); childRemovedListener = event.listener( ... childObserver, 'ChildRemoved', @obj.onChildRemoved ); sizeChangedListener = event.listener( ... obj, 'SizeChanged', @obj.onSizeChanged ); % Store observers and listeners obj.FigureObserver = figureObserver; obj.FigureListener = figureListener; obj.ChildObserver = childObserver; obj.ChildAddedListener = childAddedListener; obj.ChildRemovedListener = childRemovedListener; obj.SizeChangedListener = sizeChangedListener; % Track usage obj.track() end % constructor end % structors methods function value = get.Contents( obj ) value = obj.Contents_; end % get.Contents function set.Contents( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check [tf, indices] = ismember( value, obj.Contents_ ); assert( isequal( size( obj.Contents_ ), size( value ) ) && ... numel( value ) == numel( unique( value ) ) && all( tf ), ... 'uix:InvalidOperation', ... 'Property ''Contents'' may only be set to a permutation of itself.' ) % Call reorder obj.reorder( indices ) end % set.Contents function value = get.Padding( obj ) value = obj.Padding_; end % get.Padding function set.Padding( obj, value ) % Check assert( isa( value, 'double' ) && isscalar( value ) && ... isreal( value ) && ~isinf( value ) && ... ~isnan( value ) && value >= 0, ... 'uix:InvalidPropertyValue', ... 'Property ''Padding'' must be a non-negative scalar.' ) % Set obj.Padding_ = value; % Mark as dirty obj.Dirty = true; end % set.Padding function value = get.Dirty( obj ) value = obj.Dirty_; end % get.Dirty function set.Dirty( obj, value ) if value if obj.isDrawable() % drawable obj.redraw() % redraw now else % not drawable obj.Dirty_ = true; % flag for future redraw end end end % set.Dirty end % accessors methods( Access = private, Sealed ) function onFigureChanged( obj, ~, eventData ) %onFigureChanged Event handler % Call template method obj.reparent( eventData.OldFigure, eventData.NewFigure ) % Redraw if possible and if dirty if obj.Dirty_ && obj.isDrawable() obj.redraw() obj.Dirty_ = false; end end % onFigureChanged function onChildAdded( obj, ~, eventData ) %onChildAdded Event handler % Call template method obj.addChild( eventData.Child ) end % onChildAdded function onChildRemoved( obj, ~, eventData ) %onChildRemoved Event handler % Do nothing if container is being deleted if strcmp( obj.BeingDeleted, 'on' ), return, end % Call template method obj.removeChild( eventData.Child ) end % onChildRemoved function onSizeChanged( obj, ~, ~ ) %onSizeChanged Event handler % Mark as dirty obj.Dirty = true; end % onSizeChanged function onActivePositionPropertyChanged( obj, ~, ~ ) %onActivePositionPropertyChanged Event handler % Mark as dirty obj.Dirty = true; end % onActivePositionPropertyChanged end % event handlers methods( Abstract, Access = protected ) redraw( obj ) end % abstract template methods methods( Access = protected ) function addChild( obj, child ) %addChild Add child % % c.addChild(d) adds the child d to the container c. % Add to contents obj.Contents_(end+1,:) = child; % Add listeners if isa( child, 'matlab.graphics.axis.Axes' ) obj.ActivePositionPropertyListeners{end+1,:} = ... event.proplistener( child, ... findprop( child, 'ActivePositionProperty' ), ... 'PostSet', @obj.onActivePositionPropertyChanged ); else obj.ActivePositionPropertyListeners{end+1,:} = []; end % Mark as dirty obj.Dirty = true; end % addChild function removeChild( obj, child ) %removeChild Remove child % % c.removeChild(d) removes the child d from the container c. % Remove from contents contents = obj.Contents_; tf = contents == child; obj.Contents_(tf,:) = []; % Remove listeners obj.ActivePositionPropertyListeners(tf,:) = []; % Mark as dirty obj.Dirty = true; end % removeChild function reparent( obj, oldFigure, newFigure ) %#ok %reparent Reparent container % % c.reparent(a,b) reparents the container c from the figure a % to the figure b. end % reparent function reorder( obj, indices ) %reorder Reorder contents % % c.reorder(i) reorders the container contents using indices % i, c.Contents = c.Contents(i). % Reorder contents obj.Contents_ = obj.Contents_(indices,:); % Reorder listeners obj.ActivePositionPropertyListeners = ... obj.ActivePositionPropertyListeners(indices,:); % Mark as dirty obj.Dirty = true; end % reorder function tf = isDrawable( obj ) %isDrawable Test for drawability % % c.isDrawable() is true if the container c is drawable, and % false otherwise. To be drawable, a container must be % rooted. tf = ~isempty( obj.FigureObserver.Figure ); end % isDrawable function track( obj ) %track Track usage persistent TRACKED % single shot if isempty( TRACKED ) v = ver( 'layout' ); try %#ok uix.tracking( 'UA-82270656-2', v(1).Version, class( obj ) ) end TRACKED = true; end end % track end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Flex.m ================================================ classdef Flex < handle %uix.mixin.Flex Flex mixin % % uix.mixin.Flex is a mixin class used by flex containers to provide % various properties and helper methods. % Copyright 2016 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ properties( GetAccess = protected, SetAccess = private ) Pointer = 'unset' % mouse pointer end properties( Access = private ) Figure = gobjects( 0 ); % mouse pointer figure Token = -1 % mouse pointer token end methods function delete( obj ) %delete Destructor % Clean up if ~strcmp( obj.Pointer, 'unset' ) obj.unsetPointer() end end % destructor end % structors methods( Access = protected ) function setPointer( obj, figure, pointer ) %setPointer Set pointer % % c.setPointer(f,p) sets the pointer for the figure f to p. % If set, unset if obj.Token ~= -1 obj.unsetPointer() end % Set obj.Token = uix.PointerManager.setPointer( figure, pointer ); obj.Figure = figure; obj.Pointer = pointer; end % setPointer function unsetPointer( obj ) %unsetPointer Unset pointer % % c.unsetPointer() undoes the previous pointer set. % Check assert( obj.Token ~= -1, 'uix:InvalidOperation', ... 'Pointer is already unset.' ) % Unset uix.PointerManager.unsetPointer( obj.Figure, obj.Token ); obj.Figure = gobjects( 0 ); obj.Pointer = 'unset'; obj.Token = -1; end % unsetPointer end % helper methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Panel.m ================================================ classdef Panel < uix.mixin.Container %uix.mixin.Panel Panel mixin % % uix.mixin.Panel is a mixin class used by panels to provide various % properties and template methods. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ properties( Access = public, Dependent, AbortSet ) Selection % selected contents end properties( Access = protected ) Selection_ = 0 % backing for Selection end properties( Access = protected ) G1218142 = false % bug flag end events( NotifyAccess = protected ) SelectionChanged % selection changed end methods function value = get.Selection( obj ) value = obj.Selection_; end % get.Selection function set.Selection( obj, value ) % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''Selection'' must be of type double.' ) assert( isequal( size( value ), [1 1] ), ... 'uix:InvalidPropertyValue', ... 'Property ''Selection'' must be scalar.' ) assert( isreal( value ) && rem( value, 1 ) == 0, ... 'uix:InvalidPropertyValue', ... 'Property ''Selection'' must be an integer.' ) n = numel( obj.Contents_ ); if n == 0 assert( value == 0, 'uix:InvalidPropertyValue', ... 'Property ''Selection'' must be 0 for a container with no children.' ) else assert( value >= 1 && value <= n, 'uix:InvalidPropertyValue', ... 'Property ''Selection'' must be between 1 and the number of children.' ) end % Set oldSelection = obj.Selection_; newSelection = value; obj.Selection_ = newSelection; % Show selected child obj.showSelection() % Mark as dirty obj.Dirty = true; % Raise event notify( obj, 'SelectionChanged', ... uix.SelectionData( oldSelection, newSelection ) ) end % set.Selection end % accessors methods( Access = protected ) function addChild( obj, child ) % Check for bug if verLessThan( 'MATLAB', '8.5' ) && strcmp( child.Visible, 'off' ) obj.G1218142 = true; end % Select new content oldSelection = obj.Selection_; newSelection = numel( obj.Contents_ ) + 1; obj.Selection_ = newSelection; % Call superclass method addChild@uix.mixin.Container( obj, child ) % Show selected child obj.showSelection() % Notify selection change obj.notify( 'SelectionChanged', ... uix.SelectionData( oldSelection, newSelection ) ) end % addChild function removeChild( obj, child ) % Adjust selection if required contents = obj.Contents_; index = find( contents == child ); oldSelection = obj.Selection_; if index < oldSelection newSelection = oldSelection - 1; elseif index == oldSelection newSelection = min( oldSelection, numel( contents ) - 1 ); else % index > oldSelection newSelection = oldSelection; end obj.Selection_ = newSelection; % Call superclass method removeChild@uix.mixin.Container( obj, child ) % Show selected child obj.showSelection() % Notify selection change if oldSelection ~= newSelection obj.notify( 'SelectionChanged', ... uix.SelectionData( oldSelection, newSelection ) ) end end % removeChild function reorder( obj, indices ) %reorder Reorder contents % % c.reorder(i) reorders the container contents using indices % i, c.Contents = c.Contents(i). % Reorder selection = obj.Selection_; if selection ~= 0 obj.Selection_ = find( indices == selection ); end % Call superclass method reorder@uix.mixin.Container( obj, indices ) end % reorder function showSelection( obj ) %showSelection Show selected child, hide the others % % c.showSelection() shows the selected child of the container % c, and hides the others. % Set positions and visibility selection = obj.Selection_; children = obj.Contents_; for ii = 1:numel( children ) child = children(ii); if ii == selection if obj.G1218142 warning( 'uix:G1218142', ... 'Selected child of %s is not visible due to bug G1218142. The child will become visible at the next redraw.', ... class( obj ) ) obj.G1218142 = false; else child.Visible = 'on'; end if isa( child, 'matlab.graphics.axis.Axes' ) child.ContentsVisible = 'on'; end else child.Visible = 'off'; if isa( child, 'matlab.graphics.axis.Axes' ) child.ContentsVisible = 'off'; end % As a remedy for g1100294, move off-screen too margin = 1000; if isa( child, 'matlab.graphics.axis.Axes' ) ... && strcmp(child.ActivePositionProperty, 'outerposition' ) child.OuterPosition(1) = -child.OuterPosition(3)-margin; else child.Position(1) = -child.Position(3)-margin; end end end end % showSelection end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Box.m ================================================ classdef Box < uix.Container & uix.mixin.Container %uix.Box Box and grid base class % % uix.Box is a base class for containers with spacing between % contents. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $ properties( Access = public, Dependent, AbortSet ) Spacing = 0 % space between contents, in pixels end properties( Access = protected ) Spacing_ = 0 % backing for Spacing end methods function value = get.Spacing( obj ) value = obj.Spacing_; end % get.Spacing function set.Spacing( obj, value ) % Check assert( isa( value, 'double' ) && isscalar( value ) && ... isreal( value ) && ~isinf( value ) && ... ~isnan( value ) && value >= 0, ... 'uix:InvalidPropertyValue', ... 'Property ''Spacing'' must be a non-negative scalar.' ) % Set obj.Spacing_ = value; % Mark as dirty obj.Dirty = true; end % set.Spacing end % accessors end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/ChildEvent.m ================================================ classdef( Hidden, Sealed ) ChildEvent < event.EventData %uix.ChildEvent Event data for child event % % e = uix.ChildEvent(c) creates event data including the child c. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $ properties( SetAccess = private ) Child % child end methods function obj = ChildEvent( child ) %uix.ChildEvent Event data for child event % % e = uix.ChildEvent(c) creates event data including the child % c. % Set properties obj.Child = child; end % constructor end % structors end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/ChildObserver.m ================================================ classdef ( Hidden, Sealed ) ChildObserver < handle %uix.ChildObserver Child observer % % co = uix.ChildObserver(o) creates a child observer for the graphics % object o. A child observer raises events when objects are added to % and removed from the property Children of o. % % See also: uix.Node % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Access = private ) Root % root node end events( NotifyAccess = private ) ChildAdded % child added ChildRemoved % child removed end methods function obj = ChildObserver( oRoot ) %uix.ChildObserver Child observer % % co = uix.ChildObserver(o) creates a child observer for the % graphics object o. A child observer raises events when % objects are added to and removed from the property Children % of o. % Check assert( iscontent( oRoot ) && ... isequal( size( oRoot ), [1 1] ), 'uix.InvalidArgument', ... 'Object must be a graphics object.' ) % Create root node nRoot = uix.Node( oRoot ); childAddedListener = event.listener( oRoot, ... 'ObjectChildAdded', ... @(~,e)obj.addChild(nRoot,e.Child) ); childAddedListener.Recursive = true; nRoot.addprop( 'ChildAddedListener' ); nRoot.ChildAddedListener = childAddedListener; childRemovedListener = event.listener( oRoot, ... 'ObjectChildRemoved', ... @(~,e)obj.removeChild(nRoot,e.Child) ); childRemovedListener.Recursive = true; nRoot.addprop( 'ChildRemovedListener' ); nRoot.ChildRemovedListener = childRemovedListener; % Add children oChildren = hgGetTrueChildren( oRoot ); for ii = 1:numel( oChildren ) obj.addChild( nRoot, oChildren(ii) ) end % Store properties obj.Root = nRoot; end % constructor end % structors methods( Access = private ) function addChild( obj, nParent, oChild ) %addChild Add child object to parent node % % co.addChild(np,oc) adds the child object oc to the parent % node np, either as part of construction of the child % observer co, or in response to an ObjectChildAdded event on % an object of interest to co. This may lead to ChildAdded % events being raised on co. % Create child node nChild = uix.Node( oChild ); nParent.addChild( nChild ) if iscontent( oChild ) % Add Internal PreSet property listener internalPreSetListener = event.proplistener( oChild, ... findprop( oChild, 'Internal' ), 'PreSet', ... @(~,~)obj.preSetInternal(nChild) ); nChild.addprop( 'InternalPreSetListener' ); nChild.InternalPreSetListener = internalPreSetListener; % Add Internal PostSet property listener internalPostSetListener = event.proplistener( oChild, ... findprop( oChild, 'Internal' ), 'PostSet', ... @(~,~)obj.postSetInternal(nChild) ); nChild.addprop( 'InternalPostSetListener' ); nChild.InternalPostSetListener = internalPostSetListener; else % Add ObjectChildAdded listener childAddedListener = event.listener( oChild, ... 'ObjectChildAdded', ... @(~,e)obj.addChild(nChild,e.Child) ); nChild.addprop( 'ChildAddedListener' ); nChild.ChildAddedListener = childAddedListener; % Add ObjectChildRemoved listener childRemovedListener = event.listener( oChild, ... 'ObjectChildRemoved', ... @(~,e)obj.removeChild(nChild,e.Child) ); nChild.addprop( 'ChildRemovedListener' ); nChild.ChildRemovedListener = childRemovedListener; end % Raise ChildAdded event if iscontent( oChild ) && oChild.Internal == false notify( obj, 'ChildAdded', uix.ChildEvent( oChild ) ) end % Add grandchildren if ~iscontent( oChild ) oGrandchildren = hgGetTrueChildren( oChild ); for ii = 1:numel( oGrandchildren ) obj.addChild( nChild, oGrandchildren(ii) ) end end end % addChild function removeChild( obj, nParent, oChild ) %removeChild Remove child object from parent node % % co.removeChild(np,oc) removes the child object oc from the % parent node np, in response to an ObjectChildRemoved event % on an object of interest to co. This may lead to % ChildRemoved events being raised on co. % Get child node nChildren = nParent.Children; tf = oChild == [nChildren.Object]; nChild = nChildren(tf); % Raise ChildRemoved event(s) notifyChildRemoved( nChild ) % Delete child node delete( nChild ) function notifyChildRemoved( nc ) % Process child nodes ngc = nc.Children; for ii = 1:numel( ngc ) notifyChildRemoved( ngc(ii) ) end % Process this node oc = nc.Object; if iscontent( oc ) && oc.Internal == false notify( obj, 'ChildRemoved', uix.ChildEvent( oc ) ) end end % notifyChildRemoved end % removeChild function preSetInternal( ~, nChild ) %preSetInternal Perform property PreSet tasks % % co.preSetInternal(n) caches the previous value of the % property Internal of the object referenced by the node n, to % enable PostSet tasks to identify whether the value changed. % This is necessary since Internal AbortSet is false. oldInternal = nChild.Object.Internal; nChild.addprop( 'OldInternal' ); nChild.OldInternal = oldInternal; end % preSetInternal function postSetInternal( obj, nChild ) %postSetInternal Perform property PostSet tasks % % co.postSetInternal(n) raises a ChildAdded or ChildRemoved % event on the child observer co in response to a change of % the value of the property Internal of the object referenced % by the node n. % Retrieve old and new values oChild = nChild.Object; newInternal = oChild.Internal; oldInternal = nChild.OldInternal; % Clean up node delete( findprop( nChild, 'OldInternal' ) ) % Raise event switch newInternal case oldInternal % no change % no event case true % false to true notify( obj, 'ChildRemoved', uix.ChildEvent( oChild ) ) case false % true to false notify( obj, 'ChildAdded', uix.ChildEvent( oChild ) ) end end % postSetInternal end % event handlers end % classdef function tf = iscontent( o ) %iscontent True for graphics that can be Contents (and can be Children) % % uix.ChildObserver needs to determine which objects can be Contents, % which is equivalent to can be Children if HandleVisibility is 'on' and % Internal is false. Prior to R2016a, this condition could be checked % using isgraphics. From R2016a, isgraphics returns true for a wider % range of objects, including some that can never by Contents, e.g., % JavaCanvas. Therefore this function checks whether an object is of type % matlab.graphics.internal.GraphicsBaseFunctions, which is what isgraphics % did prior to R2016a. tf = isa( o, 'matlab.graphics.internal.GraphicsBaseFunctions' ) &&... isprop( o, 'Position' ); end % iscontent ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Container.m ================================================ classdef Container < matlab.ui.container.internal.UIContainer %uix.Container Container base class % % uix.Container is base class for containers that extend uicontainer. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1137 $ $Date: 2015-05-29 21:48:21 +0100 (Fri, 29 May 2015) $ end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Divider.m ================================================ classdef Divider < matlab.mixin.SetGet %uix.Divider Draggable divider % % d = uix.Divider() creates a divider. % % d = uix.Divider(p1,v1,p2,v2,...) creates a divider and sets % specified property p1 to value v1, etc. % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Dependent ) Parent % parent Units % units [inches|centimeters|characters|normalized|points|pixels] Position % position Visible % visible [on|off] BackgroundColor % background color [RGB] HighlightColor % border highlight color [RGB] ShadowColor % border shadow color [RGB] Orientation % orientation [vertical|horizontal] Markings % markings [pixels] end properties( Access = private ) Control % uicontrol BackgroundColor_ = get( 0, 'DefaultUicontrolBackgroundColor' ) % backing for BackgroundColor HighlightColor_ = [1 1 1] % backing for HighlightColor ShadowColor_ = [0.7 0.7 0.7] % backing for ShadowColor Orientation_ = 'vertical' % backing for Orientation Markings_ = zeros( [0 1] ) % backing for Markings SizeChangedListener % listener end methods function obj = Divider( varargin ) %uix.Divider Draggable divider % % d = uix.Divider() creates a divider. % % d = uix.Divider(p1,v1,p2,v2,...) creates a dividerand sets % specified property p1 to value v1, etc. % Create control control = matlab.ui.control.UIControl( ... 'Style', 'checkbox', 'Internal', true, ... 'Enable', 'inactive', 'DeleteFcn', @obj.onDeleted,... 'Tag', 'uix.Divider' ); % Store control obj.Control = control; % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end % Force update obj.update() % Create listener sizeChangedListener = event.listener( control, 'SizeChanged', ... @obj.onSizeChanged ); % Store listener obj.SizeChangedListener = sizeChangedListener; end % constructor function delete( obj ) %delete Destructor control = obj.Control; if isgraphics( control ) && strcmp( control.BeingDeleted, 'off' ) delete( control ) end end % destructor end % structors methods function value = get.Parent( obj ) value = obj.Control.Parent; end % get.Parent function set.Parent( obj, value ) obj.Control.Parent = value; end % set.Parent function value = get.Units( obj ) value = obj.Control.Units; end % get.Units function set.Units( obj, value ) obj.Control.Units = value; end % set.Units function value = get.Position( obj ) value = obj.Control.Position; end % get.Position function set.Position( obj, value ) obj.Control.Position = value; end % set.Position function value = get.Visible( obj ) value = obj.Control.Visible; end % get.Visible function set.Visible( obj, value ) obj.Control.Visible = value; end % set.Visible function value = get.BackgroundColor( obj ) value = obj.BackgroundColor_; end % get.BackgroundColor function set.BackgroundColor( obj, value ) % Check assert( isa( value, 'double' ) && ... isequal( size( value ), [1 3] ) && ... all( value >= 0 ) && all( value <= 1 ), ... 'uix:InvalidArgument', ... 'Property ''BackgroundColor'' must be a valid colorspec.' ) % Set obj.BackgroundColor_ = value; % Update obj.update() end % set.BackgroundColor function value = get.HighlightColor( obj ) value = obj.HighlightColor_; end % get.HighlightColor function set.HighlightColor( obj, value ) % Check assert( isnumeric( value ) && isequal( size( value ), [1 3] ) && ... all( isreal( value ) ) && all( value >= 0 ) && all( value <= 1 ), ... 'uix:InvalidPropertyValue', ... 'Property ''HighlightColor'' must be an RGB triple.' ) % Set obj.HighlightColor_ = value; % Update obj.update() end % set.HighlightColor function value = get.ShadowColor( obj ) value = obj.ShadowColor_; end % get.ShadowColor function set.ShadowColor( obj, value ) % Check assert( isnumeric( value ) && isequal( size( value ), [1 3] ) && ... all( isreal( value ) ) && all( value >= 0 ) && all( value <= 1 ), ... 'uix:InvalidPropertyValue', ... 'Property ''ShadowColor'' must be an RGB triple.' ) % Set obj.ShadowColor_ = value; % Update obj.update() end % set.ShadowColor function value = get.Orientation( obj ) value = obj.Orientation_; end % get.Orientation function set.Orientation( obj, value ) % Check assert( ischar( value ) && ismember( value, ... {'horizontal','vertical'} ) ) % Set obj.Orientation_ = value; % Update obj.update() end % set.Orientation function value = get.Markings( obj ) value = obj.Markings_; end % get.Markings function set.Markings( obj, value ) % Check assert( isa( value, 'double' ) && ndims( value ) == 2 && ... size( value, 2 ) == 1 && all( isreal( value ) ) && ... all( ~isinf( value ) ) && all( ~isnan( value ) ) && ... all( value > 0 ), 'uix:InvalidPropertyValue', ... 'Property ''Markings'' must be a vector of positive values.' ) %#ok % Set obj.Markings_ = value; % Update obj.update() end % set.Markings end % accessors methods function tf = isMouseOver( obj, eventData ) %isMouseOver Test for mouse over % % tf = d.isMouseOver(wmd) tests whether the WindowMouseData % wmd is consistent with the mouse pointer being over the % divider d. tf = reshape( [obj.Control] == eventData.HitObject, size( obj ) ); end % isMouseOver end % methods methods( Access = private ) function onDeleted( obj, ~, ~ ) %onDeleted Event handler % Call destructor obj.delete() end % onDeleted function onSizeChanged( obj, ~, ~ ) %onSizeChanged Event handler % Update obj.update() end % onSizeChanged end % event handlers methods( Access = private ) function update( obj ) %update Update divider % % d.update() updates the divider markings. % Get properties control = obj.Control; position = control.Position; backgroundColor = obj.BackgroundColor; highlightColor = obj.HighlightColor; shadowColor = obj.ShadowColor; orientation = obj.Orientation; markings = obj.Markings; % Assemble mask mask = zeros( floor( position([4 3]) ) - [1 1] ); % initialize switch orientation case 'vertical' markings(markings < 4) = []; markings(markings > position(4)-6) = []; for ii = 1:numel( markings ) marking = markings(ii); mask(floor( marking ) + [-3 0 3],1:end-1) = 1; mask(floor( marking ) + [-2 1 4],1:end-1) = 2; end case 'horizontal' markings(markings < 4) = []; markings(markings > position(3)-6) = []; for ii = 1:numel( markings ) marking = markings(ii); mask(2:end,floor( marking ) + [-3 0 3]) = 1; mask(2:end,floor( marking ) + [-2 1 4]) = 2; end end % Assemble color data cData1 = repmat( backgroundColor(1), size( mask ) ); cData1(mask==1) = highlightColor(1); cData1(mask==2) = shadowColor(1); cData2 = repmat( backgroundColor(2), size( mask ) ); cData2(mask==1) = highlightColor(2); cData2(mask==2) = shadowColor(2); cData3 = repmat( backgroundColor(3), size( mask ) ); cData3(mask==1) = highlightColor(3); cData3(mask==2) = shadowColor(3); cData = cat( 3, cData1, cData2, cData3 ); % Set properties control.ForegroundColor = backgroundColor; control.BackgroundColor = backgroundColor; control.CData = cData; end % update end % methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/FigureData.m ================================================ classdef ( Hidden, Sealed ) FigureData < event.EventData %uix.FigureData Event data for FigureChanged on uix.FigureObserver % Copyright 2014-2015 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ properties( SetAccess = private ) OldFigure % old figure NewFigure % new figure end methods( Access = ?uix.FigureObserver ) function obj = FigureData( oldFigure, newFigure ) %uix.FigureData Create event data % % d = uix.FigureData(oldFigure,newFigure) obj.OldFigure = oldFigure; obj.NewFigure = newFigure; end % constructor end % methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/FigureObserver.m ================================================ classdef ( Hidden, Sealed ) FigureObserver < handle %uix.FigureObserver Figure observer % % A figure observer raises an event FigureChanged when the figure % ancestor of a subject changes. % Copyright 2014-2015 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ properties( SetAccess = private ) Subject % subject Figure % figure ancestor end properties( Access = private ) PreSetListeners % listeners to Parent PreGet PostSetListeners % listeners to Parent PreGet OldFigure = gobjects( 0 ) % previous figure ancestor end events( NotifyAccess = private ) FigureChanged end methods function obj = FigureObserver( subject ) %uix.FigureObserver Create figure observer % % o = uix.FigureObserver(s) creates a figure observer for the % subject s. % Check validateattributes( subject, {'matlab.graphics.Graphics'}, ... {'scalar'}, '', 'subject' ) % Store subject obj.Subject = subject; % Set up object obj.update() end % constructor end % structors methods( Access = private ) function update( obj ) %update Update listeners and Figure property % Create fresh listeners obj.PreSetListeners = event.proplistener.empty( [1 0] ); % clear obj.PostSetListeners = event.proplistener.empty( [1 0] ); % clear o = obj.Subject; while ~isempty( o ) && ~isa( o, 'matlab.ui.Figure' ) obj.PreSetListeners(end+1) = event.proplistener( o, ... findprop( o, 'Parent' ), 'PreSet', @obj.onParentPreSet ); obj.PostSetListeners(end+1) = event.proplistener( o, ... findprop( o, 'Parent' ), 'PostSet', @obj.onParentPostSet ); o = o.Parent; end % Store figure obj.Figure = o; end % update function onParentPreSet( obj, ~, ~ ) %onParentPreSet Event handler % Store old figure obj.OldFigure = obj.Figure; end % onParentPreSet function onParentPostSet( obj, ~, ~ ) %onParentPostSet Event handler % Update object obj.update() % Raise event oldFigure = obj.OldFigure; newFigure = obj.Figure; if ~isequal( oldFigure, newFigure ) notify( obj, 'FigureChanged', ... uix.FigureData( oldFigure, newFigure ) ) end % Clear old figure obj.OldFigure = gobjects( 0 ); end % onParentPostSet end % private methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Grid.m ================================================ classdef Grid < uix.Box %uix.Grid Grid % % b = uix.Grid(p1,v1,p2,v2,...) constructs a grid and sets parameter % p1 to value v1, etc. % % A grid lays out contents from top to bottom and left to right. % % See also: uix.HBox, uix.VBox, uix.GridFlex % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Access = public, Dependent, AbortSet ) Widths % widths of contents, in pixels and/or weights MinimumWidths % minimum widths of contents, in pixels Heights % heights of contents, in pixels and/or weights MinimumHeights % minimum heights of contents, in pixels end properties( Access = protected ) Widths_ = zeros( [0 1] ) % backing for Widths MinimumWidths_ = zeros( [0 1] ) % backing for MinimumWidths Heights_ = zeros( [0 1] ) % backing for Heights MinimumHeights_ = zeros( [0 1] ) % backing for MinimumHeights end methods function obj = Grid( varargin ) %uix.Grid Grid constructor % % b = uix.Grid() constructs a grid. % % b = uix.Grid(p1,v1,p2,v2,...) sets parameter p1 to value v1, % etc. % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end end % constructor end % structors methods function value = get.Widths( obj ) value = obj.Widths_; end % get.Widths function set.Widths( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''Widths'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ... 'Elements of property ''Widths'' must be real and finite.' ) n = numel( obj.Contents_ ); b = numel( obj.Widths_ ); q = numel( obj.Heights_ ); c = numel( value ); r = ceil( n / c ); if c < min( [1 n] ) error( 'uix:InvalidPropertyValue' , ... 'Property ''Widths'' must be non-empty for non-empty contents.' ) elseif ceil( n / r ) < c error( 'uix:InvalidPropertyValue' , ... 'Size of property ''Widths'' must not lead to empty columns.' ) elseif c > n error( 'uix:InvalidPropertyValue' , ... 'Size of property ''Widths'' must be no larger than size of contents.' ) end % Set obj.Widths_ = value; if c < b % number of columns decreasing obj.MinimumWidths_(c+1:end,:) = []; if r > q % number of rows increasing obj.Heights_(end+1:r,:) = -1; obj.MinimumHeights_(end+1:r,:) = 1; end elseif c > b % number of columns increasing obj.MinimumWidths_(end+1:c,:) = -1; if r < q % number of rows decreasing obj.Heights_(r+1:end,:) = []; obj.MinimumHeights_(r+1:end,:) = []; end end % Mark as dirty obj.Dirty = true; end % set.Widths function value = get.MinimumWidths( obj ) value = obj.MinimumWidths_; end % get.MinimumWidths function set.MinimumWidths( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''MinimumWidths'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... all( value >= 0 ), 'uix:InvalidPropertyValue', ... 'Elements of property ''MinimumWidths'' must be non-negative.' ) assert( isequal( size( value ), size( obj.Widths_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''MinimumWidths'' must match size of contents.' ) % Set obj.MinimumWidths_ = value; % Mark as dirty obj.Dirty = true; end % set.MinimumWidths function value = get.Heights( obj ) value = obj.Heights_; end % get.Heights function set.Heights( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''Heights'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ... 'Elements of property ''Heights'' must be real and finite.' ) n = numel( obj.Contents_ ); b = numel( obj.Widths_ ); q = numel( obj.Heights_ ); r = numel( value ); c = ceil( n / r ); if r < min( [1 n] ) error( 'uix:InvalidPropertyValue' , ... 'Property ''Heights'' must be non-empty for non-empty contents.' ) elseif r > n error( 'uix:InvalidPropertyValue' , ... 'Size of property ''Heights'' must be no larger than size of contents.' ) end % Set obj.Heights_ = value; if r < q % number of rows decreasing obj.MinimumHeights_(r+1:end,:) = []; if c > b % number of columns increasing obj.Widths_(end+1:c,:) = -1; obj.MinimumWidths_(end+1:c,:) = 1; end elseif r > q % number of rows increasing obj.MinimumHeights_(end+1:r,:) = 1; if c < b % number of columns decreasing obj.Widths_(c+1:end,:) = []; obj.MinimumWidths_(c+1:end,:) = []; end end % Mark as dirty obj.Dirty = true; end % set.Heights function value = get.MinimumHeights( obj ) value = obj.MinimumHeights_; end % get.MinimumHeights function set.MinimumHeights( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''MinimumHeights'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... all( value >= 0 ), 'uix:InvalidPropertyValue', ... 'Elements of property ''MinimumHeights'' must be non-negative.' ) assert( isequal( size( value ), size( obj.Heights_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''MinimumHeights'' must match size of contents.' ) % Set obj.MinimumHeights_ = value; % Mark as dirty obj.Dirty = true; end % set.MinimumHeights end % accessors methods( Access = protected ) function redraw( obj ) %redraw Redraw % % c.redraw() redraws the container c. % Compute positions bounds = hgconvertunits( ancestor( obj, 'figure' ), ... [0 0 1 1], 'normalized', 'pixels', obj ); widths = obj.Widths_; minimumWidths = obj.MinimumWidths_; heights = obj.Heights_; minimumHeights = obj.MinimumHeights_; padding = obj.Padding_; spacing = obj.Spacing_; c = numel( widths ); r = numel( heights ); n = numel( obj.Contents_ ); xSizes = uix.calcPixelSizes( bounds(3), widths, ... minimumWidths, padding, spacing ); xPositions = [cumsum( [0; xSizes(1:end-1,:)] ) + padding + ... spacing * transpose( 0:c-1 ) + 1, xSizes]; ySizes = uix.calcPixelSizes( bounds(4), heights, ... minimumHeights, padding, spacing ); yPositions = [bounds(4) - cumsum( ySizes ) - padding - ... spacing * transpose( 0:r-1 ) + 1, ySizes]; [iy, ix] = ind2sub( [r c], transpose( 1:n ) ); positions = [xPositions(ix,1), yPositions(iy,1), ... xPositions(ix,2), yPositions(iy,2)]; % Set positions children = obj.Contents_; for ii = 1:numel( children ) uix.setPosition( children(ii), positions(ii,:), 'pixels' ) end end % redraw function addChild( obj, child ) %addChild Add child % % c.addChild(d) adds the child d to the container c. % Add column and even a row if necessary n = numel( obj.Contents_ ); c = numel( obj.Widths_ ); r = numel( obj.Heights_ ); if n == 0 obj.Widths_(end+1,:) = -1; obj.MinimumWidths_(end+1,:) = 1; obj.Heights_(end+1,:) = -1; obj.MinimumHeights_(end+1,:) = 1; elseif ceil( (n+1)/r ) > c obj.Widths_(end+1,:) = -1; obj.MinimumWidths_(end+1,:) = 1; end % Call superclass method addChild@uix.Box( obj, child ) end % addChild function removeChild( obj, child ) %removeChild Remove child % % c.removeChild(d) removes the child d from the container c. % Remove column and even row if necessary n = numel( obj.Contents_ ); c = numel( obj.Widths_ ); r = numel( obj.Heights_ ); if n == 1 obj.Widths_(end,:) = []; obj.MinimumWidths_(end,:) = []; obj.Heights_(end,:) = []; obj.MinimumHeights_(end,:) = []; elseif c == 1 obj.Heights_(end,:) = []; obj.MinimumHeights_(end,:) = []; elseif ceil( (n-1)/r ) < c obj.Widths_(end,:) = []; obj.MinimumWidths_(end,:) = []; end % Call superclass method removeChild@uix.Box( obj, child ) end % removeChild end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/GridFlex.m ================================================ classdef GridFlex < uix.Grid & uix.mixin.Flex %uix.GridFlex Flexible grid % % b = uix.GridFlex(p1,v1,p2,v2,...) constructs a flexible grid and % sets parameter p1 to value v1, etc. % % A grid lays out contents from top to bottom and left to right. % Users can resize contents by dragging the dividers. % % See also: uix.HBoxFlex, uix.VBoxFlex, uix.Grid % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Access = public, Dependent, AbortSet ) DividerMarkings % divider markings [on|off] end properties( Access = private ) RowDividers = uix.Divider.empty( [0 1] ) ColumnDividers = uix.Divider.empty( [0 1] ) FrontDivider % front divider DividerMarkings_ = 'on' % backing for DividerMarkings MousePressListener = event.listener.empty( [0 0] ) % mouse press listener MouseReleaseListener = event.listener.empty( [0 0] ) % mouse release listener MouseMotionListener = event.listener.empty( [0 0] ) % mouse motion listener ActiveDivider = 0 % active divider index ActiveDividerPosition = [NaN NaN NaN NaN] % active divider position MousePressLocation = [NaN NaN] % mouse press location BackgroundColorListener % background color listener end methods function obj = GridFlex( varargin ) %uix.GridFlex Flexible grid constructor % % b = uix.GridFlex() constructs a flexible grid. % % b = uix.GridFlex(p1,v1,p2,v2,...) sets parameter p1 to value % v1, etc. % Create front divider frontDivider = uix.Divider( 'Parent', obj, ... 'Orientation', 'vertical', ... 'BackgroundColor', obj.BackgroundColor * 0.75, ... 'Visible', 'off' ); % Create listeners backgroundColorListener = event.proplistener( obj, ... findprop( obj, 'BackgroundColor' ), 'PostSet', ... @obj.onBackgroundColorChange ); % Store properties obj.FrontDivider = frontDivider; obj.BackgroundColorListener = backgroundColorListener; % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end end % constructor end % structors methods function value = get.DividerMarkings( obj ) value = obj.DividerMarkings_; end % get.DividerMarkings function set.DividerMarkings( obj, value ) % Check assert( ischar( value ) && any( strcmp( value, {'on','off'} ) ), ... 'uix:InvalidArgument', ... 'Property ''DividerMarkings'' must be ''on'' or ''off'.' ) % Set obj.DividerMarkings_ = value; % Mark as dirty obj.Dirty = true; end % set.DividerMarkings end % accessors methods( Access = protected ) function onMousePress( obj, source, eventData ) %onMousePress Handler for WindowMousePress events % Check whether mouse is over a divider locr = find( obj.RowDividers.isMouseOver( eventData ) ); locc = find( obj.ColumnDividers.isMouseOver( eventData ) ); if ~isempty( locr ) loc = locr; divider = obj.RowDividers(locr); elseif ~isempty( locc ) loc = -locc; divider = obj.ColumnDividers(locc); else return end % Capture state at button down obj.ActiveDivider = loc; obj.ActiveDividerPosition = divider.Position; root = groot(); obj.MousePressLocation = root.PointerLocation; % Make sure the pointer is appropriate obj.updateMousePointer( source, eventData ); % Activate divider frontDivider = obj.FrontDivider; frontDivider.Position = divider.Position; frontDivider.Orientation = divider.Orientation; divider.Visible = 'off'; frontDivider.Parent = []; frontDivider.Parent = obj; frontDivider.Visible = 'on'; end % onMousePress function onMouseRelease( obj, ~, ~ ) %onMousePress Handler for WindowMouseRelease events % Compute new positions loc = obj.ActiveDivider; if loc > 0 root = groot(); delta = root.PointerLocation(2) - obj.MousePressLocation(2); ih = loc; jh = loc + 1; ic = loc; jc = loc + 1; divider = obj.RowDividers(loc); contents = obj.Contents_; oldPixelHeights = [contents(ic).Position(4); contents(jc).Position(4)]; minimumHeights = obj.MinimumHeights_(ih:jh,:); if delta < 0 % limit to minimum distance from lower neighbor delta = max( delta, minimumHeights(2) - oldPixelHeights(2) ); else % limit to minimum distance from upper neighbor delta = min( delta, oldPixelHeights(1) - minimumHeights(1) ); end oldHeights = obj.Heights_(loc:loc+1); newPixelHeights = oldPixelHeights - delta * [1;-1]; if oldHeights(1) < 0 && oldHeights(2) < 0 % weight, weight newHeights = oldHeights .* newPixelHeights ./ oldPixelHeights; elseif oldHeights(1) < 0 && oldHeights(2) >= 0 % weight, pixels newHeights = [oldHeights(1) * newPixelHeights(1) / ... oldPixelHeights(1); newPixelHeights(2)]; elseif oldHeights(1) >= 0 && oldHeights(2) < 0 % pixels, weight newHeights = [newPixelHeights(1); oldHeights(2) * ... newPixelHeights(2) / oldPixelHeights(2)]; else % sizes(1) >= 0 && sizes(2) >= 0 % pixels, pixels newHeights = newPixelHeights; end obj.Heights_(loc:loc+1) = newHeights; elseif loc < 0 root = groot(); delta = root.PointerLocation(1) - obj.MousePressLocation(1); iw = -loc; jw = -loc + 1; r = numel( obj.Heights_ ); ic = r * (-loc-1) + 1; jc = r * -loc + 1; divider = obj.ColumnDividers(iw); contents = obj.Contents_; oldPixelWidths = [contents(ic).Position(3); contents(jc).Position(3)]; minimumWidths = obj.MinimumWidths_(iw:jw,:); if delta < 0 % limit to minimum distance from left neighbor delta = max( delta, minimumWidths(1) - oldPixelWidths(1) ); else % limit to minimum distance from right neighbor delta = min( delta, oldPixelWidths(2) - minimumWidths(2) ); end oldWidths = obj.Widths_(iw:jw); newPixelWidths = oldPixelWidths + delta * [1;-1]; if oldWidths(1) < 0 && oldWidths(2) < 0 % weight, weight newWidths = oldWidths .* newPixelWidths ./ oldPixelWidths; elseif oldWidths(1) < 0 && oldWidths(2) >= 0 % weight, pixels newWidths = [oldWidths(1) * newPixelWidths(1) / ... oldPixelWidths(1); newPixelWidths(2)]; elseif oldWidths(1) >= 0 && oldWidths(2) < 0 % pixels, weight newWidths = [newPixelWidths(1); oldWidths(2) * ... newPixelWidths(2) / oldPixelWidths(2)]; else % sizes(1) >= 0 && sizes(2) >= 0 % pixels, pixels newWidths = newPixelWidths; end obj.Widths_(iw:jw) = newWidths; else return end % Deactivate divider obj.FrontDivider.Visible = 'off'; divider.Visible = 'on'; % Reset state at button down obj.ActiveDivider = 0; obj.ActiveDividerPosition = [NaN NaN NaN NaN]; obj.MousePressLocation = [NaN NaN]; % Mark as dirty obj.Dirty = true; end % onMouseRelease function onMouseMotion( obj, source, eventData ) %onMouseMotion Handler for WindowMouseMotion events loc = obj.ActiveDivider; if loc == 0 % hovering, update pointer obj.updateMousePointer( source, eventData ); elseif loc > 0 % dragging row divider root = groot(); delta = root.PointerLocation(2) - obj.MousePressLocation(2); ih = loc; jh = loc + 1; ic = loc; jc = loc + 1; contents = obj.Contents_; oldPixelHeights = [contents(ic).Position(4); contents(jc).Position(4)]; minimumHeights = obj.MinimumHeights_(ih:jh,:); if delta < 0 % limit to minimum distance from lower neighbor delta = max( delta, minimumHeights(2) - oldPixelHeights(2) ); else % limit to minimum distance from upper neighbor delta = min( delta, oldPixelHeights(1) - minimumHeights(1) ); end obj.FrontDivider.Position = ... obj.ActiveDividerPosition + [0 delta 0 0]; else % loc < 0, dragging column divider root = groot(); delta = root.PointerLocation(1) - obj.MousePressLocation(1); iw = -loc; jw = -loc + 1; r = numel( obj.Heights_ ); ic = r * (-loc-1) + 1; jc = r * -loc + 1; contents = obj.Contents_; oldPixelWidths = [contents(ic).Position(3); contents(jc).Position(3)]; minimumWidths = obj.MinimumWidths_(iw:jw,:); if delta < 0 % limit to minimum distance from left neighbor delta = max( delta, minimumWidths(1) - oldPixelWidths(1) ); else % limit to minimum distance from right neighbor delta = min( delta, oldPixelWidths(2) - minimumWidths(2) ); end obj.FrontDivider.Position = ... obj.ActiveDividerPosition + [delta 0 0 0]; end end % onMouseMotion function onBackgroundColorChange( obj, ~, ~ ) %onBackgroundColorChange Handler for BackgroundColor changes backgroundColor = obj.BackgroundColor; highlightColor = min( [backgroundColor / 0.75; 1 1 1] ); shadowColor = max( [backgroundColor * 0.75; 0 0 0] ); rowDividers = obj.RowDividers; for ii = 1:numel( rowDividers ) rowDivider = rowDividers(ii); rowDivider.BackgroundColor = backgroundColor; rowDivider.HighlightColor = highlightColor; rowDivider.ShadowColor = shadowColor; end columnDividers = obj.ColumnDividers; for jj = 1:numel( columnDividers ) columnDivider = columnDividers(jj); columnDivider.BackgroundColor = backgroundColor; columnDivider.HighlightColor = highlightColor; columnDivider.ShadowColor = shadowColor; end frontDivider = obj.FrontDivider; frontDivider.BackgroundColor = shadowColor; end % onBackgroundColorChange end % event handlers methods( Access = protected ) function redraw( obj ) %redraw Redraw contents % % c.redraw() redraws the container c. % Call superclass method redraw@uix.Grid( obj ) % Create or destroy column dividers b = numel( obj.ColumnDividers ); % current number of dividers c = max( [numel( obj.Widths_ )-1 0] ); % required number of dividers if b < c % create for ii = b+1:c columnDivider = uix.Divider( 'Parent', obj, ... 'Orientation', 'vertical', ... 'BackgroundColor', obj.BackgroundColor ); obj.ColumnDividers(ii,:) = columnDivider; end elseif b > c % destroy % Destroy dividers delete( obj.ColumnDividers(c+1:b,:) ) obj.ColumnDividers(c+1:b,:) = []; % Update pointer if c == 0 && strcmp( obj.Pointer, 'left' ) obj.unsetPointer() end end % Create or destroy row dividers q = numel( obj.RowDividers ); % current number of dividers r = max( [numel( obj.Heights_ )-1 0] ); % required number of dividers if q < r % create for ii = q+1:r columnDivider = uix.Divider( 'Parent', obj, ... 'Orientation', 'horizontal', ... 'BackgroundColor', obj.BackgroundColor ); obj.RowDividers(ii,:) = columnDivider; end % Bring front divider to the front frontDivider = obj.FrontDivider; frontDivider.Parent = []; frontDivider.Parent = obj; elseif q > r % destroy % Destroy dividers delete( obj.RowDividers(r+1:q,:) ) obj.RowDividers(r+1:q,:) = []; % Update pointer if r == 0 && strcmp( obj.Pointer, 'top' ) obj.unsetPointer() end end % Compute container bounds bounds = hgconvertunits( ancestor( obj, 'figure' ), ... [0 0 1 1], 'normalized', 'pixels', obj ); % Retrieve size properties widths = obj.Widths_; minimumWidths = obj.MinimumWidths_; heights = obj.Heights_; minimumHeights = obj.MinimumHeights_; padding = obj.Padding_; spacing = obj.Spacing_; % Compute row divider positions xRowPositions = [padding + 1, max( bounds(3) - 2 * padding, 1 )]; xRowPositions = repmat( xRowPositions, [r 1] ); yRowSizes = uix.calcPixelSizes( bounds(4), heights, ... minimumHeights, padding, spacing ); yRowPositions = [bounds(4) - cumsum( yRowSizes(1:r,:) ) - padding - ... spacing * transpose( 1:r ) + 1, repmat( spacing, [r 1] )]; rowPositions = [xRowPositions(:,1), yRowPositions(:,1), ... xRowPositions(:,2), yRowPositions(:,2)]; % Compute column divider positions xColumnSizes = uix.calcPixelSizes( bounds(3), widths, ... minimumWidths, padding, spacing ); xColumnPositions = [cumsum( xColumnSizes(1:c,:) ) + padding + ... spacing * transpose( 0:c-1 ) + 1, repmat( spacing, [c 1] )]; yColumnPositions = [padding + 1, max( bounds(4) - 2 * padding, 1 )]; yColumnPositions = repmat( yColumnPositions, [c 1] ); columnPositions = [xColumnPositions(:,1), yColumnPositions(:,1), ... xColumnPositions(:,2), yColumnPositions(:,2)]; % Position row dividers for ii = 1:r rowDivider = obj.RowDividers(ii); rowDivider.Position = rowPositions(ii,:); switch obj.DividerMarkings_ case 'on' rowDivider.Markings = cumsum( xColumnSizes ) + ... spacing * transpose( 0:c ) - xColumnSizes / 2; case 'off' rowDivider.Markings = zeros( [0 1] ); end end % Position column dividers for ii = 1:c columnDivider = obj.ColumnDividers(ii); columnDivider.Position = columnPositions(ii,:); switch obj.DividerMarkings_ case 'on' columnDivider.Markings = cumsum( yRowSizes ) + ... spacing * transpose( 0:r ) - yRowSizes / 2; case 'off' columnDivider.Markings = zeros( [0 1] ); end end end % redraw function reparent( obj, oldFigure, newFigure ) %reparent Reparent container % % c.reparent(a,b) reparents the container c from the figure a % to the figure b. % Update listeners if isempty( newFigure ) mousePressListener = event.listener.empty( [0 0] ); mouseReleaseListener = event.listener.empty( [0 0] ); mouseMotionListener = event.listener.empty( [0 0] ); else mousePressListener = event.listener( newFigure, ... 'WindowMousePress', @obj.onMousePress ); mouseReleaseListener = event.listener( newFigure, ... 'WindowMouseRelease', @obj.onMouseRelease ); mouseMotionListener = event.listener( newFigure, ... 'WindowMouseMotion', @obj.onMouseMotion ); end obj.MousePressListener = mousePressListener; obj.MouseReleaseListener = mouseReleaseListener; obj.MouseMotionListener = mouseMotionListener; % Call superclass method reparent@uix.Grid( obj, oldFigure, newFigure ) % Update pointer if ~isempty( oldFigure ) && ~strcmp( obj.Pointer, 'unset' ) obj.unsetPointer() end end % reparent end % template methods methods( Access = protected ) function updateMousePointer ( obj, source, eventData ) oldPointer = obj.Pointer; if any( obj.RowDividers.isMouseOver( eventData ) ) newPointer = 'top'; elseif any( obj.ColumnDividers.isMouseOver( eventData ) ) newPointer = 'left'; else newPointer = 'unset'; end switch newPointer case oldPointer % no change % do nothing case 'unset' % change, unset obj.unsetPointer() otherwise % change, set obj.setPointer( source, newPointer ) end end % updateMousePointer end % helpers methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/HBox.m ================================================ classdef HBox < uix.Box %uix.HBox Horizontal box % % b = uix.HBox(p1,v1,p2,v2,...) constructs a horizontal box and sets % parameter p1 to value v1, etc. % % A horizontal box lays out contents from left to right. % % See also: uix.VBox, uix.Grid, uix.HButtonBox, uix.HBoxFlex % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Access = public, Dependent, AbortSet ) Widths % widths of contents, in pixels and/or weights MinimumWidths % minimum widths of contents, in pixels end properties( Access = protected ) Widths_ = zeros( [0 1] ) % backing for Widths MinimumWidths_ = zeros( [0 1] ) % backing for MinimumWidths end methods function obj = HBox( varargin ) %uix.HBox Horizontal box constructor % % b = uix.HBox() constructs a horizontal box. % % b = uix.HBox(p1,v1,p2,v2,...) sets parameter p1 to value v1, % etc. % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end end % constructor end % structors methods function value = get.Widths( obj ) value = obj.Widths_; end % get.Widths function set.Widths( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''Widths'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ... 'Elements of property ''Widths'' must be real and finite.' ) assert( isequal( size( value ), size( obj.Contents_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''Widths'' must match size of contents.' ) % Set obj.Widths_ = value; % Mark as dirty obj.Dirty = true; end % set.Widths function value = get.MinimumWidths( obj ) value = obj.MinimumWidths_; end % get.MinimumWidths function set.MinimumWidths( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''MinimumWidths'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... all( value >= 0 ), 'uix:InvalidPropertyValue', ... 'Elements of property ''MinimumWidths'' must be non-negative.' ) assert( isequal( size( value ), size( obj.Widths_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''MinimumWidths'' must match size of contents.' ) % Set obj.MinimumWidths_ = value; % Mark as dirty obj.Dirty = true; end % set.MinimumWidths end % accessors methods( Access = protected ) function redraw( obj ) %redraw Redraw % % c.redraw() redraws the container c. % Compute positions bounds = hgconvertunits( ancestor( obj, 'figure' ), ... [0 0 1 1], 'normalized', 'pixels', obj ); widths = obj.Widths_; minimumWidths = obj.MinimumWidths_; padding = obj.Padding_; spacing = obj.Spacing_; c = numel( widths ); xSizes = uix.calcPixelSizes( bounds(3), widths, ... minimumWidths, padding, spacing ); xPositions = [cumsum( [0; xSizes(1:c-1,:)] ) + padding + ... spacing * transpose( 0:c-1 ) + 1, xSizes]; yPositions = [padding + 1, max( bounds(4) - 2 * padding, 1 )]; yPositions = repmat( yPositions, [c 1] ); positions = [xPositions(:,1), yPositions(:,1), ... xPositions(:,2), yPositions(:,2)]; % Set positions children = obj.Contents_; for ii = 1:numel( children ) uix.setPosition( children(ii), positions(ii,:), 'pixels' ) end end % redraw function addChild( obj, child ) %addChild Add child % % c.addChild(d) adds the child d to the container c. % Add to sizes obj.Widths_(end+1,:) = -1; obj.MinimumWidths_(end+1,:) = 1; % Call superclass method addChild@uix.Box( obj, child ) end % addChild function removeChild( obj, child ) %removeChild Remove child % % c.removeChild(d) removes the child d from the container c. % Remove from sizes tf = obj.Contents_ == child; obj.Widths_(tf,:) = []; obj.MinimumWidths_(tf,:) = []; % Call superclass method removeChild@uix.Box( obj, child ) end % removeChild function reorder( obj, indices ) %reorder Reorder contents % % c.reorder(i) reorders the container contents using indices % i, c.Contents = c.Contents(i). % Reorder obj.Widths_ = obj.Widths_(indices,:); obj.MinimumWidths_ = obj.MinimumWidths_(indices,:); % Call superclass method reorder@uix.Box( obj, indices ) end % reorder end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Node.m ================================================ classdef ( Hidden ) Node < dynamicprops %uix.Node Node % % n = uix.Node(o) creates a node for the handle o. % % Node is a helper class for managing trees of objects and associated % listeners. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $ properties( SetAccess = private ) Object % object Children = uix.Node.empty( [0 1] ) % children end properties( Access = private ) ChildListeners = event.listener.empty( [0 1] ) % internal listeners end methods function obj = Node( object ) %uix.Node Node % % n = uix.Node(o) creates a node for the handle o. % Check assert( isa( object, 'handle' ) && ... isequal( size( object ), [1 1] ) && isvalid( object ), ... 'uix:InvalidArgument', 'Object must be a handle.' ) % Set properties obj.Object = object; end % constructor end % structors methods function addChild( obj, child ) %addChild Add child % % n.addChild(c) adds the child node c to the parent node n. % Check assert( isa( child, 'uix.Node' ) && ... isequal( size( child ), [1 1] ), ... 'uix:InvalidArgument', 'Invalid node.' ) % Add childListener = event.listener( child, ... 'ObjectBeingDestroyed', @obj.onChildDeleted ); obj.Children(end+1,:) = child; obj.ChildListeners(end+1,:) = childListener; end % addChild function removeChild( obj, child ) %removeChild Remove child % % n.removeChild(c) removes the child node c from the parent % node n. % Check assert( isa( child, 'uix.Node' ) && ... isequal( size( child ), [1 1] ), ... 'uix:InvalidArgument', 'Invalid node.' ) assert( ismember( child, obj.Children ), ... 'uix:ItemNotFound', 'Node not found.' ) % Remove tf = child == obj.Children; obj.Children(tf,:) = []; obj.ChildListeners(tf,:) = []; end % removeChild end % public methods methods( Access = private ) function onChildDeleted( obj, source, ~ ) %onChildDeleted Event handler for deletion of child nodes % Remove obj.removeChild( source ) end % onChildDeleted end % event handlers end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Panel.m ================================================ classdef Panel < matlab.ui.container.Panel & uix.mixin.Panel %uix.Panel Standard panel % % b = uix.Panel(p1,v1,p2,v2,...) constructs a standard panel and sets % parameter p1 to value v1, etc. % % A card panel is a standard panel (uipanel) that shows one its % contents and hides the others. % % See also: uix.CardPanel, uix.BoxPanel, uipanel % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ methods function obj = Panel( varargin ) %uix.Panel Standard panel constructor % % p = uix.Panel() constructs a standard panel. % % p = uix.Panel(p1,v1,p2,v2,...) sets parameter p1 to value % v1, etc. % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end end % constructor end % structors methods( Access = protected ) function redraw( obj ) % Compute positions bounds = hgconvertunits( ancestor( obj, 'figure' ), ... [0 0 1 1], 'normalized', 'pixels', obj ); padding = obj.Padding_; xSizes = uix.calcPixelSizes( bounds(3), -1, 1, padding, 0 ); ySizes = uix.calcPixelSizes( bounds(4), -1, 1, padding, 0 ); position = [padding+1 padding+1 xSizes ySizes]; % Redraw contents selection = obj.Selection_; if selection ~= 0 uix.setPosition( obj.Contents_(selection), position, 'pixels' ) end end % redraw end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/PointerManager.m ================================================ classdef ( Hidden, Sealed ) PointerManager < handle %uix.PointerManager Pointer manager % Copyright 2016 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ properties( SetAccess = private ) Figure % figure end properties( Access = private ) Tokens % tokens Pointers % pointers NextToken % next token PointerListener % listener end methods( Access = private ) function obj = PointerManager( figure ) %uix.PointerManager Create pointer manager % % m = uix.PointerManager(f) creates a pointer manager for the % figure f. obj.Figure = figure; obj.Tokens = 0; obj.Pointers = {figure.Pointer}; obj.NextToken = 1; obj.PointerListener = event.proplistener( figure, ... findprop( figure, 'Pointer' ), 'PostSet', ... @obj.onPointerChanged ); end % constructor end % structors methods( Access = private ) function doSetPointer( obj, token, pointer ) %doSetPointer Set pointer % % m.doSetPointer(t,p) sets the pointer to p with the token t. % Remove old entry tf = obj.Tokens == token; obj.Tokens(tf) = []; obj.Pointers(tf) = []; % Add new entry obj.Tokens(end+1) = token; obj.Pointers{end+1} = pointer; % Set pointer obj.PointerListener.Enabled = false; obj.Figure.Pointer = pointer; obj.PointerListener.Enabled = true; end % doSetPointer function doUnsetPointer( obj, token ) %doUnsetPointer Unset pointer % % m.doUnsetPointer(s) unsets the pointer with the token t. % Remove old entry tf = obj.Tokens == token; obj.Tokens(tf) = []; obj.Pointers(tf) = []; % Update pointer obj.PointerListener.Enabled = false; obj.Figure.Pointer = obj.Pointers{end}; obj.PointerListener.Enabled = true; end % doUnsetPointer end % private methods methods function onPointerChanged( obj, ~, ~ ) %onPointerChanged Event handler % Log as unknown setter obj.doSetPointer( 0, obj.Figure.Pointer ) end % onPointerChanged end % event handlers methods( Static ) function token = setPointer( figure, pointer ) %setPointer Set pointer % % t = uix.PointerManager.setPointer(f,p) sets the pointer of % the figure f to p. The returned token t can be used % subsequently to unset the pointer. % Get pointer manager obj = uix.PointerManager.getInstance( figure ); % Retrieve token token = obj.NextToken; % Set obj.doSetPointer( token, pointer ) % Increment token obj.NextToken = token + 1; end % setPointer function unsetPointer( figure, token ) %unsetPointer Unset pointer % % uix.PointerManager.unsetPointer(f,t) unsets the pointer of % the figure f using the token t. % Check ID validateattributes( token, {'numeric'}, {'scalar','integer','>',0} ) % Get pointer manager obj = uix.PointerManager.getInstance( figure ); % Unset obj.doUnsetPointer( token ) end % unsetPointer function obj = getInstance( figure ) %getInstance Get pointer manager % % m = uix.PointerManager.getInstance(f) gets the pointer % manager for the figure f. % Get pointer manager name = 'UIxPointerManager'; if isprop( figure, name ) % existing, retrieve obj = figure.( name ); else % new, create and store obj = uix.PointerManager( figure ); p = addprop( figure, name ); p.Hidden = true; figure.( name ) = obj; end end % getInstance end % static methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/SelectionData.m ================================================ classdef( Hidden, Sealed ) SelectionData < event.EventData %uix.SelectionData Event data for selection event % % e = uix.SelectionData(o,n) creates event data including the old % value o and the new value n. % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $ properties( SetAccess = private ) OldValue % old value NewValue % newValue end methods function obj = SelectionData( oldValue, newValue ) %uix.SelectionData Event data for selection event % % e = uix.SelectionData(o,n) creates event data including the % old value o and the new value n. % Set properties obj.OldValue = oldValue; obj.NewValue = newValue; end % constructor end % structors end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/VBox.m ================================================ classdef VBox < uix.Box %uix.VBox Vertical box % % b = uix.VBox(p1,v1,p2,v2,...) constructs a vertical box and sets % parameter p1 to value v1, etc. % % A vertical box lays out contents from top to bottom. % % See also: uix.HBox, uix.Grid, uix.VButtonBox, uix.VBoxFlex % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $ properties( Access = public, Dependent, AbortSet ) Heights % heights of contents, in pixels and/or weights MinimumHeights % minimum heights of contents, in pixels end properties( Access = protected ) Heights_ = zeros( [0 1] ) % backing for Heights MinimumHeights_ = zeros( [0 1] ) % backing for MinimumHeights end methods function obj = VBox( varargin ) %uix.VBox Vertical box constructor % % b = uix.VBox() constructs a horizontal box. % % b = uix.VBox(p1,v1,p2,v2,...) sets parameter p1 to value v1, % etc. % Set properties if nargin > 0 try assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ... 'Parameters and values must be provided in pairs.' ) set( obj, varargin{:} ) catch e delete( obj ) e.throwAsCaller() end end end % constructor end % structors methods function value = get.Heights( obj ) value = obj.Heights_; end % get.Heights function set.Heights( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''Heights'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ... 'Elements of property ''Heights'' must be real and finite.' ) assert( isequal( size( value ), size( obj.Contents_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''Heights'' must match size of contents.' ) % Set obj.Heights_ = value; % Mark as dirty obj.Dirty = true; end % set.Heights function value = get.MinimumHeights( obj ) value = obj.MinimumHeights_; end % get.MinimumHeights function set.MinimumHeights( obj, value ) % For those who can't tell a column from a row... if isrow( value ) value = transpose( value ); end % Check assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ... 'Property ''MinimumHeights'' must be of type double.' ) assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ... all( value >= 0 ), 'uix:InvalidPropertyValue', ... 'Elements of property ''MinimumHeights'' must be non-negative.' ) assert( isequal( size( value ), size( obj.Heights_ ) ), ... 'uix:InvalidPropertyValue', ... 'Size of property ''MinimumHeights'' must match size of contents.' ) % Set obj.MinimumHeights_ = value; % Mark as dirty obj.Dirty = true; end % set.MinimumHeights end % accessors methods( Access = protected ) function redraw( obj ) %redraw Redraw % % c.redraw() redraws the container c. % Compute positions bounds = hgconvertunits( ancestor( obj, 'figure' ), ... [0 0 1 1], 'normalized', 'pixels', obj ); heights = obj.Heights_; minimumHeights = obj.MinimumHeights_; padding = obj.Padding_; spacing = obj.Spacing_; r = numel( heights ); xPositions = [padding + 1, max( bounds(3) - 2 * padding, 1 )]; xPositions = repmat( xPositions, [r 1] ); ySizes = uix.calcPixelSizes( bounds(4), heights, ... minimumHeights, padding, spacing ); yPositions = [bounds(4) - cumsum( ySizes ) - padding - ... spacing * transpose( 0:r-1 ) + 1, ySizes]; positions = [xPositions(:,1), yPositions(:,1), ... xPositions(:,2), yPositions(:,2)]; % Set positions children = obj.Contents_; for ii = 1:numel( children ) uix.setPosition( children(ii), positions(ii,:), 'pixels' ) end end % redraw function addChild( obj, child ) %addChild Add child % % c.addChild(d) adds the child d to the container c. % Add to sizes obj.Heights_(end+1,:) = -1; obj.MinimumHeights_(end+1,:) = 1; % Call superclass method addChild@uix.Box( obj, child ) end % addChild function removeChild( obj, child ) %removeChild Remove child % % c.removeChild(d) removes the child d from the container c. % Remove from sizes tf = obj.Contents_ == child; obj.Heights_(tf,:) = []; obj.MinimumHeights_(tf,:) = []; % Call superclass method removeChild@uix.Box( obj, child ) end % removeChild function reorder( obj, indices ) %reorder Reorder contents % % c.reorder(i) reorders the container contents using indices % i, c.Contents = c.Contents(i). % Reorder obj.Heights_ = obj.Heights_(indices,:); obj.MinimumHeights_ = obj.MinimumHeights_(indices,:); % Call superclass method reorder@uix.Box( obj, indices ) end % reorder end % template methods end % classdef ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/calcPixelSizes.m ================================================ function pSizes = calcPixelSizes( pTotal, mSizes, pMinima, pPadding, pSpacing ) %calcPixelSizes Calculate child sizes in pixels % % pSizes = uix.calcPixelSizes(total,mSizes,minSizes,padding,spacing) % computes child sizes (in pixels) given total available size (in pixels), % child sizes (in pixels and/or relative), minimum child sizes (in % pixels), padding (in pixels) and spacing (in pixels). % % Notes: % * All children are at least as large as the minimum specified size % * Relative sizes are respected for children larger than then minimum % specified size % * Children may extend beyond the total available size if the minimum % sizes, padding and spacing are too large % Copyright 2009-2015 The MathWorks, Inc. % $Revision: 1182 $ $Date: 2015-12-07 14:27:30 -0500 (Mon, 07 Dec 2015) $ % Initialize pSizes = NaN( size( mSizes ) ); % output n = numel( mSizes ); % need this later % Apply absolute sizes a = mSizes >= 0; % absolute pSizes(a) = max( mSizes(a), pMinima(a) ); while true u = isnan( pSizes ); % unsolved pUnsolvedTotal = pTotal - max( (n-1), 0 ) * pSpacing ... - 2 * sign( n ) * pPadding - sum( pSizes(~u) ); pUnsolvedSizes = mSizes(u) / sum( mSizes(u) ) * pUnsolvedTotal; pUnsolvedMinima = pMinima(u); s = pUnsolvedSizes < pUnsolvedMinima; % small if any( s ) pUnsolvedSizes(s) = pUnsolvedMinima(s); pUnsolvedSizes(~s) = NaN; pSizes(u) = pUnsolvedSizes; % repeat else pSizes(u) = pUnsolvedSizes; break % done end end end % calcPixelSizes ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/setPosition.m ================================================ function setPosition( o, p, u ) %setPosition Set position of graphics object % % setPosition(o,p,u) sets the position of a graphics object o to value p % with units u. % % In contrast to setting the Position property directly, this function % honors the ActivePositionProperty of axes. % Copyright 2009-2016 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ o.Units = u; if isa( o, 'matlab.graphics.axis.Axes' ) switch o.ActivePositionProperty case 'position' o.Position = p; case 'outerposition' o.OuterPosition = p; otherwise error( 'uix:InvalidState', ... 'Unknown value ''%s'' for property ''ActivePositionProperty'' of %s.', ... o.ActivePositionProperty, class( o ) ) end else o.Position = p; end end % setPosition ================================================ FILE: leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/tracking.m ================================================ function varargout = tracking( varargin ) %tracking Track anonymized usage data % % tracking(p,v,id) tracks usage to the property p for the product version % v and identifier id. No personally identifiable information is tracked. % % r = tracking(...) returns the server response r, for debugging purposes. % % tracking('on') turns tracking on. tracking('off') turns tracking off. % tracking('query') returns the tracking state. % tracking('spoof') sets the tracking settings -- domain, language, % client, MATLAB version, operating system version -- to spoof values. % tracking('reset') sets the tracking settings to normal values. % % [t,s] = tracking('query') returns the tracking state t and settings s. % Copyright 2016 The MathWorks, Inc. % $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $ persistent STATE USERNAME DOMAIN LANGUAGE CLIENT MATLAB OS if isempty( STATE ) STATE = getpref( 'Tracking', 'State', 'on' ); if strcmp( STATE, 'snooze' ) % deprecated setpref( 'Tracking', 'State', 'on' ) STATE = 'on'; end if ispref( 'Tracking', 'Date' ) % deprecated rmpref( 'Tracking', 'Date' ) end USERNAME = getenv( 'USERNAME' ); reset() end % initialize switch nargin case 1 switch varargin{1} case {'on','off'} STATE = varargin{1}; setpref( 'Tracking', 'State', varargin{1} ) % persist case 'spoof' spoof() case 'reset' reset() case 'query' varargout{1} = STATE; varargout{2} = query(); otherwise error( 'tracking:InvalidArgument', ... 'Valid options are ''on'', ''off'' and ''query''.' ) end case 3 switch nargout case 0 if strcmp( STATE, 'off' ), return, end uri = 'https://www.google-analytics.com/collect'; track( uri, varargin{:} ); case 1 uri = 'https://www.google-analytics.com/debug/collect'; varargout{1} = track( uri, varargin{:} ); otherwise nargoutchk( 0, 1 ) end otherwise narginchk( 3, 3 ) end % switch function reset() %reset Set normal settings DOMAIN = lower( getenv( 'USERDOMAIN' ) ); LANGUAGE = char( java.util.Locale.getDefault() ); CLIENT = getpref( 'Tracking', 'Client', uuid() ); MATLAB = matlab(); OS = os(); end % reset function spoof() %spoof Set spoof settings DOMAIN = randomDomain(); LANGUAGE = randomLanguage(); CLIENT = randomClient(); MATLAB = randomMatlab(); OS = randomOs(); end % spoof function s = query() %query Return settings s.Username = USERNAME; s.Domain = DOMAIN; s.Language = LANGUAGE; s.Client = CLIENT; s.Matlab = MATLAB; s.Os = OS; end % query function varargout = track( uri, p, v, s ) %track Do tracking a = sprintf( '%s/%s (%s)', MATLAB, v, OS ); if isdeployed() ds = 'deployed'; elseif strcmp( DOMAIN, 'mathworks' ) ds = DOMAIN; else ds = 'unknown'; end pv = {'v', '1', 'tid', p, 'ua', escape( a ), 'ul', LANGUAGE, ... 'cid', CLIENT, 'ht', 'pageview', ... 'dp', sprintf( '/%s', s ), 'ds', ds}; [varargout{1:nargout}] = urlread( uri, 'Post', pv ); end % track end % tracking function s = randomDomain() %randomDomain Random domain string switch randi( 4 ) case 1 s = 'mathworks'; otherwise s = hash( uuid() ); end end % randomDomain function s = randomLanguage() %randomLanguage Random language string lo = java.util.Locale.getAvailableLocales(); s = char( lo(randi( numel( lo ) )) ); end % randomLanguage function s = randomClient() %randomClient Random client identifier s = uuid(); end % randomClient function s = matlab() %matlab MATLAB version string v = ver( 'MATLAB' ); s = v.Release; s(s=='('|s==')') = []; end % matlab function s = randomMatlab() %randomMatlab Random MATLAB version string releases = {'R2014b' 'R2015a' 'R2015b' 'R2016a' 'R2016b'}; s = releases{randi( numel( releases ) )}; end % randomMatlab function s = os() %os Operating system string if ispc() s = sprintf( 'Windows NT %s', ... char( java.lang.System.getProperty( 'os.version' ) ) ); elseif isunix() s = 'Linux x86_64'; elseif ismac() s = sprintf( 'Macintosh; Intel OS X %s', ... strrep( char( java.lang.System.getProperty( 'os.version' ) ), ' ', '_' ) ); else s = 'unknown'; end end % os function s = randomOs() %randomOs Random operating system string switch randi( 3 ) case 1 versions = [5.1 5.2 6 6.1 6.2 6.3 10]; s = sprintf( 'Windows NT %.1f', ... versions(randi( numel( versions ) )) ); case 2 s = 'Linux x86_64'; case 3 s = sprintf( 'Macintosh; Intel OS X 10_%d', ... randi( [10 12] ) ); end end % randomOs function s = escape( s ) %escape Escape string s = char( java.net.URLEncoder.encode( s, 'UTF-8' ) ); end % escape function h = hash( s ) %hash Hash string % % See also: rptgen.hash persistent MD5 if isempty( MD5 ) MD5 = java.security.MessageDigest.getInstance( 'MD5' ); end MD5.update( uint8( s(:) ) ); h = typecast( MD5.digest, 'uint8' ); h = dec2hex( h )'; h = lower( h(:) )'; end % hash function s = uuid() %uuid Unique identifier s = char( java.util.UUID.randomUUID() ); end % uuid ================================================ FILE: leap/toolbox/graphics/distributionPlot/colorCode2rgb.m ================================================ function rgbVec = colorCode2rgb(c) %COLORCODE2RGB converts a color code to an rgb vector % % SYNOPSIS rgbVec = colorCode2rgb(c) % % INPUT c : color code % The following colors are supported: % 'y' 'yellow' % 'm' 'magenta' % 'c' 'cyan' % 'r' 'red' % 'g' 'green' % 'b' 'blue' % 'w' 'white' % 'k' 'black' % % OUTPUT rgbVec : vector with the rgb value % % EXAMPLE % rgb = colorCode2rgb('r') % rgb = % [1 0 0] if iscell(c) rgbVec = cell2mat(cellfun(@colorCode2rgb,c,'uni',false)); return end switch c case {'y','yellow'}, rgbVec = [1,1,0]; case {'m','magenta'}, rgbVec = [1,0,1]; case {'c','cyan'}, rgbVec = [0,1,1]; case {'r','red'}, rgbVec = [1,0,0]; case {'g','green'}, rgbVec = [0,1,0]; case {'b','blue'}, rgbVec = [0,0,1]; case {'w','white'}, rgbVec = [1,1,1]; case {'k','black'}, rgbVec = [0,0,0]; otherwise, error('unknown color code %s',c) end; ================================================ FILE: leap/toolbox/graphics/draggable/draggable.m ================================================ function draggable(h,varargin) % DRAGGABLE - Make it so that a graphics object can be dragged in a figure. % This function makes an object interactive by allowing it to be dragged % accross a set of axes, following or not certain constraints. This % allows for intuitive control elements which are not buttons or other % standard GUI objects, and which reside inside an axis. Typical use % involve markers on an axis, whose position alters the output of a % computation or display % % >> draggable(h); % % makes the object with handle "h" draggable. Use the "Position" property % of the object to retrieve its position, by issuing a get(h,'Position') % command. % % If h is a vector of handles, then draggable is called on each handle % using the same following arguments, if any. % % >> draggable(h,...,motionfcn) % % where "motionfcn" is a function handle, executes the given function % while the object is dragged. Handle h is passed to motionfcn as an % argument. Argument "motionfcn" can be put anywhere after handle "h". % % >> draggable(h,...,constraint,p); % % enables the object with handle "h" to be dragged, with a constraint. % Arguments "constraint" (a string) and "p" (a vector) can be put % anywhere after handle "h". % % >> draggable(h,...,'endfcn',endfcn); % % where "endfcn" is a function handle, executes the given function AFTER % the object is dragged (more specifically, on the next WindowButtonUp % event). The function handle must come after the string 'endfcn', to % avoid ambiguity with the "motionfcn" argument (above). Handle h is % passed to endfcn as an argument. % % >> draggable(h,'off') % % returns object h to its original, non-draggable state. % % CONSTRAINTS % % The argument "constraint" may be one of the following strings: % % 'n' or 'none': The object is unconstrained (default). % 'h' or 'horizontal': The object can only be moved horizontally. % 'v' or 'vertical': The object can only be moved vertically. % 'd' or 'diagonal': The object can only be moved along an % arbitrary line of a given slope. % % The argument "p" is an optional parameter which depends upon the % constraint type: % % Constraint p Description % ----------------------------------------------------------------------- % % 'none' [x1 x2 y1 y2] Drag range (for the object's outer % limits, from x1 to x2 on the x-axis % and from y1 to y2 on the y-axis). % Default is the current axes range. % Use "inf" if no limit is desired. % % 'horizontal' [xmin xmax] Drag range (for the object's outer % limits). Default is the x-axis % range. Use "inf" if no limit is % desired. Note that full limits of % the form [x1 x2 y1 y2] can also be % used. % % 'vertical' [ymin ymax] Drag range (for the object's outer % limits). Default is the y-axis % range. Use "inf" if no limit is % desired. Note that full limits of % the form [x1 x2 y1 y2] can also be % used. % % 'diagonal' [m x1 x2 y1 y2] Slope m of the line along which the % movement is constrained (default is % 1); x1 x2 y1 y2 as in 'none'. % % ----------------------------------------------------------------------- % % VERSION INFORMATION: % 2003-11-20: Initially submitted to MatlabCentral.Com % 2004-01-06: Addition of the renderer option, as proposed by Ohad Gal % as a feedback on MatlabCentral.Com. % 2004-02-18: Bugfix: now works with 1-element plots and line objects % 2004-03-04: Bugfix: sanitized the way the object's new position is % computed; it now always follow the mouse even after the % mouse pointer was out of the axes. % 2004-03-05: Bugfix: movement when mouse is out of the axes is now % definitely correct ;) % 2006-05-23: Bugfix: fix a rendering issue using Matlab 7 & + % Deprecated the rendering options: rendering seems ok with % every renderer. % 2010-01-11: Bugfix by Gilles Fortin (odd jumping on limits caused by % round-off error due to successive addition then subtraction % of the same value) % 2010-02-26: endfcn code by Steven Bierer included. % Some suggested M-Lint fixes performed. % 2012-01-18: - Refactoring % - Limits of the form [x1 x2 y1 y2] can be used for 'h' and % 'v' constraint types; % - Support for text objects; % - Added diagonal constraint type % 2012-01-20: - Tested % - Added support for h as a vector of handles % - 'sliders' demo added in dragdemo % 2013-01-10: Bugfix: finding the figure's handle through gcbf in order % to fix a bug when axes are embedded into a Panel. % (Bug found by Esmerald Aliai) % IMPLEMENTATION NOTES: % % This function uses the dragged object's "ButtonDownFcn" function and set % it so that the objec becomes draggable. Any previous "ButtonDownFcn" is % thus lost during operation, but is retrieved after issuing the % draggable(h,'off') command. % % Information about the object's behavior is also stored in the object's % 'UserData' property, using setappdata() and getappdata(). The original % 'UserData' property is restored after issuing the draggable(h,'off') % command. % % The corresponding figure's "WindowButtonDownFcn", "WindowButtonUpFcn" and % "WindowButtonMotionFcn" functions. During operation, those functions are % set by DRAGGABLE; however, the original ones are restored after the user % stops dragging the object. % % By default, DRAGGABLE also switches the figure's renderer to 'zbuffer' % during operation: 'painters' is not fast enough and 'opengl' sometimes % produce curious results. However there may be a need to switch to another % renderer, so the user can now specify a specific figure renderer during % object drag (thanks to Ohad Gal for the suggestion). % % The "motionfcn" function handle is called at each displacement, after the % object's position is updated, using "feval(motionfcn,h)", where h is the % object's handle. % ========================================================================= % Copyright (C) 2003-2012 % Francois Bouffard % fbouffard@gmail.com % ========================================================================= % ========================================================================= % Input arguments management % ========================================================================= % If h is a vector of handle, applying draggable on each object and % returning. if length(h) > 1 for k = 1:length(h) draggable(h(k),varargin{:}); end return end % Initialization of some default arguments user_renderer = 'zbuffer'; user_movefcn = []; constraint = 'none'; p = []; user_endfcn = []; % added by SMB (see 'for k' loop below) endinput = 0; % At least the handle to the object must be given Narg = nargin; if Narg == 0 error('Not engough input arguments'); elseif numel(h)>1 error('Only one object at a time can be made draggable'); end; % Fetching informations about the parent axes axh = get(h,'Parent'); if iscell(axh) axh = axh{1}; end; %fgh = get(axh,'Parent'); % This fails if the axes are embedded in a Panel fgh = gcbf; % This should always work ax_xlim = get(axh,'XLim'); ax_ylim = get(axh,'YLim'); % Assigning optional arguments Noptarg = Narg - 1; for k = 1:Noptarg current_arg = varargin{k}; if isa(current_arg,'function_handle') && endinput user_endfcn = current_arg; % added by SMB endinput = 0; % 'movefcn' can still be a later argument elseif isa(current_arg,'function_handle') user_movefcn = current_arg; end; if ischar(current_arg); switch lower(current_arg) case {'off'} set_initial_state(h); return; case {'painters','zbuffer','opengl'} warning('DRAGGABLE:DEPRECATED_OPTION', ... 'The renderer option is deprecated and will not be taken into account'); user_renderer = current_arg; case {'endfcn'} % added by SMB endinput = 1; otherwise constraint = current_arg; end; end; if isnumeric(current_arg); p = current_arg; end; end; % Assigning defaults for constraint parameter switch lower(constraint) case {'n','none'} constraint = 'n'; if isempty(p); p = [ax_xlim ax_ylim]; end; case {'h','horizontal'} constraint = 'h'; if isempty(p) p = ax_xlim; elseif length(p) == 4 p = p(1:2); end case {'v','vertical'} constraint = 'v'; if isempty(p) p = ax_ylim; elseif length(p) == 4 p = p(3:4); end case {'d','diagonal','l','locked'} constraint = 'd'; if isempty(p) p = [1 ax_xlim ax_ylim]; elseif length(p) == 1 p = [p ax_xlim ax_ylim]; end; otherwise error('Unknown constraint type'); end; % ========================================================================= % Saving initial state and parameters, setting up the object callback % ========================================================================= % Saving object's and parent figure's initial state setappdata(h,'initial_userdata',get(h,'UserData')); setappdata(h,'initial_objbdfcn',get(h,'ButtonDownFcn')); setappdata(h,'initial_renderer',get(fgh,'Renderer')); setappdata(h,'initial_wbdfcn',get(fgh,'WindowButtonDownFcn')); setappdata(h,'initial_wbufcn',get(fgh,'WindowButtonUpFcn')); setappdata(h,'initial_wbmfcn',get(fgh,'WindowButtonMotionFcn')); % Saving parameters setappdata(h,'constraint_type',constraint); setappdata(h,'constraint_parameters',p); setappdata(h,'user_movefcn',user_movefcn); setappdata(h,'user_endfcn',user_endfcn); % added by SMB setappdata(h,'user_renderer',user_renderer); % Setting the object's ButtonDownFcn set(h,'ButtonDownFcn',@click_object); % ========================================================================= % FUNCTION click_object % Executed when the object is clicked % ========================================================================= function click_object(obj,eventdata) % obj here is the object to be dragged and gcf is the object's parent % figure since the user clicked on the object setappdata(obj,'initial_position',get_position(obj)); setappdata(obj,'initial_extent',compute_extent(obj)); setappdata(obj,'initial_point',get(gca,'CurrentPoint')); set(gcf,'WindowButtonDownFcn',{@activate_movefcn,obj}); set(gcf,'WindowButtonUpFcn',{@deactivate_movefcn,obj}); activate_movefcn(gcf,eventdata,obj); % ========================================================================= % FUNCTION activate_movefcn % Activates the WindowButtonMotionFcn for the figure % ========================================================================= function activate_movefcn(obj,eventdata,h) % We were once setting up renderers here. Now we only set the movefcn set(obj,'WindowButtonMotionFcn',{@movefcn,h}); % ========================================================================= % FUNCTION deactivate_movefcn % Deactivates the WindowButtonMotionFcn for the figure % ========================================================================= function deactivate_movefcn(obj,eventdata,h) % obj here is the figure containing the object % Setting the original MotionFcn, DuttonDownFcn and ButtonUpFcn back set(obj,'WindowButtonMotionFcn',getappdata(h,'initial_wbmfcn')); set(obj,'WindowButtonDownFcn',getappdata(h,'initial_wbdfcn')); set(obj,'WindowButtonUpFcn',getappdata(h,'initial_wbufcn')); % Executing the user's drag end function user_endfcn = getappdata(h,'user_endfcn'); if ~isempty(user_endfcn) feval(user_endfcn,h); % added by SMB, modified by FB end % ========================================================================= % FUNCTION set_initial_state % Returns the object to its initial state % ========================================================================= function set_initial_state(h) initial_objbdfcn = getappdata(h,'initial_objbdfcn'); initial_userdata = getappdata(h,'initial_userdata'); set(h,'ButtonDownFcn',initial_objbdfcn); set(h,'UserData',initial_userdata); % ========================================================================= % FUNCTION movefcn % Actual code for dragging the object % ========================================================================= function movefcn(obj,eventdata,h) % obj here is the *figure* containing the object % Retrieving data saved in the figure % Reminder: "position" refers to the object position in the axes % "point" refers to the location of the mouse pointer initial_point = getappdata(h,'initial_point'); constraint = getappdata(h,'constraint_type'); p = getappdata(h,'constraint_parameters'); user_movefcn = getappdata(h,'user_movefcn'); % Getting current mouse position current_point = get(gca,'CurrentPoint'); % Computing mouse movement (dpt is [dx dy]) cpt = current_point(1,1:2); ipt = initial_point(1,1:2); dpt = cpt - ipt; % Dealing with the pathetic cases of zero or infinite slopes if strcmpi(constraint,'d') if p(1) == 0 constraint = 'h'; p = p(2:end); elseif isinf(p(1)) constraint = 'v'; p = p(2:end); end end % Computing movement range and imposing movement constraints % (p is always [xmin xmax ymin ymax]) switch lower(constraint) case 'n' range = p; case 'h' dpt(2) = 0; range = [p -inf inf]; case 'v' dpt(1) = 0; range = [-inf inf p]; case 'd' % Multiple options here as to how we use dpt to move the object % along a diagonal. % We could use the largest of abs(dpt) for judging movement, but % this causes weird behavior in some cases. E.g. when the slope % is gentle (<1) and dy is the largest, the object will move % rapidly far away from the mouse pointer. % Another option (see below) is to follow dx when the % slope is <1 and dy when the slope is >= 1. %if abs(p(1)) >=1 % dpt = [dpt(2)/p(1) dpt(2)]; %else % dpt = [dpt(1) p(1)*dpt(1)]; %end % Projecting dpt along the diagonal seems to work really well. v = [1; p(1)]; Pv = v*v'/(v'*v); dpt = dpt*Pv; range = p(2:5); end % Computing new position. % What we want is actually a bit complex: we want the object to adopt the % new position, unless it gets out of range. If it gets out of range in a % direction, we want it to stick to the limit in that direction. Also, if % the object is out of range at the beginning of the movement, we want to % be able to move it back into range; movement must then be allowed. % For debugging purposes only; setting debug to 1 shows range, extents, % dpt, corrected dpt and in-range status of the object in the command % window. Note: this will clear the command window. debug = 0; idpt = dpt; % Computing object extent in the [x y w h] format before and after moving initial_extent = getappdata(h,'initial_extent'); new_extent = initial_extent + [dpt 0 0]; % Verifying if old and new objects breach the allowed range in any % direction (see the function is_inside_range below) initial_inrange = is_inside_range(initial_extent,range); new_inrange = is_inside_range(new_extent,range); % Modifying dpt to stick to range limit if range violation occured, % but the movement won't get restricted if the object was out of % range to begin with. % % We use if/ends and no elseif's because once an object hits a range limit, % it is still free to move along the other axis, and another range limit % could be hit aftwards. That is, except for diagonal constraints, in % which a first limit hit must completely lock the object until the mouse % is inside the range. % In-line correction functions to dpt due to range violations xminc = @(dpt) [range(1) - initial_extent(1) dpt(2)]; xmaxc = @(dpt) [range(2) - (initial_extent(1) + initial_extent(3)) dpt(2)]; yminc = @(dpt) [dpt(1) range(3) - initial_extent(2)]; ymaxc = @(dpt) [dpt(1) range(4) - (initial_extent(2) + initial_extent(4))]; % We build a list of corrections to apply corrections = {}; if initial_inrange(1) && ~new_inrange(1) % was within, now out of xmin range -- add xminc corrections = [corrections {xminc}]; end if initial_inrange(2) && ~new_inrange(2) % was within, now out of xmax range -- add xmaxc corrections = [corrections {xmaxc}]; end if initial_inrange(3) && ~new_inrange(3) % was within, now out of ymin range -- add yminc corrections = [corrections {yminc}]; end if initial_inrange(4) && ~new_inrange(4) % was within, now out of ymax range -- add ymaxc corrections = [corrections {ymaxc}]; end % Applying all corrections, except for objects following a diagonal % constraint, which must stop at the first one if ~isempty(corrections) if strcmpi(constraint,'d') c = corrections{1}; dpt = c(dpt); % Forcing the object to remain on the diagonal constraint if isequal(c,xminc) || isequal(c,xmaxc) % horizontal correction dpt(2) = p(1)*dpt(1); elseif isequal(c,yminc) || isequal(c,ymaxc) % vertical correction dpt(1) = dpt(2)/p(1); end else % Just applying all corrections for c = corrections dpt = c{1}(dpt); end end end % Debug messages if debug if all(new_inrange) status = 'OK'; else status = 'RANGE VIOLATION'; end clc disp(sprintf(' range: %0.3f %0.3f %0.3f %0.3f', range)); disp(sprintf(' initial extent: %0.3f %0.3f %0.3f %0.3f', initial_extent)) disp(sprintf(' new extent: %0.3f %0.3f %0.3f %0.3f', new_extent)) disp(sprintf('initial inrange: %d %d %d %d', initial_inrange)) disp(sprintf(' new inrange: %d %d %d %d [%s]', new_inrange, status)) disp(sprintf(' initial dpt: %0.3f %0.3f', idpt)) disp(sprintf(' corrected dpt: %0.3f %0.3f', dpt)) end % Re-computing new position with modified dpt newpos = update_position(getappdata(h,'initial_position'),dpt); % Setting the new position which actually moves the object set_position(h,newpos); % Calling user-provided function handle if ~isempty(user_movefcn) feval(user_movefcn,h); end; % ========================================================================= % FUNCTION get_position % Return an object's position: [x y [z / w h]] or [xdata; ydata] % ========================================================================= function pos = get_position(obj) props = get(obj); if isfield(props,'Position') pos = props.Position; elseif isfield(props,'XData') pos = [props.XData(:)'; props.YData(:)']; else error('Unable to find position'); end % ========================================================================= % FUNCTION update_position % Adds dpt to a position specification as returned by get_position % ========================================================================= function newpos = update_position(pos,dpt) newpos = pos; if size(pos,1) == 1 % [x y [z / w h]] newpos(1:2) = newpos(1:2) + dpt; else % [xdata; ydata] newpos(1,:) = newpos(1,:) + dpt(1); newpos(2,:) = newpos(2,:) + dpt(2); end % ========================================================================= % FUNCTION set_position % Sets the position of an object obj using get_position's format % ========================================================================= function set_position(obj,pos) if size(pos,1) == 1 % 'Position' property set(obj,'Position',pos); else % 'XData/YData' properties set(obj,'XData',pos(1,:),'YData',pos(2,:)); end % ========================================================================= % FUNCTION compute_extent % Computes an object's extent for different object types; % extent is [x y w h] % ========================================================================= function extent = compute_extent(obj) props = get(obj); if isfield(props,'Extent') extent = props.Extent; elseif isfield(props,'Position') extent = props.Position; elseif isfield(props,'XData') minx = min(props.XData); miny = min(props.YData); w = max(props.XData) - minx; h = max(props.YData) - miny; extent = [minx miny w h]; else error('Unable to compute extent'); end % ========================================================================= % FUNCTION is_inside_range % Checks if a rectangular object is entirely inside a rectangular range % ========================================================================= function inrange = is_inside_range(extent,range) % extent is in the [x y w h] format % range is in the [xmin xmax ymin ymax] format % inrange is a 4x1 vector of boolean values corresponding to range limits inrange = [extent(1) >= range(1) ... extent(1) + extent(3) <= range(2) ... extent(2) >= range(3) ... extent(2) + extent(4) <= range(4)]; ================================================ FILE: leap/toolbox/graphics/figclosekey.m ================================================ function figclosekey(h, key) %FIGCLOSEKEY Add a hotkey for closing the figure. % Usage: % figclosekey(h, key) % % Args: % h: figure handle (default: gcf) % key: hotkey to use (default: 'q') % % See also: event, addlistener if nargin < 2 || isempty(key); key = 'q'; end if nargin == 1 && ischar(h); [h,key] = swap(h,key); end if nargin < 1 || isempty(h); h = gcf(); end if isfield(h.UserData,'hasCloseKey') h = figure(); end set(h, 'KeyPressFcn',@(h,evt)KeyPressFcn_cb(h,evt,key)); h.UserData.hasCloseKey = true; end function KeyPressFcn_cb(h,evt,key) if strcmp(evt.Key,key) delete(h) end end ================================================ FILE: leap/toolbox/graphics/figsize.m ================================================ function sz = figsize(h, width, height) %FIGSIZE Resizes the specified figure while keeping it on screen. % Usage: % figsize([width, height]) % figsize(width, height) % figsize % returns figure [width, height] % figsize(h, ...) % % Args: % h: handle of figure to resize (default: gcf) % width: new width ([] = unchanged) % height: new height ([] = unchanged) % % Returns: % sz: [width, height] of the figure. Returns new size if output % specified, if single figure handle specified, or if no args specified % % See also: movegui if nargin < 1; h = []; end if nargin < 2; width = []; end if nargin < 3; height = []; end args = {h,width,height}; % Figure out figure handle isfigarg = cellfun(@isfig,args); if any(isfigarg) fig = args{isfigarg}; args(isfigarg) = []; else fig = gcf; end % Get current size fig_sz = fig.Position(3:4); % Validate inputs N = cellfun(@numel, args); assert(sum(N) <= 2, 'Invalid function syntax.') % Figure out new dimensions sz = fig_sz; if sum(N) == 2 % vector or 2 scalars specified sz = [args{:}]; elseif sum(N) == 1 % only 1 term specified w = args{1}; h = args{2}; if numel(N) == 3 && N(1) < N(3) % {[], [], h} case w = args{2}; h = args{3}; end if isempty(w); w = fig_sz(1); end if isempty(h); h = fig_sz(2); end sz = [w,h]; end if ~isequal(fig.Position(3:4),sz) % Resize fig.Position(3:4) = sz; % Keep on screen movegui(fig,'onscreen') end % Return new size if output specified, if single figure handle specified, % or if no args specified if ~(nargout > 0 || (sum(isfigarg) > 0 && nargin == 1) || nargin == 0) clear sz end end ================================================ FILE: leap/toolbox/graphics/fontsize.m ================================================ function fontsize(size, h) %FONTSIZE Sets the fontsize across a graphics object. % Usage: % fontsize(size) % default: gca % fontsize(size, fig) % fontsize(size, ax) % % See also: fontname if nargin < 2; h = gca; end set(findobj(h,'-property','FontSize'),'FontSize',size) end ================================================ FILE: leap/toolbox/graphics/hline.m ================================================ function h = hline(y, varargin) %HLINE Easy plotting of a horizonal line. % Usage: % hline % hline(y) % hline(ax, _) % hline(_, ...) % plot args (e.g., linespec) % h = hline(_) % % Args: % y: y-value % % See also: vline if nargin < 1; y = []; end ax = []; if isax(y) ax = y; y = []; if nargin > 1 arg1 = varargin{1}; if isnumeric(arg1) y = arg1; varargin(1) = []; end end end if isempty(ax) ax = gca; end if isempty(y) y = mean(ylim(ax)); end state = ax.NextPlot; ax.NextPlot = 'add'; X = xlim(ax) .* ones(numel(y),1); Y = [1 1] .* y(:); if isvector(y) X = [X NaN(size(X,1),1)]'; Y = [Y NaN(size(Y,1),1)]'; end h = plot(ax, X(:), Y(:), varargin{:}); ax.NextPlot = state; if nargout < 1; clear h; end end ================================================ FILE: leap/toolbox/graphics/isax.m ================================================ function TF = isax(h) %ISAX Check if input is an axes handle. % Usage: % TF = isax(h) % % Args: % h: % % See also: isfig, isgraphics TF = isa(h,'matlab.graphics.axis.Axes'); end ================================================ FILE: leap/toolbox/graphics/isfig.m ================================================ function TF = isfig(h) %ISFIG Checks whether the handle(s) specified are existing figures. % Usage: % TF = isfig(h) % % See also: isax, isgraphics % Check if they are existing graphics object handles TF = ~isempty(h) && ishghandle(h); % Check if they are figures TF(TF) = strcmp(get(h(TF), 'type'), 'figure'); end ================================================ FILE: leap/toolbox/graphics/noticks.m ================================================ function noticks(ax, whichAxes) %NOTICKS Hides ticks in the axes. % Usage: % noticks % noticks('x') % hides only x-ticks % noticks(ax, ...) if nargin == 1 if ischar(ax) whichAxes = ax; clear ax end end % Defaults if ~exist('whichAxes', 'var'); whichAxes = 'xyz'; end if ~exist('ax', 'var'); ax = gca; end for i = 1:numel(ax) if any(whichAxes == 'x') ax(i).XAxis.TickLength = [0 0]; ax(i).XAxis.TickLabelsMode = 'manual'; ax(i).XAxis.TickLabels = {}; end if any(whichAxes == 'y') ax(i).YAxis.TickLength = [0 0]; ax(i).YAxis.TickLabelsMode = 'manual'; ax(i).YAxis.TickLabels = {}; end if any(whichAxes == 'z') ax(i).ZAxis.TickLength = [0 0]; ax(i).ZAxis.TickLabelsMode = 'manual'; ax(i).ZAxis.TickLabels = {}; end end end ================================================ FILE: leap/toolbox/graphics/pareto2.m ================================================ function [ax, b, p] = pareto2(X, leftYLabel, rightYLabel) %PARETO2 Prettier pareto function. % Usage: % pareto2(X) % pareto2(X, leftYLabel, rightYLabel) % [ax, b, p] = pareto2(...) % % See also: pareto, plotExplainedVar % Plot [ax, b, p] = plotyy(1:numel(X), X, 1:numel(X), cumsum(X), 'bar', 'plot'); % Y axes labels if nargin >= 2; ylabel(ax(1), leftYLabel); end if nargin >= 3; ylabel(ax(2), rightYLabel); end % Make it prettier :) p.LineWidth = 2.0; axis(ax, 'tight') grid on % Fix Y ticks ax(1).YTickMode = 'auto'; ax(2).YTickMode = 'auto'; if nargout < 1 clear ax b p end end ================================================ FILE: leap/toolbox/graphics/plotExplainedVar.m ================================================ function [ax, b, p] = plotExplainedVar(explained) %PLOTEXPLAINEDVAR Pretty plot PCA explained variance. % Usage: % plotExplainedVar(explained) % [ax, b, p] = plotExplainedVar(explained) % % See also: pareto2, plotyy if isstruct(explained) && isfield(explained,'explained'); explained = explained.explained; end [ax, b, p] = pareto2(explained, 'Component (%)', 'Cumulative (%)'); % b.FaceColor = [0.85 0.325 0.098]; b.FaceColor = [1 1 1] * 0.7; % b.FaceColor = min(b.FaceColor .* 1.2,1); % b.FaceAlpha = 0.4; % p.Color = [1 1 1] .* 0.4; linkaxes(ax,'x') xlabel('Principal Components') title('PCA Explained Variance') figclosekey if nargout < 1 clear ax b p end end ================================================ FILE: leap/toolbox/graphics/plotpts.m ================================================ function h = plotpts(pts, varargin) %PLOTPTS Convenience wrapper for plotting scatters. Same syntax as plot(). % Usage: % plotpts(pts) % plotpts(pts, ...) % h = plotpts(_) % % See also: plot, scatter if nargin < 2; varargin = {'.'}; end h = plot(pts(:,1), pts(:,2), varargin{:}); if isempty(get(gcf,'KeyPressFcn')) set(gcf,'KeyPressFcn',@KeyPressFcn_cb) end if nargout < 1; clear h; end end function KeyPressFcn_cb(h,evt) if strcmp(evt.Key,'q') delete(h) end end ================================================ FILE: leap/toolbox/graphics/redblue.m ================================================ function map = redblue(N, dark) %REDBLUE Red and blue colormap going from blue to white to red. % Usage: % map = redblue(N) % % Args: % N: number of samples in the colormap (default: 64) % % Returns: % map: N x 3 matrix of RGB colors % % Example: % imagesc(repmat(linspace(-1,1,200),100,1)),colorbar,colormap redblue % % See also: colormap, parula if nargin < 1 f = get(groot,'CurrentFigure'); if isempty(f) N = size(get(groot,'DefaultFigureColormap'),1); else N = size(f.Colormap,1); end end if nargin < 2 || isempty(dark); dark = false; end % basis = [ % 0 0 1 % blue % 1 1 1 % white % 1 0 0 % red % ]; mid_col = [1 1 1]; if dark; mid_col = [0 0 0]; end % similar to redbluecmap (bioinformatics toolbox) basis = [ 0.0196078431372549 0.188235294117647 0.380392156862745 % blue mid_col 0.403921568627451 0 0.12156862745098 % red ]; P = size(basis,1); map = interp1(1:size(basis,1), basis, linspace(1,P,N), 'linear'); end ================================================ FILE: leap/toolbox/graphics/sc/gray.m ================================================ %GRAY Black-white colormap % % Examples: % map = gray; % map = gray(len); % B = gray(A); % B = gray(A, lims); % % Similar to MATLAB's gray function, but also able to return a concise % colormap table. % % The function can additionally be used to convert a real-valued array into % a truecolor array using the colormap. % % IN: % len - Scalar length of the output colormap. If len == Inf the concise % table is returned. Default: len = size(get(gcf, 'Colormap'), 1); % A - Non-scalar numeric array of real values to be converted into % truecolor. % lims - 1x2 array of saturation limits to be used on A. Default: % [min(A(:)) max(A(:))]. % % OUT: % map - (len)xJ colormap table. J = 3, except in the concise case, when % J = 4, map(1:end-1,4) giving the relative sizes of the % inter-color bins. % B - size(A)x3 truecolor array. % Copyright: Oliver Woodford, 2009 function map = gray(varargin) map = [0 0 0; 1 1 1]; map = colormap_helper(map, varargin{:}); ================================================ FILE: leap/toolbox/graphics/sc/private/colormap_helper.m ================================================ function map = colormap_helper(map, len, lims) %COLORMAP_HELPER Helper function for colormaps % % Examples: % map = colormap_helper(map); % map = colormap_helper(map, len); % B = colormap_helper(map, A); % B = colormap_helper(map, A, lims); % % Given a concise colormap table (i.e. one that contains all the % information required to create a full colormap, without any redundancy), % this function can return a colormap of the desired length, or convert a % real-valued array into truecolor array using the colormap. % % IN: % map - KxJ colormap table. J = 3, except in the non-linear case, when % J = 4, map(1:end-1,4) giving the relative sizes of the % inter-color bins. % len - Scalar length of the output colormap. If len == Inf the concise % table is returned. Default: len = size(get(gcf, 'Colormap'), 1); % A - Non-scalar numeric array of real values to be converted into % truecolor. % lims - 1x2 array of saturation limits to be used on A. Default: % [min(A(:)) max(A(:))]. % % OUT: % map - (len)xJ colormap table. J = 3, except in the concise case, when % J = 4, map(1:end-1,4) giving the relative sizes of the % inter-color bins. % B - size(A)x3 truecolor array. % $Id: colormap_helper.m,v 1.4 2009/04/13 12:16:22 ojw Exp $ % Copyright: Oliver Woodford, 2009 if nargin < 2 len = size(get(gcf, 'Colormap'), 1); end if isscalar(len) if len == Inf % Return the concise colormap table return end len = 1:len; sz = numel(len); lims = [1 sz]; else sz = size(len); if nargin < 3 lims = []; end end map = reshape(real2rgb(len(:), map, lims), [sz 3]); ================================================ FILE: leap/toolbox/graphics/sc/private/rescale.m ================================================ function [B, lims] = rescale(A, lims, out_lims) %RESCALE Linearly rescale values in an array % % Examples: % B = rescale(A) % B = rescale(A, lims) % B = rescale(A, lims, out_lims) % [B lims] = rescale(A) % % Linearly rescales values in an array, saturating values outside limits. % % IN: % A - Input array of any size and class. % lims - 1x2 array of saturation limits to be used on A. Default: % [min(A(:)) max(A(:))]. % out_lims - 1x2 array of output limits the values in lims are to be % rescaled to. Default: [0 1]. % % OUT: % B - size(A) double array. % lims - 1x2 array of saturation limits used on A. Equal to the input % lims, if given. % Copyright: Oliver Woodford, 2009 - 2011 % 14/01/11 Fixed a bug brought to my attention by Ming Wu (Many thanks!). if nargin < 3 out_lims = [0 1]; end if nargin < 2 || isempty(lims) M = isfinite(A); if ~any(reshape(M, numel(M), 1)) % All NaNs, Infs or -Infs B = double(A > 0); lims = [0 1]; else lims = [min(A(M)) max(A(M))]; B = normalize(A, lims, out_lims); B = min(max(B, out_lims(1)), out_lims(2)); end clear M else B = normalize(A, lims, out_lims); B = min(max(B, out_lims(1)), out_lims(2)); end return function B = normalize(A, lims, out_lims) if lims(2) == lims(1) || out_lims(1) == out_lims(2) B = zeros(size(A)); else B = double(A); if lims(1) B = B - lims(1); end v = (out_lims(2) - out_lims(1)) / (lims(2) - lims(1)); if v ~= 1 B = B * v; end end if out_lims(1) B = B + out_lims(1); end return ================================================ FILE: leap/toolbox/graphics/sc/real2rgb.m ================================================ %REAL2RGB Converts a real-valued matrix into a truecolor image % % Examples: % B = real2rgb(A, cmap); % B = real2rgb(A, cmap, lims); % [B lims map] = real2rgb(...); % % This function converts a real-valued matrix into a truecolor image (i.e. % double array with values between 0 and 1) using the colormap specified % (either user-defined or the name of a colormap function). The output % image is suitable for display using IMAGE or IMSHOW, exporting using % IMWRITE, texture mapping a surface etc. % % Colormaps specified by name, e.g. 'hot', can be reversed ('-hot'), made % to convert linearly to grayscale when printed on a black & white printer % ('hot*'), or both ('-hot*'). % % Value limits and a colormap table can be output, for use generating the % correct colorbar, e.g.: % [B lims map] = real2rgb(peaks(256), '-hot*'); % hIm = imshow(B); % set(gcf, 'Colormap', map); % set(gca, 'CLim', lims); % set(hIm, 'CDataMapping', 'scaled'); % colorbar; % % IN: % A - MxN real matrix. % cmap - JxK user-defined colormap, or a string indicating the name % of the colormap to be used. K = 3 or 4. If K == 4 then % cmap(1:end-1,4) contains the relative widths of the bins between % colors. If cmap is a colormap function name then the prefix '-' % indicates that the colormap is to be reversed, while the suffix % '*' indicates that the colormap bins are to be rescaled so that % each bin produces the same change in gray level, such that the % colormap converts linearly to grayscale when printed in black % and white. % lims - 1x2 array of saturation limits to be used on A. Default: % [min(A(:)) max(A(:))]. % % OUT: % B - MxNx3 truecolor image. % lims - 1x2 array of saturation limits used on A. Same as input lims, if % given. % map - 256x3 colormap similar to that used to generate B. % Copyright: Oliver Woodford, 2009-2011 % Thank you to Peter Nave for reporting a bug whereby colormaps larger than % 256 entries long are returned. function [B, lims, map] = real2rgb(A, cmap, lims) % Don't do much if A is wrong size [y, x, c] = size(A); if c > 1 error('A can only have 2 dimensions'); end if y*x*c == 0 % Create an empty array with the correct dimensions B = zeros(y, x, (c~=0)*3); return end if nargin < 2 cmap = 'parula'; end % Generate the colormap if ischar(cmap) % If map starts with a '-' sign, invert the colormap reverseMap = cmap(1) == '-'; % If the map ends with a '*', attempt to make map convert linearly to % grayscale grayMap = cmap(end) == '*'; % Extract the map name cmap = lower(cmap(reverseMap+1:end-grayMap)); % Load the map try % Check for a concise table first map = feval(cmap, Inf); catch map = []; end if invalid_map(map) try % Just load a large table map = feval(cmap, 256); catch error('Colormap ''%s'' not found', cmap); end if invalid_map(map) error('Invalid colormap'); end end if reverseMap % Reverse the map map = map(end:-1:1,:); if size(map, 2) == 4 % Shift up the bin lengths map(1:end-1,4) = map(2:end,4); end end if grayMap && size(map, 1) > 2 % Ensure the map converts linearly to grayscale map(1:end-1,4) = abs(diff(map(:,1:3) * [0.299; 0.587; 0.114])); end else % Table-based colormap given map = cmap; end % Only work with real doubles B = reshape(double(real(A)), y*x, c); % Compute limits and scaled values maxInd = 1 + (size(map, 1) - 2) * (size(map, 2) ~= 4); if nargin < 3 lims = []; end [B, lims] = rescale(B, lims, [0 maxInd]); % Compute indices and offsets if size(map, 2) == 4 % Non-linear colormap bins = map(1:end-1,4); cbins = cumsum(bins); bins(bins==0) = 1; bins = cbins(end) ./ bins; cbins = [0; cbins(1:end-1) ./ cbins(end); 1+eps]; [ind, ind] = histc(B, cbins); B = (B - cbins(ind)) .* bins(ind); clear bins cbins else % Linear colormap ind = min(floor(B), maxInd-1); B = B - ind; ind = ind + 1; end % Compute the output image try B = bsxfun(@times, map(ind,1:3), 1 - B) + bsxfun(@times, map(ind+1,1:3), B); catch % If no bsxfun B = B(:,[1 1 1]); B = map(ind,1:3) .* (1 - B) + map(ind+1,1:3) .* B; end B = min(max(B, 0), 1); % Rounding errors can make values slip outside bounds B = reshape(B, y, x, 3); if nargout > 2 && (size(map, 1) ~= 256 || size(map, 2) == 4) % Generate the colormap (for creating a colorbar with) map = reshape(real2rgb(0:255, map, [0 255]), 256, 3); end return function notmap = invalid_map(map) notmap = isempty(map) || ndims(map) ~= 2 || size(map, 1) < 1 || size(map, 2) < 3 || size(map, 2) > 4 || ~all(reshape(map(:,1:3) >= 0 & map(:,1:3) <= 1, [], 1)); ================================================ FILE: leap/toolbox/graphics/shortticks.m ================================================ function shortticks(ax) %SHORTTICKS Make the axis ticks short. % Usage: % shortticks % shortticks(ax) % % See also: noticks if nargin < 1; ax = gca; end set(findobj(ax,'-property','TickLength'),'TickLength',[0 0]) end ================================================ FILE: leap/toolbox/hdf5/h5att2struct.m ================================================ function S = h5att2struct(filename, location) %H5ATT2STRUCT Reads a set of HDF5 attributes into a named structure. % Usage: % S = h5att2struct(filename, location) % % Returns: % S: structure with fields corresponding to the attribute names, or empty % if no attributes are found if nargin < 2; location = '/'; end if location(1) ~= '/'; location = ['/' location]; end info = h5info(filename, location); S = struct(); if isempty(info.Attributes) return end % Pull out data fieldnames = {info.Attributes.Name}; values = {info.Attributes.Value}; % Wrap cell arrays with {} to ensure they are scalar fields is_cell_arr = ~cellfun(@isscalar,values); values(is_cell_arr) = cf(@(x){x},values(is_cell_arr)); % Create structure S = horz([horz(fieldnames); horz(values)]); S = struct(S{:}); end ================================================ FILE: leap/toolbox/hdf5/h5getdatasets.m ================================================ function datasets = h5getdatasets(filepath, grp, recurse) %H5GETDATASETS Returns a list of all datasets in an HDF5 file. % Usage: % datasets = h5getdatasets(filepath) % datasets = h5getdatasets(filepath, grp) % % Args: % filepath: file path to HDF5 file % grp: path to group within HDF5 file (default: '/') % recurse: traverse subgroups to find datasets (default: true) % % Returns: % datasets: cell array of paths to each dataset within the file % % See also: h5file, h5info if nargin < 2 || isempty(grp); grp = '/'; end if nargin < 3 || isempty(recurse); recurse = true; end info = h5info(filepath, grp); datasets = getdsets(info, recurse); end function ds = getdsets(G, recurse) % Recurse through info structure and pull out datasets ds = {}; if ~isempty(G.Datasets) base = G.Name; if base(end) == '/'; base = base(1:end-1); end ds = strcat(base, '/', {G.Datasets.Name})'; end if recurse for i = 1:numel(G.Groups) ds = [ds; getdsets(G.Groups(i),recurse)]; end end end ================================================ FILE: leap/toolbox/hdf5/h5readframes.m ================================================ function [frames, numFrames] = h5readframes(filepath, dataset, idx) %H5READFRAMES Reads video frames from an HDF5 file. % Usage: % frames = h5readframes(filepath) % frames = h5readframes(filepath, idx) % frames = h5readframes(filepath, dataset) % frames = h5readframes(filepath, dataset, idx) % [frames, numFrames] = h5readframes(_) % % See also: h5att2struct, h5struct2att, h5size if nargin < 2; dataset = []; end if nargin < 3; idx = []; end if (isnumeric(dataset) && ~isempty(dataset)) || ischar(idx) tmp = idx; idx = dataset; dataset = tmp; end if isempty(dataset) dataset = '/video/data'; if contains(filepath,'fg.h5'); dataset = '/fg'; end if contains(filepath,'box'); dataset = '/box'; end end if dataset(1) ~= '/' dataset = ['/' dataset]; end if isempty(idx) frames = h5read(filepath, dataset); else startFrame = min(idx); endFrame = max(idx); stride = 1; if numel(idx) > 1 % Find largest stride that hits every frame dFrames = diff(idx); dFrames = sort(dFrames,'descend'); for stride = [horz(dFrames) 1] if all(rem(dFrames,stride) == 0); break; end end idx0 = idx; % requested indices idx = startFrame:stride:endFrame; % indices to be read end sz = h5size(filepath, dataset); if numel(sz) == 3 stride = [1 1 max([1 stride])]; start = [1 1 startFrame]; count = [inf inf numel(idx)]; else stride = [1 1 1 max([1 stride])]; start = [1 1 1 startFrame]; count = [inf inf inf numel(idx)]; end frames = h5read(filepath, dataset, start, count, stride); if numel(idx) > 1 [~,idx_sub] = ismember(idx0,idx); if ndims(frames) == 3 frames = frames(:,:,idx_sub); else frames = frames(:,:,:,idx_sub); end end end numFrames = size(frames); numFrames = numFrames(end); end ================================================ FILE: leap/toolbox/hdf5/h5readgroup.m ================================================ function data = h5readgroup(filepath, group) %H5READGROUP Reads a group into a structure containing all the data in the group. % Usage: % data = h5readgroup(filepath, group) % % Args: % filepath: path to the HDF5 file % group: name or path to the group % % % % See also: h5read, h5save, h5att2struct if nargin < 2; group = '/'; end if group(1) ~= '/'; group = ['/' group]; end data = struct(); info = h5info(filepath, group); if ~isempty(info.Groups) groups = {info.Groups.Name}; for i = 1:numel(groups) grp_path = strsplit(groups{i},'/'); field_name = matlab.lang.makeValidName(grp_path{end}); data.(field_name) = h5readgroup(filepath,groups{i}); end end if ~isempty(info.Attributes) data.Attributes = h5att2struct(filepath, group); end if ~isempty(info.Datasets) datasets = {info.Datasets.Name}; for i = 1:numel(datasets) dset_path = strsplit(datasets{i},'/'); field_name = matlab.lang.makeValidName(dset_path{end}); data.(field_name) = h5read(filepath, [group '/' datasets{i}]); if ~isempty(info.Datasets(i).Attributes) data.Attributes.(datasets{i}) = h5att2struct(filepath, [group '/' datasets{i}]); end end end end ================================================ FILE: leap/toolbox/hdf5/h5save.m ================================================ function h5save(filepath, X, dset, varargin) %H5SAVE Create and save a variable to an HDF5 file. % Usage: % h5save(filepath, X) % h5save(filepath, X, dset) % % Args: % filepath: path to HDF5 file % X: variable to save % dset: name or path to dataset (optional if variable name can be determined by inputname) % % Params: % 'compress': use GZIP compression filter for dataset (default: false) % 'chunking': shape to use for dataset chunking or dimension to use as singleton (default: []) % if compress is true and this is not specified, chunking % will default to the last dimension of the dataset % % See also: h5write, h5att2struct, h5struct2att, inputname if nargin < 3 || isempty(dset) dset = inputname(2); if isempty(dset) error('Dataset name must be specified if not saving a named variable.') end end % Make sure dataset starts with '/' if dset(1) ~= '/'; dset = ['/' dset]; end % Parse ptional arguments: defaults = struct('compress',false,'chunking',[]); [params, unmatched] = parse_params(varargin, defaults); if isfield(unmatched, 'ChunkSize'); params.chunking = unmatched.ChunkSize; end if isfield(unmatched, 'Deflate'); params.compress = unmatched.Deflate; end if params.compress > 0; params.compress = double(params.compress); end % If compression is enabled, use last dimension as chunking dim if isempty(params.chunking) && params.compress > 0 params.chunking = ndims(X); end % If chunking is specified as dimension instead of full size, expand to chunk size if isscalar(params.chunking) d = params.chunking; params.chunking = size(X); params.chunking(d) = 1; end % Infer data type from input dtype = class(X); if islogical(X) X = uint8(X); end % Exceptions identifiers: % MATLAB:imagesci:h5write:datasetDoesNotExist % MATLAB:imagesci:h5create:datasetAlreadyExists try args = {}; if ~isempty(params.chunking); args = [args, {'ChunkSize', params.chunking}]; end if params.compress; args = [args, {'Deflate', params.compress}]; end h5create(filepath, dset, size(X), 'Datatype', class(X), args{:}) h5writeatt(filepath, dset, 'dtype', dtype) catch ME switch ME.identifier case 'MATLAB:imagesci:h5create:datasetAlreadyExists' warning('h5save:overwrite','Overwriting existing dataset: %s', dset) otherwise rethrow(ME) end end h5write(filepath, dset, X) end ================================================ FILE: leap/toolbox/hdf5/h5savegroup.m ================================================ function h5savegroup(filepath, S, grp, varargin) %H5SAVEGROUP Saves a struct as a HDF5 group. % Usage: % h5savegroup(filepath, S, grp) % % Args: % filepath: path to HDF5 file to save % S: structure to save as an HDF5 group % grp: path to group (default: '/[S]' where [S] is the variable name of the struct) % % Params: see h5save % % See also: h5readgroup, h5save if nargin < 3; grp = inputname(2); end if isempty(grp); grp = ''; end if ~startsWith(grp,'/'); grp = ['/' grp]; end fns = fieldnames(S); isAttr = strcmp(fns,'Attributes'); fns = [fns(~isAttr); fns(isAttr)]; % move attributes to last so file is created for i = 1:numel(fns) dset = [grp '/' fns{i}]; if isstruct(S.(fns{i})) if strcmp(fns{i},'Attributes') h5struct2att(filepath, grp, S.(fns{i})) else h5savegroup(filepath, S.(fns{i}), dset, varargin{:}) end else try h5save(filepath, S.(fns{i}), dset, varargin{:}) catch warning('Failed to save dataset: %s', dset) end end end end ================================================ FILE: leap/toolbox/hdf5/h5size.m ================================================ function [sz, maxSize] = h5size(filepath, dataset, dim) %H5SIZE Returns the size of the specified dataset. % Usage: % [size, maxSize] = h5size(filepath, dataset) % [size, maxSize] = h5size(filepath, dataset, dim) if nargin < 3; dim = []; end if nargout > 1 info = h5info(filepath, dataset); sz = info.Dataspace.Size; maxSize = info.Dataspace.MaxSize; if ~isempty(dim) sz = sz(dim); maxSize = maxSize(dim); end else sz = size(h5file(filepath, dataset)); if ~isempty(dim) sz = sz(dim); end end end ================================================ FILE: leap/toolbox/hdf5/h5struct2att.m ================================================ function h5struct2att(filepath, location, S) %H5STRUCT2ATT Writes attributes to an HDF5 file from a scalar structure. % Usage: % h5struct2att(filepath, location, S) % % See also: h5att2struct if nargin == 2 && isstruct(location); S = location; location = inputname(2); end if isempty(location); location = ''; end if ~startsWith(location,'/'); location = ['/' location]; end names = fieldnames(S); for i = 1:numel(names) if islogical(S.(names{i})); S.(names{i}) = uint8(S.(names{i})); end if iscellstr(S.(names{i})); S.(names{i}) = strjoin(S.(names{i}),'\n'); end if isstruct(S.(names{i})); continue; end % TODO: recursive group attributes h5writeatt(filepath, location, names{i}, S.(names{i})) end end ================================================ FILE: leap/toolbox/hdf5/hdf5prop/h5datacreate.m ================================================ function datasetId = h5datacreate(h5file,varname,varargin) %H5datacreate Create HDF5 dataset. % % H5DATACREATE(HFILE,VARNAME,parameter,value,...) creates a dataset % named VARNAME. HFILE may be either a path to the HDF5 file or a file % ID to an already opened file. % % If VARNAME is a full pathname, all % intermediate groups are created if they don't already exist. % % parameter % 'size' - size of the dataset % 'chunk_size' - size of the chunks of the dataset % 'compression' - 0-9 compression level % 'type' - type of the dataset to create. This can be a string % such as 'double', in which case the datatype maps to % 'H5T_NATIVE_DOUBLE', or it can be a derived HDF5 % datatype. % 'max_size' - the maximum size of the dataset to create % 'fill' - the fill value % % % Example: create a 10x20 single precision dataset named 'DS1'. % h5filecreate('myfile.h5'); % h5datacreate('myfile.h5','DS1','type','single','size',[10 20]); % % Example: create a 4x1 string dataset where each string has length % 8 and is null-terminated. % mytype = H5T.copy('H5T_C_S1'); % H5T.set_size(mytype,8); % H5T.set_strpad(mytype,'H5T_STR_NULLTERM'); % h5datacreate('myfile.h5','/path/to/DS2','type',mytype,'size',4); % % Credit where credit is due: Philip Top at LLNL % % See also h5filecreate. % Copyright 2010 The MathWorks, Inc. p = inputParser; p.addParamValue('size',0,@isnumeric); p.addParamValue('chunk_size',0,@isnumeric); p.addParamValue('compression',0,@isnumeric); p.addParamValue('type',H5T.copy('H5T_NATIVE_DOUBLE'),@(x)ischar(x) || isa(x,'H5ML.id')); p.addParamValue('max_size',0,@isnumeric); p.addParamValue('fill',0,@isnumeric); p.parse(varargin{:}); params = p.Results; params = validate_params(h5file,varname,params); flags = 'H5F_ACC_RDWR'; fapl = 'H5P_DEFAULT'; if ischar(h5file) file_id = H5F.open(h5file,flags,fapl); else file_id=h5file; end params = set_dataspace_id (params); params = set_datatype_id (params); params = set_dcpl(params); datasetId = set_data_id(file_id,varname,params); H5S.close(params.dataspaceId); H5T.close(params.datatypeId); H5P.close(params.dcpl); if ischar(h5file) H5F.close(file_id); end %========================================================================== function params = set_dcpl(params) % Setup the dataset creation property list. params.dcpl = H5P.create('H5P_DATASET_CREATE'); % create property list if (~isequal(params.chunk_size,0)) % set chunk size H5P.set_chunk(params.dcpl,fliplr(params.chunk_size)); end if (params.compression~=0) % set gzip compression level H5P.set_deflate(params.dcpl,params.compression); end if (params.fill~=0) % set fill value H5P.set_fill_value(params.dcpl, params.datatypeId, params.fill.*ones(params.type)); end return %========================================================================== % SET_DATASET_ID % % Setup the dataet ID. We need to check as to whether or not the dataset % already exists. function datasetID = set_data_id(fid,dataname,params) v = version('-release'); switch(v) case { '2007b', '2008a', '2008b' } % Give it our best shot. datasetID = H5D.create(fid,dataname,... params.datatypeId, params.dataspaceId, ... params.dcpl); otherwise % create any intermediate groups along the way. params.lcpl = H5P.create('H5P_LINK_CREATE'); H5P.set_create_intermediate_group(params.lcpl,1); dapl = 'H5P_DEFAULT'; % need to use the 1.8.x version of H5D.create for this. datasetID = H5D.create(fid,dataname,... params.datatypeId, params.dataspaceId, ... params.lcpl,params.dcpl,dapl); end return %=============================================================================== % SET_DATASPACE_ID % % Setup the dataspace ID. function params = set_dataspace_id (params) params.dataspaceId=H5S.create('H5S_SIMPLE'); H5S.set_extent_simple(params.dataspaceId,length(params.size),... fliplr(params.size),fliplr(params.max_size)); return %=============================================================================== % SET_DATATYPE_ID % % We need to choose an appropriate HDF5 datatype function params = set_datatype_id (params) if ischar(params.type) switch params.type case 'double' params.datatypeId = H5T.copy('H5T_NATIVE_DOUBLE'); case {'single','float'} params.datatypeId = H5T.copy('H5T_NATIVE_FLOAT'); case 'int64' params.datatypeId = H5T.copy('H5T_NATIVE_LLONG'); case 'uint64' params.datatypeId = H5T.copy('H5T_NATIVE_ULLONG'); case {'int32','int'} params.datatypeId = H5T.copy('H5T_NATIVE_INT'); case {'uint32','uint'} params.datatypeId = H5T.copy('H5T_NATIVE_UINT'); case {'int16','short'} params.datatypeId = H5T.copy('H5T_NATIVE_SHORT'); case 'uint16' params.datatypeId = H5T.copy('H5T_NATIVE_USHORT'); case 'int8' params.datatypeId = H5T.copy('H5T_NATIVE_SCHAR'); case 'uint8' params.datatypeId = H5T.copy('H5T_NATIVE_UCHAR'); case 'complex' % special case for complex data. We'll actually create 3 % datasets. One to hold the real data, one to hold the % complex data, and one to hold references to both. params.datatypeId = H5T.copy('H5T_STD_REF_OBJ'); otherwise error('hdf5tools:H5DATACREATE:unsupportedDatatype', ... '''%s'' is not a supported H5DATACREATE datatype.\n', params.type ); end return end if isa(params.type,'H5ML.id') params.datatypeId = H5T.copy(params.type); return end %-------------------------------------------------------------------------- function params = validate_params(hfile,varname,params) if (params.size == 0 ) error('hdf5tools:h5datacreate:zeroSize', ... 'Size must be specified and be greater than zero.'); end if ~ischar(hfile) if ~isequal(class(hfile),'H5ML.id') error('hdf5tools:H%DATACREATE:badDatatype', ... 'Filename input argument must have datatype char.' ); end end if ~ischar(varname) error('hdf5tools:H5DATACREATE:badDatatype', ... 'VARNAME input argument must have datatype char.' ); end if (~isequal(params.chunk_size,0)) if (numel(params.chunk_size)~=numel(params.size)) error('dataset size and chunk size have different dimensions'); end end if (~isequal(params.max_size,0)) if (isequal(params.chunk_size,0)) error('chunk size must be specified if max size is different from size') end if (numel(params.max_size)~=numel(params.size)) error('dataset max size and initial size have different dimensions'); elseif (any(isinf(params.max_size))) mxs=cell(size(params.max_size)); for kk=1:length(params.max_size) if (isinf(params.max_size(kk))) mxs{kk}='H5S_UNLIMITED'; else mxs{kk}=params.max_size(kk); end end params.max_size = mxs; end else params.max_size=params.size; end if ((params.compression<0)||(params.compression>9)) error('invalid compression level'); else if (params.compression>0) if (isequal(params.chunk_size,0)) params.chunk_size=params.size; end end end return ================================================ FILE: leap/toolbox/hdf5/hdf5prop/hdf5prop.m ================================================ classdef hdf5prop < handle % HDF5PROP Class for transparent file data access. % Matlab class to create and access HDF5 datasets transparently as Matlab % variable. Data can be accessed and written with subscript referencing and % assignment methods, just like a matlab variable, only size must be % explicitly set or changed. % % prop = hdf5prop(file, dataset) % Creates a 'hdf5prop' object for the given dataset in the given HDF5 % file. % % prop = hdf5prop(file, dataset, mode) % Specify the access mode, % mode = 'r' : readonly access (default) % mode = 'rw': readwrite access % % prop = hdf5prop(file, dataset, parameter, value, ...) % Creates a 'hdf5prop' object for a new dataset with given parameters. % % parameter % 'size' - size of the dataset. Necessary. % 'chunk_size' - size of the chunks of the dataset. % 'compression' - 0-9 compression level. % 'type' - type of the dataset to create. This can be a string % such as 'double', in which case the datatype maps to % 'H5T_NATIVE_DOUBLE', or it can be a derived HDF5 % datatype. % 'max_size' - the maximum size of the dataset to create. % 'fill' - the fill value. % copyleft 2011, Piers Titus van der Torren % TODO: replace all H5 calls to direct mex calls for speed properties( SetAccess = protected ) file dataset mode = 'H5F_ACC_RDONLY' propsize end properties( SetAccess = protected, Transient = true ) dataset_id end methods function self = hdf5prop(file,dataset,varargin) self.file = file; self.dataset = dataset; if nargin>2 switch varargin{1} case {'H5F_ACC_RDWR','rw'} self.mode = 'H5F_ACC_RDWR'; varargin = varargin(2:end); case {'H5F_ACC_RDONLY','r'} varargin = varargin(2:end); otherwise end end if ~exist(file, 'file') % create file file_id = H5F.create(file, 'H5F_ACC_EXCL', 'H5P_DEFAULT', 'H5P_DEFAULT'); self.mode = 'H5F_ACC_RDWR'; else file_id = H5F.open(self.file, self.mode, 'H5P_DEFAULT'); end try self.dataset_id = H5D.open(file_id,self.dataset); if ~isempty(varargin) error('hdf5prop:exists', 'dataset already exists, even though creation parameters are given') end catch ex if strcmp(ex.identifier, 'hdf5prop:exists') rethrow(ex); end % create dataset if strcmp(self.mode, 'H5F_ACC_RDONLY') self.mode = 'H5F_ACC_RDWR'; H5F.close(file_id); file_id = H5F.open(self.file, self.mode, 'H5P_DEFAULT'); end self.dataset_id = h5datacreate(file_id, self.dataset, varargin{:}); end space = H5D.get_space(self.dataset_id); [ans, dims] = H5S.get_simple_extent_dims(space); self.propsize = fliplr(dims); end function open( self ) if ~isa(self.dataset_id, 'H5ML.id') || self.dataset_id.identifier < 0 file_id=H5F.open(self.file,self.mode,'H5P_DEFAULT'); self.dataset_id=H5D.open(file_id,self.dataset); % get size of dataset % TODO: size is not valid when file is changed elsewhere while open space = H5D.get_space(self.dataset_id); [ndims, dims] = H5S.get_simple_extent_dims(space); self.propsize = fliplr(dims); end end function close( self ) % CLOSE Close file if open. if isa(self.dataset_id, 'H5ML.id') H5D.close(self.dataset_id); end end function set_extent( self, sz) % SET_EXTENT Allocate space % % hdf5prop.set_extent(sz) % Extend allocated space to size sz. self.open(); H5D.extend(self.dataset_id, fliplr(sz)); %H5D.set_extent(self.dataset_id, fliplr(sz)); self.propsize = sz; end function set_mode( self, mode ) self.close() switch mode case {'H5F_ACC_RDWR','rw'} self.mode = 'H5F_ACC_RDWR'; case {'H5F_ACC_RDONLY','r'} self.mode = 'H5F_ACC_RDONLY'; otherwise error('unknown mode') end self.open() end function lock( self ) self.close() self.mode = 'H5F_ACC_RDONLY'; self.open() end end methods( Hidden=true ) function varargout = subsref(self, S) switch S(1).type case {'.', '{}'} [varargout{1:nargout}] = builtin('subsref',self,S); case '()' [varargout{1:nargout}] = self.get_data(S(1).subs{:}); % case '{}' % error('{} not allowed') end end function self = subsasgn(self, S, B) switch S(1).type case {'.', '{}'} builtin('subsasgn',self,S,B); case '()' self.set_data(S(1).subs, B); % case '{}' % error('{} not allowed') end end function out = get_data(self, varargin) self.open() % return all data if nargin == 1 || all(strcmp(varargin,':')) out = H5D.read(self.dataset_id,'H5ML_DEFAULT','H5S_ALL','H5S_ALL','H5P_DEFAULT'); return; end outsize = []; propsize = size(self); dim = numel(propsize); % fill singular dimensions if there's only 1 nonsingular dimension if dim > 1 && numel(varargin)==1 && sum(propsize ~= 1) <= 1 a = varargin; varargin = repmat({':'},1,dim); if any(propsize ~= 1) varargin{propsize ~= 1} = a{1}; end outsize = size(a{1}); if sum(outsize~=1)==1 outsize = []; end end assert(dim == numel(varargin),'linear or multidimensional boolean indexing is not yet supported, now use all %d dimensions',dim); % divide selection into hyperslabs start = nan(1,dim); count = nan(1,dim); stride = nan(1,dim); hyperslab = true; for n = 1:dim sel = varargin{dim+1-n}; % traverse reversed instead of fliplr if isa(sel,'char') && sel == ':' start(n) = 1; count(n) = propsize(dim+1-n); stride(n) = 1; idxc{n} = sel; selc{n} = sel; else %if isempty(sel) % % if one dimension is empty the result is empty too % out = []; % return %end sel = sel(:); if isa(sel,'logical') sel = find(sel); idxc{n} = ':'; elseif numel(sel) <= 1 || ( issorted(sel) && min(diff(sel))>0 ) idxc{n} = ':'; else [sel, ans, idxc{n}] = unique(sel); end selc{n} = sel; count(n) = numel(sel); dsel = diff(sel); if isempty(sel) start(n) = 1; stride(n) = 1; elseif numel(sel)==1 start(n) = sel(1); stride(n) = 1; elseif all(dsel == dsel(1)) start(n) = sel(1); stride(n) = dsel(1); else hyperslab = false; end end end % TODO: fix datatype if any(count==0) out = zeros(fliplr(count)); return end space=H5D.get_space(self.dataset_id); if hyperslab %H5ML.hdf5lib2('H5Sselect_hyperslab',space_double,'H5S_SELECT_SET',start,stride,count,[]); H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]); else s = find(isnan(start)); selend = cellfun(@(x) x(end),selc(s)); sel1 = cellfun(@(x) x(1),selc(s)); if prod(count(s)) > .1 * prod(selend - sel1) % since hyperslab is way faster than select_elements, it is % faster to read a full hypeslab and do the selection in % matlab. The factor above should match the speed ratio. start(s) = sel1; stride(s) = 1; count(s) = selend-sel1+1; for n=s idxc{n} = selc{n}(idxc{n}) - selc{n}(1) + 1; end H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]); else sel = zeros(dim,prod(count)); % ndgrid of selc{:} for n=1:dim, if ischar(selc{n}) x = (1:count(n))'; else x = selc{n}; % Extract and reshape as a vector. end s = count; s(n) = []; % Remove i-th dimension x = reshape(x(:,ones(1,prod(s))),[length(x) s]); % Expand x x = permute(x,[2:dim-n+1 1 dim-n+2:dim]); % Permute to i'th dimension sel(n,:) = x(:); end %warning('using H5S.select_elements, might be slow') H5S.select_elements(space,'H5S_SELECT_SET',sel-1); end end % reserve space for reading mem_space = H5S.create_simple(dim, count, []); % read the actual data out = H5D.read(self.dataset_id,'H5ML_DEFAULT',mem_space,space,'H5P_DEFAULT'); % shuffle output to match selection out=out(idxc{end:-1:1}); if ~isempty(outsize) out = reshape(out,outsize); end H5S.close(space); H5S.close(mem_space); end function set_data(self, argin, B) if ~strcmp(self.mode, 'H5F_ACC_RDWR') error('this hdf5prop is read only') end self.open() propsize = size(self); dim = numel(propsize); % fill singular dimensions if there's only 1 nonsingular dimension if dim > 1 && numel(argin)==1 && sum(propsize ~= 1) <= 1 a = argin; argin = repmat({':'},1,dim); if any(propsize ~= 1) argin{propsize ~= 1} = a{1}; end end assert(dim == numel(argin),'linear or multidimensional boolean indexing is not yet supported, now use all %d dimensions',dim); colondim = strcmp(argin,':'); % get input indexing dimensions indim = 1:dim; for n=1:dim if colondim(n) indim(n) = propsize(n); elseif isa(argin{n},'logical') indim(n) = sum(argin{n}(:)); else indim(n) = numel(argin{n}); end end % check if squeezed dimensions match indim = max(cellfun(@numel,argin), colondim.*propsize); szB = size(B); assert(numel(B)==1 || all(indim(indim~=1)==szB(szB~=1)),'Subscripted assignment dimension mismatch.') if any(indim==0) % nothing to be done return; end % unsqueeze input if numel(B)~=1 B = reshape(B,indim); end % write all data if all(colondim) if numel(B) == 1 B = repmat(B,propsize); % H5D.fill ? end H5D.write(self.dataset_id,'H5ML_DEFAULT','H5S_ALL','H5S_ALL','H5P_DEFAULT',B); return; end % divide selection into hyperslabs start = zeros(1,dim); count = zeros(1,dim); stride = zeros(1,dim); hyperslab = true; for n = 1:dim sel = argin{dim+1-n}; % traverse reversed instead of fliplr if isa(sel,'char') && sel == ':' start(n) = 1; count(n) = propsize(dim+1-n); stride(n) = 1; idxc{n} = sel; selc{n} = sel; else sel = sel(:); if isa(sel,'logical') sel = find(sel); idxc{n} = ':'; elseif numel(sel) == 1 || ( issorted(sel) && min(diff(sel))>0 ) idxc{n} = ':'; else % this line is different from get_data [sel, idxc{n}] = unique(sel); end selc{n} = sel; count(n) = numel(sel); if hyperslab dsel = diff(sel); if numel(sel)==1 start(n) = sel(1); stride(n) = 1; elseif all(dsel == dsel(1)) start(n) = sel(1); stride(n) = dsel(1); else hyperslab = false; end end end end space=H5D.get_space(self.dataset_id); if hyperslab %H5ML.HDF5lib2('H5Sselect_hyperslab',space_double,'H5S_SELECT_SET',start,stride,count,[]); H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]); else sel = zeros(dim,prod(count)); % ndgrid of selc{:} for n=1:dim, if ischar(selc{n}) x = (1:count(n))'; else x = selc{n}; % Extract and reshape as a vector. end s = count; s(n) = []; % Remove i-th dimension x = reshape(x(:,ones(1,prod(s))),[length(x) s]); % Expand x x = permute(x,[2:dim-n+1 1 dim-n+2:dim]); % Permute to i'th dimension sel(n,:) = x(:); end H5S.select_elements(space,'H5S_SELECT_SET',sel-1); end % reserve space for reading mem_space = H5S.create_simple(dim, count, []); % prepare input if prod(count) > 1 if numel(B) == 1 B = repmat(B,count); else % shuffle input to match selection B = B(idxc{end:-1:1}); end end % write the actual data H5D.write(self.dataset_id,'H5ML_DEFAULT',mem_space,space,'H5P_DEFAULT',B); H5S.close(space); H5S.close(mem_space); end function varargout = size(self, dim) self.open() sz = self.propsize; if nargout > 1 varargout = num2cell(sz); elseif nargin > 1 varargout = {sz(dim)}; else varargout = {sz}; end end % overloading numel causes problems with builtin subsref % function out = numel(self) % out = 1;%prod(size(self)); % end function out = length(self) out = max(size(self)); end function out = end(self, dim, n_dim) out = size(self,dim); end function out = vertcat(varargin) error('array of hdf5prop objects is not allowed') end function out = horzcat(varargin) error('array of hdf5prop objects is not allowed') end function out = cat(varargin) error('array of hdf5prop objects is not allowed') end function out = double( self ) out = double(self.get_data()); end function out = eq( self, other ) out = eq(self.get_data(), other()); end function out = ne( self, other ) out = ne(self.get_data(), other()); end function out = le( self, other ) out = le(self.get_data(), other()); end function out = ge( self, other ) out = qe(self.get_data(), other()); end function out = lt( self, other ) out = lt(self.get_data(), other()); end function out = gt( self, other ) out = gt(self.get_data(), other()); end end end ================================================ FILE: leap/toolbox/imageproc/ind2im.m ================================================ function I = ind2im(ind, sz, vals, fillval) %IND2IM Create image from a set of linear indices. % Usage: % I = ind2im(ind, sz) % I = ind2im(ind, sz, vals) % I = ind2im(ind, sz, vals, fillval) % I = ind2im(BW, vals) % I = ind2im(BW, vals, fillval) % I = ind2im(S) % % Args: % ind: linear indices of the image % sz: size of the output image % vals: values to fill in with (default: true) % S: structure with the fields 'sz, 'vals' and 'ind' % fillval: value to fill the rest of the array with % % Output: % I: image of size sz, the same type as vals, and values filled in at ind % % See also: im2ind, mask2im, ind2sub if nargin < 4 || isempty(fillval); fillval = 0; end if nargin < 3 || isempty(vals); vals = []; end if nargin >= 2 && islogical(ind) && numel(sz) == sum(ind(:)) if nargin == 3; fillval = vals; end vals = sz; sz = size(ind); end if nargin == 1 && isstruct(ind) if isfield(ind,'sz'); sz = ind.sz; end if isfield(ind,'size'); sz = ind.size; end if isfield(ind,'val'); vals = ind.val; end if isfield(ind,'vals'); vals = ind.vals; end if isfield(ind,'ind'); ind = ind.ind; end if isfield(ind,'idx'); ind = ind.idx; end end if isempty(vals); vals = true; end I = zeros(sz, 'like', vals); I(:) = fillval; I(ind) = vals; end ================================================ FILE: leap/toolbox/inputParsing/get_caller_name.m ================================================ function caller = get_caller_name(varargin) %GET_CALLER_NAME Returns the name of the caller function. % Usage: % caller = get_caller_name() % caller = get_caller_name('Path', true) % caller = get_caller_name(..., 'Warn', true) % warning if called from workspace % % See also: mfilename, funpath, dbstack % Parse inputs defaults.Path = false; defaults.Warn = false; params = defaults; if nargin > 0 params = parse_params(varargin, defaults); end % Get function call stack if params.Path [ST, I] = dbstack('-completenames'); else [ST, I] = dbstack(); end % This function or the function that called it were called directly from % the workspace if numel(ST) < I + 2 caller = ''; if params.Warn if numel(ST) < I + 1 warning('%s called directly from workspace.', mfilename) else warning('%s called directly from workspace.', ST(I + 1).name) end end return end % Return caller if params.Path caller = ST(I + 2).file; else caller = ST(I + 2).name; end end ================================================ FILE: leap/toolbox/inputParsing/nameval2struct.m ================================================ function S = nameval2struct(C) %NAMEVAL2STRUCT Converts a cell array of name-value pairs to a struct. % Usage: % struct = nameval2struct(nameval_cell) % % See also: struct2nameval C = C(:); % Check input validateattributes(C, {'cell'}, {'vector', 'nonempty'}) assert(iseven(length(C)), ... 'Cell arrays of name-value pairs must have an even number of elements.') assert(all(cellfun(@ischar, C(1:2:end))), ... ['Cell arrays of name-value pairs must be in the format: ' ... '{''Name1'', Val1, ..., ''NameN'', ValN}']) % Fix non-array inputs N = cellfun(@numel, C(2:2:end)); if numel(unique(N)) ~= 1 C(2:2:end) = cf(@(x) {x}, C(2:2:end)); end % Convert to structure S = struct(C{:}); end ================================================ FILE: leap/toolbox/inputParsing/parse_params.m ================================================ function [results, unmatched] = parse_params(args, defaults, varargin) %PARSE_PARAMS Parses a set of name-value pairs. % Usage: % parsed = parse_params(args, defaults) % parsed = parse_params(args, defaults, 'Name', Val, ...) % [parsed, unmatched] = parse_params(...) % % Args: % args: a cell array or structure of name-value pairs % defaults: a cell array or structure of name-value pairs % % Params: % This function takes any public properties of inputParser as parameters, % e.g., 'StructExpand'. % % Returns: % results: a structure containing the parsed results % unmatched: a structure containing any args that did not have a default % % See also: inputParser, struct2nameval, nameval2struct % Make sure args are name-val cells if isstruct(args); args = struct2nameval(args); end % Make sure defaults are structs if iscell(defaults); defaults = nameval2struct(defaults); end % Default inputParser parameters caller = get_caller_name(); parser_defaults.CaseSensitive = false; parser_defaults.FunctionName = caller; parser_defaults.KeepUnmatched = true; parser_defaults.PartialMatching = true; parser_defaults.StructExpand = true; % Parse inputParser parameters p = inputParser(); p.FunctionName = mfilename; p = add_params(p, parser_defaults); p.parse(varargin{:}); parser_params = p.Results; % Create new inputParser instance p = inputParser; % Set inputParser properties from parsed parameters param_names = fieldnames(parser_params); for i = 1:numel(param_names) p.(param_names{i}) = parser_params.(param_names{i}); end % Add parameters p = add_params(p, defaults); % Parse p.parse(args{:}); % Return results of parsing results = p.Results; unmatched = p.Unmatched; end function p = add_params(p, defaults) % Add defaults as parameters names = fieldnames(defaults); for i = 1:numel(names) p.addParameter(names{i}, defaults.(names{i})); end end ================================================ FILE: leap/toolbox/inputParsing/struct2nameval.m ================================================ function C = struct2nameval(S) %STRUCT2NAMEVAL Converts a structure to a cell array of name-value pairs. % Usage: % nameval_cell = struct2nameval(struct) % % See also: nameval2struct % Check input validateattributes(S, {'struct'}, {'nonempty', 'scalar'}) % Convert to cell array names = fieldnames(S); C = cell(1, numel(names) * 2); C(1:2:end) = names; C(2:2:end) = struct2cell(S); end ================================================ FILE: leap/toolbox/io/GetFullPath.m ================================================ function File = GetFullPath(File, Style) % GetFullPath - Get absolute canonical path of a file or folder % Absolute path names are safer than relative paths, when e.g. a GUI or TIMER % callback changes the current directory. Only canonical paths without "." and % ".." can be recognized uniquely. % Long path names (>259 characters) require a magic initial key "\\?\" to be % handled by Windows API functions, e.g. for Matlab's FOPEN, DIR and EXIST. % % FullName = GetFullPath(Name, Style) % INPUT: % Name: String or cell string, absolute or relative name of a file or % folder. The path need not exist. Unicode strings, UNC paths and long % names are supported. % Style: Style of the output as string, optional, default: 'auto'. % 'auto': Add '\\?\' or '\\?\UNC\' for long names on demand. % 'lean': Magic string is not added. % 'fat': Magic string is added for short names also. % The Style is ignored when not running under Windows. % % OUTPUT: % FullName: Absolute canonical path name as string or cell string. % For empty strings the current directory is replied. % '\\?\' or '\\?\UNC' is added on demand. % % NOTE: The M- and the MEX-version create the same results, the faster MEX % function works under Windows only. % Some functions of the Windows-API still do not support long file names. % E.g. the Recycler and the Windows Explorer fail even with the magic '\\?\' % prefix. Some functions of Matlab accept 260 characters (value of MAX_PATH), % some at 259 already. Don't blame me. % The 'fat' style is useful e.g. when Matlab's DIR command is called for a % folder with les than 260 characters, but together with the file name this % limit is exceeded. Then "dir(GetFullPath([folder, '\*.*], 'fat'))" helps. % % EXAMPLES: % cd(tempdir); % Assumed as 'C:\Temp' here % GetFullPath('File.Ext') % 'C:\Temp\File.Ext' % GetFullPath('..\File.Ext') % 'C:\File.Ext' % GetFullPath('..\..\File.Ext') % 'C:\File.Ext' % GetFullPath('.\File.Ext') % 'C:\Temp\File.Ext' % GetFullPath('*.txt') % 'C:\Temp\*.txt' % GetFullPath('..') % 'C:\' % GetFullPath('..\..\..') % 'C:\' % GetFullPath('Folder\') % 'C:\Temp\Folder\' % GetFullPath('D:\A\..\B') % 'D:\B' % GetFullPath('\\Server\Folder\Sub\..\File.ext') % % '\\Server\Folder\File.ext' % GetFullPath({'..', 'new'}) % {'C:\', 'C:\Temp\new'} % GetFullPath('.', 'fat') % '\\?\C:\Temp\File.Ext' % % COMPILE: % Automatic: InstallMex GetFullPath.c uTest_GetFullPath % Manual: mex -O GetFullPath.c % Download: http://www.n-simon.de/mex % Run the unit-test uTest_GetFullPath after compiling. % % Tested: Matlab 6.5, 7.7, 7.8, 7.13, WinXP/32, Win7/64 % Compiler: LCC2.4/3.8, BCC5.5, OWC1.8, MSVC2008/2010 % Assumed Compatibility: higher Matlab versions % Author: Jan Simon, Heidelberg, (C) 2009-2013 matlab.THISYEAR(a)nMINUSsimon.de % % See also: CD, FULLFILE, FILEPARTS. % $JRev: R-G V:032 Sum:7Xd/JS0+yfax Date:15-Jan-2013 01:06:12 $ % $License: BSD (use/copy/change/redistribute on own risk, mention the author) $ % $UnitTest: uTest_GetFullPath $ % $File: Tools\GLFile\GetFullPath.m $ % History: % 001: 20-Apr-2010 22:28, Successor of Rel2AbsPath. % 010: 27-Jul-2008 21:59, Consider leading separator in M-version also. % 011: 24-Jan-2011 12:11, Cell strings, '~File' under linux. % Check of input types in the M-version. % 015: 31-Mar-2011 10:48, BUGFIX: Accept [] as input as in the Mex version. % Thanks to Jiro Doke, who found this bug by running the test function for % the M-version. % 020: 18-Oct-2011 00:57, BUGFIX: Linux version created bad results. % Thanks to Daniel. % 024: 10-Dec-2011 14:00, Care for long names under Windows in M-version. % Improved the unittest function for Linux. Thanks to Paul Sexton. % 025: 09-Aug-2012 14:00, In MEX: Paths starting with "\\" can be non-UNC. % The former version treated "\\?\C:\\file" as UNC path and % replied "\\?\UNC\?\C:\\file". % 032: 12-Jan-2013 21:16, 'auto', 'lean' and 'fat' style. % Initialize: ================================================================== % Do the work: ================================================================= % ############################################# % ### USE THE MUCH FASTER MEX ON WINDOWS!!! ### % ############################################# % Difference between M- and Mex-version: % - Mex does not work under MacOS/Unix. % - Mex calls Windows API function GetFullPath. % - Mex is much faster. % Magix prefix for long Windows names: if nargin < 2 Style = 'auto'; end % Handle cell strings: % NOTE: It is faster to create a function @cell\GetFullPath.m under Linux, but % under Windows this would shadow the fast C-Mex. if isa(File, 'cell') for iC = 1:numel(File) File{iC} = GetFullPath(File{iC}, Style); end return; end % Check this once only: isWIN = strncmpi(computer, 'PC', 2); MAX_PATH = 260; % Warn once per session (disable this under Linux/MacOS): persistent hasDataRead if isempty(hasDataRead) % Test this once only - there is no relation to the existence of DATAREAD! %if isWIN % Show a warning, if the slower Matlab version is used - commented, because % this is not a problem and it might be even useful when the MEX-folder is % not inlcuded in the path yet. % warning('JSimon:GetFullPath:NoMex', ... % ['GetFullPath: Using slow Matlab-version instead of fast Mex.', ... % char(10), 'Compile: InstallMex GetFullPath.c']); %end % DATAREAD is deprecated in 2011b, but still available. In Matlab 6.5, REGEXP % does not know the 'split' command, therefore DATAREAD is preferred: hasDataRead = ~isempty(which('dataread')); end if isempty(File) % Accept empty matrix as input: if ischar(File) || isnumeric(File) File = cd; return; else error(['JSimon:', mfilename, ':BadTypeInput1'], ... ['*** ', mfilename, ': Input must be a string or cell string']); end end if ischar(File) == 0 % Non-empty inputs must be strings error(['JSimon:', mfilename, ':BadTypeInput1'], ... ['*** ', mfilename, ': Input must be a string or cell string']); end if isWIN % Windows: -------------------------------------------------------- FSep = '\'; File = strrep(File, '/', FSep); % Remove the magic key on demand, it is appended finally again: if strncmp(File, '\\?\', 4) if strncmpi(File, '\\?\UNC\', 8) File = ['\', File(7:length(File))]; % Two leading backslashes! else File = File(5:length(File)); end end isUNC = strncmp(File, '\\', 2); FileLen = length(File); if isUNC == 0 % File is not a UNC path % Leading file separator means relative to current drive or base folder: ThePath = cd; if File(1) == FSep if strncmp(ThePath, '\\', 2) % Current directory is a UNC path sepInd = strfind(ThePath, '\'); ThePath = ThePath(1:sepInd(4)); else ThePath = ThePath(1:3); % Drive letter only end end if FileLen < 2 || File(2) ~= ':' % Does not start with drive letter if ThePath(length(ThePath)) ~= FSep if File(1) ~= FSep File = [ThePath, FSep, File]; else % File starts with separator: File = [ThePath, File]; end else % Current path ends with separator: if File(1) ~= FSep File = [ThePath, File]; else % File starts with separator: ThePath(length(ThePath)) = []; File = [ThePath, File]; end end elseif FileLen == 2 && File(2) == ':' % "C:" current directory on C! % "C:" is the current directory on the C-disk, even if the current % directory is on another disk! This was ignored in Matlab 6.5, but % modern versions considers this strange behaviour. if strncmpi(ThePath, File, 2) File = ThePath; else try File = cd(cd(File)); catch % No MException to support Matlab6.5... if exist(File, 'dir') % No idea what could cause an error then! rethrow(lasterror); else % Reply "K:\" for not existing disk: File = [File, FSep]; end end end end end else % Linux, MacOS: --------------------------------------------------- FSep = '/'; File = strrep(File, '\', FSep); if strcmp(File, '~') || strncmp(File, '~/', 2) % Home directory: HomeDir = getenv('HOME'); if ~isempty(HomeDir) File(1) = []; File = [HomeDir, File]; end elseif strncmpi(File, FSep, 1) == 0 % Append relative path to current folder: ThePath = cd; if ThePath(length(ThePath)) == FSep File = [ThePath, File]; else File = [ThePath, FSep, File]; end end end % Care for "\." and "\.." - no efficient algorithm, but the fast Mex is % recommended at all! if ~isempty(strfind(File, [FSep, '.'])) if isWIN if strncmp(File, '\\', 2) % UNC path index = strfind(File, '\'); if length(index) < 4 % UNC path without separator after the folder: return; end Drive = File(1:index(4)); File(1:index(4)) = []; else Drive = File(1:3); File(1:3) = []; end else % Unix, MacOS: isUNC = false; Drive = FSep; File(1) = []; end hasTrailFSep = (File(length(File)) == FSep); if hasTrailFSep File(length(File)) = []; end if hasDataRead if isWIN % Need "\\" as separator: C = dataread('string', File, '%s', 'delimiter', '\\'); %#ok else C = dataread('string', File, '%s', 'delimiter', FSep); %#ok end else % Use the slower REGEXP, when DATAREAD is not available anymore: C = regexp(File, FSep, 'split'); end % Remove '\.\' directly without side effects: C(strcmp(C, '.')) = []; % Remove '\..' with the parent recursively: R = 1:length(C); for dd = reshape(find(strcmp(C, '..')), 1, []) index = find(R == dd); R(index) = []; if index > 1 R(index - 1) = []; end end if isempty(R) File = Drive; if isUNC && ~hasTrailFSep File(length(File)) = []; end elseif isWIN % If you have CStr2String, use the faster: % File = CStr2String(C(R), FSep, hasTrailFSep); File = sprintf('%s\\', C{R}); if hasTrailFSep File = [Drive, File]; else File = [Drive, File(1:length(File) - 1)]; end else % Unix: File = [Drive, sprintf('%s/', C{R})]; if ~hasTrailFSep File(length(File)) = []; end end end % "Very" long names under Windows: if isWIN if ~ischar(Style) error(['JSimon:', mfilename, ':BadTypeInput2'], ... ['*** ', mfilename, ': Input must be a string or cell string']); end if (strncmpi(Style, 'a', 1) && length(File) >= MAX_PATH) || ... strncmpi(Style, 'f', 1) % Do not use [isUNC] here, because this concerns the input, which can % '.\File', while the current directory is an UNC path. if strncmp(File, '\\', 2) % UNC path File = ['\\?\UNC', File(2:end)]; else File = ['\\?\', File]; end end end % return; ================================================ FILE: leap/toolbox/io/dir_ext.m ================================================ function matches = dir_ext(path, extensions, return_paths) %DIR_EXT Returns files in a directory with the matching extension. % Usage: % matches = dir_ext(path, extension) % matches = dir_ext(path, extensions) % cell array of strings % matches = dir_ext(path, _, true) % returns full paths % % Note: The leading '.' can be omitted. % % See also: dir_regex, dir_paths if nargin < 2 extensions = path; path = pwd; end % Process arguments if ischar(extensions); extensions = {extensions}; end if ~iscellstr(extensions) error('Extension must be a string or cell array of strings.') end if nargin < 3; return_paths = false; end % Remove leading '.' extensions = regexprep(extensions, '^\.', ''); % Build regex pattern pattern = ['\.(' strjoin(extensions, '|') ')$']; % Test for the extensions matches = dir_regex(path, pattern, return_paths); end ================================================ FILE: leap/toolbox/io/dir_paths.m ================================================ function [paths, base_path] = dir_paths(path, type) %DIR_PATHS Returns the full paths of a directory listing. % Usage: % paths = dir_paths % lists paths in pwd % paths = dir_paths(path) % paths = dir_paths(path, 'files') % 'files', 'folders' or 'both' (default) % [paths, base_path] = dir_paths(...) % % See also: dir, dir_files, dir_folders % Process arguments if nargin < 1; path = pwd; end if nargin < 2; type = 'both'; end type = validatestring(type, {'folders', 'files', 'both', 'all'}); % Get directory listing listing = dir(path); names = {listing.name}'; % Filter relative names ('.' and '..') rel_names = cellfun(@(x) isequal(x, '.') || isequal(x, '..'), {listing.name}); % Filter files or folders folders = [listing.isdir]; files = ~[listing.isdir]; switch type case 'folders' names = names(folders & ~rel_names); case 'files' names = names(files & ~rel_names); otherwise names = names((folders | files) & ~rel_names); end % Account for wilcard usage or direct filenames if instr('*', path) || isfile(path) base_path = GetFullPath(fileparts(path)); % get parent directory else base_path = GetFullPath(path); end base_path = regexprep(base_path, '[\\/]*$', ''); % remove any trailing slash % Append base path paths = fullfile(base_path, names); end ================================================ FILE: leap/toolbox/io/dir_regex.m ================================================ function matches = dir_regex(path, expression, return_paths) %DIR_REGEX Returns contents in the path matching a regular expression. % Usage: % matches = dir_regex(path, expression) % matches = dir_regex(path, expression, true) % returns full paths % % See also: regexp, dir_ext, dir_paths % Default: returns just filenames if nargin < 3; return_paths = false; end % Get directory listing listing = dir_paths(path); % Test regex matches = listing(~areempty(regexp(listing, expression))); % Singleton output if iscell(matches) && numel(matches) == 1 matches = matches{1}; end % Return just the filenames if ~return_paths matches = get_filename(matches); end end ================================================ FILE: leap/toolbox/io/exists.m ================================================ function TF = exists(path, dir_only) %EXISTS Returns true if the specified path exists in the filesystem. % Usage: % TF = exists(path) % TF = exists(paths) % TF = exists(_, dir_only) % % Args: % path: string path % paths: cell array of paths or scalar structure where fields are paths % dir_only: if true, returns true only if the path is a folder (default: false) % % Notes: % - This function differs from exist() in that it returns true only if % the path exists in the filesystem. % The exist function may return 2 if the path is a file and a function % exists in the MATLAB search path but the file does not exist. % - Existence is checked using the dir() function. % % See also: exist, isfile, isfolder narginchk(1, 2) validateattributes(path, {'char', 'cell', 'struct'}, {'nonempty'}) if nargin < 2 dir_only = false; end % Single path specified if ischar(path) path = {path}; end % Structure of paths specified struct_output = false; if isstruct(path) fields = fieldnames(path); path = struct2cell(path); struct_output = true; end % Convert [] to '' path(areempty(path)) = {''}; % Check input if ~iscellstr(path) error('Expected input to be string, cell array of strings or structure of paths.') end % Check if paths exist TF = cellfun(@(p) ~isempty(dir(p)), path); % Only paths to directories if dir_only TF = TF & isfolder(path); end % Return structure if struct_output TF = cell2struct(num2cell(TF), fields); end end ================================================ FILE: leap/toolbox/io/ext2filter_spec.m ================================================ function filter_spec = ext2filter_spec(exts) %EXT2FILTER_SPEC Generates a filter specification from a list of file extensions. % Usage: % filter_spec = ext2filter_spec(ext) % single extension % filter_spec = ext2filter_spec(exts) % % See also: uigetfile, uigetdir, uibrowse if ischar(exts) exts = {exts}; end if ~iscellstr(exts) error('Expected a string or cell array of strings.') end % Keep only characters afte the period exts = regexprep(exts, '.*\.', ''); % Create filter specification string filter_spec = ['*.' strjoin(exts, ';*.')]; end ================================================ FILE: leap/toolbox/io/extrep.m ================================================ function new_path = extrep(filepath, new_ext) %EXTREP Replace the extension of a file path. % Usage: % newpath = extrep(filepath, old_ext, new_ext) % % Args: % filepath: a path to a file with an extension % new_ext: new file extension % % Returns: % new_path: path with extension replaced % % See also: get_ext, dir_ext if new_ext(1) == '.'; new_ext = new_ext(2:end); end pattern = '(?<=\.)[^\\/.]+$'; new_path = regexprep(filepath, pattern, new_ext); end ================================================ FILE: leap/toolbox/io/funpath.m ================================================ function path = funpath(~) %FUNPATH Returns the path to the calling function. % Usage: % path = funpath() % path = funpath(_) % returns the path to the parent directory % % See also: get_caller_name, which, dbstack % Get the path to the calling function path = get_caller_name('path', true); % Return the path to the function's parent directory if nargin > 0 path = fileparts(path); end end ================================================ FILE: leap/toolbox/io/get_ext.m ================================================ function ext = get_ext(path, no_dot) %GET_EXT Returns the extension in a path. The path need not exist. % Usage: % ext = get_ext(path) % ext = get_ext(paths) % cell array of paths % ext = get_ext(_, no_dot) % if true, returns ext without dot (default: false) % % Notes: % - Extension returned will be the characters after the LAST dot. % - Returns '' if path has no extension. % % See also: get_filename, fileparts % Default if nargin < 2 no_dot = false; end % Regular expression pattern = '\.[^\\/\.]+$'; if no_dot pattern = '(?<=\.)[^\\/.]+$'; end % Get matches ext = regexp(path, pattern, 'match', 'once'); end ================================================ FILE: leap/toolbox/io/get_filename.m ================================================ function filename = get_filename(path, no_ext) %GET_FILENAME Returns the filename in a path. The path need not exist. % Usage: % filename = get_filename(path) % filename = get_filename(paths) % cell array of paths % filename = get_filename(_, true) % no extension % % See also: get_ext, fileparts if nargin < 2 no_ext = false; end if ischar(path) [~, filename, ext] = fileparts(path); if ~no_ext filename = [filename ext]; end elseif iscellstr(path) filename = cellfun(@(p) get_filename(p, no_ext), path, 'UniformOutput', false); end end ================================================ FILE: leap/toolbox/io/get_filesize.m ================================================ function bytes = get_filesize(file_path) %GET_FILESIZE Returns the size of the specified file in bytes. % This is a wrapper for dir(). % % Usage: % bytes = get_filesize(file_path) % % See also: dir % Get file attributes attributes = dir(file_path); bytes = attributes.bytes; if nargout == 0 printf('%s: *%s*', get_filename(file_path), bytes2str(bytes)) clear bytes end end ================================================ FILE: leap/toolbox/io/get_new_filename.m ================================================ function new_filename = get_new_filename(filename, noSpaces) %GET_NEW_FILENAME Returns a new filename based on the one specified. % Usage: % new_filename = get_new_filename(filename) % new_filename = get_new_filename(filename, true) % no spaces if nargin < 2 || isempty(noSpaces); noSpaces = false; end % Break filename into components [pathstr, name, ext] = fileparts(filename); base = fullfile(pathstr, name); % Iterate until we find a non-existing filename num = 1; new_filename = [base ext]; while exists(new_filename) num = num + 1; if noSpaces new_filename = [base '_' num2str(num) ext]; else new_filename = [base ' (' num2str(num) ')' ext]; end end end ================================================ FILE: leap/toolbox/io/lastdir.m ================================================ function dir_path = lastdir(new_path) %LASTDIR Remembers the last directory used for use in UI browsing dialogs. % Usage: % dir_path = lastdir % lastuidir(new_path) % % Args: % new_path: relative or absolute path to a folder or file. % Saves parent folder if path to a file is specified. % % Returns: % dir_path: absolute path to the last used directory (defaults to current % directory) persistent last_dir; cache_file = fullfile(fileparts(funpath()), '.lastdir'); %% Update last directory if nargin > 0 % Get absolute path (pwd if invalid path) new_path = GetFullPath(new_path); % Get folder from path if ~isdir(new_path) new_path = fileparts(new_path); end % Check if it exists if ~exists(new_path) error('Specified path does not exist.') end % Check if we changed anything before updating path_updated = isequal(last_dir, new_path); % Update to new path last_dir = new_path; % Write to cache if path_updated || ~exists(cache_file) try f = fopen(cache_file, 'w'); fprintf(f, '%s', last_dir); fclose(f); catch end end return end %% Get the last directory if isempty(last_dir) || ~ischar(last_dir) || ~exists(last_dir) % Reset to current folder by default last_dir = pwd; % Use last directory from cache file if it exists if exists(cache_file) cache = fileread(cache_file); if exists(cache) last_dir = cache; end end end %% Return last directory if nargout > 0 dir_path = last_dir; end %% Output last directory to console if nargout == 0 fprintf('Last saved directory: %s\n', last_dir) end end ================================================ FILE: leap/toolbox/io/mkdirto.m ================================================ function TF = mkdirto(path) %MKDIRTO Quietly makes all directories to path that do not currently exist. % Usage: % mkdirto(path) % TF = mkdirto(path) % returns true if a folder was created % % See also: mkdir warning('off','MATLAB:MKDIR:DirectoryExists') if ~isempty(get_ext(path)); path = fileparts(path); end TF = mkdir(path); warning('on','MATLAB:MKDIR:DirectoryExists') if nargout < 1; clear TF; end end ================================================ FILE: leap/toolbox/io/uibrowse.m ================================================ function [path, filter_idx] = uibrowse(filter_spec, start_path, dialog_title, type) %UIBROWSE Displays a file or folder selection dialog. % Usage: % path = uibrowse % path = uibrowse(filter_spec) % path = uibrowse(filter_spec, start_path) % path = uibrowse(filter_spec, start_path, dialog_title) % path = uibrowse(filter_spec, start_path, dialog_title, type) % [path, filter_idx] = uibrowse(...) % % Args: % filter_spec: file filter specification (default = '') % start_path: starting path, optionally including filename (default = last % used directory) % dialog_title: dialog window title (default = 'Select file...') % type: 'file': select existing file (default) % 'savefile': choose filename and location for saving % 'dir': select folder % % Returns: % path: absolute path to selection % filter_idx: index of the filter specification chosen % % Notes: % - This function is a wrapper for uigetfile, uiputfile and uigetdir. % See the help for those functions for help on filter_spec format, or % use ext2filter_spec(). % - Last directory is remembered between calls to this function. % - Error is thrown if the user cancels or does not select a file. % % See also: ext2filter_spec, uigetvideo, uigetfile, uigetdir, lastdir % Default filter specification if nargin < 1 || isempty(filter_spec) filter_spec = ''; end % Get last directory if nargin < 2 || isempty(start_path) || ~exists(start_path) start_path = lastdir(); end % Defaults if nargin < 3; dialog_title = []; end if nargin < 4; type = 'file'; end type = validatestring(type, {'file', 'savefile', 'dir'}); % Display browse dialog based on type switch type case 'file' if isempty(dialog_title); dialog_title = 'Select file...'; end [filename, dir_path, filter_idx] = uigetfile(filter_spec, dialog_title, start_path); case 'savefile' if isempty(dialog_title); dialog_title = 'Select save location...'; end [filename, dir_path, filter_idx] = uiputfile(filter_spec, dialog_title, start_path); case 'dir' if isempty(dialog_title); dialog_title = 'Select folder...'; end dir_path = uigetdir(start_path, dialog_title); filename = ''; filter_idx = []; end % Check if user hit Cancel, closed the dialog or didn't select a file if dir_path == 0 error('No file or folder selected.') end % Save last dir lastdir(dir_path); % Return full path to selection path = fullfile(dir_path, filename); end ================================================ FILE: leap/toolbox/ml/ezpca.m ================================================ function pcs = ezpca(X, varargin) %EZPCA PCA -- quick and easy! % Usage: % pcs = ezpca(X) % % Args: % X: N x D data % % Notes: % Project: (X - pcs.mu) * pcs.coeff % Reconstruct: (pcs.score * pcs.coeff') + pcs.mu % % See also: pca [coeff, score, latent, tsquared, explained, mu] = pca(X, varargin{:}); if nargout < 1 figure, figclosekey subplot(1,2,1) imgsc(coeff) % columns = pcs xlabel('PCs'), ylabel('Dimensions') subplot(1,2,2) ax = plotExplainedVar(explained); hold(ax(2),'on') hline(ax(2), 95, 'r-') hline(ax(2), 99.5, 'g-') cum_explained = cumsum(explained); i95 = find(cum_explained >= 95, 1); i995 = find(cum_explained >= 99.5, 1); plot(ax(2), i95, cum_explained(i95), 'r.', 'MarkerSize',15) plot(ax(2), i995, cum_explained(i995), 'g.', 'MarkerSize',15) figsize(1200,400) return end pcs = varstruct(coeff, score, latent, tsquared, explained, mu); end ================================================ FILE: leap/toolbox/strings/bytes2str.m ================================================ function [str, x_bytes, unit] = bytes2str(bytes, precision) %BYTES2STR Returns the number of bytes in a more readable format. % Usage: % bytes2str(bytes) % str = bytes2str(bytes) % str = bytes2str(filename) % str = bytes2str(_, precision) % [str, x_bytes, unit] = bytes2str(...) if ischar(bytes) bytes = get_filesize(bytes); end if nargin < 2 precision = 3; end units = {'bytes', 'KB', 'MB', 'GB', 'TB', 'PB'}; % Find closest units base = floor(log(bytes) / log(1024)); % Convert to new units x_bytes = bytes * (1024 ^ -base); % Convert to string unit = units{base + 1}; str = sprintf('%s %s', num2str(x_bytes, precision), unit); end ================================================ FILE: leap/toolbox/strings/instr.m ================================================ function TF = instr(needle, haystack, flags) %INSTR Returns true if (any) needle is in (any) haystack. % Usage: % TF = instr(needle, haystack) % TF = instr(needle, haystack, flags) % % Args: % needle: string or cell array of strings to look for. % haystack: the string or cell array of strings to look in. % flags: indicates the matching mode: % 's' => True if needle is a substring of haystack (default) % 'r' => True if regexp(needle, haystack) returns a match % 'e' => Looks for exact match (may be case-insensitive) % % You can combine these with the modifier flags: % 'c' => Case-sensitive % 'a' => Evaluate ALL needles (strcmp behavior) % Default: 's' % % Returns: % TF logical indicating if needle is in haystack. % If either the needle or the haystack are cell arrays and the 'a' % flag is not set (default), this function returns a scalar % indicating whether ANY needle was found in ANY haystack. % % See also: strcmp, strfind, validatestr, regexp % Parse input narginchk(2, 3) if nargin < 3; flags = 's'; else flags = lower(flags); end valid_flags = 'rseca'; p = inputParser; p.addRequired('needle', @(x) ischar(x) || iscellstr(x)); p.addRequired('haystack', @(x) ischar(x) || iscellstr(x)); p.addOptional('flags', 's', @(x) ischar(x) && all(arrayfun(@(flagchar) any(valid_flags == flagchar), x))); p.parse(needle, haystack, flags); needle = p.Results.needle; haystack = p.Results.haystack; flags = p.Results.flags; % Matching mode flags regex = any(flags == 'r'); exact = any(flags == 'e'); substr = any(flags == 's') || (~regex && ~exact); % Modifier flags case_sensitive = any(flags == 'c'); return_all = any(flags == 'a'); % Make sure inputs are cell strings if ~iscellstr(needle); needle = {needle}; end if ~iscellstr(haystack); haystack = {haystack}; end % Figure out matching function if substr if case_sensitive % strfind(str, pattern): searches str for occurrences of pattern f = @(pattern, str) any(cellfun(@(s) ~isempty(strfind(s, pattern)), str)); else f = @(pattern, str) any(cellfun(@(s) ~isempty(strfind(lower(s), lower(pattern))), str)); end elseif regex % regexp(str, expressions): tests str with expressions case_option = 'ignorecase'; if case_sensitive; case_option = 'matchcase'; end f = @(str, expressions) any(~cellfun('isempty', regexp(str, expressions, case_option))); elseif exact if case_sensitive f = @(needle, haystacks) any(strcmp(needle, haystacks)); else f = @(needle, haystacks) any(strcmpi(needle, haystacks)); end end % Match TF = false(size(needle)); for i = 1:numel(needle) % Returns true if needle i is in ANY haystack found = f(needle{i}, haystack); % Save result for needle i TF(i) = found; % Return as soon as we find needle if ~return_all && found TF = true; return end end if ~return_all TF = false; end end ================================================ FILE: leap/toolbox/strings/printf.m ================================================ function formatted = printf(str, varargin) %PRINTF Prints formatted output. % Usage: % printf(str, ...) % formatted = printf(str, ...) % % Formatting: % - Surround text with '*' for bold or '_' for underlining % - Set flags by adding '#[flagname]' to the END of the string % Flags: % '#nonl': Will not print with a newline at the end of the string. % '#[color]': Sets the color of the string. % Ex: '#red', '#blue', '#green', '#orange' % % Example: % printf('*bold* _underlined_ #red') if nargin < 1; str = ''; end % Flag defaults color = 'black'; appendNewline = true; % Look for tags [match, tokens] = regexp(str, '(\s?#\w+)+[\\n]*$', 'match', 'tokens'); % match: whole match, include possible newline % tokens: cell with only the capturing group (the tags) if ~isempty(match) flags = tokens{1}{1}; % Remove the tags from the original string str = regexprep(str, [match '$'], strrep(match, flags, '')); % Process flags flags = regexp(flags, '#(\w+)', 'tokens'); for i = 1:numel(flags) flag = flags{i}{1}; switch flag case 'nonl' appendNewline = false; otherwise color = flag; end end end % Add bold tags str = regexprep(str, '\*(.+?)\*', '$1'); % Add underlining (empty link) str = regexprep(str, '\_(.+?)\_', '$1'); % Add color if ~strcmpi(color, 'black') str = ['' str '']; end % Append newline endsWithNewline = ~isempty(regexp(str, '\\n()?$', 'once')); if ~endsWithNewline && appendNewline str = [str '\n']; end if nargout < 1 % Print fprintf(str, varargin{:}) else % Format formatted = sprintf(str, varargin{:}); end end ================================================ FILE: leap/toolbox/strings/secs2hms.m ================================================ function [h, m, s] = secs2hms(numSecs) %SECS2HMS Converts a number of seconds to hours, minutes and fractional seconds. % Usage: % hms = secs2hms(numSecs) % numeric vector % [h, m, s] = secs2hms(numSecs) % % See also: duration, hms, secs2str [h,m,s] = hms(duration([0 0 numSecs])); end ================================================ FILE: leap/toolbox/strings/secsf.m ================================================ function str = secsf(format, numSecs) %SECSF Yet another seconds formatting function. % Usage: % str = secsf(format, numSecs) % % Args: % format: formatted string with any of these tokens: % No leading zero: %h, %m, %s % Leading zero: %H, %M, %S % Fractional seconds: %s.ms or %S.ms % % See also: secs2str, secs2hms, secstr if nargin < 2 [format, numSecs] = swap(format, '%hh %mm %ss'); end [h,m,s] = secs2hms(numSecs); str = format; str = regexprep(str, '%h', num2str(round(h),'%d')); str = regexprep(str, '%H', num2str(round(h),'%02d')); str = regexprep(str, '%m', num2str(round(m),'%d')); str = regexprep(str, '%M', num2str(round(m),'%02d')); str = regexprep(str, '%s\.ms', num2str(s,'%f')); ms = num2str(s-round(s),'%f'); ms = ms(2:end); % '.#####' str = regexprep(str, '%S\.ms', [num2str(s,'%02d') ms]); str = regexprep(str, '%s', num2str(round(s),'%d')); str = regexprep(str, '%S', num2str(round(s),'%02d')); end ================================================ FILE: leap/toolbox/utilities/af.m ================================================ function out = af(func,varargin) %AF Convenience wrapper for arrayfun with non-uniform output. % Usage: % out = af(func, A); % equivalent to out = arrayfun(func, A, 'UniformOutput, false) out = arrayfun(func, varargin{:}, 'UniformOutput', false); end ================================================ FILE: leap/toolbox/utilities/arange.m ================================================ function [min_val, max_val] = arange(X) %ARANGE Returns the range (min and max) of an entire array. % Usage: % R = arange(X) % [min_val, max_val] = arange(X) % % See also: alims, amin, amax min_val = min(X(:)); max_val = max(X(:)); if nargout < 2 min_val = [min_val, max_val]; end end ================================================ FILE: leap/toolbox/utilities/areempty.m ================================================ function empties = areempty(cellarr) %AREEMPTY Returns a logical array of the size of the cell array indicating whether each cell is empty. % Usage: % empties = areempty(cellarr) % % See also: isempty empties = cellfun('isempty', cellarr); end ================================================ FILE: leap/toolbox/utilities/argmin.m ================================================ function idx = argmin(X, dim) %ARGMAX Returns the index at which the min is found. % Usage: % idx = argmin(X) % idx = argmin(X, dim) if isvector(X) [~, idx] = min(X); else if nargin < 2; dim = 1; end [~, idx] = min(X, [], dim); end end ================================================ FILE: leap/toolbox/utilities/arr2cell.m ================================================ function C = arr2cell(X, dim) %ARR2CELL Splits an array into a cell across the specified dimension. % Usage: % C = arr2cell(X) % default: last dimension % C = arr2cell(X, dim) % % See also: num2cell, stack2cell if nargin < 2; dim = ndims(X); end % Select every other dimension dims = setdiff(1:ndims(X), dim); % Convert to cell and squeeze C = squeeze(num2cell(X,dims)); end ================================================ FILE: leap/toolbox/utilities/cell1.m ================================================ function varargout = cell1(sz, dim) %CELL1 Creates a 1d empty cell array. Convenience for cell(sz, 1). % Usage: % C = cell1(sz) % C = cell1(sz, dim) % % Args: % sz: length of the cell array % dim: along which dimension to create cell array (default = 1) % % Returns: % C: empty cell array with sz elements in dimension dim % % See also: cell if nargin < 2 dim = 1; end if ~isscalar(sz); sz = length(sz); end sz2 = ones(1,max(2,dim)); sz2(dim) = sz; C = cell(sz2); varargout = {C}; if nargout > 1 varargout = repmat({C},1,nargout); end end ================================================ FILE: leap/toolbox/utilities/cellcat.m ================================================ function [X,idx] = cellcat(C, dim) %CELLCAT Unpacks and concatenates a cell array. Shorthand for: cat(dim, C{:}) % Usage: % X = cellcat(C) % X = cellcat(C, dim) % [X, idx] = cellcat(_) % % Args: % C: cell array % dim: dimension along which to concatenate (default = 1) % if empty, finds first dimension along which not all sizes are the same % % Returns: % X: array resulting from concatenating elements of C % idx: vector of the same length as X with the corresponding indices in C % % See also: cat if nargin < 2; dim = 1; end if isempty(dim) maxDims = max(cellfun(@ndims, C)); for dim = 1:maxDims sz = cellfun(@(x)size(x,dim), C); if nunique(sz) > 1 break end end end X = cat(dim, C{:}); if nargout > 1 N = cellfun(@(x)size(x,dim),C); idx = repelem(vert(1:numel(C)),N(:)); end end ================================================ FILE: leap/toolbox/utilities/cf.m ================================================ function out = cf(func,varargin) %CF Convenience wrapper for cellfun with non-uniform output. % Usage: % out = cf(func, C); % equivalent to out = cellfun(func, C, 'UniformOutput, false) out = cellfun(func, varargin{:}, 'UniformOutput', false); end ================================================ FILE: leap/toolbox/utilities/clip.m ================================================ function X2 = clip(X, bounds, varargin) %CLIP Clip values in an array to lower and upper bounds. % Usage: % X2 = clip(X, [lo up]) % X2 = clip(X, [lo up], 'perc') % % Args: % X: numeric array % lo: scalar lower bound % up: scalar upper bound % 'perc': use percentiles (0-100) % % See also: rescale, prctile if nargin > 2; bounds = prctile(X(:),bounds); end X2 = min(max(X,bounds(1)),bounds(2)); end ================================================ FILE: leap/toolbox/utilities/functional_programming/wrap.m ================================================ function out = wrap(f, out_indices) % out = wrap(f, out_indices) % % If you've ever needed multiple outputs from a function wrapped up in a % cell array, you may have felt it was a bit awkward to accomplish without % doing something like this: % % [a, b, c] = f(...) % x = {a, b, c}; % % That's not always desirable. % % This function makes it much easier. Just pass in a handle for a function % to execute and numbers indicating which outputs are desired in the cell % array. % % x = wrap(@() f(...), 1:3) % % This is especially useful for accessing multiple outputs from a function % *inside* an anonymous function, when one can't save numerous outputs as % in [a, b, c] = f(...). % % Example: % % info = wrap(@() min([4 3 2 3]), 1:2) % % Tucker McClure % Copyright 2013 The MathWorks, Inc. [outs{1:max(out_indices)}] = f(); out = outs(out_indices); end ================================================ FILE: leap/toolbox/utilities/get_new_string.m ================================================ function new_str = get_new_string(str, strings, format) %GET_NEW_STRING Returns a new string that does not exist in a provided list by incrementing a number. % Usage: % new_str = get_new_string(str, strings) % new_str = get_new_string(str, strings, format) % % Args: % str: starting string % strings: array of strings or cell array of chars % format: string format to use (default: '%s_%d') % % See also: get_new_filename, get_new_varname if nargin < 3 || isempty(format); format = '%s_%d'; end new_str = str; i = 0; while any(strcmp(new_str,strings)) i = i + 1; new_str = sprintf(format, str, i); end end ================================================ FILE: leap/toolbox/utilities/grp2cell.m ================================================ function [C, grps, G] = grp2cell(X, G, dim) %GRP2CELL Splits an array into a cell using a grouping variable. % Usage: % C = grp2cell(X, G) % C = grp2cell(X, G, dim) % [C, grps] = grp2cell(_) % % Args: % X: array with at least one dimension of the same size as G % G: grouping variable vector % dim: dimension along which to slice X (default: []) % if empty, first dimension of the same size as G is used % % Returns: % C: cell array with as many cells as unique elements in G % grps: values in G that correspond to the grouping in C % % Example: % >> C = grp2cell(X,G,dim); % >> isequal(cellcat(C, dim), X) % ans = % logical % 1 % % See also: arr2cell, cellcat, celljoin if nargin < 3; dim = []; end if isscalar(G) % Default to first non-singleton dimension dim = find(size(X) > 1, 1); if isempty(dim); dim = 1; end % or 1 % Create grouping vector G = ceil((1:size(X,dim)) / G); end if isempty(dim) dim = find(size(X) == numel(G),1); end subs = af(@(x) 1:x, size(X)); grps = unique(G); C = cell1(numel(grps),dim); for i = 1:numel(grps) subs_i = subs; subs_i{dim} = find(G == grps(i)); C{i} = X(subs_i{:}); end end ================================================ FILE: leap/toolbox/utilities/horz.m ================================================ function V = horz(X) %HORZ Returns the array as a horizontal vector. % Usage: % V = horz(X) % size(V) = [1, numel(X)] V = X(:)'; end ================================================ FILE: leap/toolbox/utilities/iseven.m ================================================ function TF = iseven(X) %ISEVEN Returns true if the input is divisible by 2, false otherwise. % Usage: % TF = iseven(X) % % See also: mod, rem TF = logical(mod(X, 2) == 0); end ================================================ FILE: leap/toolbox/utilities/loadvar.m ================================================ function varargout = loadvar(mat_file, var_name, varargin) %LOADVAR Loads one or more variables from a MAT file. % Usage: % X = loadvar(mat_file) % returns first variable in MAT file % X = loadvar(mat_file, var_name) % [X1, X2, ..., XN] = loadvar(mat_file, var_name1, var_name2, ..., var_nameN) % C = loadvar(mat_file, var_names) % variable names in cell; cell output % % Notes: % - This is a shortcut for loading variables from MAT files without creating % a temporary variable to contain the MAT file structure. % - The MAT file does not need to have the .mat extension. % % See also: load, who if ~exists(mat_file); error('MAT file does not exist.'); end if nargin < 2 mat_vars = who('-file', mat_file); var_name = mat_vars{1}; end % Check variable names input var_names = [var_name varargin]; if ~iscellstr(var_names) error('Variable names must be specified as strings.') end % Load variables from MAT file X = load(mat_file, '-mat', var_names{:}); % Return output varargout = cf(@(v) X.(v), var_names); if nargout ~= numel(varargout) varargout = {varargout}; end end ================================================ FILE: leap/toolbox/utilities/nunique.m ================================================ function N = nunique(X) %NUNIQUE Returns the number of unique elements in an array. % Usage: % N = nunique(X) % % See also: unique N = numel(unique(X)); end ================================================ FILE: leap/toolbox/utilities/rownorm.m ================================================ function n = rownorm(X, p) %ROWNORM Returns the row-wise norm of a matrix X. % Usage: % n = rownorm(X) % n = rownorm(X, p) % % Args: % X: MxN numeric matrix % p: degree of norm or 'fro' (default: 2) % % Returns: % n: Mx1 vector of norms % % See also: norm, rownorm2 if nargin < 2; p = 2; end n = sum(X.^p,2).^(1/p); end ================================================ FILE: leap/toolbox/utilities/stacks/imtile.m ================================================ function T = imtile(I, varargin) %IMTILE Tiles a stack or set of images. % Usage: % T = imtile(stack) % T = imtile(I1, I2, ...) % T = imtile(stack, cols) % % Args: % stack: 3-d, 4-d or cell array of images % I1, I2, ...: 2-d images % cols: number of columns to use % % Returns: % T: single tiled image % % See also: catpadarr, montage if ~iscell(I) && ismatrix(I) I = {I}; end if ndims(I) == 3 I = arr2cell(I,3); end if ndims(I) == 4 I = stack2cell(I); end cols = []; if nargin > 1 if isscalar(varargin{end}) cols = varargin{end}; varargin(end) = []; end I = [horz(I) varargin]; end N = numel(I); if isempty(cols); cols = ceil(sqrt(N)); end rows = ceil(N / cols); if N < cols*rows I((N+1):(cols*rows)) = {zeros(size(I{1}),'like',I{1})}; end I = reshape(I,cols,rows)'; T = af(@(r)cellcat(I(r,:),2),1:rows); T = cellcat(T,1); if nargout < 1; imgsc(T); clear T; end end ================================================ FILE: leap/toolbox/utilities/stacks/stack2cell.m ================================================ function C = stack2cell(S) %STACK2CELL Returns a cell array with one slice of the stack in each cell. % C = stack2cell(S) % % See also: cat, validate_stack, num2cell, mat2cell S = validate_stack(S); C = squeeze(num2cell(S,[1 2 3])); end ================================================ FILE: leap/toolbox/utilities/stacks/stack2vecs.m ================================================ function X = stack2vecs(S) %STACK2VECS Convert stack to observation-by-features matrix. % Usage: % X = stack2vecs(S) % % Args: % S: 4-d array of size [h, w, c, n] % % Returns: % X: 2-d array of size [n, (h * w * c)] % % See also: vecs2stack if ndims(S) == 3; S = permute(S,[1 2 4 3]); end X = reshape(S,[],size(S,4))'; end ================================================ FILE: leap/toolbox/utilities/stacks/vecs2stack.m ================================================ function S = vecs2stack(X,sz) %VECS2STACK Convert observation-by-features matrix to stack. % Usage: % X = stack2vecs(S) % % Args: % X: 2-d array of size [n, (h * w * c)] % sz: [h w c] or [h w c n] % % Returns: % S: 4-d array of size [h, w, c, n] % % See also: stack2vecs if numel(sz) < 3; sz(3) = 1; end if numel(sz) < 4; sz(4) = size(X,1); end S = reshape(X',sz); end ================================================ FILE: leap/toolbox/utilities/swap.m ================================================ function [B, A] = swap(A, B) %SWAP Swap two variables without an intermediate copy. % Usage: % [B, A] = swap(A, B) end ================================================ FILE: leap/toolbox/utilities/time/GetSystemTimePreciseAsFileTime.m ================================================ %GETSYSTEMTIMEPRECISEASFILETIME Returns system time with high precision. % Usage: % t = GetSystemTimePreciseAsFileTime % % Returns: % t: seconds from 00:00:00 UTC, 1/1/1601 with 0.1 ns tick precision % % Example: % >> a = GetSystemTimePreciseAsFileTime % a = % 1.3113e+10 % >> b = GetSystemTimePreciseAsFileTime % b = % 1.3113e+10 % >> b - a % ans = % 4.0448 % % Reference: % https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx % https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx ================================================ FILE: leap/toolbox/utilities/time/stic.m ================================================ function timer_id = stic %STIC TIC equivalent using precise system time. % Usage: % stic % timer_id = tic % % See also: stoc, systime global stic_timers; timer_id = numel(stic_timers) + 1; stic_timers(timer_id) = systime; if nargout < 1 clear timer_id end end ================================================ FILE: leap/toolbox/utilities/time/stoc.m ================================================ function [dt, timer_id] = stoc(timer_id) %STIC TOC equivalent using precise system time. % Usage: % stoc % stoc(timer_id) % [dt, timer_id] = stoc % % See also: stic, systime global stic_timers; if nargin < 1 timer_id = numel(stic_timers); end if numel(stic_timers) == 0 error('You must call STIC before calling this function.') end if ~isscalar(timer_id) || timer_id ~= round(timer_id) || timer_id > numel(stic_timers) || timer_id < 1 error('Invalid timer ID specified.') end dt = systime - stic_timers(timer_id); % stic_timers(timer_id) = []; if nargout == 0 fprintf('[%d] Elapsed time is %f seconds.\n', timer_id, dt) clear dt end if nargout < 2 clear timer_id end end ================================================ FILE: leap/toolbox/utilities/time/stocf.m ================================================ function [dt, timer_id] = stocf(timer_id, str, varargin) %STOCF Report elapsed time with print formatting. % Usage: % stocf(str, ...) % stocf(timer_id, str, ...) % [dt, timer_id] = stocf(_) % % See also: stic, stoc % Get elapsed time if ischar(timer_id) if nargin > 1; varargin = [{str} varargin]; end % No timer specified, so just use last str = timer_id; [dt, timer_id] = stoc; else dt = stoc(timer_id); end % Figure out where to plug in the elapsed time dt_idx = find(areempty(varargin),1,'last'); % Use default formatting if no empty arrays specified if isempty(dt_idx) dt_idx = numel(varargin) + 1; if str(end) ~= ' '; str(end+1) = ' '; end % pad with space if dt > 5*60 % after 5 mins, use string representation str = [str '[' secsf(dt) ']']; else % Put elapsed time in argument list str = [str '[%.2fs]']; varargin{dt_idx} = dt; end end % Print! printf(str, varargin{:}) % No returns if no output requested if nargout == 0 clear dt timer_id end end ================================================ FILE: leap/toolbox/utilities/time/systime.m ================================================ function secs = systime %SYSTIME Returns precise system time in seconds. % Usage: % secs = systime % % Note: This is just a wrapper for GetSystemTimePreciseAsFileTime % % See also: GetSystemTimePreciseAsFileTime if ispc [~,win_ver] = system('ver'); % Ref: https://en.wikipedia.org/wiki/Ver_(command) if contains(win_ver,'Version 10') secs = GetSystemTimePreciseAsFileTime; return end end % Fallback secs = now * 86400 - 50522817600; % same units end ================================================ FILE: leap/toolbox/utilities/varsize.m ================================================ function size = varsize(X, units) %VARSIZE Returns the size of a variable in bytes. % Usage: % size = varsize(X) % size = varsize(X, units) % 'bytes' (default), 'KB', 'MB', 'GB' % % See also: whos narginchk(1, 2) if nargin < 2 units = 'bytes'; end % Get variable size in bytes S = whos('X'); size = S.bytes; if nargout < 1 % Just output to console if not storing variable disp(bytes2str(size, 4)) clear size return end % Convert to units switch lower(units) case {'kb', 'k', 'kilo', 'kilobyte', 'kib'} size = size / 1024; case {'mb', 'm', 'mega', 'megabyte', 'mib'} size = size / 1024 / 1024; case {'gb', 'g', 'giga', 'gigabyte', 'gib'} size = size / 1024 / 1024 / 1024; case {'auto', 'str'} size = bytes2str(size, 4); otherwise return end end ================================================ FILE: leap/toolbox/utilities/varstruct.m ================================================ function S = varstruct(var1, var2, varargin) %VARSTRUCT Creates a structure out of a set of variables. Fieldnames are inferred from variable names. % Usage: % S = varstruct(var1, var2, ...) % S = varstruct('field1', val1, 'field2', val2, ...) % same as struct() % % Args: % var1, var2, ...: variables used to create the struct % If the variables are defined (see inputname) rather than just % passed by value, their workspace names are used as fieldnames. % 'field1', val1, 'field2', val2, ...: name-value pairs like struct() % % Returns: % S: created structure % % Note: The two forms of input can be used in the same call. % Variables without a name will be named 'varN' according to their % position in the argument list. % % Example: % >> S = varstruct(x, y, 'a', 3, 5, 5) % S = % struct with fields: % % x: [55 double] % y: 2 % a: 3 % var5: 5 % var6: 5 % % See also: cell2struct, struct, inputname % Get input names N = nargin; names = cell(1,N); for i = 1:N names{i} = inputname(i); end % Concatenate all args args_in = {var1}; if N > 1; args_in{end+1} = var2; end args_in = [args_in varargin]; % Let's take care of the defined variables first noName = areempty(names); args = horz([names(~noName); args_in(~noName)]); % Now let's take care of name-val pairs isChar = cellfun(@ischar, args_in); isName = isChar(1:end-1) & noName(1:end-1) & noName(2:end); % names in name-val pairs are also not named isVal = [false isName]; % val cannot be first isName = [isName false]; % name cannot be last if any(isName) name_vals = horz([args_in(isName); args_in(isVal)]); args = [args name_vals]; end % Now let's take care of unnamed args isUnnamed = noName & ~(isName | isVal); if any(isUnnamed) orderedNames = strcat('var', strsplit(num2str(find(isUnnamed)))); unnamed_args = horz([orderedNames; args_in(isUnnamed)]); args = [args unnamed_args]; end % Now just create the structure! % S = struct(args{:}); S = cell2struct(args(2:2:end),args(1:2:end),2); % prevents cell array issues end ================================================ FILE: leap/toolbox/utilities/vert.m ================================================ function V = vert(X) %VERT Returns the matrix as a vertical vector. % Usage: % V = vert(X) % % See also: mat2vec, reshape V = X(:); end ================================================ FILE: leap/toolbox/utilities/vplay.m ================================================ function vp = vplay(mov, varargin) %VPLAY Play a movie using the vplayer class. % Usage: % vplay(mov) % vplay(mov, ...) % name-val params % vp = vplay(_) % % Args: % mov: 4-d stack, cell array of 2-d images or ind-val structure % % See also: vplayer vp = vplayer(mov, varargin{:}); if nargout < 1; clear vp; end end ================================================ FILE: leap/toolbox/video/validate_stack.m ================================================ function [images, numFrames] = validate_stack(images, no_error) %VALIDATE_STACK Validates a stack of images and converts to 4-D array. % Usage: % images = validate_stack(images) % [images, numFrames] = validate_stack(images) % _ = validate_stack(images, true) % doesn't error if invalid stack % % Returns a WxHxCxF array. if nargin < 2; no_error = false; end if isstruct(images) && isfield(images, 'cdata') % getframe struct array images = af(@(x) frame2im(x), images); images = cat(4, images{:}); end if iscell(images) images = cat(4, images{:}); end if ndims(images) == 3 images = permute(images, [1 2 4 3]); end if ndims(images) ~= 4 if ~no_error error('Images must be an MxNxCxF array.') else warning('Images must be an MxNxCxF array.') end end numFrames = size(images,4); end ================================================ FILE: leap/toolbox/video/vplayer.m ================================================ classdef vplayer < matlab.mixin.SetGet %VPLAYER Image video/stack player class. % Usage: % vplayer(S) % % See also: imstacksc properties (SetAccess = immutable) numFrames, numChannels, frameSize end properties S % stack matField % variable name of stack in MatFile getFrameFcn % function that returns frame ndims = 4 fig, ax, img % graphics objects ui = struct() % uix components closeWithPlayer = {} % other graphics object to delete when closing clims, cmap % color scaling title = '' scale = true % scale to colormap frame % current frame idx % current frame index within S channel % current channel doCache = false % whether caching is enabled cached % logical indicator cache % cell array for storage isPlaying = false fps = 25 stride = 1 pb_timer doSave = false % set to true to save frames on draw saved = {} params = struct() callbacks = {} hotkeys = [] %table([],[],[],'VariableNames',{'Key','Modifiers','Callback'}) bookmarks = [] % indices seekbar end events NewFrame end methods function h = vplayer(S, varargin) % Input formats if iscell(S); S = cellcat(S,4); end if ischar(S) if endsWith(S,'.mat') S = matfile(S); elseif endsWith(S,'fg.h5') S = h5file(S,'/fg'); elseif endsWith(S,'processed.h5') S = h5file(S,'/video/data'); elseif endsWith(S,'.h5') && nargin > 1 && varargin{1}(1) == '/' S = h5file(S,varargin{1}); varargin(1) = []; elseif endsWith(S,'.h5') dsets = h5getdatasets(S); if any(strcmp(dsets,'/box')) S = h5file(S,'/box'); end end end % if ischar(S) && strcmpi(get_ext(S),'.mat'); S = matfile(S); end % if ischar(S) && strcmpi(get_ext(S),'.h5') % if strcmpi(get_filename(S),'processed.h5') || endsWith(get_filename(S),'.lz4.h5') % S = h5file(S,'/video/data'); % elseif strcmpi(get_filename(S),'fg.h5') % S = h5file(S,'/fg'); % end % end h.S = S; def_stackName = inputname(1); default_clims = []; switch class(h.S) case 'matlab.io.MatFile' % assumes 4-D numeric stack % Check inputs if nargin < 2 || ~ischar(varargin{1}) error(['Variable name must be specified if playing from MatFile.\\n' ... ' Example: vplayer(matfile(path),''mov'')']); end h.matField = varargin{1}; varargin(1) = []; % Check variable name vars = who(h.S); if ~ismember(h.matField,vars); error('Variable ''%s'' is not in MatFile.', h.matField); end % Check size sz = size(h.S,h.matField); if numel(sz) ~= 4; error('Stack variable ''%s'' must be numeric 4-D array.', h.matField); end % Setup h.numFrames = sz(4); h.getFrameFcn = @getFrame_MatFile; h.doCache = true; def_stackName = h.matField; case {'hdf5prop','hdf5file'} % Ref: http://www.mathworks.com/matlabcentral/fileexchange/31703-hdf5-diskmap-class sz = size(h.S); h.ndims = numel(sz); h.numFrames = sz(end); h.getFrameFcn = @getFrame_stack; h.doCache = false; % hdf5prop does caching already def_stackName = h.S.dataset; case 'struct' fields = fieldnames(h.S); if ~any(ismember({'sz','size'},fields)) || ... ~any(ismember({'ind','idx'},fields)) error('Structure array must have ''idx'' and ''size'' fields (see ind2im).') end h.numFrames = numel(h.S); h.getFrameFcn = @getFrame_struct; h.doCache = true; otherwise if ~isnumeric(h.S) && ~islogical(h.S); error('Invalid stack format specified.'); end if ndims(h.S) == 3; h.S = permute(h.S, [1 2 4 3]); end if ndims(h.S) < 4; error('Numeric stack must be 4-D array.'); end default_clims = alims(h.S); h.numFrames = size(h.S,4); h.getFrameFcn = @getFrame_stack; end % Initialize caching h.cached = false(1,h.numFrames); h.cache = cell(1,h.numFrames); % Initialize saving container h.saved = cell(1,h.numFrames); % Shortcut args default_cmap = []; if numel(varargin) > 0 p = varargin{1}; if ischar(p) && any(strcmpi(p,{'parula','gray','jet','hot'})) default_cmap = p; varargin(1) = []; end end if numel(varargin) > 0 p = varargin{1}; % Parse frame draw callbacks from args if iscell(p) && ~isempty(p) && all(cellfun(@(x)isa(x, 'function_handle'),p)) % callbacks h.callbacks = p; varargin(1) = []; elseif isa(p, 'function_handle') % single callback h.callbacks = {p}; varargin(1) = []; end end % Parameters defaults = struct(); defaults.colormap = default_cmap; defaults.clims = default_clims; defaults.scale = []; % heuristic default below defaults.stackName = def_stackName; defaults.position = []; defaults.initialFrame = 1; defaults.initialChannel = 1; defaults.autoplay = false; defaults.fps = 25; defaults.stride = 1; defaults.save = false; defaults.saveTarget = 'ax'; % 'fig' or 'ax' defaults.clear = true; % clear graphics objects on axes between frames defaults.colorbar = false; defaults.tight = true; defaults.autotile = false; % tiles channels defaults.bookmarks = []; defaults.seekbar = false; defaults.zoom = 1; h.params = parse_params(varargin, defaults); if isempty(h.params.colormap) if isa(h.S,'uint8') || isa(h.S,'hdf5prop') || isa(h.S,'hdf5file') h.params.colormap = 'gray'; else h.params.colormap = 'parula'; end end % Update properties with params h.cmap = h.params.colormap; h.doSave = h.params.save; h.fps = h.params.fps; h.stride = h.params.stride; h.bookmarks = h.params.bookmarks; h.seekbar = h.params.seekbar; % Load first frame h.frame = h.getFrame(h.params.initialFrame); % Image metadata h.numChannels = size(h.frame,3); h.frameSize = size(h.frame(:,:,1)); h.idx = h.params.initialFrame; h.channel = h.params.initialChannel; % Set color limits h.clims = h.params.clims; if isempty(h.clims) if isa(h.frame,'uint8') h.clims = [0 255]; elseif isfloat(h.frame) && (isa(h.S,'hdf5prop') || isa(h.S,'hdf5file')) h.clims = [0 1]; else h.clims = alims(h.frame); end end if ~isnumeric(h.clims); h.clims = double(h.clims); end % Color scaling if isempty(h.params.scale) h.scale = h.numChannels ~= 3; % size(S,3) == 3 h.channel = 1:h.numChannels; else % User provided parameter h.scale = h.params.scale; end % Add hotkeys h.addHotkey('leftarrow', @()h.seek(-1)) h.addHotkey('rightarrow', @()h.seek(+1)) h.addHotkey('leftarrow', @()h.seek(-5),'shift') h.addHotkey('rightarrow', @()h.seek(+5),'shift') h.addHotkey('leftarrow', @()h.seek(-50),'control') h.addHotkey('rightarrow', @()h.seek(+50),'control') h.addHotkey('leftarrow', @()h.seek(-500),{'control','shift'}) h.addHotkey('rightarrow', @()h.seek(+500),{'control','shift'}) h.addHotkey('leftarrow', @()h.prevBookmark,'alt') h.addHotkey('rightarrow', @()h.nextBookmark,'alt') % h.addHotkey('space', @()play(h,true)) h.addHotkey('space', @()h.play()) h.addHotkey('uparrow', @()set(h,'fps',h.fps + 5)) h.addHotkey('downarrow', @()set(h,'fps',h.fps - 5)) h.addHotkey('add', @()set(h,'stride',h.stride + 1)) h.addHotkey('subtract', @()set(h,'stride',h.stride - 1)) h.addHotkey('uparrow', @()set(h,'stride',h.stride + 1),'shift') h.addHotkey('downarrow', @()set(h,'stride',h.stride - 1),'shift') h.addHotkey('add', @()h.zoom(2),'control') h.addHotkey('subtract', @()h.zoom(0.5),'control') h.addHotkey('uparrow', @()h.zoom(2),'control') h.addHotkey('downarrow', @()h.zoom(0.5),'control') h.addHotkey('pageup', @()set(h,'channel',mod(h.channel-1 +1,h.numChannels)+1)) h.addHotkey('pagedown', @()set(h,'channel',mod(h.channel-1 -1,h.numChannels)+1)) h.addHotkey('uparrow', @()set(h,'channel',mod(h.channel-1 +1,h.numChannels)+1),'alt') h.addHotkey('downarrow', @()set(h,'channel',mod(h.channel-1 -1,h.numChannels)+1),'alt') h.addHotkey('home', @()plot(h,1)) h.addHotkey('end', @()plot(h,h.numFrames)) h.addHotkey('a', @()autoAdjust(h,true)) % frame h.addHotkey('a', @()autoAdjust(h,false),'shift') % default h.addHotkey('a', @()stretchLims(h),'control') % stretchlim h.addHotkey('q', @()delete(h.fig)) % Initialize graphics h.show(); % Redraw initial frame to trigger callbacks and etc. h.plot(h.idx); % Initial zoom h.zoom(h.params.zoom); h.plot(h.idx); h.pb_timer = timer(... 'BusyMode','drop',... 'ExecutionMode','fixedRate',... 'Name','vp_playback',... 'Period',1/h.fps,... 'TimerFcn',@(tmr,evt)pb_callback(h,tmr,evt) ... ); end function show(h) if ishghandle(h.fig); delete(h.fig); end % Show figure h.fig = figure('KeyPressFcn', @(~,evt)KeyPressCB(h,evt), ... 'CloseRequestFcn', @(~,~)OnClose(h), ... 'DeleteFcn', @(~,~)OnClose(h), ... 'NumberTitle', 'off', 'Name', h.params.stackName); borderStyle = 'loose'; if h.params.tight; borderStyle = 'tight'; end h.ui.grid = uix.GridFlex('Parent', h.fig, 'Spacing', 1); h.ax = axes('Parent',uicontainer('Parent',h.ui.grid,'BackgroundColor',[0 0 0])); h.ui.img_ax = h.ax; h.img = imshow(h.frame, 'Border',borderStyle, 'Parent',h.ax); h.ui.img_h = h.img; if h.seekbar h.ui.seekbar_ax = axes('Parent',uicontainer('Parent',h.ui.grid)); h.ui.seekbar_img = imagesc(zeros(1,h.numFrames,'uint8'),'Parent',h.ui.seekbar_ax); noticks(h.ui.seekbar_ax); end function constrained_pos = seekbar_constraint(new_pos) constrained_pos = new_pos; constrained_pos(:,1) = min(max(constrained_pos(:,1),0.5),h.numFrames+0.5); constrained_pos(:,2) = [0.5 1.5]; constrained_pos(:,1) = round(mean(constrained_pos(:,1))); end function seekbar_update(new_pos) h.plot(max(1,min(round(new_pos(1)),h.numFrames))); end if h.seekbar h.ui.seekbar_line = imline(h.ui.seekbar_ax, [1 1], [0.5 1.5], ... 'PositionConstraintFcn',@seekbar_constraint); h.ui.seekbar_line.setColor('r'); h.ui.seekbar_line.addNewPositionCallback(@seekbar_update); h.callbacks{end+1} = @(h,idx)h.ui.seekbar_line.setConstrainedPosition([idx 0.5; idx 1.5]); end % screen_size = get(0,'ScreenSize'); % set(h.ui.grid, 'Widths', [-1], 'Heights', [min(screen_size(end),h.frameSize(1)) 20]); if h.seekbar set(h.ui.grid, 'Widths', [-1], 'Heights', [-1 20]); else set(h.ui.grid, 'Widths', [-1], 'Heights', [-1]); end if h.scale caxis(h.ax,h.clims); colormap(h.ax, h.cmap); end if ~isempty(h.params.position); h.fig.Position = h.params.position; end if h.params.colorbar; h.ui.img_colorbar = colorbar(h.ax); end if h.seekbar caxis(h.ui.seekbar_ax,[0 1]); colormap(h.ui.seekbar_ax, 'parula'); h.ui.seekbar_ax.Units = 'normalized'; h.ui.seekbar_ax.Position = [0 0 1 1]; end h.ax.Units = 'normalized'; h.ax.Position = [0 0 1 1]; if h.seekbar seekbar_pos = getpixelposition(h.ui.seekbar_ax.Parent); h.fig.Position(4) = h.fig.Position(4) + seekbar_pos(4); end end function hide(h) if ishghandle(h.fig); delete(h.fig); end end function set.scale(h,val) if isempty(val); return; end h.scale = val; if ~isempty(h.img) && ishghandle(h.img) if h.scale set(h.img,'CDataMapping','scaled'); else h.channel = 1:h.numChannels; set(h.img,'CDataMapping','direct'); end end end function set.clims(h,val) if isempty(val); return; end if ~isnumeric(val); val = double(val); end h.clims = val; if ~isempty(h.ax) && ishghandle(h.ax) && numel(h.clims) == 2 caxis(h.ax,h.clims); end end function autoAdjust(h,frameOnly) if nargin < 2; frameOnly = true; end if frameOnly h.clims = alims(h.frame); else h.clims = h.params.clims; end end function stretchLims(h) % disp(alims(h.frame)) % disp(horz(stretchlim(h.frame))) lims = horz(stretchlim(h.frame)); if isa(h.frame,'uint8'); lims = lims .* 255; end h.clims = lims; end function zoom(h,factor,relativeToFull) if nargin < 2; factor = 1; end if nargin < 3; relativeToFull = false; end pos = h.fig.Position; if relativeToFull new_sz = round(h.frameSize([2 1]) .* factor); else new_sz = round(pos(3:4) .* factor); end delta = pos(3:4) - new_sz; % h.fig.Position = h.fig.Position - [-delta delta]; % h.fig.Position = h.fig.Position - [0 -delta(2) delta]; h.fig.Position = h.fig.Position - [-delta(1)/2 -delta(2) delta]; % h.fig.Position = h.fig.Position - [0 0 delta]; h.fig.Position(1:2) = max(1, h.fig.Position(1:2)); end function frame = getFrame(h, idx) if h.doCache && h.cached(idx) && ~isempty(h.cache{idx}) frame = h.cache{idx}; return end % Get frame frame = h.getFrameFcn(h,idx); % Save to cache if h.doCache h.cached(idx) = true; h.cache{idx} = frame; end end function frame = getFrame_stack(h, idx) if h.ndims == 3 frame = h.S(:,:,idx); else frame = h.S(:,:,:,idx); end end function frame = getFrame_MatFile(h, idx) if h.ndims == 3 frame = h.S.(h.matField)(:,:,idx); else frame = h.S.(h.matField)(:,:,:,idx); end end function frame = getFrame_struct(h, idx) frame = ind2im(h.S(idx)); end function clearCache(h) h.cache = cell(1,h.numFrames); h.cached = false(1,h.numFrames); end function redraw(h) h.plot(h.idx); end function plot(h, idx) % Update frame index h.idx = mod(idx-1,h.numFrames)+1; % Get frame image h.frame = h.getFrame(h.idx); if h.params.autotile, h.frame = imtile(h.frame); else; h.frame = h.frame(:,:,h.channel); end % Update image h.img.CData = h.frame; % Clear old graphics if h.params.clear % isImage = arrayfun(@(x) isa(x,'matlab.graphics.primitive.Image'), h.ax.Children); delete(h.ax.Children(h.ax.Children ~= h.img)) end % Update title h.updateTitle(); % Invoke frame drawing callbacks h.fig.CurrentAxes = h.ax; hold(h.ax,'on'); for j = 1:numel(h.callbacks) cb = h.callbacks{j}; % Pass inputs if nargin(cb) == 1 args = {h.frame}; elseif nargin(cb) == 2 args = {h, h.idx}; else args = {h, h.idx, h.frame}; end % Collect outputs if nargout(cb) < 1 % simple callback cb(args{:}); else % update frame if there's anything returned h.frame = cb(args{:}); h.img.CData = h.frame; end end % Redraw graphics drawnow; % Save frame if h.doSave if ischar(h.params.saveTarget) switch h.params.saveTarget case 'fig' h.params.saveTarget = h.fig; case 'ax' h.params.saveTarget = h.ax; end end h.saved{idx} = frame2im(getframe(h.params.saveTarget)); end % Fire event h.notify('NewFrame'); end % Playback % function play(h,toggle) % if nargin < 2; toggle = true; end % % if toggle % h.isPlaying = ~h.isPlaying; % else % h.isPlaying = true; % end % % while ishghandle(h.fig) && h.isPlaying % % Draw next frame % h.plot(h.idx + h.stride) % % % Wait % % pause(h.stride / h.fps) % pause(1 / h.fps) % end % end function play(h,~) if h.isPlaying h.isPlaying = false; % h.pb_timer.stop(); else h.isPlaying = true; start(h.pb_timer); end end function pb_callback(h,tmr,evt) if h.isPlaying h.seek(h.stride); else stop(tmr); end end function stop(h) h.isPlaying = false; end function playTo(h,stop_idx) % Plays until specified frame and then stops if nargin < 2; stop_idx = h.numFrames; end if h.isPlaying; h.play(); end % stop if already playing for i = h.idx:h.stride:stop_idx h.plot(i); end end function goTo(h,idx) % Wrapper for h.plot h.plot(idx); end function seek(h,delta) new_idx = h.idx + delta; new_idx = mod(new_idx-1,h.numFrames)+1; h.plot(new_idx); end function nextBookmark(h) isAfter = h.bookmarks > h.idx; if any(isAfter) next_idx = h.bookmarks(find(isAfter,1)); h.plot(next_idx); end end function prevBookmark(h) isBefore = h.bookmarks < h.idx; if any(isBefore) prev_idx = h.bookmarks(find(isBefore,1,'last')); h.plot(prev_idx); end end function set.fps(h,val) h.fps = max(val,0); if ishghandle(h.fig) if h.fps == 0; h.stop(); else; h.play(false); end end end function set.stride(h,val) h.stride = max(val,1); if ishghandle(h.fig); h.play(false); end end function set.channel(h,val) h.channel = mod(val-1,h.numChannels) + 1; if ishghandle(h.fig); h.redraw(); end end function updateTitle(h) newTitle = sprintf('%d/%d', h.idx, h.numFrames); if h.numChannels > 1 && ~h.params.autotile && h.scale newTitle = sprintf('%s (C: %d/%d)', newTitle, h.channel, h.numChannels); end if h.doSave newTitle = sprintf('%s | Saved: %d', newTitle, sum(~cellfun(@isempty,h.saved))); end if h.isPlaying newTitle = sprintf('%s | FPS: %d | Stride: %d', ... newTitle, h.fps, h.stride); end h.title = newTitle; if ~isempty(h.params.stackName) h.fig.Name = [h.params.stackName ': ' newTitle]; else h.fig.Name = newTitle; end end function KeyPressCB(h, evt) isKey = strcmpi(h.hotkeys.Key, evt.Key); if isempty(evt.Modifier) isMod = areempty(h.hotkeys.Modifiers); else isMod = cellfun(@(x) isequal(sort(evt.Modifier),sort(x)), h.hotkeys.Modifiers); end if any(isKey & isMod) f = h.hotkeys.Callback{isKey & isMod}; if nargin(f) == 0; f(); else; f(h,h.idx); end else if ~contains(evt.Key,evt.Modifier) && ~contains(evt.Key,'windows') printf('No hotkey bound for: %s', evt.Key) end % printf('No hotkey bound for: %s + %s', evt.Modifier, evt.Key) end end function addHotkey(h, key, fun, modifiers) if nargin < 4; modifiers = {}; end if ~iscell(modifiers); modifiers = {modifiers}; end hotkey = cell2table({key,{modifiers},fun},'VariableNames',{'Key','Modifiers','Callback'}); if isempty(h.hotkeys) h.hotkeys = hotkey; else h.hotkeys = [h.hotkeys; hotkey]; end % h.hotkeys.(key) = fun; end % function rmHotkey(h, key) % h.hotkeys = rmfield(h.hotkeys, key); % end function closeWith(h,h_fig) % Add another figure to close when the player closes h.closeWithPlayer{end+1} = h_fig; end function OnClose(h) % Prevent error from quitting while playing h.isPlaying = false; for i = 1:numel(h.closeWithPlayer) delete(h.closeWithPlayer{i}) end delete(h.fig) delete(h) end end end ================================================ FILE: leap/training.py ================================================ import numpy as np import h5py import os from time import time from scipy.io import loadmat, savemat import re import shutil import clize import keras from keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, LambdaCallback from leap import models from leap.image_augmentation import PairedImageAugmenter, MultiInputOutputPairedImageAugmenter from leap.viz import show_pred, show_confmap_grid, plot_history from leap.utils import load_dataset def train_val_split(X, Y, val_size=0.15, shuffle=True): """ Splits datasets into training and validation sets. """ if val_size < 1: val_size = int(np.round(len(X) * val_size)) idx = np.arange(len(X)) if shuffle: np.random.shuffle(idx) val_idx = idx[:val_size] idx = idx[val_size:] return X[idx], Y[idx], X[val_idx], Y[val_idx], idx, val_idx def create_run_folders(run_name, base_path="models", clean=False): """ Creates subfolders necessary for outputs of training. """ def is_empty_run(run_path): weights_path = os.path.join(run_path, "weights") has_weights_folder = os.path.exists(weights_path) return not has_weights_folder or len(os.listdir(weights_path)) == 0 run_path = os.path.join(base_path, run_name) if not clean: initial_run_path = run_path i = 1 while os.path.exists(run_path): #and not is_empty_run(run_path): run_path = "%s_%02d" % (initial_run_path, i) i += 1 if os.path.exists(run_path): shutil.rmtree(run_path) os.makedirs(run_path) os.makedirs(os.path.join(run_path, "weights")) os.makedirs(os.path.join(run_path, "viz_pred")) os.makedirs(os.path.join(run_path, "viz_confmaps")) print("Created folder:", run_path) return run_path class LossHistory(keras.callbacks.Callback): def __init__(self, run_path): super().__init__() self.run_path = run_path def on_train_begin(self, logs={}): self.history = [] def on_epoch_end(self, batch, logs={}): # Append to log list self.history.append(logs.copy()) # Save history so far to MAT file savemat(os.path.join(self.run_path, "history.mat"), {k: [x[k] for x in self.history] for k in self.history[0].keys()}) # Plot graph plot_history(self.history, save_path=os.path.join(self.run_path, "history.png")) def create_model(net_name, img_size, output_channels, **kwargs): """ Wrapper for initializing a network for training. """ # compile_model = getattr(models, net_name) compile_model = dict( leap_cnn=models.leap_cnn, hourglass=models.hourglass, stacked_hourglass=models.stacked_hourglass, ).get(net_name) if compile_model == None: return None return compile_model(img_size, output_channels, **kwargs) def train(data_path, *, base_output_path="models", run_name=None, data_name=None, net_name="leap_cnn", clean=False, box_dset="box", confmap_dset="confmaps", val_size=0.15, preshuffle=True, filters=64, rotate_angle=15, epochs=50, batch_size=32, batches_per_epoch=50, val_batches_per_epoch=10, viz_idx=0, reduce_lr_factor=0.1, reduce_lr_patience=3, reduce_lr_min_delta=1e-5, reduce_lr_cooldown=0, reduce_lr_min_lr=1e-10, save_every_epoch=False, amsgrad=False, upsampling_layers=False, ): """ Trains the network and saves the intermediate results to an output directory. :param data_path: Path to an HDF5 file with box and confmaps datasets :param base_output_path: Path to folder in which the run data folder will be saved :param run_name: Name of the training run. If not specified, will be formatted according to other parameters. :param data_name: Name of the dataset for use in formatting run_name :param net_name: Name of the network for use in formatting run_name :param clean: If True, deletes the contents of the run output path :param box_dset: Name of the box dataset in the HDF5 data file :param confmap_dset: Name of the confidence maps dataset in the HDF5 data file :param preshuffle: If True, shuffle prior to splitting the dataset, otherwise validation set will be the last frames :param val_size: Fraction of dataset to use as validation :param filters: Number of filters to use as baseline (see create_model) :param rotate_angle: Images will be augmented by rotating by +-rotate_angle :param epochs: Number of epochs to train for :param batch_size: Number of samples per batch :param batches_per_epoch: Number of batches per epoch (validation is evaluated at the end of the epoch) :param val_batches_per_epoch: Number of batches for validation :param viz_idx: Index of the sample image to use for visualization :param reduce_lr_factor: Factor to reduce the learning rate by (see ReduceLROnPlateau) :param reduce_lr_patience: How many epochs to wait before reduction (see ReduceLROnPlateau) :param reduce_lr_min_delta: Minimum change in error required before reducing LR (see ReduceLROnPlateau) :param reduce_lr_cooldown: How many epochs to wait after reduction before LR can be reduced again (see ReduceLROnPlateau) :param reduce_lr_min_lr: Minimum that the LR can be reduced down to (see ReduceLROnPlateau) :param save_every_epoch: Save weights at every epoch. If False, saves only initial, final and best weights. :param amsgrad: Use AMSGrad variant of optimizer. Can help with training accuracy on rare examples (see Reddi et al., 2018) :param upsampling_layers: Use simple bilinear upsampling layers as opposed to learned transposed convolutions """ # Load print("data_path:", data_path) box, confmap = load_dataset(data_path, X_dset=box_dset, Y_dset=confmap_dset) viz_sample = (box[viz_idx], confmap[viz_idx]) box, confmap, val_box, val_confmap, train_idx, val_idx = train_val_split(box, confmap, val_size=val_size, shuffle=preshuffle) print("box.shape:", box.shape) print("val_box.shape:", val_box.shape) # Pull out metadata img_size = box.shape[1:] num_output_channels = confmap.shape[-1] print("img_size:", img_size) print("num_output_channels:", num_output_channels) # Build run name if needed if data_name == None: data_name = os.path.splitext(os.path.basename(data_path))[0] if run_name == None: # Ex: "WangMice-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05" # run_name = "%s-%s_filters=%d_rot=%d_lrfactor=%.1f_lrmindelta=%g" % (data_name, net_name, filters, rotate_angle, reduce_lr_factor, reduce_lr_min_delta) run_name = "%s-%s_epochs=%d" % (data_name, net_name, epochs) print("data_name:", data_name) print("run_name:", run_name) # Create network if isinstance(net_name, keras.models.Model): model = net_name net_name = model.name else: model = create_model(net_name, img_size, num_output_channels, filters=filters, amsgrad=amsgrad, upsampling_layers=upsampling_layers, summary=True) if model == None: print("Could not find model:", net_name) return # Initialize run directories run_path = create_run_folders(run_name, base_path=base_output_path, clean=clean) savemat(os.path.join(run_path, "training_info.mat"), {"data_path": data_path, "val_idx": val_idx, "train_idx": train_idx, "base_output_path": base_output_path, "run_name": run_name, "data_name": data_name, "net_name": net_name, "clean": clean, "box_dset": box_dset, "confmap_dset": confmap_dset, "preshuffle": preshuffle, "val_size": val_size, "filters": filters, "rotate_angle": rotate_angle, "epochs": epochs, "batch_size": batch_size, "batches_per_epoch": batches_per_epoch, "val_batches_per_epoch": val_batches_per_epoch, "viz_idx": viz_idx, "reduce_lr_factor": reduce_lr_factor, "reduce_lr_patience": reduce_lr_patience, "reduce_lr_min_delta": reduce_lr_min_delta, "reduce_lr_cooldown": reduce_lr_cooldown, "reduce_lr_min_lr": reduce_lr_min_lr, "save_every_epoch": save_every_epoch, "amsgrad": amsgrad, "upsampling_layers": upsampling_layers}) # Save initial network model.save(os.path.join(run_path, "initial_model.h5")) # Data generators/augmentation input_layers = model.input_names output_layers = model.output_names if len(input_layers) > 1 or len(output_layers) > 1: train_datagen = MultiInputOutputPairedImageAugmenter(input_layers, output_layers, box, confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle)) val_datagen = MultiInputOutputPairedImageAugmenter(input_layers, output_layers, val_box, val_confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle)) else: train_datagen = PairedImageAugmenter(box, confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle)) val_datagen = PairedImageAugmenter(val_box, val_confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle)) # Initialize training callbacks history_callback = LossHistory(run_path=run_path) reduce_lr_callback = ReduceLROnPlateau(monitor="val_loss", factor=reduce_lr_factor, patience=reduce_lr_patience, verbose=1, mode="auto", epsilon=reduce_lr_min_delta, cooldown=reduce_lr_cooldown, min_lr=reduce_lr_min_lr) if save_every_epoch: checkpointer = ModelCheckpoint(filepath=os.path.join(run_path, "weights/weights.{epoch:03d}-{val_loss:.9f}.h5"), verbose=1, save_best_only=False) else: checkpointer = ModelCheckpoint(filepath=os.path.join(run_path, "best_model.h5"), verbose=1, save_best_only=True) viz_grid_callback = LambdaCallback(on_epoch_end=lambda epoch, logs: show_confmap_grid(model, *viz_sample, plot=True, save_path=os.path.join(run_path, "viz_confmaps/confmaps_%03d.png" % epoch), show_figure=False)) viz_pred_callback = LambdaCallback(on_epoch_end=lambda epoch, logs: show_pred(model, *viz_sample, save_path=os.path.join(run_path, "viz_pred/pred_%03d.png" % epoch), show_figure=False)) # Train! epoch0 = 0 t0_train = time() training = model.fit_generator( train_datagen, initial_epoch=epoch0, epochs=epochs, verbose=1, # use_multiprocessing=True, # workers=8, steps_per_epoch=batches_per_epoch, max_queue_size=512, shuffle=False, validation_data=val_datagen, validation_steps=val_batches_per_epoch, callbacks = [ reduce_lr_callback, checkpointer, history_callback, viz_pred_callback, viz_grid_callback ] ) # Compute total elapsed time for training elapsed_train = time() - t0_train print("Total runtime: %.1f mins" % (elapsed_train / 60)) # Save final model model.history = history_callback.history model.save(os.path.join(run_path, "final_model.h5")) if __name__ == "__main__": # Turn interactive plotting off # plt.ioff() # Wrapper for running from commandline clize.run(train) ================================================ FILE: leap/utils.py ================================================ import os import numpy as np import re from time import time import h5py def versions(list_devices=False): """ Prints system info and version strings for finicky libraries. """ import keras import tensorflow as tf import h5py import platform print("Platform:", platform.platform()) print("h5py:\n" + h5py.version.info) # print("numpy:",np.version.full_version) # h5py already reports this print("Keras:", str(keras.__version__)) print("Tensorflow:", str(tf.__version__)) if list_devices: from tensorflow.python.client import device_lib print("Devices:\n" + str(device_lib.list_local_devices())) def find_weights(model_path): """ Returns paths to saved weights in the run's subfolder. """ weights_folder = os.path.join(model_path, "weights") weights_paths = sorted(os.listdir(weights_folder)) weights_paths = [x for x in weights_paths if "weights" in x] matches = [re.match("weights[.]([0-9]+)-([0-9.]+)[.]h5", x).groups() for x in weights_paths] epochs = np.array([int(x[0]) for x in matches]) val_losses = np.array([np.float(x[1]) for x in matches]) weights_paths = [os.path.join(weights_folder, x) for x in weights_paths] return weights_paths, epochs, val_losses def find_best_weights(model_path): """ Returns the path to the model weights with the lowest validation loss. """ weights_paths, epochs, val_losses = find_weights(model_path) if len(val_losses) > 0: idx = np.argmin(val_losses) return weights_paths[idx] else: return None def load_dataset(data_path, X_dset="box", Y_dset="confmaps", permute=(0,3,2,1)): """ Loads and normalizes datasets. """ # Load t0 = time() with h5py.File(data_path,"r") as f: X = f[X_dset][:] Y = f[Y_dset][:] print("Loaded %d samples [%.1fs]" % (len(X), time() - t0)) # Adjust dimensions t0 = time() X = preprocess(X, permute) Y = preprocess(Y, permute) print("Permuted and normalized data. [%.1fs]" % (time() - t0)) return X, Y def preprocess(X, permute=(0,3,2,1)): """ Normalizes input data. """ # Add singleton dim for single images if X.ndim == 3: X = X[None,...] # Adjust dimensions X = np.transpose(X, permute) # Normalize if X.dtype == "uint8": X = X.astype("float32") / 255 return X ================================================ FILE: leap/viz.py ================================================ import numpy as np import matplotlib.pyplot as plt plt.switch_backend('agg') def show_pred(net, X, Y, joint_idx=0, alpha_pred=0.7, save_path=None, show_figure=False): """ Shows a prediction from the model. net: network to use for prediction idx: index into box/confmap to use or tuple of (box, confmap) with a single sample joint_idx: index of confmap channel to overlay alpha_pred: opacity of confmap overlay """ # Check inputs # if np.isscalar(idx): # X = box[idx] # Y = confmap[idx] # if len(idx) == 2: # X, Y = idx if X.ndim == 2: X = X[None,...,None] if X.ndim == 3: if X.shape[0] == 1: # missing singleton channel X = X[..., None] elif X.shape[-1] == 1 or X.shape[-1] == 3: # missing sample singleton X = X[None,...] if Y.ndim > 3: Y = Y.squeeze(axis=0) # Predict Y2 = net.predict(X) if type(Y2) == list: Y2 = Y2[-1] Y2 = Y2.squeeze(axis=0) X = X.squeeze() # Find peaks pks_pred = [] pks_gt = [] for i in range(Y.shape[-1]): Yi = Y[...,i] peak_coord = np.unravel_index(np.argmax(Yi), Yi.shape) pks_gt.append(peak_coord) Yi = Y2[...,i] peak_coord = np.unravel_index(np.argmax(Yi), Yi.shape) pks_pred.append(peak_coord) # Show box image plt.figure(figsize=(6,6)) plt.imshow(X, cmap="gray") # Normalize channels for i in range(Y2.shape[-1]): Y2[...,i] /= Y2[...,i].max() # Show prediction overlay plt.imshow(Y2[:,:,joint_idx], alpha=alpha_pred) # Plot peak markers for i in range(Y2.shape[-1]): plt.plot(*pks_gt[i][::-1], 'o', markersize=12, mew=3, mec='w', mfc=[0,0,0,0]) plt.plot(*pks_gt[i][::-1], 'o', markersize=12, mew=1, mec='g', mfc=[0,0,0,0]) plt.plot(*pks_pred[i][::-1], 'x', markersize=12, mew=3, mec='w') plt.plot(*pks_pred[i][::-1], 'x', markersize=12, mew=1, mec='r') plt.xticks([]), plt.yticks([]) plt.tight_layout() if save_path is not None: plt.savefig(save_path, bbox_inches='tight', pad_inches=0) if show_figure: plt.show(); else: plt.close() def gallery(array, ncols=4): """ Utility function for tiling a set of images into a grid. """ array = np.transpose(array.squeeze(), (2,0,1)) nindex, height, width = array.shape nrows = int(np.ceil(nindex / ncols)) if nindex != nrows * ncols: delta = (nrows * ncols) - nindex array = np.concatenate((array, np.zeros((delta, height, width), dtype=array.dtype)), axis=0) assert len(array) == nrows * ncols result = (array.reshape(nrows, ncols, height, width) .swapaxes(1,2) .reshape(height*nrows, width*ncols)) return result def show_confmap_grid(net, X, Y, plot=True, save_path=None, show_figure=False): """ Shows predictions from the model using every channel in the confmap. """ if X.ndim == 2: X = X[None,...,None] if X.ndim == 3: if X.shape[0] == 1: # missing singleton channel X = X[..., None] elif X.shape[-1] == 1 or X.shape[-1] == 3: # missing sample singleton X = X[None,...] if Y.ndim > 3: Y = Y.squeeze(axis=0) # Predict Y2 = net.predict(X) if type(Y2) == list: Y2 = Y2[-1] Y2 = Y2.squeeze(axis=0) X = X.squeeze() # Montage all_Y = np.stack((Y,Y2),axis=-1).reshape(Y.shape[:2] + (-1,)) preds = gallery(all_Y, ncols=8) if plot or save_path is not None: # Display plt.figure(figsize=(12,12)) plt.imshow(preds) plt.xticks([]),plt.yticks([]) if save_path is not None: plt.savefig(save_path, bbox_inches='tight', pad_inches=0) if show_figure: plt.show(); else: plt.close() else: return preds def plot_history(history, save_path=None, show_figure=False): """ Plots the training history. """ loss = [x["loss"] for x in history] val_loss = [x["val_loss"] for x in history] plt.figure(figsize=(8,4)) plt.plot(loss) plt.plot(val_loss) plt.semilogy() plt.grid() plt.xlabel("Epochs") plt.ylabel("Loss") plt.legend(["Training", "Validation"]) if save_path is not None: plt.savefig(save_path) if show_figure: plt.show() else: plt.close() ================================================ FILE: readme.md ================================================ # SLEAP: Social LEAP Estimates Animal Poses ![Social LEAP Estimates Animal Poses](https://raw.githubusercontent.com/talmo/leap/master/docs/sleap_movie.gif "Social LEAP Estimates Animal Poses") **Looking to get some SLEAP?** We've released the next version of LEAP, with full support for *multi-animal* pose tracking in addition to all of the functionality of the original LEAP. ***Check it out now at [sleap.ai](https://sleap.ai)!*** ## Related software - [DeepLabCut](https://github.com/AlexEMG/DeepLabCut): The popular deep learning framework for pose estimation. Soon to support full multi-animal pose tracking! - [DeepPoseKit](https://github.com/jgraving/deepposekit): Supports multi-animal pose tracking via top-down centroid detection (e.g., from Ctrax, idTracker, Tractor, etc.). - [Animal Part Tracker](https://github.com/kristinbranson/APT): Supports multi-animal pose tracking and lots of other functionality! # LEAP (deprecated) ![LEAP Estimates Animal Pose](https://raw.githubusercontent.com/talmo/leap/master/docs/supp_mov1-long_clip.gif "LEAP Estimates Animal Pose") _Full movie: [YouTube](https://youtu.be/ZmLQNbCbstk)_ This repository contains code for **LEAP** (_**L**EAP **E**stimates **A**nimal **P**ose_), a framework for animal body part position estimation via deep learning. **Preprint:** [Pereira et al., bioRxiv (2018)](https://doi.org/10.1101/331181) We are still working on documentation and preparing parts of the code. See the Features section below for an overview and status of each component. We recommend starting with the [Tutorial: Training Leap From Scratch](https://github.com/talmo/leap/wiki/Tutorial:-Training-LEAP-from-scratch). ## Features - [ ] Tracking and alignment code - [x] Cluster sampling GUI - [x] Skeleton creation GUI (`create_skeleton`) - [x] GUI for labeling new dataset (`label_joints`) - [x] Network training through the labeling GUI - [x] MATLAB (`predict_box.m`) and Python (`leap.predict_box`) interfaces for predicting on new data - [ ] GUI for predicting on new data - [x] Training data + labels for main fly dataset used in analyses - [x] Trained network for predicting on main fly dataset - [ ] Analysis/figure generation code - [ ] Documentation - [x] Examples of usage ## Installation ### Pre-requisites All neural network and GPU functionality is implemented in Python. The library was designed to be easy to use by providing commandline interfaces, but it can also be used programatically if the MATLAB GUIs are not required. For the Python environment, we recommend [Anaconda 5.1.0](https://www.anaconda.com/download/) with Python 3.6.4. Note that **Python 2.x is not supported**. For GPU support, you'll want to first install the CUDA drivers with CuDNN and then install these packages: ```bash pip install -Iv tensorflow-gpu==1.6.0 pip install -Iv keras==2.1.4 ``` See the [TensorFlow installation guide](https://www.tensorflow.org/install/) for more info. If you don't have a GPU that supports CUDA, install the regular TensorFlow distribution: ```bash pip install tensorflow ``` Please note that CPU execution will be MUCH slower (10-20x) than on a GPU. Consider investing in a GPU for your machine if you're thinking of using LEAP routinely. ### Automated installation To get started with using LEAP, open up MATLAB and download the repository: ```matlab >> !git clone https://github.com/talmo/leap.git ``` Then, install the package and add to the MATLAB path: ```matlab >> cd leap >> install_leap ``` That's it! To avoid having to run this function every time you start MATLAB, save the [MATLAB Search Path](https://www.mathworks.com/help/matlab/matlab_env/what-is-the-matlab-search-path.html) after running it (or just add the `leap` subfolder to your permanent path). ### Manual: MATLAB dependencies GUIs and analyses are implemented in MATLAB, but is not required for using the neural network functionality implemented in Python. We use **MATLAB R2018a** with the following toolboxes: Parallel Computing Toolbox, Statistics and Machine Learning Toolbox, Computer Vision Toolbox, Image Processing Toolbox, Signal Processing Toolbox. All MATLAB external toolboxes are included in the `leap/toolbox` subfolder. Just add the `leap` subdirectory to the [MATLAB Search Path](https://www.mathworks.com/help/matlab/matlab_env/what-is-the-matlab-search-path.html) to access all functionality: ```matlab addpath(genpath('leap')) ``` ### Manual: Python dependencies The versions below were used during development of LEAP but other versions will also likely work. Libraries required are easily installable via the pip package manager: ```bash pip install -Iv numpy==1.14.1 pip install -Iv h5py==2.7.1 pip install -Iv clize==4.0.3 ``` You will also need OpenCV 3 with Python bindings. We recommend using [skvark's excellent precompiled packages](https://github.com/skvark/opencv-python): ```bash pip install -Iv opencv-python==3.4.0.12 ``` You can install this library as a python package by downloading this git repository: ```bash git clone https://github.com/talmo/leap.git ``` then typing: ```bash pip install -e leap # installs the leap directory using pip ``` If you are using [anaconda](https://conda.io/docs/user-guide/getting-started.html) to manage different python environments, it is highly recommended that you use this matlab script to manage your python envs within matlab: [condalab](https://github.com/wingillis/condalab) (see their readme for how to use it) ## Usage Refer to the [Tutorial: Training Leap From Scratch](https://github.com/talmo/leap/wiki/Tutorial:-Training-LEAP-from-scratch). ### Preprocessing ### GUI Workflow 1. **Cluster sampling**: Call `cluster_sample` from MATLAB commandline to launch GUI. 2. **Create skeleton**: Call `create_skeleton` from MATLAB commandline to launch GUI. 3. **Label data and train**: Call `label_joints` from MATLAB commandline to launch GUI. 4. **Batch estimation**: _Coming soon._ ### Programmatic API See `leap/training.py` and `leap/predict_box.py` for more info. ## Contact and more information Reach out to us via email: Talmo Pereira (`talmo@princeton.edu`) ================================================ FILE: setup.py ================================================ from setuptools import setup setup( name="leap", version="0.0.1", author="Talmo Pereira", author_email="talmo@princeton.edu", install_requires=[ "numpy>=1.14.1", "h5py>=2.7.1", "matplotlib", "PyQt5", "opencv-python>=3.4.0.12", "clize>=4.0.3" ] ) ================================================ FILE: uninstall_leap.m ================================================ function uninstall_leap() %UNINSTALL_LEAP Removes LEAP code from the MATLAB path and Python environment. % Usage: % uninstall_leap % % See also: install_leap, test_leap % Find base repository path (where this file is contained) basePath = fileparts(which('uninstall_leap')); % Check if Python package is importable status = system('pip uninstall -y leap'); works = test_leap(); if ~works disp('LEAP uninstalled successfully.') end % Remove from MATLAB path rmpath(genpath(fullfile(basePath,'leap'))) end