=(stackLevelTwo)
+ callingFxnParent = ST(stackLevelTwo).name;
+ callingFxnParentPath = which(ST(stackLevelTwo).file);
+ callingFxnParentLine = num2str(ST(stackLevelTwo).line);
+ callingFxnParentStr = [callingFxnParentStr ' | ' callingFxnParent ' line ' callingFxnParentLine];
+ else
+ callingFxnParentStr = '';
+ end
+ stackLevelTwo = stackLevelTwo+1;
+ end
+ end
+
+ % Display different information based on what type of warning occurred.
+ switch displayType
+ case 'struct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not a valid option for ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'notstruct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not originally a STRUCT, ignoring. ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'name-value incorrect'
+ warning(['WARNING: enter the parameter name before its associated value in ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'name-value'
+ warning(['WARNING: ' val ' is not a valid option for ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ otherwise
+ % do nothing
+ end
+ catch err
+ localShowErrorReport(err);
+ subfxnShowWarningsError(stackLevel,displayType,toStructName,fromField,val,nParentStacks);
+ end
+end
+function subfxnShowWarningsError(stackLevel,displayType,toStructName,fromField,val,nParentStacks)
+ callingFxn = 'UNKNOWN FUNCTION';
+ % Display different information based on what type of warning occurred.
+ switch displayType
+ case 'struct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not a valid option for "' callingFxn '"'])
+ case 'notstruct'
+ warning('Unknown error.')
+ case 'name-value incorrect'
+ warning(['WARNING: enter the parameter name before its associated value in "' callingFxn '"'])
+ case 'name-value'
+ warning(['WARNING: ' val ' is not a valid option for "' callingFxn '"'])
+ otherwise
+ % do nothing
+ end
+end
\ No newline at end of file
diff --git a/+ciapkg/+io/readFrame.m b/+ciapkg/+io/readFrame.m
index 4f61d58..854e5c3 100644
--- a/+ciapkg/+io/readFrame.m
+++ b/+ciapkg/+io/readFrame.m
@@ -18,6 +18,7 @@
% 2021.07.03 [09:02:14] - Updated to have backup read method for different tiff styles.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2022.02.24 [10:24:28] - AVI now read(...,'native') is faster.
+ % 2022.07.27 [13:48:43] - Ensure subfxn_loadFrame nested function outputs frame data (even if file does not contain the requested frame, e.g. empty matrix).
% TODO
%
@@ -88,6 +89,7 @@
end
function [thisFrame, inputMovieDims] = subfxn_loadFrame(inputMoviePathHere,options)
inputMovieDims = options.inputMovieDims;
+ thisFrame = [];
switch movieType
case 'hdf5'
% Much faster to input the existing movie dimensions.
@@ -97,7 +99,7 @@
else
inputMovieDims = options.inputMovieDims;
end
-
+ % inputMovieDims
thisFrame = h5read(inputMoviePathHere,options.inputDatasetName,[1 1 frameNo],[inputMovieDims(1) inputMovieDims(2) 1]);
case 'tiff'
warning off;
diff --git a/+ciapkg/+io/saveMatrixToFile.m b/+ciapkg/+io/saveMatrixToFile.m
index 50cdf71..55b87ca 100644
--- a/+ciapkg/+io/saveMatrixToFile.m
+++ b/+ciapkg/+io/saveMatrixToFile.m
@@ -1,16 +1,21 @@
function [success] = saveMatrixToFile(inputMatrix,savePath,varargin)
- % Save 3D matrix to arbitrary file type (HDF5, TIF, AVI for now).
+ % [success] = saveMatrixToFile(inputMatrix,savePath,varargin)
+ %
+ % Save 3D matrix to user-specified file type. Currently supports HDF5, TIF, NWB, and AVI.
+ %
% Biafra Ahanonu
% started: 2016.01.12 [11:09:53]
- % inputs
- % inputMatrix - [x y frame] movie matrix
- % savePath - character string of path to file with extension included,
- % outputs
- %
+ %
+ % Inputs
+ % inputMatrix - 3D matrix: [x y t] movie matrix, where t = frames normally.
+ % savePath - Str: character string of path to save file with extension included.
+ % Outputs
+ % success - Binary: 1 = saved successfully, 0 = error during save.
% changelog
% 2021.04.24 [16:00:01] - updated TIFF saving to add support for export of multi-channel color timeseries TIFF stack if in format [x y C t] where x,y = width/height, C = RGB channels, t = frames
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.03.06 [12:27:29] - Use Fast_Tiff_Write to write out TIF files instead of saveastiff by default. Give option with options.tifWriter.
% TODO
% Add checking of data size so tiff can be automatically switched
@@ -37,6 +42,8 @@
options.deflateLevel = 1;
% Str: description of imaging plane
options.descriptionImagingPlane = 'NA';
+ % Str: 'saveastiff' (normally slower) or 'Fast_Tiff_Write'.
+ options.tifWriter = 'Fast_Tiff_Write';
% get options
options = getOptions(options,varargin);
% display(options)
@@ -83,14 +90,31 @@
end
close(writerObj);
case 'tiff'
- tiffOptions.comp = 'no';
+ % tiffOptions.comp = 'no';
+ tiffOptions.compress = 'lzw';
tiffOptions.overwrite = true;
if length(size(inputMatrix))==4
tiffOptions.color = true;
disp('Saving TIFF as color timeseries stack.');
end
fprintf('Saving to: %s\n',savePath);
- saveastiff(inputMatrix, savePath, tiffOptions);
+
+ switch options.tifWriter
+ case 'saveastiff'
+ saveastiff(inputMatrix, savePath, tiffOptions);
+ case 'Fast_Tiff_Write'
+ compression = 0;
+ tic;
+ fTIF = Fast_Tiff_Write(savePath,1,compression);
+ for i=1:size(inputMatrix,3)
+ fTIF.WriteIMG(squeeze(inputMatrix(:,:,i))');
+ end
+ fTIF.close;
+ otherwise
+ saveastiff(inputMatrix, savePath, tiffOptions);
+ end
+
+
case 'hdf5'
fprintf('Saving to: %s\n',savePath);
[output] = writeHDF5Data(inputMatrix,savePath,'datasetname',options.inputDatasetName,'addInfo',options.addInfo,'addInfoName',options.addInfoName,'writeMode',options.writeMode,'deflateLevel',options.deflateLevel);
diff --git a/+ciapkg/+io/writeDataToMatfile.m b/+ciapkg/+io/writeDataToMatfile.m
new file mode 100644
index 0000000..9ecddc9
--- /dev/null
+++ b/+ciapkg/+io/writeDataToMatfile.m
@@ -0,0 +1,121 @@
+function [success] = writeDataToMatfile(inputDataPath,varName,outputMatfilePath,varargin)
+ % [success] = writeDataToMatfile(inputDataPath,varName,outputMatfilePath,varargin)
+ %
+ % Converts 3D movie matrix stored in a file (HDF5, TIF, AVI, NWB) into a MAT-file for use with larger-than-memory processing.
+ %
+ % Biafra Ahanonu
+ % started: 2022.03.14 [00:44:08]
+ %
+ % inputs
+ % inputMatfilePath - Str: path to matfile object containing data.
+ % varName - Str: name of variable in MAT-file to save. Should be matrix of [x y frames].
+ % inputFilename - Str: path where HDF5 file should be saved.
+ %
+ % outputs
+ % success - Binary: 1 = data saved correctly, 0 = data not saved correctly.
+ %
+
+ % changelog
+ %
+ % TODO
+ %
+
+ % ========================
+ % Str: name of HDF5 dataset to save data into.
+ options.inputDatasetname = '/1';
+ % Int: Number of frames to use for chunking the data to save in parts.
+ options.chunkSize = 200;
+ % Int: Defines gzip compression level (0-9). 0 = no compression, 9 = most compression.
+ options.deflateLevel = 9;
+ % Int: chunk size in [x y z] of the dataset, leave empty for auto chunking
+ options.dataDimsChunkCopy = [];
+ % Str: class to force data to be, e.g. 'single', 'double', 'uint16', etc.
+ options.saveSpecificImgClass = '';
+ % get options
+ options = ciapkg.io.getOptions(options,varargin);
+ % disp(options)
+ % unpack options into current workspace
+ % fn=fieldnames(options);
+ % for i=1:length(fn)
+ % eval([fn{i} '=options.' fn{i} ';']);
+ % end
+ % ========================
+
+ try
+ success = 0;
+
+ % Get size of the input data, call this way instead of size(inputObj.varName) to avoid loading entire dataset into memory.
+ % dataDim = size(inputObj,varName);
+ dataDimStruct = ciapkg.io.getMovieInfo(inputDataPath);
+ dataDim = [dataDimStruct.one dataDimStruct.two dataDimStruct.three];
+
+ if length(dataDim)~=3
+ disp('Data is not a 3D matrix of size [:, :, >1], returning...')
+ return;
+ end
+
+ % Use saving of individual fields of a structure to dynamically store user variable name.
+ saveStruct = struct;
+ % Save a temporary empty vector variable to MAT-file. Use -v7.3 to allow partial loading, saves memory.
+ if isempty(options.saveSpecificImgClass)
+ saveStruct.(varName) = zeros([dataDim(1) dataDim(2) 2],'single');
+ else
+ saveStruct.(varName) = zeros([dataDim(1) dataDim(2) 2],options.saveSpecificImgClass);
+ end
+ disp(['Creating MAT-file: ' outputMatfilePath])
+ save(outputMatfilePath,'-struct','saveStruct','-v7.3');
+ disp('Done creating MAT-file')
+
+ % Create matfile object to saved MAT-file.
+ disp('Creating matfile object.')
+ inputObj = matfile(outputMatfilePath,'Writable',true);
+
+ % Get coordinates to use.
+ nFrames = dataDim(3);
+ subsetSize = options.chunkSize;
+ movieLength = nFrames;
+ numSubsets = ceil(movieLength/subsetSize)+1;
+ subsetList = round(linspace(1,movieLength,numSubsets));
+ nSubsets = (length(subsetList)-1);
+
+ disp('Writing file data to matfile in chunks:')
+ % Write data out in chunks.
+ for thisSet = 1:nSubsets
+ % subsetStartTime = tic;
+
+ subsetStartIdx = subsetList(thisSet);
+ subsetEndIdx = subsetList(thisSet+1);
+ disp(repmat('$',1,7))
+ if thisSet==nSubsets
+ movieSubset = subsetStartIdx:subsetEndIdx;
+ else
+ movieSubset = subsetStartIdx:(subsetEndIdx-1);
+ end
+ disp([num2str(movieSubset(1)) '-' num2str(movieSubset(end)) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+
+ % Get slice of the data
+ inputDataSlice = ciapkg.io.loadMovieList(inputDataPath,'frameList',movieSubset,'inputDatasetName',options.inputDatasetname,'largeMovieLoad',1);
+
+ if ~isempty(options.saveSpecificImgClass)
+ inputDataSlice = cast(inputDataSlice,options.saveSpecificImgClass);
+ end
+
+ % Slice into the desired variable using dynamic field references to avoid loading entire dataset into memory.
+ inputObj.(varName)(:,:,movieSubset) = inputDataSlice;
+
+ % toc(subsetStartTime)
+ end
+
+ success = 1;
+
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ end
+
+ function [outputs] = nestedfxn_exampleFxn(arg)
+ % Always start nested functions with "nestedfxn_" prefix.
+ % outputs = ;
+ end
+end
\ No newline at end of file
diff --git a/+ciapkg/+motion_correction/computeManualMotionCorrection.m b/+ciapkg/+motion_correction/computeManualMotionCorrection.m
index ae1a209..022b599 100644
--- a/+ciapkg/+motion_correction/computeManualMotionCorrection.m
+++ b/+ciapkg/+motion_correction/computeManualMotionCorrection.m
@@ -28,6 +28,10 @@
% 2021.11.17 [02:30:23] - Improve speed of GUI and responsiveness.
% 2021.12.19 [11:00:15] - Force altInputImages to initialize as input class type to avoid automatic conversion to double (save space/ram).
% 2022.01.29 [19:13:55] - Significant speed improvements by removing certain calls to figure, axis, imagesc, etc. that slow down UI over time when many sessions aligned.
+ % 2022.03.09 [16:34:47] - Check if inputs are sparse, convert to single.
+ % 2022.03.16 [08:54:11] - By default use 'painters' renderer as that can produce smoother rendering than opengl.
+ % 2022.04.25 [11:16:24] - Update to make sure titles update correctly for test images.
+ % 2022.09.14 [08:55:37] - Add renderer option to allow users to choose painters or opengl.
% TODO
% Add ability to auto-crop if inputs are not of the right size them convert back to correct size after manual correction
% inputRegisterImage - [x y nCells] - Image to register to.
@@ -61,6 +65,8 @@
options.translationAmt = 1;
% Binary: 1 = include images output struct, 0 = do not include.
options.includeImgsOutputStruct = 1;
+ % Str: 'painters' or 'opengl'
+ options.renderer = 'opengl';
% get options
options = getOptions(options,varargin);
% display(options)
@@ -99,6 +105,14 @@
switchInputImagesBack = 0;
end
+ % ========================
+ % Check for sparse inputs
+ if issparse(inputImages)
+ disp('Converting from sparse to single.')
+ inputImages = single(full(inputImages));
+ end
+ % ========================
+
% Get register frame
inputRegisterImage = inputImages(:,:,options.refFrame);
@@ -266,7 +280,11 @@
end
inputRegisterImageOutlinesOriginal = inputRegisterImageOutlines;
- [figHandle, figNo] = openFigure(options.translationFigNo, '');
+ [figHandle, ~] = openFigure(options.translationFigNo, '');
+
+ % Set the default renderer
+ set(figHandle,'Renderer',options.renderer);
+
colormap(ciapkg.api.customColormap());
% Force current character to be a new figure.
set(gcf,'currentch','0');
@@ -312,7 +330,7 @@
disp('Setup #3');
disp(toc);tic
- imgTitleFxn = @(imgNo,imgN,gammaCorrection,gammaCorrectionRef,translationVector,rotationVector,translationAmt) sprintf('Image %d/%d\nup/down/left/right arrows for translation | A/S = rotate +1 left/right, Q/W = rotate +90 left/right | 5/6 = flip x/y | f to finish\n1/2 keys for image gamma down/up | gamma = %0.3f | gamma(ref) = %0.3f | translation %d %d | rotation %d\npurple = reference image, green = image to manually translate | %d px/click',imgNo,imgN,gammaCorrection,gammaCorrectionRef,translationVector,rotationVector,translationAmt);
+ imgTitleFxn = @(imgNo,imgN,gammaCorrection,gammaCorrectionRef,translationVector,rotationVector,translationAmt) sprintf('Image %d/%d\nup/down/left/right arrows for translation\nA/S = rotate +1 left/right, Q/W = rotate +90 left/right | 5/6 = flip x/y | f to finish\n1/2 keys for image gamma down/up | gamma = %0.3f | gamma(ref) = %0.3f | translation %d %d | rotation %d\npurple = reference image, green = image to manually translate | %d px/click',imgNo,imgN,gammaCorrection,gammaCorrectionRef,translationVector,rotationVector,translationAmt);
imgTitle = imgTitleFxn(imgNo,imgN,gammaCorrection,gammaCorrectionRef,translationVector,rotationVector,options.translationAmt);
@@ -347,7 +365,7 @@
axis image;
% axis equal tight
box off;
- title('Reference')
+ title(['Reference (#' num2str(options.refFrame) ')'])
hold on;
horzLineMid(rgbImage,'k-');
vertLineMid(rgbImage,'k-');
@@ -364,14 +382,17 @@
axis image;
% axis equal tight
box off;
- title('Test')
+ % title('Test')
+ imgHandle3_title = title(['Test (#' num2str(imgNo) ')']);
hold on;
horzLineMid(rgbImage,'k-');
vertLineMid(rgbImage,'k-');
imgStruct.imgHandle3 = imgHandle3;
+ imgStruct.imgHandle3_title = imgHandle3_title;
else
imgHandle3 = imgStruct.imgHandle3;
+ set(imgStruct.imgHandle3_title,'String',['Test (#' num2str(imgNo) ')']);
end
userClickTimer = tic;
diff --git a/+ciapkg/+motion_correction/getNoRMCorreParams.m b/+ciapkg/+motion_correction/getNoRMCorreParams.m
new file mode 100644
index 0000000..d3792f2
--- /dev/null
+++ b/+ciapkg/+motion_correction/getNoRMCorreParams.m
@@ -0,0 +1,255 @@
+function [optsNC] = getNoRMCorreParams(movieDims,varargin)
+ % [optsNC] = getNoRMCorreParams(varargin)
+ %
+ % Automatically returns default NoRMCorre parameters or ask user to input parameters.
+ %
+ % Biafra Ahanonu
+ % started: 2022.07.18 [10:30:01]
+ %
+ % Inputs
+ % No inputs by default, all Name-Value pairs.
+ %
+ % Outputs
+ % optsNC - structure consisting of NormCorre options.
+ %
+ % Options (input as Name-Value with Name = options.(Name))
+ % % DESCRIPTION
+ % options.exampleOption = '';
+
+ % Changelog
+ %
+ % TODO
+ %
+
+ % ========================
+ % Binary: 1 = use GUI display for user to customize parameters.
+ options.guiDisplay = 0;
+ % Struct: Options from prior run of getNoRMCorreParams().
+ options.priorOpts = struct;
+ % get options
+ options = ciapkg.io.getOptions(options,varargin);
+ % disp(options)
+ % unpack options into current workspace
+ % fn=fieldnames(options);
+ % for i=1:length(fn)
+ % eval([fn{i} '=options.' fn{i} ';']);
+ % end
+ % ========================
+
+ optsNC = struct;
+
+ try
+ % [d1,d2,T] = size(inputMovieHere);
+ d1 = movieDims(1);
+ d2 = movieDims(2);
+ T = movieDims(3);
+ bound = 0;
+
+ % Update to CIAtah default settings
+ if isempty(fieldnames(options.priorOpts))
+ optsNC2.d1 = d1-bound;
+ optsNC2.d2 = d2-bound;
+ optsNC2.init_batch = 10;
+ optsNC2.bin_width = 50;
+ optsNC2.grid_size = [128,128];
+ optsNC2.mot_uf = 4;
+ optsNC2.correct_bidir = false;
+ optsNC2.overlap_pre = 32;
+ optsNC2.overlap_post = 32;
+ optsNC2.max_dev = 50;
+ optsNC2.use_parallel = true;
+ optsNC2.print_msg = true;
+ optsNC2.us_fac = 4;
+ optsNC2.max_shift = 100;
+ optsNC2.boundary = 'NaN';
+ else
+ optsNC2 = options.priorOpts;
+ end
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ end
+
+ try
+ optsNC = normcorre.NoRMCorreSetParms(...
+ 'd1', d1-bound,...
+ 'd2', d2-bound,...
+ 'init_batch', optsNC2.init_batch,...
+ 'bin_width', optsNC2.bin_width,...
+ 'grid_size', optsNC2.grid_size,...
+ 'mot_uf', optsNC2.mot_uf,...
+ 'correct_bidir', optsNC2.correct_bidir,...
+ 'overlap_pre', optsNC2.overlap_pre,...
+ 'overlap_post', optsNC2.overlap_post,...
+ 'max_dev', optsNC2.max_dev,...
+ 'use_parallel', optsNC2.use_parallel,...
+ 'print_msg', optsNC2.print_msg,...
+ 'us_fac', optsNC2.us_fac,...
+ 'max_shift', optsNC2.max_shift,...
+ 'boundary', optsNC2.boundary...
+ );
+ % 'init_batch_interval',options.refFrame,...
+
+ % [optsNC] = ciapkg.io.mergeStructs(optsNC,optsNC2);
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ end
+ if options.guiDisplay==0
+ return;
+ end
+
+ try
+ % optsNC = normcorre.NoRMCorreSetParms(...
+ % 'd1', d1-bound,...
+ % 'd2', d2-bound,...
+ % 'd3', 1,...
+ % 'init_batch', optsNC2.init_batch,...
+ % 'bin_width', optsNC2.bin_width,...
+ % 'grid_size', optsNC2.grid_size,...
+ % 'mot_uf', optsNC2.mot_uf,...
+ % 'correct_bidir', optsNC2.correct_bidir,...
+ % 'overlap_pre', optsNC2.overlap_pre,...
+ % 'overlap_post', optsNC2.overlap_post,...
+ % 'max_dev', optsNC2.max_dev,...
+ % 'use_parallel', optsNC2.use_parallel,...
+ % 'print_msg', optsNC2.print_msg,...
+ % 'us_fac', optsNC2.us_fac,...
+ % 'max_shift', optsNC2.max_shift,...
+ % 'boundary', optsNC2.boundary...
+ % );
+
+ modFn = fieldnames(optsNC2);
+
+ % [optsNC] = ciapkg.io.mergeStructs(optsNC,optsNC2);
+
+ optsFn = {
+ % dataset info
+ {'d1', "number of rows"};
+ {'d2', "number of cols"};
+ % {'d3', "number of planes (for 3d imaging, default: 1)"};
+ % patches
+ {'grid_size', "size of non-overlapping regions (default: [d1,d2,d3])"};
+ {'overlap_pre', "size of overlapping region (default: [32,32,16])"};
+ {'min_patch_size', "minimum size of patch (default: [32,32,16]) "};
+ {'min_diff', "minimum difference between patches (default: [16,16,5])"};
+ {'us_fac', "upsampling factor for subpixel registration (default: 20)"};
+ {'mot_uf', "degree of patches upsampling (default: [4,4,1])"};
+ {'max_dev', "maximum deviation of patch shift from rigid shift (default: [3,3,1])"};
+ {'overlap_post', "size of overlapping region after upsampling (default: [32,32,16])"};
+ {'max_shift', "maximum rigid shift in each direction (default: [15,15,5])"};
+ {'phase_flag', "flag for using phase correlation (default: false)"};
+ {'shifts_method', "method to apply shifts ('FFT','cubic','linear')"};
+
+ % template updating
+ {'upd_template', "flag for online template updating (default: true)"};
+ {'init_batch', "length of initial batch (default: 100)"};
+ {'bin_width', "width of each bin (default: 200)"};
+ {'buffer_width', "number of local means to keep in memory (default: 50)"};
+ {'method', "method for averaging the template (default: {'median';'mean})"};
+ {'iter', "number of data passes (default: 1)"};
+ {'boundary', "method of boundary treatment 'NaN','copy','zero','template' (default:} 'copy')"};
+
+ % misc
+ {'add_value', "add dc value to data (default: 0)"};
+ {'use_parallel', "for each frame, update patches in parallel (default: false)"};
+ {'memmap', "flag for saving memory mapped motion corrected file (default: false)"};
+ {'mem_filename', "name for memory mapped file (default: 'motion_corrected.mat')"};
+ {'mem_batch_size', "batch size during memory mapping for speed (default: 5000)"};
+ {'print_msg', "flag for printing progress to command line (default: true)"};
+
+ % plotting
+ {'plot_flag', "flag for plotting results in real time (default: false)"};
+ {'make_avi', "flag for making movie (default: false)"};
+ {'name', "name for movie (default: 'motion_corrected.avi')"};
+ {'fr', "frame rate for movie (default: 30)"};
+
+ % output type
+ {'output_type', "'mat' (load in memory), 'memmap', 'tiff', 'hdf5', 'bin' (default:mat)"};
+ {'h5_groupname', "name for hdf5 dataset (default: 'mov')"};
+ {'h5_filename', "name for hdf5 saved file (default: 'motion_corrected.h5')"};
+ {'tiff_filename', "name for saved tiff stack (default: 'motion_corrected.tif')"};
+ {'output_filename', "name for saved file will be used if `h5_,tiff_filename` are not} specified"};
+
+ % use windowing
+ {'use_windowing', "flag for windowing data before fft (default: false)"};
+ {'window_length', "length of window on each side of the signal as a fraction of signal length. Total length = length(signal)(1 + 2*window_length). (default: 0.5)"};
+ {'bitsize', "bitsize for reading .raw files (default: 2 (uint16). other choices 1 (uint8), 4 (single), 8 (double))"};
+
+ % offset from bidirectional sampling
+ {'correct_bidir', "check for offset due to bidirectional scanning (default: true)"};
+ {'nFrames', "number of frames to average (default: 50)"};
+ {'bidir_us', "upsampling factor for bidirectional sampling (default: 10)"};
+ {'col_shift', "known bi-directional offset provided by the user (default: [])"};
+ };
+
+ for iz = 1:length(optsFn)
+ optsFn{iz}{2} = char(optsFn{iz}{2});
+ end
+
+ optsDefault = optsFn;
+
+ movieSettingsStrs = {};
+ defaultVals = {};
+ for iz = 1:length(optsFn)
+ movieSettingsStrs{end+1} = optsFn{iz}{2};
+ end
+
+ % optsTmp = cellfun(@num2str,struct2cell(optsDefault),'UniformOutput',false);
+ for iz = 1:length(movieSettingsStrs)
+ valStr = optsFn{iz}{1};
+ dVal = optsNC.(valStr);
+ if isnumeric(dVal)|islogical(dVal)
+ dVal = num2str(dVal);
+ elseif iscell(dVal)
+ dVal = cell2str(dVal);
+ else
+
+ end
+ defaultVals{end+1} = dVal;
+ movieSettingsStrs{iz} = strrep([valStr ' | ' movieSettingsStrs{iz} ' | default: ' dVal],'_','\_');
+ if any(strcmp(modFn,valStr))==1
+ movieSettingsStrs{iz} = ['\bf\color{red}' movieSettingsStrs{iz}];
+ end
+ end
+ % defaultVals
+ % cellfun(@class,defaultVals,'UniformOutput',false)
+
+ dlgStr = 'NoRMCorre (red options are those to focus on';
+ AddOpts.Resize='on';
+ AddOpts.WindowStyle='normal';
+ % AddOpts.WindowStyle='non-modal';
+ AddOpts.Interpreter='tex';
+ AddOpts.DlgSize = [1200 100];
+ mSet = inputdlgcol(movieSettingsStrs,...
+ dlgStr,1,...
+ defaultVals,...
+ AddOpts,3);
+
+ sNo = 1;
+
+ for iz = 1:length(movieSettingsStrs)
+ valStr = optsFn{iz}{1};
+ dVal = optsNC.(valStr);
+ if isnumeric(dVal)|islogical(dVal)
+ optsNC.(valStr) = str2num(mSet{iz});
+ elseif iscell(dVal)
+ optsNC.(valStr) = eval(mSet{iz});
+ else
+ optsNC.(valStr) = mSet{iz};
+ end
+ end
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ end
+
+ function [outputs] = nestedfxn_exampleFxn(arg)
+ % Always start nested functions with "nestedfxn_" prefix.
+ % outputs = ;
+ end
+end
+
diff --git a/+ciapkg/+motion_correction/turboregMovie.m b/+ciapkg/+motion_correction/turboregMovie.m
index f297e75..1f5a86b 100644
--- a/+ciapkg/+motion_correction/turboregMovie.m
+++ b/+ciapkg/+motion_correction/turboregMovie.m
@@ -1,11 +1,24 @@
function [inputMovie, ResultsOutOriginal] = turboregMovie(inputMovie, varargin)
- % Motion corrects (using turboreg) a movie.
- % - Both turboreg (to get 2D translation coordinates) and registering images (transfturboreg, imwarp, imtransform) have been parallelized.
- % - Can also turboreg to one set of images and apply the registration to another set (e.g. for cross-day alignment).
- % - Spatial filtering is applied after obtaining registration coordinates but before transformation, this reduced chance that 0s or NaNs at edge after transformation mess with proper spatial filtering.
+ % [inputMovie, ResultsOutOriginal] = turboregMovie(inputMovie, varargin)
+ %
+ % Motion corrects (using turboreg or NoRMCorre) a movie.
+ % - Both turboreg (to get 2D translation coordinates) and registering images (transfturboreg, imwarp, imtransform) have been parallelized.
+ % - Can also turboreg to one set of images and apply the registration to another set (e.g. for cross-day alignment).
+ % - Spatial filtering is applied after obtaining registration coordinates but before transformation, this reduced chance that 0s or NaNs at edge after transformation mess with proper spatial filtering.
+ %
% Biafra Ahanonu
% started 2013.11.09 [11:04:18]
- % modified from code created by Jerome Lecoq in 2011 and parallel code update by biafra ahanonu
+ %
+ % Parts of code based on that from Jerome Lecoq (2011) and parallel code update by Biafra Ahanonu (2013).
+ %
+ % Input
+ % inputMovie - 3D matrix: [x y frames] matrix containing data to be motion corrected across frames.
+ %
+ % Output
+ % inputMovie - 3D matrix: [x y frames] matrix containing data after motion correction across frames.
+ % ResultsOutOriginal
+ % TurboReg - Cell array {1 frames}: contains the motion correction data for each frame for later use.
+ % NoRMCorre - Structure: contains the motion correction data for each frame for later use.
% changelog
% 2013.03.29 - parallelizing turboreg v1
@@ -27,6 +40,12 @@
% 2021.09.11 [10:40:05] - Additional matlab disk normalizeType options.
% 2021.11.01 [12:15:10] - Additional display of information.
% 2021.11.16 [11:58:14] - Added verification that turboreg MEX function is in the path.
+ % 2022.01.22 [20:46:51] - Refactor code to remove need to transform movie into cell array, performance and memory improvements.
+ % 2022.03.08 [12:23:53] - Added NoRMCorre support, function will eventually be merged with "registerMovie" or renamed to indicate support for multiple algorithms. Reason to integrate NoRMCorre into this function is takes advantage of existing pre-processing pipeline and integration with other algorithms (e.g. cross-session).
+ % 2022.03.09 [16:34:47] - Check if inputs are sparse, convert to single.
+ % 2022.03.09 [17:32:42] - Use custom bahanonu NoRMCorre that is within a package and update to use reference picture instead of template.
+ % 2022.04.23 [18:40:22] - Updated to which('normcorre.normcorre') from which('normcorre') since normcorre now inside a package.
+ % 2022.09.12 [20:46:04] - Additional NoRMCorre support and getNoRMCorreParams checking before running NoRMCorre.
% TO-DO
% Add support for "imregtform" based registration.
@@ -53,6 +72,11 @@
% ========================
% get options
% =======IMPORTANT OPTIONS=====
+ % Str: motion correction algorithm.
+ % 'turboreg' - TurboReg as developed by Philippe Thevenaz.
+ % 'normcorre' - NoRMCorre as developed by several authors at Flatiron Institute.
+ options.mcMethod = 'turboreg';
+ % options.mcMethod = 'normcorre';
% Str: dataset name in HDF5 file where data is stored, if inputMovie is a path to a movie.
options.inputDatasetName = '/1';
% Int vector: if loading movie inside function, provide frameList to load specific frames
@@ -171,7 +195,11 @@
options.returnNormalizedMovie = 0;
% Binary: 1 = run correlation before spatial filtering, 0 = do not run correlation.
options.computeCorr = 0;
-
+ % =======
+ % NoRMCorre
+ % Struct: NoRMCorre settings
+ options.optsNoRMCorre = ciapkg.motion_correction.getNoRMCorreParams([1 1 1],'guiDisplay',0);
+ %
% get options
options = getOptions(options,varargin);
% display(options)
@@ -181,17 +209,31 @@
% eval([fn{i} '=options.' fn{i} ';']);
% end
if options.displayOptions==1
- options
+ fn_structdisp(options)
end
% ========================
% Verify that turboreg MEX function is in the path.
- if isempty(which('turboreg'))==1
- ciapkg.loadBatchFxns();
+ if isempty(which('turboreg'))==1||isempty(which('normcorre.normcorre'))==1
+ % ciapkg.loadBatchFxns();
+ ciapkg.loadBatchFxns('removeDirFxnToFindExclude','normcorre.m');
+ end
+
+ % ========================
+ % Algorithm specific options
+ switch options.mcMethod
+ case 'turboreg'
+
+ case 'normcorre'
+ % NoRMCorre is to be run on the entire input matrix.
+ options.cropCoords = [];
+ otherwise
+
end
+
% ========================
% check that Miji is present
- if strcmp(options.normalizeType,'imagejFFT')|strcmp(options.normalizeBeforeRegister,'imagejFFT')
+ if strcmp(options.normalizeType,'imagejFFT')||strcmp(options.normalizeBeforeRegister,'imagejFFT')
% if exist('Miji.m','file')==2
% disp(['Miji located in: ' which('Miji.m')]);
% % Miji is loaded, continue
@@ -209,6 +251,12 @@
modelAddOutsideDependencies('miji');
end
% ========================
+ % Check for sparse inputs
+ if issparse(inputMovie)
+ disp('Converting from sparse to single.')
+ inputMovie = single(full(inputMovie));
+ end
+ % ========================
inputMovieClass = class(inputMovie);
if ischar(inputMovie)
inputMovie = loadMovieList(inputMovie,'inputDatasetName',options.inputDatasetName,'frameList',options.frameList);
@@ -248,14 +296,17 @@
ResultsOut = ResultsOutTemp;
% Remove NaNs from inputMovie so transfturboreg doesn't run into issue.
inputMovie(isnan(inputMovie)) = 0;
- convertInputMovieToCell();
+ % convertInputMovieToCell();
% size(inputMovie)
% class(inputMovie)
% size(inputMovie{1})
% class(inputMovie{1})
InterpListSelection = turboRegOptions.Interp;
registerMovie();
- inputMovie = cat(3,inputMovie{:});
+
+ % Convert back into matrix.
+ % inputMovie = cat(3,inputMovie{:});
+
ResultsOutOriginal = ResultsOut;
return;
end
@@ -271,14 +322,17 @@
% ResultsOut = ResultsOutTemp;
% Remove NaNs from inputMovie so transfturboreg doesn't run into issue.
inputMovie(isnan(inputMovie)) = 0;
- convertInputMovieToCell();
+ % convertInputMovieToCell();
% size(inputMovie)
% class(inputMovie)
% size(inputMovie{1})
% class(inputMovie{1})
InterpListSelection = turboRegOptions.Interp;
registerMovie();
- inputMovie = cat(3,inputMovie{:});
+
+ % Convert back into matrix.
+ % inputMovie = cat(3,inputMovie{:});
+
ResultsOutOriginal = ResultsOut;
return;
end
@@ -327,8 +381,8 @@
end
if ~isempty(options.saveTurboregCoords)
- options.saveTurboregCoords
- ResultsOut
+ disp(options.saveTurboregCoords)
+ disp(ResultsOut)
end
ResultsOutOriginal = ResultsOut;
@@ -361,7 +415,8 @@
% ===
% if we are using the turboreg coordinates for frame #options.altMovieRegisterNum to register all frames from options.altMovieRegister, want to give registerMovie an identical sized array to altMovieRegister like it normally expects
% this was made for having refCellmap and testCellmap, aligning the testCellmap to the refCellmap then registering all the cell images for testCellmap to refCellmap
- for resultNo=1:size(options.altMovieRegister,3);
+ ResultsOutTemp = cell([1 size(options.altMovieRegister,3)]);
+ for resultNo=1:size(options.altMovieRegister,3)
ResultsOutTemp{resultNo} = ResultsOut{options.altMovieRegisterNum};
end
ResultsOut = ResultsOutTemp;
@@ -369,17 +424,17 @@
%Convert array to cell array, allows slicing (not contiguous memory block)
% add input movie to
inputMovie = options.altMovieRegister;
- convertInputMovieToCell();
+ % convertInputMovieToCell();
elseif ~isempty(options.cropCoords)
disp('restoring uncropped movie and converting to cell...');
clear registeredMovie;
%Convert array to cell array, allows slicing (not contiguous memory block)
- convertInputMovieToCell();
+ % convertInputMovieToCell();
% ===
else
- disp('converting movie to cell...');
+ % disp('converting movie to cell...');
%Convert array to cell array, allows slicing (not contiguous memory block)
- convertInputMovieToCell();
+ % convertInputMovieToCell();
% ===
end
% ========================
@@ -399,11 +454,11 @@
toc(startTime)
% ========================
- disp('converting cell array back to matrix')
+ % disp('converting cell array back to matrix')
%Convert cell array back to 3D matrix
- inputMovie = cat(3,inputMovie{:});
- inputMovie = single(inputMovie);
+ % inputMovie = cat(3,inputMovie{:});
+ inputMovie = single(inputMovie);
subfxn_dispMovieFrames(inputMovie,'Registration==1',2);
@@ -439,7 +494,7 @@
function convertInputMovieToCell()
%Get dimension information about 3D movie matrix
[inputMovieX, inputMovieY, inputMovieZ] = size(inputMovie);
- reshapeValue = size(inputMovie);
+ % reshapeValue = size(inputMovie);
%Convert array to cell array, allows slicing (not contiguous memory block)
inputMovie = squeeze(mat2cell(inputMovie,inputMovieX,inputMovieY,ones(1,inputMovieZ)));
end
@@ -447,7 +502,7 @@ function convertInputMovieToCell()
function convertinputMovieCroppedToCell()
%Get dimension information about 3D movie matrix
[inputMovieX, inputMovieY, inputMovieZ] = size(inputMovieCropped);
- reshapeValue = size(inputMovieCropped);
+ % reshapeValue = size(inputMovieCropped);
%Convert array to cell array, allows slicing (not contiguous memory block)
inputMovieCropped = squeeze(mat2cell(inputMovieCropped,inputMovieX,inputMovieY,ones(1,inputMovieZ)));
end
@@ -469,197 +524,270 @@ function nUpdateParforProgress(~)
% function [ResultsOut averagePictureEdge] = turboregMovieParallel(inputMovie,turboRegOptions,options)
function turboregMovieParallel()
- % get reference picture and other pre-allocation
- postProcessPic = single(squeeze(inputMovieCropped(:,:,options.refFrame)));
- mask = single(ones(size(postProcessPic)));
- imgRegMask = single(double(mask));
- % we add an offset to be able to give NaN to black borders
- averagePictureEdge = zeros(size(imgRegMask));
- refPic = single(squeeze(inputMovieCropped(:,:,options.refFrame)));
- % refPic = squeeze(inputMovieCropped(:,:,options.refFrame));
-
- MatrixMotCorrDispl=zeros(3,options.maxFrame);
-
- % ===
- %Convert array to cell array, allows slicing (not contiguous memory block)
- convertinputMovieCroppedToCell();
- % ===
+ switch options.mcMethod
+ case 'turboreg'
+ % get reference picture and other pre-allocation
+ postProcessPic = single(squeeze(inputMovieCropped(:,:,options.refFrame)));
+ mask = single(ones(size(postProcessPic)));
+ imgRegMask = single(double(mask));
+ % we add an offset to be able to give NaN to black borders
+ averagePictureEdge = zeros(size(imgRegMask));
+ refPic = single(squeeze(inputMovieCropped(:,:,options.refFrame)));
+ % refPic = squeeze(inputMovieCropped(:,:,options.refFrame));
+
+ MatrixMotCorrDispl = zeros(3,options.maxFrame);
+
+ % ===
+ %Convert array to cell array, allows slicing (not contiguous memory block)
+ % convertinputMovieCroppedToCell();
+ % ===
+
+ % Get data class, can be removed...
+ movieClass = class(inputMovieCropped);
+ % you need this FileExchange function for progress in a parfor loop
+ disp('turboreg-ing...');
+ disp('');
+ % parallel for loop, since each turboreg operation is independent, can send each frame to separate workspaces
+ startTurboRegTime = tic;
+ %
+ nFramesToTurboreg = options.maxFrame;
+ if options.parallel==1; nWorkers=Inf;else;nWorkers=0;end
+ parfor (frameNo=1:nFramesToTurboreg,nWorkers)
+ % get current frames
+ % thisFrame = inputMovieCropped{frameNo};
+
+ thisFrame = inputMovieCropped(:,:,frameNo);
+
+ thisFrameToAlign=single(thisFrame);
+ % thisFrameToAlign=thisFrame;
+
+ if ismac
+ % Code to run on Mac platform
+ [ImageOut,ResultsOut{frameNo}] = turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
+ % create a mask
+ averagePictureEdge = averagePictureEdge | ImageOut==0;
+ elseif isunix
+ % Code to run on Linux platform
+ [ResultsOut{frameNo}] = turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
+ % create a mask
+ % averagePictureEdge = averagePictureEdge | ImageOut==0;
+ elseif ispc
+ % Code to run on Windows platform
+ [ImageOut,ResultsOut{frameNo}] = turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
+ % create a mask
+ averagePictureEdge = averagePictureEdge | ImageOut==0;
+ else
+ % return;
+ disp('Platform not supported')
+ end
- % Get data class, can be removed...
- movieClass = class(inputMovieCropped);
- % you need this FileExchange function for progress in a parfor loop
- disp('turboreg-ing...');
- disp('');
- % parallel for loop, since each turboreg operation is independent, can send each frame to separate workspaces
- startTurboRegTime = tic;
- %
- nFramesToTurboreg = options.maxFrame;
- if options.parallel==1; nWorkers=Inf;else;nWorkers=0;end
- parfor (frameNo=1:nFramesToTurboreg,nWorkers)
- % get current frames
- thisFrame = inputMovieCropped{frameNo};
- thisFrameToAlign=single(thisFrame);
- % thisFrameToAlign=thisFrame;
-
- if ismac
- % Code to run on Mac platform
- [ImageOut,ResultsOut{frameNo}]=turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
- % create a mask
- averagePictureEdge = averagePictureEdge | ImageOut==0;
- elseif isunix
- % Code to run on Linux platform
- [ResultsOut{frameNo}]=turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
- % create a mask
- % averagePictureEdge = averagePictureEdge | ImageOut==0;
- elseif ispc
- % Code to run on Windows platform
- [ImageOut,ResultsOut{frameNo}]=turboreg(refPic,thisFrameToAlign,mask,imgRegMask,turboRegOptions);
- % create a mask
- averagePictureEdge = averagePictureEdge | ImageOut==0;
- else
- % return;
- disp('Platform not supported')
- end
+ if ~verLessThan('matlab', '9.2')
+ send(D, frameNo); % Update
+ end
+ end
+ % dispstat('Finished.','keepprev');
+ toc(startTurboRegTime);
+ drawnow;
+ % save('ResultsOutFile','ResultsOut');
+ case 'normcorre'
+ startTurboRegTime = tic;
+
+ bound = 0;
+ refPic = single(squeeze(inputMovieCropped(:,:,options.refFrame)));
+ inputMovieCroppedTmp = inputMovieCropped(bound/2+1:end-bound/2,bound/2+1:end-bound/2,:);
+
+ optsNoRMCorre = options.optsNoRMCorre;
+ if isempty(fieldnames(optsNoRMCorre))
+ disp('Getting NoRMCorre params...')
+ optsNoRMCorre = ciapkg.motion_correction.getNoRMCorreParams(size(inputMovieCropped),'guiDisplay',0);
+ end
- if ~verLessThan('matlab', '9.2')
- send(D, frameNo); % Update
- end
+ % Ensure NoRMCorre gets the correct inputs
+ optsNoRMCorre.d1 = size(inputMovieCropped,1);
+ optsNoRMCorre.d2 = size(inputMovieCropped,2);
+ % fn_structdisp(optsNoRMCorre)
+ % [optsNoRMCorre] = subfxn_getNoRMCorreParams(inputMovieCropped);
+ [~,ResultsOut,template2] = normcorre.normcorre_batch(...
+ inputMovieCroppedTmp,...
+ optsNoRMCorre,...
+ refPic);
+ clear inputMovieCroppedTmp
+ toc(startTurboRegTime);
+ otherwise
end
- % dispstat('Finished.','keepprev');
- toc(startTurboRegTime);
- drawnow;
- % save('ResultsOutFile','ResultsOut');
+
+ end
+
+ function [optsNC] = subfxn_getNoRMCorreParams(inputMovieHere)
+ [d1,d2,T] = size(inputMovieHere);
+ bound = 0;
+ sizeCorFactor = 2;
+ % sizeCorFactor = 1;
+ optsNC = normcorre.NoRMCorreSetParms(...
+ 'd1', d1-bound,...
+ 'd2', d2-bound,...
+ 'init_batch', 10,...
+ 'bin_width', 50,...
+ 'grid_size', [128,128]/sizeCorFactor,... % [128,128]/2 [64,64]
+ 'mot_uf', 4,...
+ 'correct_bidir', false,...
+ 'overlap_pre', 32,...
+ 'overlap_post', 32,...
+ 'max_dev', 50,...
+ 'use_parallel', true,...
+ 'print_msg', true,...
+ 'us_fac', 4,...
+ 'max_shift', 100,...
+ 'boundary', 'NaN');
+ % 'init_batch_interval',options.refFrame,...
end
+
% function registerMovie(movieData,ResultsOut,InterpListSelection,TransformationType,options)
function registerMovie()
disp('registering frames...');
disp('');
- % need to register subsets of the movie so parfor won't crash due to serialization errors
- % TODO: make this subset based on the size of the movie, e.g. only send 1GB chunks to workers
- subsetSize = options.subsetSizeFrames;
- numSubsets = ceil(length(inputMovie)/subsetSize)+1;
- subsetList = round(linspace(1,length(inputMovie),numSubsets));
- display(['registering sublists: ' num2str(subsetList)]);
- if options.turboregRotation==1
- disp('Using rotation in registration')
- end
- fprintf('Performing %s registration and %s transformation.\n',options.registrationFxn,TransformationType);
- % ResultsOut{1}.Rotation
- nSubsets = (length(subsetList)-1);
- for thisSet=1:nSubsets
- subsetStartIdx = subsetList(thisSet);
- subsetEndIdx = subsetList(thisSet+1);
- if thisSet==nSubsets
- movieSubset = subsetStartIdx:subsetEndIdx;
- display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx) ' ' num2str(thisSet) '/' num2str(nSubsets)])
- else
- movieSubset = subsetStartIdx:(subsetEndIdx-1);
- display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
- end
- movieDataTemp(movieSubset) = inputMovie(movieSubset);
- % loop over and register each frame
- if options.parallel==1; nWorkers=Inf;else;nWorkers=0;end
- turboregRotationOption = options.turboregRotation;
- registrationFxnOption = options.registrationFxn;
- nMovieSubsets = length(movieSubset);
-
- % Create anonymous transform function to save CPU cycles in loop
- switch TransformationType
- case 'affine'
- transformFxn = @(xform) affine2d(xform);
- % tform = affine2d(double(xform));
- case 'projective'
- transformFxn = @(xform) projective2d(xform);
- % tform = projective2d(double(xform));
- otherwise
- transformFxn = @(xform) affine2d(xform);
- % tform = affine2d(double(xform));
- end
+ switch options.mcMethod
+ case 'normcorre'
+ startTurboRegTime = tic;
+ [optsNC] = subfxn_getNoRMCorreParams(inputMovie);
+ bound = 0;
+ inputMovie = normcorre.apply_shifts(inputMovie,ResultsOut,optsNC,bound/2,bound/2); % apply the shifts to the removed percentile
+ toc(startTurboRegTime);
+ case 'turboreg'
+ % Need to register subsets of the movie so parfor won't crash due to serialization errors.
+ % TODO: make this subset based on the size of the movie, e.g. only send 1GB chunks to workers.
+ subsetSize = options.subsetSizeFrames;
+ nFramesHere = size(inputMovie,3);
+ % numSubsets = ceil(length(inputMovie)/subsetSize)+1;
+ numSubsets = ceil(nFramesHere/subsetSize)+1;
+ % subsetList = round(linspace(1,length(inputMovie),numSubsets));
+ subsetList = round(linspace(1,nFramesHere,numSubsets));
+ display(['registering sublists: ' num2str(subsetList)]);
+ if options.turboregRotation==1
+ disp('Using rotation in registration')
+ end
+ fprintf('Performing %s registration and %s transformation.\n',options.registrationFxn,TransformationType);
+ % ResultsOut{1}.Rotation
+ nSubsets = (length(subsetList)-1);
+ for thisSet=1:nSubsets
+ subsetStartIdx = subsetList(thisSet);
+ subsetEndIdx = subsetList(thisSet+1);
+ if thisSet==nSubsets
+ movieSubset = subsetStartIdx:subsetEndIdx;
+ display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+ else
+ movieSubset = subsetStartIdx:(subsetEndIdx-1);
+ display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+ end
+
+ % Get a slice of the data.
+ % movieDataTemp(movieSubset) = inputMovie(movieSubset);
+
+ % movieDataTemp = inputMovie(:,:,movieSubset);
+
+ % loop over and register each frame
+ if options.parallel==1; nWorkers=Inf;else;nWorkers=0;end
+
+ turboregRotationOption = options.turboregRotation;
+ registrationFxnOption = options.registrationFxn;
+ nMovieSubsets = length(movieSubset);
+
+ % [transformFxn] = subfxn_transformFxn(TransformationType);
+
+ parfor (i = movieSubset,nWorkers)
+ rOutTmp = ResultsOut{i};
- parfor (i = movieSubset,nWorkers)
- % thisFrame = movieDataTemp{i};
- % get rotation and translation profile for image
- % if turboregRotationOption==1
- % MatrixMotCorrDispl(:,i)=[ResultsOut{i}.Translation(1) ResultsOut{i}.Translation(2) ResultsOut{i}.Rotation];
- % else
- % MatrixMotCorrDispl(:,i)=[ResultsOut{i}.Translation(1) ResultsOut{i}.Translation(2) 0];
- % end
-
- % Transform movie given results of turboreg
- switch registrationFxnOption
- case {'imtransform','imwarp'}
+ % thisFrameT = movieDataTemp{i};
+ thisFrameT = inputMovie(:,:,i);
+
+ % get rotation and translation profile for image
% if turboregRotationOption==1
- % rotMat = [...
- % cos(ResultsOut{i}.Rotation) sin(ResultsOut{i}.Rotation) 0;...
- % -sin(ResultsOut{i}.Rotation) cos(ResultsOut{i}.Rotation) 0;...
- % 0 0 0];
+ % MatrixMotCorrDispl(:,i)=[rOutTmp.Translation(1) rOutTmp.Translation(2) rOutTmp.Rotation];
% else
- % rotMat = [0 0 0;0 0 0;0 0 0];
+ % MatrixMotCorrDispl(:,i)=[rOutTmp.Translation(1) rOutTmp.Translation(2) 0];
% end
- % translateMat =...
- % [0 0 0;...
- % 0 0 0;...
- % ResultsOut{i}.Translation(2) ResultsOut{i}.Translation(1) 0];
- % xform = translateMat + SkewingMat;
-
- % Get the skew/translation/rotation matrix from turboreg
- SkewingMat = ResultsOut{i}.Skew;
-
- rotMat = [...
- cos(ResultsOut{i}.Rotation) sin(ResultsOut{i}.Rotation) 0;...
- -sin(ResultsOut{i}.Rotation) cos(ResultsOut{i}.Rotation) 0;...
- 0 0 1];
-
- translateMat =...
- [1 0 0;...
- 0 1 0;...
- ResultsOut{i}.Translation(2) ResultsOut{i}.Translation(1) 1];
-
- xform = translateMat*SkewingMat*rotMat;
-
- if strcmp(registrationFxnOption,'imtransform')==1
- % Perform the transformation
- tform = maketform(TransformationType,double(xform));
- % InterpListSelection = 'nearest';
- movieDataTemp{i} = single(imtransform(movieDataTemp{i},tform,char(InterpListSelection),...
- 'UData',[1 size(movieDataTemp{i},2)]-ResultsOut{i}.Origin(2)-1,...
- 'VData',[1 size(movieDataTemp{i},1)]-ResultsOut{i}.Origin(1)-1,...
- 'XData',[1 size(movieDataTemp{i},2)]-ResultsOut{i}.Origin(2)-1,...
- 'YData',[1 size(movieDataTemp{i},1)]-ResultsOut{i}.Origin(1)-1,...
- 'fill',NaN));
- elseif strcmp(registrationFxnOption,'imwarp')==1
- tform = transformFxn(double(xform));
- % Define input spatial referencing.
- RI = imref2d(size(movieDataTemp{i}),[[1 size(movieDataTemp{i},2)]-ResultsOut{i}.Origin(2)-1],[[1 size(movieDataTemp{i},1)]-ResultsOut{i}.Origin(1)-1]);
-
- % Define output spatial referencing.
- Rout = imref2d(size(movieDataTemp{i}),[[1 size(movieDataTemp{i},2)]-ResultsOut{i}.Origin(2)-1],[[1 size(movieDataTemp{i},1)]-ResultsOut{i}.Origin(1)-1]);
-
- movieDataTemp{i} = single(imwarp(movieDataTemp{i},RI,tform,char(InterpListSelection),...
- 'OutputView',Rout,...
- 'FillValues',NaN));
+ % Transform movie given results of turboreg
+
+ switch registrationFxnOption
+ case {'imtransform','imwarp'}
+ % if turboregRotationOption==1
+ % rotMat = [...
+ % cos(rOutTmp.Rotation) sin(rOutTmp.Rotation) 0;...
+ % -sin(rOutTmp.Rotation) cos(rOutTmp.Rotation) 0;...
+ % 0 0 0];
+ % else
+ % rotMat = [0 0 0;0 0 0;0 0 0];
+ % end
+
+ % translateMat =...
+ % [0 0 0;...
+ % 0 0 0;...
+ % rOutTmp.Translation(2) rOutTmp.Translation(1) 0];
+ % xform = translateMat + SkewingMat;
+
+ % Get the skew/translation/rotation matrix from turboreg
+ SkewingMat = rOutTmp.Skew;
+
+ rotMat = [...
+ cos(rOutTmp.Rotation) sin(rOutTmp.Rotation) 0;...
+ -sin(rOutTmp.Rotation) cos(rOutTmp.Rotation) 0;...
+ 0 0 1];
+
+ translateMat =...
+ [1 0 0;...
+ 0 1 0;...
+ rOutTmp.Translation(2) rOutTmp.Translation(1) 1];
+
+ xform = translateMat*SkewingMat*rotMat;
+
+ if strcmp(registrationFxnOption,'imtransform')==1
+ % Perform the transformation
+ tform = maketform(TransformationType,double(xform));
+ % InterpListSelection = 'nearest';
+ thisFrameT = single(imtransform(thisFrameT,tform,char(InterpListSelection),...
+ 'UData',[1 size(thisFrameT,2)]-rOutTmp.Origin(2)-1,...
+ 'VData',[1 size(thisFrameT,1)]-rOutTmp.Origin(1)-1,...
+ 'XData',[1 size(thisFrameT,2)]-rOutTmp.Origin(2)-1,...
+ 'YData',[1 size(thisFrameT,1)]-rOutTmp.Origin(1)-1,...
+ 'fill',NaN));
+ elseif strcmp(registrationFxnOption,'imwarp')==1
+ tform = subfxn_transformFxn(TransformationType,xform);
+ % Define input spatial referencing.
+ RI = imref2d(size(thisFrameT),[[1 size(thisFrameT,2)]-rOutTmp.Origin(2)-1],[[1 size(thisFrameT,1)]-rOutTmp.Origin(1)-1]);
+
+ % Define output spatial referencing.
+ Rout = imref2d(size(thisFrameT),[[1 size(thisFrameT,2)]-rOutTmp.Origin(2)-1],[[1 size(thisFrameT,1)]-rOutTmp.Origin(1)-1]);
+
+ thisFrameT = single(imwarp(thisFrameT,RI,tform,char(InterpListSelection),...
+ 'OutputView',Rout,...
+ 'FillValues',NaN));
+ end
+ case 'transfturboreg'
+ frameClass = class(thisFrameT);
+ thisFrameT = ...
+ cast(...
+ transfturboreg(...
+ single(thisFrameT),...
+ ones(size(thisFrameT),'single'),...
+ rOutTmp),...
+ frameClass);
+ otherwise
+ % do nothing
end
- case 'transfturboreg'
- frameClass = class(movieDataTemp{i});
- movieDataTemp{i} = ...
- cast(...
- transfturboreg(...
- single(movieDataTemp{i}),...
- ones(size(movieDataTemp{i}),'single'),...
- ResultsOut{i}),...
- frameClass);
- otherwise
- % do nothing
+
+ inputMovie(:,:,i) = thisFrameT;
+ end
+ dispstat('Finished.','keepprev');
+
+ % inputMovie(movieSubset)=movieDataTemp(movieSubset);
+ % clear movieDataTemp;
end
+ otherwise
+ % Do nothing
end
- dispstat('Finished.','keepprev');
-
- inputMovie(movieSubset)=movieDataTemp(movieSubset);
- clear movieDataTemp;
- end
end
function removeInputMovieEdges()
% turboreg outputs 0s where movement goes off the screen
@@ -669,13 +797,13 @@ function removeInputMovieEdges()
case 'imtransform'
reverseStr = '';
for row=1:size(inputMovie,1)
- thisMovieMinMask(row,:) = logical(nanmax(isnan(squeeze(inputMovie(3,:,:))),[],2));
+ thisMovieMinMask(row,:) = logical(max(isnan(squeeze(inputMovie(3,:,:))),[],2,'omitnan'));
reverseStr = cmdWaitbar(row,size(inputMovie,1),reverseStr,'inputStr','getting crop amount','waitbarOn',1,'displayEvery',5);
end
case 'transfturboreg'
reverseStr = '';
for row=1:size(inputMovie,1)
- thisMovieMinMask(row,:) = logical(nanmin(squeeze(inputMovie(row,:,:))~=0,[],2)==0);
+ thisMovieMinMask(row,:) = logical(min(squeeze(inputMovie(row,:,:))~=0,[],2,'omitnan')==0);
reverseStr = cmdWaitbar(row,size(inputMovie,1),reverseStr,'inputStr','getting crop amount','waitbarOn',1,'displayEvery',5);
end
otherwise
@@ -765,8 +893,8 @@ function cropMatrixPreProcess(pxToCropPreprocess)
bottomRowCrop = size(inputMovie,1)-pxToCropPreprocess; % bottom row
rightColCrop = size(inputMovie,2)-pxToCropPreprocess; % right column
- rowLen = size(inputMovie,1);
- colLen = size(inputMovie,2);
+ % rowLen = size(inputMovie,1);
+ % colLen = size(inputMovie,2);
% set leftmost columns to NaN
inputMovie(1:end,1:leftColCrop,:) = NaN;
% set rightmost columns to NaN
@@ -804,7 +932,7 @@ function addBlackEdgeToMovie()
rectCrop=[xmin ymin xmax-xmin ymax-ymin];
if options.showFigs==1
- [figHandle, figNo] = openFigure(100, '');
+ [~, ~] = openFigure(100, '');
imagesc(imcrop(inputMovie(:,:,1),rectCrop));
end
% To get the final size, we just apply on the first figure
@@ -923,11 +1051,13 @@ function cropAndNormalizeInputMovie()
%Get dimension information about 3D movie matrix
[inputMovieX, inputMovieY, inputMovieZ] = size(inputMovieCropped);
- reshapeValue = size(inputMovieCropped);
+ % reshapeValue = size(inputMovieCropped);
+
%Convert array to cell array, allows slicing (not contiguous memory block)
- inputMovieCropped = squeeze(mat2cell(inputMovieCropped,inputMovieX,inputMovieY,ones(1,inputMovieZ)));
+ % inputMovieCropped = squeeze(mat2cell(inputMovieCropped,inputMovieX,inputMovieY,ones(1,inputMovieZ)));
- imageNow = squeeze(inputMovieCropped{1});
+ % imageNow = squeeze(inputMovieCropped{1});
+ imageNow = squeeze(inputMovieCropped(:,:,1));
[rows,cols] = size(imageNow);
r1 = min(rows,cols)/options.matlabdiskR1;
r2 = options.matlabdiskR2;
@@ -935,20 +1065,24 @@ function cropAndNormalizeInputMovie()
hDisk2 = fspecial('disk', r2);
transform = @(A) transform_2(A,hDisk,hDisk2);
reverseStr = '';
- % nImages = size(inputMovieCropped,3);
- nImages = length(inputMovieCropped);
+ nImages = size(inputMovieCropped,3);
+ % nImages = length(inputMovieCropped);
if options.parallel==1; nWorkers=Inf;else;nWorkers=0;end
parfor (imageNo = 1:nImages,nWorkers)
- imageNow = squeeze(inputMovieCropped{imageNo});
- inputMovieCropped{imageNo} = transform(imageNow);
+ % imageNow = squeeze(inputMovieCropped{imageNo});
+ % inputMovieCropped{imageNo} = transform(imageNow);
+
+ imageNow = squeeze(inputMovieCropped(:,:,imageNo));
+ inputMovieCropped(:,:,imageNo) = transform(imageNow);
+
% if (mod(imageNo,20)==0|imageNo==nImages)
% reverseStr = cmdWaitbar(imageNo,nImages,reverseStr,'inputStr','fspecial normalizing');
% end
end
dispstat('Finished.','keepprev');
- inputMovieCropped = cat(3,inputMovieCropped{:});
+ % inputMovieCropped = cat(3,inputMovieCropped{:});
case 'divideByLowpass'
disp('dividing movie by lowpass...')
inputMovieCropped = normalizeMovie(single(inputMovieCropped),'normalizationType','imfilter','blurRadius',20,'waitbarOn',1);
@@ -1065,4 +1199,19 @@ function subfxn_dispMovieFrames(inputMovieCropped,titleStr,inputMod)
A_tr = imfilter(A_tr, asm_filter);
+end
+function [tform] = subfxn_transformFxn(TransformationType,xform)
+ % Create anonymous transform function to save CPU cycles in loop
+ switch TransformationType
+ case 'affine'
+ % transformFxn = @(xform) affine2d(xform);
+ tform = affine2d(double(xform));
+ % tform = subfxn_transformFxn(TransformationType,double(xform));
+ case 'projective'
+ % transformFxn = @(xform) projective2d(xform);
+ tform = projective2d(double(xform));
+ otherwise
+ % transformFxn = @(xform) affine2d(xform);
+ tform = affine2d(double(xform));
+ end
end
\ No newline at end of file
diff --git a/+ciapkg/+movie_processing/downsampleMovie.m b/+ciapkg/+movie_processing/downsampleMovie.m
index c8e8d02..fb32d30 100644
--- a/+ciapkg/+movie_processing/downsampleMovie.m
+++ b/+ciapkg/+movie_processing/downsampleMovie.m
@@ -1,27 +1,34 @@
function [inputMovie] = downsampleMovie(inputMovie, varargin)
+ % [inputMovie] = downsampleMovie(inputMovie, varargin)
+ %
% Downsamples a movie in either space or time, uses floor to calculate downsampled dimensions.
+ %
% Biafra Ahanonu
% started 2013.11.09 [09:31:32]
%
% inputs
- % inputMovie: a NxMxP matrix
+ % inputMovie: a [x y t] matrix.
% options
- % downsampleType
- % downsampleFactor - amount to downsample in time
+ % downsampleDimension - Str: 'time' or 'space'. Dimension to downsample
+ % downsampleType - Str: 'bilinear' or 'bicubic'. Type of downsampling.
+ % downsampleFactor - Int or float: amount to downsample dimension by.
+ %
% changelog
% 2013.12.19 added the spatial downsampling to the function.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2022.02.09 [23:42:18] - Update for Matlab standards.
+ % 2022.06.28 [16:58:08] - Update information for users/comments.
% TODO
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
%========================
% default options
- % time or space
+ % Str: 'time' or 'space'. Dimension to downsample
options.downsampleDimension = 'time';
+ % Str: 'bilinear' or 'bicubic'. Type of downsampling.
options.downsampleType = 'bilinear';
- % any value, integers preferred
+ % Int or float: amount to downsample dimension by.
options.downsampleFactor = 4;
% exact dimensions to downsample in Z (time)
options.downsampleZ = [];
diff --git a/+ciapkg/+movie_processing/normalizeMovie.m b/+ciapkg/+movie_processing/normalizeMovie.m
index 5423f1d..fa6c26b 100644
--- a/+ciapkg/+movie_processing/normalizeMovie.m
+++ b/+ciapkg/+movie_processing/normalizeMovie.m
@@ -15,6 +15,7 @@
% 2021.01.15 [21:09:55] - Moved detrend support into normalizeMovie.
% 2021.06.02 [20:04:49] - Detrend movie now uses nanmean to get around issues in motion corrected videos.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.05.16 [16:39:45] - Switch away from using nanmean to mean('omitnan'). Detrend flight code refactor.
% TODO
%
@@ -721,7 +722,7 @@ function subfxnDetrend()
%Get dimension information about 3D movie matrix
[inputMovieX, inputMovieY, inputMovieZ] = size(inputMovie);
- frameMeanInputMovie = squeeze(nanmean(inputMovie,[1 2]));
+ frameMeanInputMovie = squeeze(mean(inputMovie,[1 2],'omitnan'));
trendVals = frameMeanInputMovie - detrend(frameMeanInputMovie,option.detrendDegree);
@@ -740,12 +741,14 @@ function subfxnDetrend()
% end
parfor frame = 1:nFramesToNormalize
- thisFrame = inputMovie(:,:,frame);
- thisFrame = squeeze(thisFrame);
- thisFrame = thisFrame - trendVals(frame);
- thisFrame = thisFrame + meanInputMovie;
+ inputMovie(:,:,frame) = inputMovie(:,:,frame) - trendVals(frame) + meanInputMovie;
- inputMovie(:,:,frame) = thisFrame;
+ % thisFrame = inputMovie(:,:,frame);
+ % thisFrame = squeeze(thisFrame);
+ % thisFrame = thisFrame - trendVals(frame);
+ % thisFrame = thisFrame + meanInputMovie;
+
+ % inputMovie(:,:,frame) = thisFrame;
if ~verLessThan('matlab', '9.2')
send(D, frame); % Update
diff --git a/+ciapkg/+signal_extraction/computeCnmfSignalExtractionPatch.m b/+ciapkg/+signal_extraction/computeCnmfSignalExtractionPatch.m
index dbcd4db..45eefeb 100644
--- a/+ciapkg/+signal_extraction/computeCnmfSignalExtractionPatch.m
+++ b/+ciapkg/+signal_extraction/computeCnmfSignalExtractionPatch.m
@@ -1,5 +1,5 @@
function [cnmfAnalysisOutput] = computeCnmfSignalExtractionPatch(inputMovie,numExpectedComponents,varargin)
- % Brapper function for CNMF, update for most recent versions.
+ % Wrapper function for CNMF, update for most recent versions.
% Building off of demo_script.m in CNMF github repo
% Most recent commit tested on: https://github.com/epnev/ca_source_extraction/commit/187bbdbe66bca466b83b81861b5601891a95b8d1
% https://github.com/epnev/ca_source_extraction/blob/master/demo_script_class.m
diff --git a/+ciapkg/+signal_extraction/computeCnmfSignalExtraction_v2.m b/+ciapkg/+signal_extraction/computeCnmfSignalExtraction_v2.m
index e196a95..9e77f56 100644
--- a/+ciapkg/+signal_extraction/computeCnmfSignalExtraction_v2.m
+++ b/+ciapkg/+signal_extraction/computeCnmfSignalExtraction_v2.m
@@ -1,5 +1,5 @@
function [cnmfAnalysisOutput] = computeCnmfSignalExtraction_v2(inputMovie,numExpectedComponents,varargin)
- % Brapper function for CNMF, update for most recent versions.
+ % Wrapper function for CNMF, update for most recent versions.
% Building off of demo_script.m in CNMF github repo
% Most recent commit tested on: https://github.com/epnev/ca_source_extraction/commit/187bbdbe66bca466b83b81861b5601891a95b8d1
% https://github.com/epnev/ca_source_extraction/blob/master/demo_script_class.m
diff --git a/+ciapkg/+signal_extraction/computeCnmfeSignalExtraction_batch.m b/+ciapkg/+signal_extraction/computeCnmfeSignalExtraction_batch.m
index ae739d4..a1b2245 100644
--- a/+ciapkg/+signal_extraction/computeCnmfeSignalExtraction_batch.m
+++ b/+ciapkg/+signal_extraction/computeCnmfeSignalExtraction_batch.m
@@ -21,6 +21,7 @@
% 2021.01.24 [14:29:06] - Added trace origin type to output structure.
% 2021.03.20 [20:20:27] - extractedSignalsType, extractedSignalsEstType struct update.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.07.29 [20:16:13] - Updated to add option to ignore remove_false_positives.
% TODO
%
@@ -30,10 +31,14 @@
% OVERALL
% turn on parallel
options.nonCNMF.parallel = 1;
+ % Binary: 1 = classify components,
+ options.nonCNMF.classifyComponents = 1;
% Binary: 1 = run merging algorithms
options.runMerge = 1;
% Binary: 1 = remove false positives using CNMF-E algorithm
options.runRemoveFalsePositives = 1;
+ % Str: HDF5 dataset name
+ options.nonCNMF.inputDatasetName = '/1';
% ===COMPUTATION
% Float: GB, memory space you allow to use in MATLAB
options.memory_size_to_use = 8; %
@@ -309,7 +314,7 @@
%% update spatial components
%% pick neurons from the residual
- [center_res, Cn_res, PNR_res] =neuron.initComponents_residual_parallel([], save_initialization, use_parallel, min_corr_res, min_pnr_res, seed_method_res);
+ [center_res, Cn_res, PNR_res] = neuron.initComponents_residual_parallel([], save_initialization, use_parallel, min_corr_res, min_pnr_res, seed_method_res);
if show_init
axes(ax_init);
plot(center_res(:, 2), center_res(:, 1), '.g', 'markersize', 10);
@@ -331,8 +336,10 @@
% update temporal
neuron.update_temporal_parallel(use_parallel);
- % delete bad neurons
- neuron.remove_false_positives();
+ if options.runRemoveFalsePositives==1
+ % delete bad neurons
+ neuron.remove_false_positives();
+ end
% merge neurons based on temporal correlation + distances
neuron.merge_neurons_dist_corr(show_merge);
@@ -350,6 +357,7 @@
% delete neurons
tags = neuron.tag_neurons_parallel(); % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
+
ids = find(tags>0);
if ~isempty(ids)
neuron.viewNeurons(ids, neuron.C_raw);
@@ -363,14 +371,18 @@
K = size(neuron.A,2);
tags = neuron.tag_neurons_parallel(); % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
- neuron.remove_false_positives();
+ if options.runRemoveFalsePositives==1
+ neuron.remove_false_positives();
+ end
neuron.merge_neurons_dist_corr(show_merge);
neuron.merge_high_corr(show_merge, merge_thr_spatial);
if K~=size(neuron.A,2)
neuron.update_spatial_parallel(use_parallel);
neuron.update_temporal_parallel(use_parallel);
- neuron.remove_false_positives();
+ if options.runRemoveFalsePositives==1
+ neuron.remove_false_positives();
+ end
end
%% save the workspace for future analysis
diff --git a/+ciapkg/+signal_processing/computePeakStatistics.m b/+ciapkg/+signal_processing/computePeakStatistics.m
index a53d763..011ff8f 100644
--- a/+ciapkg/+signal_processing/computePeakStatistics.m
+++ b/+ciapkg/+signal_processing/computePeakStatistics.m
@@ -1,49 +1,70 @@
function [peakOutputStat] = computePeakStatistics(inputSignals,varargin)
- % Get slope ratio, the average trace from detected peaks, and other peak-related statistics
+ % [peakOutputStat] = computePeakStatistics(inputSignals,varargin)
+ %
+ % Get slope ratio, the average trace from detected peaks, and other peak-related statistics.
+ %
% Biafra Ahanonu
% started: 2013.12.09
- % inputs
- % inputSignals = [n m] matrix where n={1....N}
- % outputs
- % slopeRatio
- % traceErr
- % fwhmSignal
- % avgPeakAmplitude
- % spikeCenterTrace
- % pwelchPxx
- % pwelchf
+ %
+ % Inputs
+ % inputSignals = [n m] matrix where n = {1....N} and m = frames.
+ %
+ % Outputs
+ % peakOutputStat - structure containing the following fields, where N = number of signals, P = # of peaks, W = peak window (frames):
+ % fwhmSignal - Cell array {1 N} of [1 P] vectors: Full width at half maximum for each peak.
+ % slopeRatio - Vector [1 N]: slope ratio (e.g. area of rise vs. decay) of peaks for each signal.
+ % avgSpikeTrace - Vector [N W]: average peak for each signal.
+ % avgSpikeVar - Vector [1 N]: average variance of the signal after the peak across all peaks.
+ % avgSpikeCorr - Vector [1 N]: average correlation of all post-peak signals.
+ % traceErr - Vector [1 N]: deviation in the error across all peaks within W window.
+ % avgFwhm - Vector [1 N]: mean Full width at half maximum across all peaks for each signal.
+ % fwhmSignalSignals - Cell array {1 N} of [1 P] vectors: Full width at half maximum for each peak.
+ % avgPeakAmplitude - Vector [1 N]: mean peak amplitude across all peaks for each signal.
+ % traceSkewness - Vector [1 N]: the skewness of each signal.
+ % traceKurtosis - Vector [1 N]: the kurtosis of each signal.
+ % traceFanoFactor - Vector [1 N]: the fano factor of each signal.
+ % traceAutoCorr - Vector [1 N]: the auto-correlation of each signal defined at set frame shift (see options.frameShiftAmt).
+ % spikeCenterTrace - Cell array {1 N} of [P W] vectors: matrix containing signal sliced around all peaks.
+ % pwelchPxx - Cell array {1 N}: PSDs for each signal.
+ % pwelchf - Cell array {1 N}: frequency corresponding to the output PSDs for each signal.
+
% TODO
% - Use the SNR signal calculated to determine when to end the S ratio calculation
% changelog
% 2013.12.24 - changed output so that it is a structure, allows more flexibility when adding new statistics.
% 2019.09.10 [11:30:07] - Added fano factor, trace frame lagged auto-correlation, parallelization, and misc improvements.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.03.14 [01:31:51] - Update comments along with code standard improvements.
+ % 2022.04.26 [00:12:03] - options.testpeaksArray can be used regardless of vector (with peak frames) orientation.
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
%========================
- %
+ % Char cell array: cell array of strings for different features to include in the output.
options.featureList = {'avgSpikeTrace','spikeCenterTrace','avgSpikeVar','avgSpikeCorr','avgPeakAmplitude','slopeRatio','fwhmTrace'};
+ % Binary: 1 = only detect and output the slope ratio. 0 = perform full analysis/output.
options.onlySlopeRatio = 0;
- %
- options.spikeROI = [-40:40];
- %
+ % Int vector: window (in frames) around each peak to perform the various peak statistics analyses.
+ options.spikeROI = -40:40;
+ % Int: number of frames before and after peak to use for certain statistics, e.g. slopeRatio.
options.slopeFrameWindow = 10;
- %
+ % Binary: 1 = show the wait bar. 0 = do not show wait bar.
options.waitbarOn = 1;
- % determine whether power-spectral density should be calculated
+ % Binary: 1 = calculate power-spectral density.
options.psd = 0;
- % should fwhm analysis be plotted?
+ % Binary: 1 = plot fwhm analysis. 0 = do not plot analysis.
options.fwhmPlot = 0;
- % save time if already computed peaks
+ % Matrix: save time if already computed peaks. [nSignals frame] matrix. Binary matrix with 1 = peaks, 0 = non-peaks.
options.testpeaks = [];
+ % Cell array: save time if already computed peaks. {1 nSignals} cell array. Each cell contains [1 nPeaks] vector that stores the frame locations of each peak.
options.testpeaksArray = [];
- % Median filter the data
+ % Binary: 1 = median filter the data.
options.medianFilter = 1;
- % number of frames to calculate median filter
+ % Int: number of frames to calculate rolling median filter.
options.medianFilterLength = 200;
+ % Int: the size of the moving average to use on the input signals, to smooth out noise.
options.movAvgFiltSize = [];
- % Int: how many frames to shift when calculating auto-correlation
+ % Int: number of frames to shift when calculating auto-correlation.
options.frameShiftAmt = 2;
% get options
options = getOptions(options,varargin);
@@ -64,24 +85,6 @@
end
end
- % peakOutputStat.fwhmSignal = [];
- % peakOutputStat.slopeRatio = [];
- % peakOutputStat.avgSpikeTrace = [];
- % peakOutputStat.avgSpikeVar = [];
- % peakOutputStat.avgSpikeCorr = [];
- % peakOutputStat.traceErr = [];
- % peakOutputStat.fwhmSignal = [];
- % peakOutputStat.avgFwhm = [];
- % peakOutputStat.fwhmSignalSignals = [];
- % peakOutputStat.avgPeakAmplitude = [];
- % peakOutputStat.traceSkewness = [];
- % peakOutputStat.traceKurtosis = [];
- % peakOutputStat.traceFanoFactor = [];
- % peakOutputStat.traceAutoCorr = [];
- % peakOutputStat.spikeCenterTrace = [];
- % peakOutputStat.pwelchPxx = [];
- % peakOutputStat.pwelchf = [];
-
% get a list of all indices to pull out
nSignals = size(inputSignals,1);
% reverseStr = '';
@@ -98,7 +101,7 @@
afterEach(D, @nUpdateParforProgress);
p = 0;
N = nSignals;
- nInterval = round(nSignals/30);%25
+ nInterval = round(N/30);%25
options_waitbarOn = options.waitbarOn;
end
@@ -128,12 +131,12 @@
avgSpikeCorr(i) = peakStat.avgSpikeCorr;
traceErr(i) = peakStat.traceErr;
fwhmSignal{i} = peakStat.fwhmTrace(:);
- avgFwhm(i) = nanmean(peakStat.fwhmTrace(:));
+ avgFwhm(i) = mean(peakStat.fwhmTrace(:),'omitnan');
fwhmSignalSignals{i} = peakStat.fwhmTrace(:);
avgPeakAmplitude(i) = peakStat.avgPeakAmplitude;
traceSkewness(i) = skewness(thisSignal(:));
traceKurtosis(i) = kurtosis(thisSignal(:));
- traceFanoFactor(i) = (nanstd(thisSignal(:))^2)/nanmean(thisSignal(:));
+ traceFanoFactor(i) = (std(thisSignal(:),'omitnan')^2)/mean(thisSignal(:),'omitnan');
traceAutoCorr(i) = corr(thisSignal(:),circshift(thisSignal(:),options_frameShiftAmt));
spikeCenterTrace{i} = peakStat.spikeCenterTrace;
if options_psd==1
@@ -217,8 +220,10 @@ function nUpdateParforProgress(~)
% [testpeaks dummyVar] = computeSignalPeaks(inputSignal);
% if peaks exists, do statistics else return NaNs
if ~isempty(testpeaks)
+ % Force peaks to be correct dimensions.
+ testpeaks = testpeaks(:);
% get a list of indices around which to extract spike signals
- extractMatrix = bsxfun(@plus,testpeaks',spikeROI);
+ extractMatrix = bsxfun(@plus,testpeaks,spikeROI);
extractMatrix(extractMatrix<=0)=1;
extractMatrix(extractMatrix>=size(inputSignal,2))=size(inputSignal,2);
peakStat.spikeCenterTrace = reshape(inputSignal(extractMatrix),size(extractMatrix));
@@ -239,7 +244,7 @@ function nUpdateParforProgress(~)
if size(spikeCenterTrace,1)==1
peakStat.avgSpikeTrace = spikeCenterTrace;
else
- peakStat.avgSpikeTrace = nanmean(spikeCenterTrace);
+ peakStat.avgSpikeTrace = mean(spikeCenterTrace,'omitnan');
end
% or get correlation
@@ -250,7 +255,7 @@ function nUpdateParforProgress(~)
corrHere = corr(spikeCenterTrace(:,round(end/2):end)',spikeCenterTrace(:,round(end/2):end)','type','Spearman');
corrHere(logical(eye(size(corrHere)))) = NaN;
- peakStat.avgSpikeCorr = nanmean(corrHere(:));
+ peakStat.avgSpikeCorr = mean(corrHere(:),'omitnan');
if 0
% size(spikeCenterTrace)
@@ -259,29 +264,29 @@ function nUpdateParforProgress(~)
corrHere = corr(spikeCenterTrace',spikeCenterTrace');
imagesc(corrHere)
corrHere(logical(eye(size(corrHere)))) = NaN;
- title(num2str(nanmean(corrHere(:))))
+ title(num2str(mean(corrHere(:),'omitnan')))
subplot(1,2,2)
corrHere = corr(spikeCenterTrace(:,round(end/2):end)',spikeCenterTrace(:,round(end/2):end)');
imagesc(corrHere)
corrHere(logical(eye(size(corrHere)))) = NaN;
- title(num2str(nanmean(corrHere(:))))
+ title(num2str(mean(corrHere(:),'omitnan')))
pause(0.01);
% size(corr(spikeCenterTrace',spikeCenterTrace'))
end
% peakStat.avgSpikeVar = nanmean(squeeze(nanvar(spikeCenterTrace(:,round(end/2):end),[],1)));
% Change to index of dispersion
- varH = nanvar(spikeCenterTrace(:,round(end/2):end),[],1);
- meanH = nanmean(spikeCenterTrace(:,round(end/2):end),1);
- peakStat.avgSpikeVar = nanmean(squeeze(varH));
- peakStat.avgSpikeVMR = nanmean(squeeze(varH./meanH));
+ varH = var(spikeCenterTrace(:,round(end/2):end),[],1,'omitnan');
+ meanH = mean(spikeCenterTrace(:,round(end/2):end),1,'omitnan');
+ peakStat.avgSpikeVar = mean(squeeze(varH),'omitnan');
+ peakStat.avgSpikeVMR = mean(squeeze(varH./meanH),'omitnan');
% get the peak amplitude
peakStat.avgPeakAmplitude = peakStat.avgSpikeTrace(find(spikeROI==0));
% slopeRatio = (peakDfof-avgSpikeTrace(find(spikeROI==-slopeFrameWindow)))/(peakDfof-avgSpikeTrace(find(spikeROI==slopeFrameWindow)));
% get the deviation in the error
- peakStat.traceErr = sum(nanstd(spikeCenterTrace))/sqrt(size(spikeCenterTrace,1));
+ peakStat.traceErr = sum(std(spikeCenterTrace,'omitnan'))/sqrt(size(spikeCenterTrace,1));
% % get a ratio metric (normalized between 1 and -1) for the asymmetry in the peaks
% prePeakIdx = find(spikeROI==-(slopeFrameWindow)):find(spikeROI==-1);
@@ -314,7 +319,7 @@ function nUpdateParforProgress(~)
if options_psd==1
% get the power-spectrum
- [peakStat.pwelchPxx peakStat.pwelchf] = pwelch(inputSignal,100,25,512,5);
+ [peakStat.pwelchPxx, peakStat.pwelchf] = pwelch(inputSignal,100,25,512,5);
end
else
peakStat.avgSpikeTrace = nan(1,length(spikeROI));
@@ -331,7 +336,7 @@ function nUpdateParforProgress(~)
end
end
end
-function plotStatistics()
+% function plotStatistics()
% figure(92929)
% hist(fwhmSignal,[0:nanmax(fwhmSignal)]); box off;
% xlabel('FWHM (frames)'); ylabel('count');
@@ -352,4 +357,24 @@ function plotStatistics()
% errorbar(spikeROI, avgSpikeTrace, traceErr);
% t=1:length(traceErr);
% fill([spikeROI fliplr(spikeROI)],[avgSpikeTrace+traceErr fliplr(avgSpikeTrace-traceErr)],[4 4 4]/8, 'FaceAlpha', 0.4, 'EdgeColor','none')
-end
\ No newline at end of file
+% end
+% function localfxn_createOutput()
+
+ % peakOutputStat.fwhmSignal = [];
+ % peakOutputStat.slopeRatio = [];
+ % peakOutputStat.avgSpikeTrace = [];
+ % peakOutputStat.avgSpikeVar = [];
+ % peakOutputStat.avgSpikeCorr = [];
+ % peakOutputStat.traceErr = [];
+ % peakOutputStat.fwhmSignal = [];
+ % peakOutputStat.avgFwhm = [];
+ % peakOutputStat.fwhmSignalSignals = [];
+ % peakOutputStat.avgPeakAmplitude = [];
+ % peakOutputStat.traceSkewness = [];
+ % peakOutputStat.traceKurtosis = [];
+ % peakOutputStat.traceFanoFactor = [];
+ % peakOutputStat.traceAutoCorr = [];
+ % peakOutputStat.spikeCenterTrace = [];
+ % peakOutputStat.pwelchPxx = [];
+ % peakOutputStat.pwelchf = [];
+% end
\ No newline at end of file
diff --git a/+ciapkg/+signal_processing/computeSignalPeaks.m b/+ciapkg/+signal_processing/computeSignalPeaks.m
index b368a9f..6f16c4a 100644
--- a/+ciapkg/+signal_processing/computeSignalPeaks.m
+++ b/+ciapkg/+signal_processing/computeSignalPeaks.m
@@ -1,35 +1,43 @@
-function [signalPeaks, signalPeaksArray, signalSigmas] = computeSignalPeaks(signalMatrix, varargin)
+function [signalPeaks, signalPeaksArray, signalSigmas, signalStruct] = computeSignalPeaks(signalMatrix, varargin)
+ % [signalPeaks, signalPeaksArray, signalSigmas] = computeSignalPeaks(signalMatrix, varargin)
+ %
% Binarize [0,1] input analog signals based on peaks in the signal.
+ %
% Biafra Ahanonu
% started: 2013.10.28
+ %
% inputs
- % signalMatrix: [nSignals frame] matrix
+ % signalMatrix: [nSignals frame] matrix containing analog input signals.
% outputs
- % signalPeaks: [nSignals frame] matrix. Binary matrix with 1 = peaks.
- % signalPeaksArray: {1 nSignals} cell array. Each cell contains [1 nPeaks] vector that stores the frame locations of each peak.
+ % signalPeaks: [nSignals frame] matrix. Binary matrix with 1 = peaks, 0 = non-peaks.
+ % signalPeaksArray: {1 nSignals} cell array. Each cell contains [1 nPeaks] vector that stores the frame locations of each peak.
+ % signalSigmas: [nSignals 1] - std of each signal.
+ % signalStruct: structure containing signalPeaks and signalPeaksArray if multiple thresholds requested.
% options
- % See below.
- % % make a plot?
- % options.makePlots = 0;
- % % show waitbar?
- % options.waitbarOn = 1;
- % % make summary plots of spike information
- % options.makeSummaryPlots = 0;
- % % number of standard deviations above the threshold to count as spike
- % options.numStdsForThresh = 3;
- % % minimum number of time units between events
- % options.minTimeBtEvents = 8;
- % % shift peak detection
- % options.nFramesShift = 0;
- % % should diff and fast oopsi be done?
- % options.addedAnalysis = 0;
- % % use simulated oopsi data
- % options.oopsiSimulated = 0;
+ % See below.
+ % % make a plot?
+ % options.makePlots = 0;
+ % % show waitbar?
+ % options.waitbarOn = 1;
+ % % make summary plots of spike information
+ % options.makeSummaryPlots = 0;
+ % % number of standard deviations above the threshold to count as spike
+ % options.numStdsForThresh = 3;
+ % % minimum number of time units between events
+ % options.minTimeBtEvents = 8;
+ % % shift peak detection
+ % options.nFramesShift = 0;
+ % % should diff and fast oopsi be done?
+ % options.addedAnalysis = 0;
+ % % use simulated oopsi data
+ % options.oopsiSimulated = 0;
% changelog
% 2015.10.06 [00:14:09] Changed computePeakForSignal to shift the signal to the actual nearby peak since findpeak is sometimes off by a frame or two, should also improve the S-ratio.
% 2016.07.05 [14:52:43] Made changes to computePeakForSignal to improve diff based peak detection.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.04.20 [21:29:09] - Better comments for options.
% TODO:
+ % Add option to obtain multiple signal peak outputs from different thresholds in the same run, would save time.
% allow input of options file (e.g. for different GCaMP variants, brain regions, etc.)
% integrate nearest neighbor into analysis if there is a lot of cross-talk
% possibly integrate into identifySpikes?
@@ -42,47 +50,43 @@
%========================
% Binary: 1 = show plots with found events and other information for each signal. 0 = do not show signal plot GUI.
options.makePlots = 0;
- % show waitbar?
+ % Binary: 1 = show wait bar, 0 = do not show wait bar.
options.waitbarOn = 1;
- % make summary plots of spike information
+ % Binary: 1 = make summary plots of spike information, 0 = no summary plots.
options.makeSummaryPlots = 0;
% ===
- % number of standard deviations above the threshold to count as spike
- % options.numStdsForThresh = 3;
- % options.numStdsForThresh = 0.5;
- options.numStdsForThresh = 3;
- % alternative for display purposes
+ % Int: number of standard deviations above the threshold to count as spike
+ options.numStdsForThresh = 3; % 0.5
+ % DEPRECIATED - Int: alternative to options.numStdsForThresh for display purposes
options.numStdsForThreshTwo = 2;
- % minimum number of time units between events
+ % Int: minimum number of time units between events.
options.minTimeBtEvents = 8;
- % detect on differential ('diff') or raw ('raw') trace
- % options.detectMethod = 'raw';
- options.detectMethod = 'diff';
- % the size of the moving average
+ % Str: detect on differential ('diff') or raw ('raw') trace
+ options.detectMethod = 'diff'; % 'raw'
+ % Int: the size of the window to use to ignore smaller peaks near larger peaks.
options.movAvgReqSize = 2;
+ % Int: the size of the moving average to use on the input signals, to smooth out noise.
options.movAvgFiltSize = 3;
- % decide whether to have a moving average
+ % Binary: 1 = use moving average as specified in options.movAvgFiltSize.
options.doMovAvg = 1;
- % subtract median calculated over a filter of some range.
+ % Binary: 1 = subtract median calculated over a filter of some range.
options.doMedianFilter = 1;
- % number of frames to calculate median filter
+ % Int: number of frames to calculate rolling median filter.
options.medianFilterLength = 201;
- % report the midpoint of the rise
- options.reportMidpoint=0;
- % shift peak detection
- options.nFramesShift = 0;
- % region around each peak to look for a maximum to adjust the test peak by
+ % Binary: 1 = leave report peaks as determined by findpeaks (e.g. normally midpoint of the peak rise if using 'diff'). 0 = adjust peak location to the maximum value found within a options.peakMaxLook window.
+ options.reportMidpoint = 0;
+ % Int vector: frames before and after region around each peak to look for a maximum to adjust the test peak by.
options.peakMaxLook = -6:6;
+ % Int: number of frames to shift detected peaks.
+ options.nFramesShift = 0;
% ===
- % should diff and fast oopsi be done?
+ % Binary: 1 = perform diff and fast oopsi.
options.addedAnalysis = 0;
- % use simulated oopsi data
+ % Binary: 1 = use simulated oopsi data.
options.oopsiSimulated = 0;
- % decide whether to have a moving average
- % options.doMovAvg = 0;
- % 1 = open workers, 0 = do not open workers
+ % Binary: 1 = open workers, 0 = do not open workers
options.parallel = 1;
- % display output
+ % Binary: 1 = display output information. 0 = suppress most output (e.g. when running in batch for certain applications).
options.outputInfo = 1;
% Binary: 1 = convert input inputSignals matrix to cell array
options.convertSignalsToCell = 1;
@@ -120,6 +124,8 @@
end
end
+ signalStruct = struct;
+
% this matrix will contain binarized version of signalMatrix
signalPeaks = zeros(size(signalMatrix));
% contains a list for each signal of locations of peaks
diff --git a/+ciapkg/+signal_processing/computeSignalSnr.m b/+ciapkg/+signal_processing/computeSignalSnr.m
index 86c9c8e..686e91f 100644
--- a/+ciapkg/+signal_processing/computeSignalSnr.m
+++ b/+ciapkg/+signal_processing/computeSignalSnr.m
@@ -1,26 +1,34 @@
function [inputSnr, inputMse, inputSnrSignal, inputSnrNoise, outputSignal, outputNoise] = computeSignalSnr(inputSignals,varargin)
- % Obtains an approximate SNR for an input signal
+ % [inputSnr, inputMse, inputSnrSignal, inputSnrNoise, outputSignal, outputNoise] = computeSignalSnr(inputSignals,varargin)
+ %
+ % Calculates the SNR for an input signal. Multiple algorithms available.
+ %
% Biafra Ahanonu
% started: 2013.11.04 [11:54:09]
- % inputs
- % inputSignals: [nSignals frame] matrix
- % outputs
- % inputSnr: [1 nSignals] vector of calculated SNR. NaN used where SNR is not calculated.
- % inputMse: [1 nSignals] vector of MSE. NaN used where MSE is not calculated.
+ %
+ % Inputs
+ % inputSignals: [nSignals frame] matrix
+ %
+ % Outputs
+ % inputSnr: [1 nSignals] vector of calculated SNR. NaN used where SNR is not calculated.
+ % inputMse: [1 nSignals] vector of MSE. NaN used where MSE is not calculated.
+ %
% options
- % % type of SNR to calculate
- % options.SNRtype = 'mean(signal)/std(noise)';
- % % frames around which to remove the signal for noise estimation
- % options.timeSeq = [-10:10];
- % % show the waitbar
- % options.waitbarOn = 1;
- % % save time if already computed peaks
- % options.testpeaks = [];
- % options.testpeaksArray = [];
+ % % type of SNR to calculate
+ % options.SNRtype = 'mean(signal)/std(noise)';
+ % % frames around which to remove the signal for noise estimation
+ % options.timeSeq = [-10:10];
+ % % show the waitbar
+ % options.waitbarOn = 1;
+ % % save time if already computed peaks
+ % options.testpeaks = [];
+ % options.testpeaksArray = [];
+
% changelog
% 2013.12.08 now uses RMS to calculate the SNR after removing the signal to get an estimated noise trace.
% 2018.03.25 - Added iterative method to determine when signal ends. also added mean centering of trace to correct for offset traces causing problems.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.03.14 [01:42:21] - Better comments for options.
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
@@ -37,8 +45,9 @@
options.waitbarOn = 1;
% whether to display output information
options.displayOutput = 1;
- % save time if already computed peaks
+ % Matrix: save time if already computed peaks. [nSignals frame] matrix. Binary matrix with 1 = peaks, 0 = non-peaks.
options.testpeaks = [];
+ % Cell array: save time if already computed peaks. {1 nSignals} cell array. Each cell contains [1 nPeaks] vector that stores the frame locations of each peak.
options.testpeaksArray = [];
% alternative if want to use non-shared peaks
options.testpeaksArrayAlt = [];
diff --git a/+ciapkg/+video/createMovieFromVector.m b/+ciapkg/+video/createMovieFromVector.m
index a082ac7..3f48850 100644
--- a/+ciapkg/+video/createMovieFromVector.m
+++ b/+ciapkg/+video/createMovieFromVector.m
@@ -11,6 +11,7 @@
% changelog
% 2021.05.04 [09:29:19] - Users can now manually change value assigned to center line or signal.
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.08.02 [08:38:38] - Fixed issue where the min 2nd dimension would always be options.windowSize, leading to errors.
% TODO
%
@@ -55,7 +56,7 @@
reverseStr = '';
% amount to downsample the second dimension
movieDimY = round(movieDim(1)/options.secondDimDownsample);
-
+ size(vectorMovie)
for frameNo = 1:nFrames
frameVectorIdx = windowSize+frameNo;
@@ -65,6 +66,8 @@
frameVectorIdx(frameVectorIdx>nFrames) = 0;
% frameVectorIdx(frameVectorIdx==0) = frameVectorIdx(find(frameVectorIdx,1,'last'));
+ thisFrame = vectorMovie(:,:,frameNo);
+
% add each time point in vector to movie
for thisFrameVectorNo = 1:length(frameVectorIdx)
if frameVectorIdx(thisFrameVectorNo)==0
@@ -73,11 +76,13 @@
relativeStimValue = round(inputVector(frameVectorIdx(thisFrameVectorNo))*movieDimY);
end
% add relative (to max) value of vector to movie
- vectorMovie(1:relativeStimValue,thisFrameVectorNo,frameNo) = options.signalValue;
+ % vectorMovie(1:relativeStimValue,thisFrameVectorNo,frameNo) = options.signalValue;
+ thisFrame(1:relativeStimValue,thisFrameVectorNo) = options.signalValue;
end
% resize vector movie to match movie dimensions given
- vectorMovie(:,:,frameNo) = imresize(vectorMovie(:,1:length(frameVectorIdx),frameNo),[movieDimY movieDim(2)],'bilinear');
+ % vectorMovie(:,:,frameNo) = imresize(vectorMovie(:,1:length(frameVectorIdx),frameNo),[movieDimY movieDim(2)],'bilinear');
+ vectorMovie(:,:,frameNo) = imresize(thisFrame(:,1:length(frameVectorIdx)),[movieDimY movieDim(2)],'bilinear');
vectorMovie(:,round(end/2),frameNo) = options.centerLineValue;
reverseStr = cmdWaitbar(frameNo,nFrames,reverseStr,'inputStr','creating matrix: ','waitbarOn',1,'displayEvery',50);
diff --git a/+ciapkg/+view/changeFont.m b/+ciapkg/+view/changeFont.m
index 7ed24ec..c4f2aeb 100644
--- a/+ciapkg/+view/changeFont.m
+++ b/+ciapkg/+view/changeFont.m
@@ -14,6 +14,7 @@
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2021.11.18 [09:00:28] - Updated so can update font size, name, color more independent of one another.
% 2022.01.14 [05:53:37] - Updated so doesn't change Axes backgroundcolor when changing font color, only Axes text.
+ % 2022.03.14 [04:06:10] - Also check for matlab.ui.control.UIControl when conducting font color changes and ignore to not cause errors.
% TODO
% Add support for changing other font aspects, e.g. figure Font family, command window font, etc.
@@ -60,7 +61,7 @@
tmpList = findall(gcf,'-property','FontSize');
rmIdx = zeros([1 length(tmpList)]);
for i = 1:length(tmpList)
- if strcmp(class(tmpList(i)),'matlab.graphics.axis.Axes')==1
+ if any(strcmp(class(tmpList(i)),{'matlab.graphics.axis.Axes','matlab.ui.control.UIControl'}))
rmIdx(i) = 1;
end
end
diff --git a/+ciapkg/+view/createGroupColorMaps.m b/+ciapkg/+view/createGroupColorMaps.m
index 889117b..6efa474 100644
--- a/+ciapkg/+view/createGroupColorMaps.m
+++ b/+ciapkg/+view/createGroupColorMaps.m
@@ -10,8 +10,9 @@
% changelog
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.07.20 [14:47:26] - Added fast thresholding option.
% TODO
- %
+ %
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
@@ -20,6 +21,8 @@
options.dilateOutlinesFactor = 0;
% Float: threshold for thresholding images, fraction of maximum image value.
options.threshold = 0.4;
+ % Binary: 1 = fast thresholding (vectorized), 0 = normal thresholding
+ options.fastThresholding = 1;
% get options
options = getOptions(options,varargin);
% display(options)
@@ -35,7 +38,7 @@
% inputImages = pcaicaAnalysisOutput.IcaFilters;
% Get boundary indices (for outline of cell locations)
- [inputImagesThresholded, boundaryIndices] = thresholdImages(inputImages,'binary',0,'threshold',options.threshold,'imageFilter','none','getBoundaryIndex',1,'imageFilterBinary','none');
+ [inputImagesThresholded, boundaryIndices] = thresholdImages(inputImages,'binary',0,'threshold',options.threshold,'imageFilter','none','getBoundaryIndex',1,'imageFilterBinary','none','fastThresholding',options.fastThresholding);
dilateOutlinesFactor = options.dilateOutlinesFactor;
diff --git a/+ciapkg/+view/playMovie.m b/+ciapkg/+view/playMovie.m
index 9002186..3d6ad8f 100644
--- a/+ciapkg/+view/playMovie.m
+++ b/+ciapkg/+view/playMovie.m
@@ -1,12 +1,17 @@
function [exitSignal, ostruct] = playMovie(inputMovie, varargin)
+ % [exitSignal, ostruct] = playMovie(inputMovie, varargin)
+ %
% Plays a movie that is either a 3D xyt matrix or path to file. Additional inputs to view multiple movies or sync'd signal data and can also save the resulting figure as a movie.
+ %
% Biafra Ahanonu
% started 2013.11.09 [10:39:50]
%
% inputs
- % inputMovie - either grayscale or color movie matrix:
- % grayscale: [x y t] matrix of x,y height/width and t frames
- % RGB: [x y C t] matrix of x,y height/width, t frames, and C color channel (3 as RGB)
+ % inputMovie - either:
+ % grayscale: [x y t] matrix of x,y height/width and t frames
+ % RGB: [x y C t] matrix of x,y height/width, t frames, and C color channel (3 as RGB)
+ % Path: full file path to a movie containing a [x y t] or [x y C t] matrix.
+ % Path (MAT-file): full path to a MAT-file with a variable containing a [x y t] or [x y C t] matrix.
% options
% fps -
% extraMovie - extra movie to play, [X Y Z] matrix of X,Y height/width and Z frames
@@ -35,7 +40,12 @@
% 2021.07.03 [08:16:32] - Added feature to input line overlays on input movie (e.g. to overlay cell extraction outputs).
% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2021.10.07 [15:05:52] - Update to avoid caxis with rgb movies, making them hard to view.
- % 2022.02.24 [10:24:28] - AVI now read(...,'native') is faster. Also add `primaryTrackingPointNback` option to allow a trailing number of points from prior frames to appear.
+ % 2022.02.24 [10:24:28] - AVI now read(...,'native') is faster. Also add `primaryTrackingPointNback` option to allow a trailing number of points from prior frames to appear.
+ % 2022.03.14 [02:13:44] - Added support for read from disk (minimal RAM use) matfile reading of a MAT-file with data in a specified variable name.
+ % 2022.03.14 [04:21:16] - Default background and theme is now black.
+ % 2022.03.15 [01:33:56] - By default use 'painters' renderer as that can produce smoother rendering than opengl.
+ % 2022.07.26 [09:43:59] - Sub-sampling now has option to downsample instead of just taking every X pixel, slower but better quality.
+ % 2022.09.14 [08:55:37] - Add renderer option to allow users to choose painters or opengl.
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
@@ -43,6 +53,8 @@
% options
% Int: figure number to open
options.figNo = 42;
+ % Binary: 1 = do not close figure before loading GUI. 0 = close figure.
+ options.keepFig = 1;
% Int: set the frame rate to display the movie.
options.fps = 20;
% Int: set the min/max frames per second to display the movie.
@@ -51,6 +63,8 @@
options.fpsMin = 1/10;
% Int: By what amount to sub-sample the frames in spatial dimensions. 1 = no sub-sampling. >1 = sub-sampling enabled.
options.subSampleMovie = 1;
+ % Str: Type of sub-sampling to apply when user request subSampleMovie>1. 'Downsample' (imresize to downsample movie bi-linearly) or 'subsample' (take every X pixels).
+ options.subSampleMovieType = 'downsample';
% Matrix: [X Y Z], additional movie to show. X,Y height/width and Z frames.
options.extraMovie = [];
% 2D matrix: [signals frames] signals related to inputMovie.
@@ -118,6 +132,16 @@
options.contrastFixMultiplier = 1e2;
% Int: Multiplier to set contrast to a range usable by GUI elements.
options.contrastFixMultiplierGUI = 1e5;
+ % Str: name of variable in MAT-file to use for loading data.
+ options.matfileVarname = '';
+ % Vector: [R G B] vector for color of background for slider.
+ options.sliderBkgdColor = [1 1 1]*0.3;
+ % Vector: [R G B] vector for color of text in the menu.
+ options.menuFontColor = [1 1 1];
+ % Vector: [R G B] vector for color of menu background.
+ options.menuBkgdColor = [0 0 0]+0.2;
+ % Str: 'painters' or 'opengl'
+ options.renderer = 'opengl';
% get options
options = getOptions(options,varargin);
% options
@@ -137,35 +161,52 @@
% Obtain movie information and connect to file if user gives a path to a movie.
if ischar(inputMovie)==1
inputMovieIsChar = 1;
- % inputMovieName = inputMovie;
- inputMovieDims = loadMovieList(inputMovie,'inputDatasetName',options.inputDatasetName,'displayInfo',1,'getMovieDims',1,'displayWarnings',0);
- inputMovieDims = [inputMovieDims.one inputMovieDims.two inputMovieDims.three];
- options.nFrames = inputMovieDims(3);
- nFramesOriginal = options.nFrames;
-
- readMovieChunks = 1;
[movieType, supported, movieTypeSpecific] = ciapkg.io.getMovieFileType(inputMovie);
if ~isempty(options.extraMovie)
[movieTypeExtra, supported, movieTypeSpecificExtra] = ciapkg.io.getMovieFileType(options.extraMovie);
end
- % If NWB, change dataset name to NWB default
- if strcmp(movieTypeSpecific,'nwb')
- options.inputDatasetName = options.defaultNwbDatasetName{1};
- end
-
if supported==0
disp('Unsupported movie type provided.')
return;
+ else
+ disp(['Supported movie type provided: ' movieType])
end
- % Setup connection to file to reduce I/O for file types that need it.
- [~,movieFileID,inputMovieDims] = ciapkg.io.readFrame(inputMovie,1,'inputDatasetName',options.inputDatasetName);
+ readMovieChunks = 1;
- if ~isempty(options.extraMovie)
- [~,movieFileIDExtra,inputMovieDimsExtra] = ciapkg.io.readFrame(options.extraMovie,1,'inputDatasetName',options.inputDatasetName);
+ if strcmp(movieType,'mat')==1
+ disp(['Opening connection to MAT-file: ' inputMovie])
+ matObj = matfile(inputMovie);
+
+ % If user has not input a variable name, show a list
+ if isempty(options.matfileVarname)
+ matfileVarList = who('-file', inputMovie);
+ [userMatIdx,~] = listdlg('PromptString','Select variable in MAT-file to play.','ListString',matfileVarList);
+ options.matfileVarname = matfileVarList{userMatIdx};
+ end
+ inputMovieDims = size(matObj,options.matfileVarname);
+ else
+ % inputMovieName = inputMovie;
+ inputMovieDims = loadMovieList(inputMovie,'inputDatasetName',options.inputDatasetName,'displayInfo',1,'getMovieDims',1,'displayWarnings',0);
+ inputMovieDims = [inputMovieDims.one inputMovieDims.two inputMovieDims.three];
+
+ % If NWB, change dataset name to NWB default
+ if strcmp(movieTypeSpecific,'nwb')
+ options.inputDatasetName = options.defaultNwbDatasetName{1};
+ end
+
+ % Setup connection to file to reduce I/O for file types that need it.
+ [~,movieFileID,~] = ciapkg.io.readFrame(inputMovie,1,'inputDatasetName',options.inputDatasetName);
+
+ if ~isempty(options.extraMovie)
+ [~,movieFileIDExtra,inputMovieDimsExtra] = ciapkg.io.readFrame(options.extraMovie,1,'inputDatasetName',options.inputDatasetName);
+ end
end
+ options.nFrames = inputMovieDims(3);
+ nFramesOriginal = options.nFrames;
+
else
inputMovieIsChar = 0;
inputMovieDims = size(inputMovie);
@@ -218,13 +259,19 @@
% pass to calling functions, in case you want to exit an upper-level loop
exitSignal = 0;
- try
- close(options.figNo)
- catch
- end
+ try
+ if options.keepFig==0
+ close(options.figNo)
+ end
+ catch
+ end
fig1 = figure(options.figNo);
% fig = figure(42,'KeyPressFcn',@detectKeyPress);
+ % Set the default renderer
+ % set(fig1,'Renderer','painters'); % 'opengl'
+ set(fig1,'Renderer',options.renderer); % 'opengl'
+
clf
set(findobj(gcf,'type','axes'),'hittest','off')
@@ -287,9 +334,13 @@
% axHandle = fig1;
end
- if ischar(inputMovie)==1
+ if inputMovieIsChar==1
+ if strcmp(movieType,'mat')==1
+ [tmpFrame] = matObj.(options.matfileVarname)(:,:,1);
+ else
+ [tmpFrame] = ciapkg.io.readFrame(inputMovie,1,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
+ end
% tmpFrame = subfxn_readMovieDisk(inputMovie,1,movieType);
- [tmpFrame] = ciapkg.io.readFrame(inputMovie,1,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
% tmpFrame = loadMovieList(inputMovie,'inputDatasetName',options.inputDatasetName,'displayInfo',0,'displayDiagnosticInfo',0,'displayWarnings',0,'frameList',1);
else
if length(size(inputMovie))==4
@@ -305,7 +356,7 @@
imagesc(tmpFrame)
axHandle = gca;
- mainTitleHandle = title(options.extraTitleText);
+ % mainTitleHandle = title(options.extraTitleText);
% axHandle.Toolbar.Visible = 'off';
box off;
if ~isempty(options.extraLinePlot)
@@ -329,7 +380,8 @@
sliderStepF = [1/(nFrames*0.1) 0.2];
end
frameSlider = uicontrol('style','slider','Units', 'normalized','position',[15 1 80 2]/100,...
- 'min',1,'max',nFrames,'Value',1,'SliderStep',sliderStepF,'callback',@frameCallback,'Enable','inactive','ButtonDownFcn',@pauseLoopCallback);
+ 'min',1,'max',nFrames,'Value',1,'SliderStep',sliderStepF,'callback',@frameCallback,...
+ 'BackgroundColor',options.sliderBkgdColor,'Enable','inactive','ButtonDownFcn',@pauseLoopCallback);
% set(gcf,'WindowButtonDownFcn',@pauseLoopCallback);
% addlistener(frameSlider,'Value','PostSet',@pauseLoopCallback);
% waitfor(source,'Value')
@@ -340,6 +392,12 @@
% TITLE AND COMMANDS
% close(fig1);fig1 = figure(42);
[supTitleStr, suptitleHandle, conMenu] = subfxn_createShortcutInfo();
+ if ~isempty(options.extraTitleText)
+ % mainTitleHandle = title([supTitleStr 10 options.extraTitleText]);
+ mainTitleHandle = title([supTitleStr]);
+ % currTmpTitle = get(suptitleHandle,'String');
+ % set(suptitleHandle,'String',[currTmpTitle 10 options.extraTitleText]);
+ end
% ==========================================
% SETUP FIGURE STATES
@@ -349,7 +407,7 @@
% keydown = 0;
% set(fig1,'KeyPressFcn', '1;');
set(fig1,'KeyPressFcn', @detectKeyPress);
- set(gcf, 'WindowScrollWheelFcn', @mouseWheelChange);
+ set(gcf, 'WindowScrollWheelFcn', @mouseWheelChange);
set(gcf,'currentch','3');
keyIn = get(gcf,'CurrentCharacter');
set(gcf,'SelectionType','normal');
@@ -395,7 +453,8 @@
% options.extraLinePlotLegend = options.labelLegend;
end
% use for references to keep contrast stable across frames
- firstFrame = squeeze(inputMovie(:,:,1));
+ % firstFrame = squeeze(inputMovie(:,:,1));
+
maxAdjFactor = 1;
if ~isempty(options.movieMinMax)
minMovie(1) = double(options.movieMinMax(1));
@@ -409,14 +468,18 @@
extraMovieFrame = inputMovie(find(options.extraMovie));
end
else
- if ischar(inputMovie)==1
+ if inputMovieIsChar==1
% tmpFrame = loadMovieList(inputMovie,'inputDatasetName',options.inputDatasetName,'displayInfo',0,'displayDiagnosticInfo',0,'displayWarnings',0,'frameList',1:100);
% inputMovieFrame = subfxn_readMovieDisk(inputMovie,1,movieType);
% if ~isempty(options.extraMovie)
% extraMovieFrame = subfxn_readMovieDisk(options.extraMovie,1,movieType,1);
% end
- [inputMovieFrame] = ciapkg.io.readFrame(inputMovie,1,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
+ if strcmp(movieType,'mat')==1
+ [inputMovieFrame] = matObj.(options.matfileVarname)(:,:,1);
+ else
+ [inputMovieFrame] = ciapkg.io.readFrame(inputMovie,1,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
+ end
if ~isempty(options.extraMovie)
extraMovieFrame = ciapkg.io.readFrame(options.extraMovie,1,'movieFileID',movieFileIDExtra,'inputMovieDims',inputMovieDimsExtra,'inputDatasetName',options.inputDatasetName);
end
@@ -455,9 +518,11 @@
% guiMinMax
% pause
contrastSliderLow = uicontrol('style','slider','Units', 'normalized','position',[21 3 37 2]/100,...
- 'min',guiMinMax(1),'max',guiMinMax(2),'Value',guiMinMax(1),'SliderStep',sliderStepC,'callback',@contrastCallback,'Enable','on','ButtonDownFcn',@contrastButtonActivateCallback);
+ 'min',guiMinMax(1),'max',guiMinMax(2),'Value',guiMinMax(1),'SliderStep',sliderStepC,...
+ 'BackgroundColor',options.sliderBkgdColor,'callback',@contrastCallback,'Enable','on','ButtonDownFcn',@contrastButtonActivateCallback);
contrastSliderHigh = uicontrol('style','slider','Units', 'normalized','position',[58 3 37 2]/100,...
- 'min',guiMinMax(1),'max',guiMinMax(2),'Value',guiMinMax(2),'SliderStep',sliderStepC,'callback',@contrastCallback,'Enable','on','ButtonDownFcn',@contrastButtonActivateCallback);
+ 'min',guiMinMax(1),'max',guiMinMax(2),'Value',guiMinMax(2),'SliderStep',sliderStepC,...
+ 'BackgroundColor',options.sliderBkgdColor,'callback',@contrastCallback,'Enable','on','ButtonDownFcn',@contrastButtonActivateCallback);
contrastFrameText = uicontrol('style','edit','Units', 'normalized','position',[1 3 20 2]/100,'FontSize',9,'string',sprintf('Contrast: %0.3f, %0.3f',options.movieMinMax(1), options.movieMinMax(2)));
% =================================================
@@ -499,6 +564,10 @@
colorbarSwitchTwo = 1;
pauseLoop = 0;
+ set(gcf,'color',[0 0 0]);
+ ciapkg.view.changeFont(9,'fontColor','w')
+ set(gca,'color',[0 0 0]);
+
try
while loopSignal==1
% [figHandle figNo] = openFigure(42, '');
@@ -507,13 +576,13 @@
% set(frameText,'string',['Frame ' num2str(frame) '/' num2str(nFrames)])
% =====================
- if options.runImageJ==1&~ischar(inputMovie)
+ if options.runImageJ==1&&~ischar(inputMovie)
subfxn_imageJ(inputMovie);
% Run once else loops without exit
options.runImageJ = 0;
end
if ~isempty(options.extraMovie)
- subplotNum = [1];
+ subplotNum = 1;
end
if ~isempty(options.extraLinePlot)
subplotNum = [1 3];
@@ -521,9 +590,12 @@
% Display an image from the movie
if readMovieChunks==1
- %
- % thisFrame = subfxn_readMovieDisk(inputMovie,frame,movieType);
- [thisFrame] = ciapkg.io.readFrame(inputMovie,frame,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
+ if strcmp(movieType,'mat')==1
+ [thisFrame] = matObj.(options.matfileVarname)(:,:,frame);
+ else
+ % thisFrame = subfxn_readMovieDisk(inputMovie,frame,movieType);
+ [thisFrame] = ciapkg.io.readFrame(inputMovie,frame,'movieFileID',movieFileID,'inputMovieDims',inputMovieDims,'inputDatasetName',options.inputDatasetName);
+ end
else
if length(size(inputMovie))==4
thisFrame = squeeze(inputMovie(:,:,:,frame));
@@ -600,7 +672,19 @@
montageHandle = findobj(axHandle,'Type','image');
if options.subSampleMovie>1
ssm = options.subSampleMovie;
- set(montageHandle,'Cdata',thisFrame(1:ssm:end,1:ssm:end),'AlphaData',imAlpha(1:ssm:end,1:ssm:end));
+
+ % Spatially downsample movie, slower but improved quality
+ thisFrame2 = thisFrame;
+ thisFrame2(isnan(thisFrame2)) = 0;
+ thisFrame2 = imresize(thisFrame,1/ssm,'bilinear');
+ % imAlpha2 = imresize(imAlpha,ssm,'bilinear');
+ switch options.subSampleMovieType
+ case 'downsample'
+ set(montageHandle,'Cdata',thisFrame2,'AlphaData',imAlpha(1:ssm:end,1:ssm:end));
+ case 'subsample'
+ set(montageHandle,'Cdata',thisFrame(1:ssm:end,1:ssm:end),'AlphaData',imAlpha(1:ssm:end,1:ssm:end));
+ otherwise
+ end
else
if length(size(thisFrame))==3
% set(montageHandle,'Cdata',squeeze(thisFrame(:,:,1)),'AlphaData',imAlpha);
@@ -859,11 +943,11 @@
% if strcmp(thisKey,'f'); break; end;
% if strcmp(thisKey,'p'); pause; thisKey=[]; end;
% end
- % [frame pauseLoop]
+ % [frame pauseLoop]
if pauseLoop==0
frame = frame+round(dirChange);
- subfxn_updateSliderInfo();
- elseif pauseLoop==1
+ subfxn_updateSliderInfo();
+ elseif pauseLoop==1
end
if frame>nFrames
if options.recordMovie~=0
@@ -903,14 +987,14 @@ function detectKeyPress(H,E)
keyIn = get(H,'CurrentCharacter');
% drawnow
% keyIn
- % if double(keyIn)==112&pauseLoop==1
- % pauseLoop = 0;
- % end
+ % if double(keyIn)==112&pauseLoop==1
+ % pauseLoop = 0;
+ % end
end
function pauseLoopCallback(source,eventdata)
% disp([num2str(frame) ' - pause loop'])
- % keyIn = get(gcf,'CurrentCharacter');
- % disp(keyIn)
+ % keyIn = get(gcf,'CurrentCharacter');
+ % disp(keyIn)
set(frameSlider,'Enable','on')
addlistener(frameSlider,'Value','PostSet',@frameCallbackChange);
% pauseLoop = 1;
@@ -923,8 +1007,8 @@ function pauseLoopCallback(source,eventdata)
end
function contrastButtonActivateCallback(source,eventdata)
% disp([num2str(frame) ' - pause loop'])
- % keyIn = get(gcf,'CurrentCharacter');
- % disp(keyIn)
+ % keyIn = get(gcf,'CurrentCharacter');
+ % disp(keyIn)
set(contrastSliderLow,'Enable','on')
@@ -965,9 +1049,9 @@ function contrastCallback(source,eventdata)
addlistener(contrastSliderLow,'Value','PostSet',@blankCallback);
addlistener(contrastSliderHigh,'Value','PostSet',@blankCallback);
- % set(contrastSliderLow,'Enable','off')
- % set(contrastSliderHigh,'Enable','off')
- drawnow update;
+ % set(contrastSliderLow,'Enable','off')
+ % set(contrastSliderHigh,'Enable','off')
+ drawnow update;
% set(contrastSliderLow,'Enable','inactive')
% set(contrastSliderHigh,'Enable','inactive')
@@ -978,19 +1062,19 @@ function blankCallback(source,eventdata)
function frameCallbackChange(source,eventdata)
frame = max(1,round(get(frameSlider,'value')));
set(frameText,'visible','on','string',['Frame ' num2str(frame) '/' num2str(nFrames)])
- end
- function mouseWheelChange(hObject, callbackdata, handles)
- if callbackdata.VerticalScrollCount > 0
- frame = frame + 1;
- dirChange = 1;
- elseif callbackdata.VerticalScrollCount < 0
- frame = frame - 1;
- dirChange = -1;
- end
- subfxn_updateSliderInfo();
- end
+ end
+ function mouseWheelChange(hObject, callbackdata, handles)
+ if callbackdata.VerticalScrollCount > 0
+ frame = frame + 1;
+ dirChange = 1;
+ elseif callbackdata.VerticalScrollCount < 0
+ frame = frame - 1;
+ dirChange = -1;
+ end
+ subfxn_updateSliderInfo();
+ end
function frameCallback(source,eventdata)
- originalPauseState = pauseLoop;
+ originalPauseState = pauseLoop;
pauseLoop = 1;
frame = max(1,round(get(frameSlider,'value')));
% disp(num2str(frame))
@@ -999,26 +1083,26 @@ function frameCallback(source,eventdata)
set(frameText,'visible','on','string',['Frame ' num2str(frame) '/' num2str(nFrames)])
% pauseLoop
addlistener(frameSlider,'Value','PostSet',@blankCallback);
- set(frameSlider,'Enable','off')
- drawnow update;
+ set(frameSlider,'Enable','off')
+ drawnow update;
set(frameSlider,'Enable','inactive')
- pauseLoop = originalPauseState;
+ pauseLoop = originalPauseState;
% breakLoop = 1;
% Update the frame line indicator
% set(mainFig,'CurrentAxes',signalAxes)
% frameLineHandle.XData = [frameNo frameNo];
- end
- function subfxn_updateSliderInfo()
- if frame<=0
- frame = nFrames;
- elseif frame>nFrames
- frame = 1;
- end
- set(frameSlider,'Value',frame)
+ end
+ function subfxn_updateSliderInfo()
+ if frame<=0
+ frame = nFrames;
+ elseif frame>nFrames
+ frame = 1;
+ end
+ set(frameSlider,'Value',frame)
set(frameText,'string',['Frame ' num2str(frame) '/' num2str(nFrames)])
- end
+ end
function thisFrame = subfxn_readMovieDisk(inputMoviePath,frameNo,movieTypeT,extraMovieSwitch)
% Fast reading of frame from disk, bypass loadMovieList if possible due to overhead.
if nargin==4
@@ -1064,26 +1148,26 @@ function subfxn_updateSliderInfo()
end
end
function subfxn_respondUserInput(keyInTmp)
- if nargin>0
- keyIn = keyInTmp;
- else
- keyIn = get(gcf,'CurrentCharacter');
- if isempty(double(keyIn))
- keyIn = '/';
- elseif double(keyIn)~=51
- figure(fig1)
- set(gcf,'CurrentCharacter','3');
- pause(1/options.fps);
- drawnow
- else
- % double(keyIn)
- end
- end
-
- % inputdlgcol options
- AddOpts.Resize='on';
- AddOpts.WindowStyle='normal';
- AddOpts.Interpreter='tex';
+ if nargin>0
+ keyIn = keyInTmp;
+ else
+ keyIn = get(gcf,'CurrentCharacter');
+ if isempty(double(keyIn))
+ keyIn = '/';
+ elseif double(keyIn)~=51
+ figure(fig1)
+ set(gcf,'CurrentCharacter','3');
+ pause(1/options.fps);
+ drawnow
+ else
+ % double(keyIn)
+ end
+ end
+
+ % inputdlgcol options
+ AddOpts.Resize='on';
+ AddOpts.WindowStyle='normal';
+ AddOpts.Interpreter='tex';
mousePressState = get(gcf,'SelectionType');
switch mousePressState
@@ -1104,7 +1188,7 @@ function subfxn_respondUserInput(keyInTmp)
end
% Ignore these commands if input movie is character
- if inputMovieIsChar&any(double(keyIn)==[105 100 97 110 99])
+ if inputMovieIsChar&&any(double(keyIn)==[105 100 97 110 99])
disp([keyIn ' not valid for movies input as path.'])
return;
end
@@ -1165,12 +1249,12 @@ function subfxn_respondUserInput(keyInTmp)
frame = frame+dirChange*round(options.fps);
case 112 %'p' %pause
% dirChange = 0;
- if pauseLoop==1
- pauseLoop = 0;
- else
- pauseLoop = 1;
- end
- %ginput(1);
+ if pauseLoop==1
+ pauseLoop = 0;
+ else
+ pauseLoop = 1;
+ end
+ %ginput(1);
% while waitforbuttonpress~=0
% end
case 31 % down arrow
@@ -1202,7 +1286,7 @@ function subfxn_respondUserInput(keyInTmp)
case 106 %'j' %change contrast
[usrIdxChoice, ok] = getUserMovieChoice({'Adjust 1st movie contrast','Adjust 2nd movie contrast','optimal dF/F','Copy contrast from 1st->2nd','Copy contrast from 2nd->1st'});
- if ok==0; return; end
+ if ok==0; return; end
if usrIdxChoice==4
maxMovie(2) = maxMovie(1);
@@ -1215,7 +1299,7 @@ function subfxn_respondUserInput(keyInTmp)
minMovie(1) = -0.01;
else
[sel, ok] = listdlg('ListString',{'Adjustable contrast GUI','Contrast input dialog'},'ListSize',[300 300]);
- if ok==0; return; end
+ if ok==0; return; end
fixMultiplier = options.contrastFixMultiplierGUI;
if usrIdxChoice==1
@@ -1295,9 +1379,9 @@ function subfxn_respondUserInput(keyInTmp)
case 108 %'l' %label frame
if ~isempty(options.labelLegend)
[labelID, ok] = listdlg('ListString',options.labelLegend,'PromptString','toggle label(s), can select multiple to switch','ListSize' ,[400 350]);
- if ok==0; return; end
+ if ok==0; return; end
[impulseState, ok] = listdlg('ListString',{'continuous','single frame'},'PromptString','continuous state or single frame?','ListSize' ,[400 350]);
- if ok==0; return; end
+ if ok==0; return; end
% usrIdxChoice = options.labelLegend{sel};
% labelID = inputdlg('enter label');labelID = labelID{1};
% labelID = num2str(labelID{1});
@@ -1319,18 +1403,18 @@ function subfxn_respondUserInput(keyInTmp)
colormap(options.colormapColor);
case 100 %'d' %dfof
[usrIdxChoice, ok] = getUserMovieChoice({'1st movie','2nd movie'});
- if ok==0; return; end
+ if ok==0; return; end
% make sure selection chosen, else return
if ok~=0
switch usrIdxChoice
case 1
inputMovie = dfofMovie(inputMovie);
- maxMovie(1) = nanmax(inputMovie(:));
- minMovie(1) = nanmin(inputMovie(:));
+ maxMovie(1) = max(inputMovie(:),'omitnan');
+ minMovie(1) = min(inputMovie(:),'omitnan');
case 2
options.extraMovie = dfofMovie(options.extraMovie);
- maxMovie(2) = nanmax(options.extraMovie(:));
- minMovie(2) = nanmin(options.extraMovie(:));
+ maxMovie(2) = max(options.extraMovie(:),'omitnan');
+ minMovie(2) = min(options.extraMovie(:),'omitnan');
otherwise
% nothing
end
@@ -1345,7 +1429,7 @@ function subfxn_respondUserInput(keyInTmp)
nFrames = size(inputMovie,3);
case 110 %'n' %normalize
[usrIdxChoice , ok] = getUserMovieChoice({'1st movie','2nd movie'});
- if ok==0; return; end
+ if ok==0; return; end
[usrExtraChoice, ok] = getUserMovieChoice({'keep original','duplicate'});
% make sure selection chosen, else return
if ok~=0
@@ -1359,8 +1443,8 @@ function subfxn_respondUserInput(keyInTmp)
case 1
if usrExtraChoice==2
options.extraMovie = normalizeMovie(inputMovie,'options',ioptions);
- maxMovie(2) = nanmax(inputMovie(:));
- minMovie(2) = nanmin(inputMovie(:));
+ maxMovie(2) = max(inputMovie(:),'omitnan');
+ minMovie(2) = min(inputMovie(:),'omitnan');
else
inputMovie = normalizeMovie(inputMovie,'options',ioptions);
end
@@ -1371,11 +1455,11 @@ function subfxn_respondUserInput(keyInTmp)
end
end
case 99 %'c' %crop
- [movieChoice ok] = getUserMovieChoice({'1st movie','2nd movie'});
+ [movieChoice, ok] = getUserMovieChoice({'1st movie','2nd movie'});
[cropChoice] = getUserMovieChoice({'NaN border crop','full crop'});
dirChange = 1;
- [coords] = getCropCoords(thisFrame)
+ [coords] = getCropCoords(thisFrame);
sp = coords;
switch movieChoice
case 1
@@ -1420,11 +1504,11 @@ function subfxn_respondUserInput(keyInTmp)
end
switch movieChoice
case 1
- maxMovie(1) = nanmax(inputMovie(:));
- minMovie(1) = nanmin(inputMovie(:));
+ maxMovie(1) = max(inputMovie(:),'omitnan');
+ minMovie(1) = min(inputMovie(:),'omitnan');
case 2
- maxMovie(2) = nanmax(options.extraMovie(:));
- minMovie(2) = nanmin(options.extraMovie(:));
+ maxMovie(2) = max(options.extraMovie(:),'omitnan');
+ minMovie(2) = min(options.extraMovie(:),'omitnan');
otherwise
end
figure(42);
@@ -1433,7 +1517,7 @@ function subfxn_respondUserInput(keyInTmp)
frameChange = inputdlgcol('goto | Enter frame # to skip to:','',1,{''},AddOpts,2);
if ~isempty(frameChange)
frameChange = str2num(frameChange{1});
- if frameChange>nFrames|frameChange<1
+ if frameChange>nFrames||frameChange<1
% do nothing, invalid command
else
frame = frameChange;
@@ -1463,16 +1547,17 @@ function subfxn_displayShortcuts(src,event)
function [supTitleStr, suptitleHandle, conMenu] = subfxn_createShortcutInfo()
titleSep = '\hspace{1em}';
sepVar = ['}' 10 '\texttt{'];
- if isempty(options.extraTitleText)|1
+ % if isempty(options.extraTitleText)||1
+ if isempty(options.extraTitleText)
options.extraTitleText = '';
else
- options.extraTitleText = strrep(options.extraTitleText,'\','\char`\\');
- options.extraTitleText = strrep(options.extraTitleText,'#','\char`\#');
+ options.extraTitleText = strrep(options.extraTitleText,'\','\char`\\');
+ options.extraTitleText = strrep(options.extraTitleText,'#','\char`\#');
options.extraTitleText = [10 '\texttt{' options.extraTitleText '}'];
end
% S.fh = figure;
- shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[0 97 50 3]/100,'FontSize',9,'string','Shortcuts menu (or right-click GUI)','callback',@subfxn_displayShortcuts);
+ shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[0 97 20 3]/100,'FontSize',9,'string','Shortcuts menu (or right-click GUI)','BackgroundColor',options.menuBkgdColor,'ForegroundColor',options.menuFontColor,'HorizontalAlignment','left','callback',@subfxn_displayShortcuts);
sepMenuLins = {'','',''};
menuListInfo = {...
@@ -1507,9 +1592,9 @@ function subfxn_displayShortcuts(src,event)
mitemAll = {};
for mNo = 1:length(menuListInfo)
if isempty([menuListInfo{mNo}{1}])
- labelStr = [''];
+ labelStr = [''];
else
- labelStr = ['' menuListInfo{mNo}{1} ': ' menuListInfo{mNo}{3} '
'];
+ labelStr = ['' menuListInfo{mNo}{1} ': ' menuListInfo{mNo}{3} '
'];
end
mitemAll{mNo} = uimenu(conMenu,'label',labelStr,'MenuSelectedFcn',@(src,evnt) subfxn_respondUserInput(menuListInfo{mNo}{2}));
end
@@ -1555,9 +1640,12 @@ function subfxn_displayShortcuts(src,event)
end
disp(strrep(supTitleStr,titleSep, 10))
% suptitleHandle = ciapkg.overloaded.suptitle(strrep(strrep('/','\',supTitleStr),'_','\_'));
- suptitleHandle = ciapkg.overloaded.suptitle(supTitleStr,'titleypos',0.93);
- set(suptitleHandle,'FontName',get(0,'DefaultAxesFontName'));
- set(suptitleHandle,'FontSize',12,'FontWeight','normal')
+
+ suptitleHandle = [];
+
+ % suptitleHandle = ciapkg.overloaded.suptitle(supTitleStr,'titleypos',0.93);
+ % set(suptitleHandle,'FontName',get(0,'DefaultAxesFontName'));
+ % set(suptitleHandle,'FontSize',12,'FontWeight','normal')
hold off;
% imcontrast
end
diff --git a/+ciapkg/VERSION b/+ciapkg/VERSION
index b78bccc..9c36fd2 100644
--- a/+ciapkg/VERSION
+++ b/+ciapkg/VERSION
@@ -1,2 +1,2 @@
-v4.4.2
-2022.06.27 [13:51:31]
\ No newline at end of file
+v4.5.7
+2022.09.14 [08:55:22]
\ No newline at end of file
diff --git a/+ciapkg/exampleFxn.m b/+ciapkg/exampleFxn.m
index f0305b7..0984c2a 100644
--- a/+ciapkg/exampleFxn.m
+++ b/+ciapkg/exampleFxn.m
@@ -1,20 +1,25 @@
function [output1,output2] = exampleFxn(input1,input2,varargin)
- % EXAMPLEFXN(input1,input2,varargin)
+ % [output1,output2] = EXAMPLEFXN(input1,input2,varargin)
%
% DESCRIPTION.
%
% Biafra Ahanonu
% started: INSERT_DATE
%
- % inputs
+ % Inputs
% input1
% input2
- % outputs
+ %
+ % Outputs
% output1
% output2
+ %
+ % Options (input as Name-Value with Name = options.(Name))
+ % % DESCRIPTION
+ % options.exampleOption = '';
- % changelog
- %
+ % Changelog
+ % 2022.03.14 [01:47:04] - Added nested and local functions to the example function.
% TODO
%
@@ -23,7 +28,7 @@
options.exampleOption = '';
% get options
options = ciapkg.io.getOptions(options,varargin);
- % display(options)
+ % disp(options)
% unpack options into current workspace
% fn=fieldnames(options);
% for i=1:length(fn)
@@ -38,7 +43,16 @@
disp(getReport(err,'extended','hyperlinks','on'));
disp(repmat('@',1,7))
end
+
+ function [outputs] = nestedfxn_exampleFxn(arg)
+ % Always start nested functions with "nestedfxn_" prefix.
+ % outputs = ;
+ end
end
+function [outputs] = localfxn_exampleFxn(arg)
+ % Always start local functions with "localfxn_" prefix.
+ % outputs = ;
+end
% CIAtah method
function obj = functionName(obj,varargin)
diff --git a/+ciapkg/loadBatchFxns.m b/+ciapkg/loadBatchFxns.m
index 371a948..cca59f8 100644
--- a/+ciapkg/loadBatchFxns.m
+++ b/+ciapkg/loadBatchFxns.m
@@ -20,7 +20,7 @@ function loadBatchFxns(varargin)
% 2020.05.09 [16:40:13] - Updates to remove additional specific repositories that should not be loaded by default. Add support for overriding this feature.
% 2020.06.05 [23:35:43] - If user doesn't have Parallel Toolbox, still works
% 2020.07.21 [14:11:42] - Fix to make sure all sub-folders (not just the root) are also removed in the case of certain external_programs.
- % 2021.02.01 [??15:19:40] - Update `_external_programs` to call ciapkg.getDirExternalPrograms() to standardize call across all functions.
+ % 2021.02.01 [15:19:40] - Update `_external_programs` to call ciapkg.getDirExternalPrograms() to standardize call across all functions.
% 2021.06.20 [00:22:38] - Added manageMiji('startStop','closeAllWindows'); support.
% 2021.07.16 [13:38:55] - Remove redundant loading and unloading of external programs via additional checks.
% 2021.07.22 [19:51:50] - Moved loadBatchFxns into ciapkg package. Use ciapkg.getDir to get directory as standard IO.
@@ -28,11 +28,47 @@ function loadBatchFxns(varargin)
% 2021.08.09 [12:06:32] - Do not add docs and data folders or sub-folders to the path.
% 2021.08.24 [13:46:13] - Update to fullfile call using filesep to make platform neutral.
% 2021.11.09 [19:14:49] - Improved handling of external programs both in adding and removing from path. Additional support for removing specific packages that are not always needed.
+ % 2022.03.04 [07:03:22] - Added patchwarp to list of excluded by default folders for external programs.
+ % 2022.03.08 [15:17:16] - Directly allow use of ciapkg.io.getOptions since it is not dependent on loadBatchFxns due to being inside a package. Also allow option for user to specify which special packages that are not normally loaded to be loaded rather than loading everything if they want a specific package loaded.
+ % 2022.07.11 [14:22:30] - Speed up removal of directories not needed by default by directly locating the root path of that algorithm rather than searching for the M-file within the entire external programs directory, saves time. Users must have downloaded dependencies using loadDependencies.
+ % 2022.07.14 [20:19:47] - Add SlideBook JAR to the path for Bio-Formats support.
% TODO
%
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
+ % ========================
+ % Cell array of strings: M-files pointing to root directories that should be excluded from loading.
+ options.removeDirFxnToFind = {...
+ 'extractor.m';
+ 'tenrandblk.m';
+ 'patchwarp.m';
+ };
+ % 'runCELLMax.m';
+ options.removeDirFxnToFindDirs = {...
+ 'extract';
+ 'tensor_toolbox';
+ 'patchwarp';
+ };
+ % 'cellmax';
+ % 'normcorre.m';
+ % Cell array of strings: M-files to overload options.removeDirFxnToFind.
+ options.removeDirFxnToFindExclude = {};
+ % get options
+ options = ciapkg.io.getOptions(options,varargin);
+ % display(options)
+ % unpack options into current workspace
+ % fn=fieldnames(options);
+ % for i=1:length(fn)
+ % eval([fn{i} '=options.' fn{i} ';']);
+ % end
+ % ========================
+
+ % Backwards compatibility for prior functions that just call loadBatchFxns with single input argument.
+ if iscell(varargin)&&length(varargin)>1
+ varargin = '';
+ end
+
% Disable the handle graphics warning "The DrawMode property will be removed in a future release. Use the SortMethod property instead." from being displayed. Comment out this line for debugging purposes as needed.
warning('off','MATLAB:hg:WillBeRemovedReplaceWith')
@@ -40,7 +76,12 @@ function loadBatchFxns(varargin)
externalProgramsDir = ciapkg.getDirExternalPrograms();
% List of M-files where if found, that programs directory should by default be removed from the path.
- removeDirFxnToFind = {'runCELLMax.m','extractor.m','normcorre.m','tenrandblk.m'};
+ removeDirFxnToFind = options.removeDirFxnToFind;
+ removeDirFxnToFindDirs = options.removeDirFxnToFindDirs;
+ if ~isempty(options.removeDirFxnToFindExclude)
+ [removeDirFxnToFind, idx1] = setdiff(removeDirFxnToFind,options.removeDirFxnToFindExclude);
+ removeDirFxnToFindDirs = removeDirFxnToFindDirs(idx1);
+ end
% 'cellmax.runCELLMax', 'CELLMax_Wrapper.m'
% Add calciumImagingAnalysis directory and subdirectories to path, use dbstack to ensure only add in the root directory regardless of where user has current MATLAB folder.
@@ -73,30 +114,30 @@ function loadBatchFxns(varargin)
% pathListArray = subfxnRemoveDirs(0,pathListArray);
pathListArrayOriginal = pathListArray;
-
+
% =================================================
% Remove paths that are already in MATLAB path to save time
pathFilter = cellfun(@isempty,pathListArray);
pathListArray = pathListArray(~pathFilter);
pathFilter = ismember(pathListArray,strsplit(path,pathsep));
pathListArray = pathListArray(~pathFilter);
-
+
% Remove 'docs' and 'data', don't need to be in the path.
matchIdxD = contains(pathListArray,[functionDir filesep 'docs']);
pathListArray = pathListArray(~matchIdxD);
matchIdxD = contains(pathListArray,[functionDir filesep 'data']);
pathListArray = pathListArray(~matchIdxD);
-
+
matchIdxD = contains(pathListArray,[externalProgramsDir filesep '_downloads']);
- pathListArray = pathListArray(~matchIdxD);
+ pathListArray = pathListArray(~matchIdxD);
% If going to remove directories in removeDirFxnToFind then do so now to prevent redundant calls to addpath, etc.
findListFlag = [];
if isempty(varargin)
[pathListArray,findListFlag] = subfxnRemoveDirs(0,pathListArray);
end
-
+
if strcmp(varargin,'excludeExternalPrograms')
disp(['Excluding ' externalProgramsDir ' from PATH adding.'])
matchIdx2 = contains(pathListArray,externalProgramsDir);
@@ -104,8 +145,16 @@ function loadBatchFxns(varargin)
end
% =================================================
- % Add paths as needed and remove any paths that should not be present.
+ % Add SlideBook reader to the path
+ slideBookPath = fullfile(ciapkg.getDirExternalPrograms(),'bfmatlab_readers','SlideBook6Reader.jar');
+ if isfile(slideBookPath)==1
+ disp(['Adding to JAVA path: ' slideBookPath])
+ javaaddpath(slideBookPath);
+ end
+ % =================================================
+ % Add paths as needed and remove any paths that should not be present.
+
skipRemovePath = 0;
if isempty(pathListArray)&isempty(varargin)
disp('Folders still need to be removed.')
@@ -253,8 +302,26 @@ function loadBatchFxns(varargin)
pathToRmCell = {};
findListFlag = [];
matchIdxAll = [];
+ if rmPathFlag==0
+ % extDir = dir([functionDir filesep externalProgramsDir]);
+ extDir = dir([externalProgramsDir]);
+ extDir = extDir([extDir.isdir]);
+ if length(extDir)<3
+ disp('No external programs!')
+ return;
+ end
+ extDir = extDir(3:end);
+ end
+
+ if rmPathFlag==1
+ pathFull = strsplit(path,pathsep);
+ end
+
for iNo = 1:length(fxnRootFolder)
thisFxn = fxnRootFolder{iNo};
+ thisFxnFolder = removeDirFxnToFindDirs{iNo};
+
+ mfileLocFlag = 0;
fileLoc = which(thisFxn);
if isempty(fileLoc)
@@ -263,28 +330,28 @@ function loadBatchFxns(varargin)
findListFlag(iNo) = 1;
end
if rmPathFlag==1
- [pathToRm,~,~] = fileparts(fileLoc);
+ % [pathToRm,~,~] = fileparts(fileLoc);
+ pathToRm = fullfile(externalProgramsDir,thisFxnFolder);
else
- % extDir = dir([functionDir filesep externalProgramsDir]);
- extDir = dir([externalProgramsDir]);
- extDir = extDir([extDir.isdir]);
- if length(extDir)<3
- disp('No external programs!')
- return;
- end
- extDir = extDir(3:end);
- foundFiles = dir(fullfile([externalProgramsDir], ['**' filesep thisFxn '']));
- if isempty(foundFiles)
- pathToRm = [];
- else
- pathToRm = foundFiles.folder;
+ pathToRm = fullfile(externalProgramsDir,thisFxnFolder);
+
+ if ~isdir(pathToRm)
+ foundFiles = dir(fullfile([externalProgramsDir], ['**' filesep thisFxn '']));
+ if isempty(foundFiles)
+ pathToRm = [];
+ else
+ pathToRm = foundFiles.folder;
+ end
+ mfileLocFlag = 1;
end
end
if ~isempty(pathToRm)
- % extractor now in sub-directory
- if strcmp(thisFxn,'extractor.m')
- [pathToRm,~,~] = fileparts(pathToRm);
+ if mfileLocFlag==1
+ % extractor now in sub-directory
+ if strcmp(thisFxn,'extractor.m')
+ [pathToRm,~,~] = fileparts(pathToRm);
+ end
end
if strcmp(thisFxn,'CELLMax_Wrapper.m')|strcmp(thisFxn,'cellmax.runCELLMax')
@@ -294,20 +361,33 @@ function loadBatchFxns(varargin)
else
thisFxnStr = thisFxn;
end
+
matchIdx = contains(pathListArray,pathToRm);
- if rmPathFlag==1&any(matchIdx)==1
- % if rmPathFlag==1
- fprintf('Removing unneeded directory from path: %s.\n',thisFxnStr);
- % pathToRmCell{end+1} = pathToRm;
- pathToRmCell = [pathToRmCell{:} pathListArray(matchIdx)];
+ if rmPathFlag==1
+ matchIdx = matchIdx&contains(pathListArray,pathToRm);
+
+ if any(matchIdx)==1
+ fprintf('Removing unneeded directory from path: %s.\n',thisFxnStr);
+ % pathToRmCell{end+1} = pathToRm;
+ pathToRmCell = [pathToRmCell{:} pathListArray(matchIdx)];
+ end
+
+ matchIdx_pathFull = contains(pathFull,pathToRm);
+ if any(matchIdx_pathFull)==1
+ pathToRmCell = [pathToRmCell{:} pathFull(matchIdx_pathFull)];
+ end
+ pathToRmCell = unique(pathToRmCell);
+
+ filterIdx22 = ismember(pathToRmCell,pathFull);
+ pathToRmCell = pathToRmCell(filterIdx22);
% rmpath(pathToRm);
elseif rmPathFlag==0
fprintf('Removing unneeded directory from "to add" path list: %s.\n',thisFxnStr);
% pathListArray = pathListArray(~matchIdx);
if isempty(matchIdxAll)
- matchIdxAll = ~matchIdx;
+ matchIdxAll = matchIdx;
elseif ~isempty(matchIdx)
- matchIdxAll = matchIdxAll|~matchIdx;
+ matchIdxAll = matchIdxAll|matchIdx;
end
end
% pathToRm
@@ -318,11 +398,15 @@ function loadBatchFxns(varargin)
end
if rmPathFlag==1
+ % pathToRmCell'
% Only remove path if user requests
- rmpath(strjoin(pathToRmCell,pathsep));
+ if isempty(pathToRmCell)
+ else
+ rmpath(strjoin(pathToRmCell,pathsep));
+ end
elseif rmPathFlag==0&~isempty(matchIdxAll)
% Remove from list of folders to add to path
- pathListArray = pathListArray(matchIdxAll);
+ pathListArray = pathListArray(~matchIdxAll);
end
catch err
disp(repmat('@',1,7))
diff --git a/@ciatah/ciatahMainGui.m b/@ciatah/ciatahMainGui.m
index 811bebc..a570a87 100644
--- a/@ciatah/ciatahMainGui.m
+++ b/@ciatah/ciatahMainGui.m
@@ -24,6 +24,13 @@
% 2022.01.04 [12:15:56] - Additional check only for movie previews of supported movie files. "Folder files" list selection additional regexp support.
% 2022.01.25 [19:30:58] - Changed so that 'Start selected method' button selects the correct folders like occurs when pressing enter.
% 2022.02.25 [13:06:09] - The folder list will no longer reset when selecting certain GUI elements that should not impact folder list.
+ % 2022.03.14 [13:31:02] - Improve speed of movie display by better passing of movie information to ciapkg.io.readFrame.
+ % 2022.03.15 [01:14:23] - If processed and raw movie are the same length, sliders move in sync. By default use 'painters' renderer as that can produce smoother rendering than opengl.
+ % 2022.07.27 [13:49:59] - Added file sizes to the folder files preview. Reset movie dimension on folder select change so movie previews run without outputting initial error (movies would still preview, users just might be confused by the warning).
+ % 2022.07.27 [14:23:56] - Feature to allow preview of HDF5 datasets. Expanding to Bio-Formats and other types of data with internal structures for data.
+ % 2022.07.28 [04:48:16] - Added ability to open folder in OS GUI, e.g. Windows Explorer.
+ % 2022.07.30 [10:54:53] - If manual input for filtering folders set back to no filter to avoid GUI loops.
+ % 2022.09.14 [09:51:55] - Make sure preview data and open folder buttons are associated with correct previewDataHandle and openFoldersHandle handles.
% TODO
%
@@ -54,6 +61,7 @@
colorStruct = struct;
colorStruct.red = [255 153 153]/255;
colorStruct.yellow = [255, 255, 153]/255;
+ colorStruct.gray = [153, 153, 153]/255;
defaultFontSize = obj.fontSizeGui;
@@ -94,6 +102,17 @@
selectList = obj.inputFolders(:);
end
+ % Dimensions for each movie
+ inputMovieDims1 = [];
+ inputMovieDims2 = [];
+
+ % Folder files cell array
+ currentFolder = '';
+ currentFolderFilesList = {};
+
+ % 1 = sliders are clicked, 0 = not clicked.
+ sliderLockState = 0;
+
%% ==========================================
% SETUP FIGURE
useAltValid = {'no additional filter','manually sorted folders','not manually sorted folders','manual classification already in obj',['has ' obj.signalExtractionMethod ' extracted cells'],['missing ' obj.signalExtractionMethod ' extracted cells'],'fileFilterRegexp','valid auto',['has ' obj.fileFilterRegexp ' movie file'],'manual index entry'};
@@ -110,6 +129,10 @@
uicontrol('Style','text','String',[ciapkg.pkgName],'Units','normalized','Position',[1 96 20 3]/100,'BackgroundColor',figBackgroundColor,'HorizontalAlignment','Left','ForegroundColor',figTextColor,'FontWeight','bold','FontAngle','italic','FontSize',defaultFontSize*fontScale);
uicontrol('Style','text','String',[inputTxt ' Press TAB to select next section, ENTER to continue, and ESC to exit.'],'Units','normalized','Position',[10 96 90 3]/100,'BackgroundColor',figBackgroundColor,'HorizontalAlignment','Left','ForegroundColor',figTextColor,'FontSize',9*fontScale);
+ % Set the default renderer
+ % set(hFig,'Renderer','painters'); % 'opengl'
+ set(hFig,'Renderer','opengl'); % 'opengl'
+
%% ==========================================
% SETUP MAIN GUI ELEMENTS
@@ -152,9 +175,14 @@
% [x0 y0 width height]
addFoldersLoc = hListboxT.folders.Position;
- addFoldersLoc(3) = 0.30;
- addFoldersLoc(1) = 0.69;
- addFoldersHandle = uicontrol('style','pushbutton','Units', 'normalized','position',addFoldersLoc,'FontSize',9*fontScale,'string','Click to add folders.','BackgroundColor',[153 153 153]/255,'callback',@callback_addFolders);
+ addFoldersLoc(3) = 0.15;
+ addFoldersLoc(1) = 0.63;
+ addFoldersHandle = uicontrol('style','pushbutton','Units', 'normalized','position',addFoldersLoc,'FontSize',9*fontScale,'string','Add folders.','BackgroundColor',[153 153 153]/255,'callback',@callback_addFolders);
+
+ loadFoldersLoc = hListboxT.folders.Position;
+ loadFoldersLoc(3) = 0.20;
+ loadFoldersLoc(1) = 0.79;
+ loadFoldersHandle = uicontrol('style','pushbutton','Units', 'normalized','position',loadFoldersLoc,'FontSize',9*fontScale,'string','Load cell extraction.','BackgroundColor',[153 153 153]/255,'callback',@callback_loadFolders);
%% ==========================================
@@ -261,7 +289,7 @@
subfxn_setOutputVars()
close(hFig)
end
- validFoldersIdx
+ disp(validFoldersIdx)
return;
% idNumIdxArray = get(hListboxS.folders,'Value');
catch err
@@ -407,10 +435,22 @@ function onKeyPressRelease(src, evnt, pressRelease,hFig)
[validFoldersIdx] = pipelineFolderFilter(obj,useAltValid,validFoldersIdx);
+ % If manual input set back to no filter to avoid GUI loops
+ if strcmp(useAltValid,'manual index entry')==1
+ set(hListboxS.folderFilt,'Value',1)
+ end
+
% validFoldersIdx = hListboxStruct.ValueFolder;
% if strcmp(get(src,'Tag'),'folders')~=1
+ % If folder is changed, reset the movie dimensions to avoid errors.
+ if any(strcmp({'folders'},get(src,'Tag')))==1
+ % Dimensions for each movie
+ inputMovieDims1 = [];
+ inputMovieDims2 = [];
+ end
+
if any(strcmp({'methodBox','cellExtractionBox','cellExtractFiletypeBox','guiEnabled','folders'},get(src,'Tag')))==0
if length(get(hListboxS.folders,'Value'))==1
else
@@ -539,11 +579,17 @@ function exitCallback(source,eventdata)
% uiresume(hFig);
end
function subfxn_setOutputVars()
- hListboxStruct.ValueFolder = get(hListboxS.folders,'Value');
- hListboxStruct.Value = hListbox.Value;
- hListboxStruct.guiIdx = get(hListboxS.guiEnabled,'Value');
- hListboxStruct.nwbLoadFiles = get(hListboxS.cellExtractFiletype,'Value');
- if hListboxStruct.nwbLoadFiles==1;hListboxStruct.nwbLoadFiles=0;else;hListboxStruct.nwbLoadFiles=1;end
+ if isvalid(hFig)
+ hListboxStruct.ValueFolder = get(hListboxS.folders,'Value');
+ hListboxStruct.Value = hListbox.Value;
+ hListboxStruct.guiIdx = get(hListboxS.guiEnabled,'Value');
+ hListboxStruct.nwbLoadFiles = get(hListboxS.cellExtractFiletype,'Value');
+ if hListboxStruct.nwbLoadFiles==1
+ hListboxStruct.nwbLoadFiles=0;
+ else
+ hListboxStruct.nwbLoadFiles=1;
+ end
+ end
end
function startMethodCallback(source,eventdata)
breakMovieLoop = 1;
@@ -575,10 +621,68 @@ function runMoviesToggleCallback(source,eventdata)
elseif enableMoviePreview==1
returnStr = 'Preview movies by selecting one folder in "Loaded folders" (click to disable).';
end
+ end
+ function subfxn_previewDataInfo(source,eventdata)
+ disp('Listing out internal data for select file types.')
+ % folderIdx = get(hListboxS.folders,'Value');
+ currentFolder
+ selectFiles = get(hListboxS.folderFiles,'Value');
+ % folderFilesList = get(hListboxS.folderFiles,'string');
+ folderFilesList = currentFolderFilesList(selectFiles);
+ % cellfun(@disp,folderFilesList)
+ for iz3 = 1:length(folderFilesList)
+ disp(repmat('==',1,7))
+ thisFullFile = fullfile(currentFolder,folderFilesList{iz3});
+ if isfile(thisFullFile)~=1
+ continue;
+ end
+ disp(folderFilesList{iz3})
+ disp(thisFullFile)
+ [~,~,extH] = fileparts(thisFullFile);
+
+ [movieTypeTmp, supported] = ciapkg.io.getMovieFileType(thisFullFile);
+
+ switch movieTypeTmp
+ case 'tiff'
+ case 'hdf5'
+ % Display only HDF5 dataset names, no additional metadata.
+ h5disp(thisFullFile,'/','min')
+ case 'avi'
+ case 'isxd'
+ case 'bioformats'
+ % Just display series of names in the file
+ [bfSeriesNo] = ciapkg.bf.dispFileSeries(thisFullFile);
+ otherwise
+ end
+ end
+ disp('Done!')
+ end
+ function subfxn_openFolderGui(source,eventdata)
+ if ~isempty(currentFolder)
+ if (ispc)
+ winopen(currentFolder)
+ elseif ismac
+ cmdRun = ['open ' currentFolder];
+ [status1, cmdout1] = system(cmdRun); %#ok
+ else
+ % xdg-open opens folder in Linux, if no xdg-open, display an error.
+ cmdRun = ['xdg-open ' currentFolder];
+ [status1, cmdout1] = system(cmdRun); %#ok
+ end
+ end
end
function callback_addFolders(source,eventdata)
% Set current method to modelAddNewFolders and start method.
- hListbox.Value = 2;
+ hListbox.Value = find(strcmp('modelAddNewFolders',obj.methodsList));
+ obj.currentMethod = hListbox.String{hListbox.Value};
+ % breakMovieLoop = 1;
+ % exitFlag = 1;
+ % close(hFig)
+ subfxn_returnNormal();
+ end
+ function callback_loadFolders(source,eventdata)
+ % Set current method to modelAddNewFolders and start method.
+ hListbox.Value = find(strcmp('modelVarsFromFiles',obj.methodsList),1);
obj.currentMethod = hListbox.String{hListbox.Value};
% breakMovieLoop = 1;
% exitFlag = 1;
@@ -641,9 +745,11 @@ function pauseLoopCallback(source,eventdata)
switch switchTag
case 'First'
set(frameSlider,'Enable','on');
+ sliderLockState = 1;
% addlistener(frameSlider,'Value','PostSet',@frameCallbackChange);
case 'Second'
set(frameSliderTwo,'Enable','on');
+ sliderLockState = 2;
% addlistener(frameSliderTwo,'Value','PostSet',@frameCallbackChange);
otherwise
return;
@@ -672,19 +778,37 @@ function frameCallback(source,eventdata)
originalPauseState = breakMovieLoop;
breakMovieLoop = 1;
switchTag = get(source,'Tag');
+
switch switchTag
case 'First'
i = max(1,round(get(frameSlider,'value')));
+ % If movies are equal length, keep both sliders in sync.
+ if length(inputMovieDims1)==3&&length(inputMovieDims2)==3
+ if inputMovieDims1(3)==inputMovieDims2(3)
+ i2 = i;
+ end
+ end
case 'Second'
i2 = max(1,round(get(frameSliderTwo,'value')));
+ % If movies are equal length, keep both sliders in sync.
+ if length(inputMovieDims1)==3&&length(inputMovieDims2)==3
+ if inputMovieDims1(3)==inputMovieDims2(3)
+ i = i2;
+ end
+ end
otherwise
return;
end
addlistener(frameSlider,'Value','PostSet',@blankCallback);
set(frameSlider,'Enable','off')
+ set(frameSliderTwo,'Enable','off')
drawnow update;
set(frameSlider,'Enable','inactive')
+ set(frameSliderTwo,'Enable','inactive')
breakMovieLoop = originalPauseState;
+ sliderLockState = 0;
+ end
+ function blankCallback(source,eventdata)
end
function [selBoxInfo] = subfxn_guiElementSetupInfo()
% set(hFig,'Color',[0,0,0]);
@@ -764,7 +888,11 @@ function frameCallback(source,eventdata)
bOff = 1;
fontSizeH = 11*fontScale;
- runMoviesToggleHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[bOff 45 98 2]/100,'FontSize',fontSizeH,'string',subfxn_previewMovieState(),'BackgroundColor',buttonBackgroundColor,'callback',@runMoviesToggleCallback);
+ runMoviesToggleHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[bOff 45 50 2]/100,'FontSize',fontSizeH,'string',subfxn_previewMovieState(),'BackgroundColor',buttonBackgroundColor,'callback',@runMoviesToggleCallback);
+
+ previewDataHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[bOff+50 45 24 2]/100,'FontSize',fontSizeH,'string','Preview file internal structure (HDF5, BioFormats)','BackgroundColor',colorStruct.gray,'callback',@subfxn_previewDataInfo);
+
+ openFoldersHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[bOff+50+24 45 24 2]/100,'FontSize',fontSizeH,'string','Open folder OS gui','BackgroundColor',colorStruct.gray,'callback',@subfxn_openFolderGui);
fileFilterRegexpHandle = uicontrol('style','edit','Units', 'normalized','position',[bOff dy txtW 2]/100,'FontSize',fontSizeH,'string',obj.fileFilterRegexp,'callback',@movieSettingsCallback,'KeyReleaseFcn',@movieSettingsCallback);
uicontrol('Style','text','String','Processed movie regular expression:','Units','normalized','Position',[bOff dy+dz txtW dz]/100,'BackgroundColor',figBackgroundColor,'ForegroundColor',figTextColor,'HorizontalAlignment','Left','FontWeight','normal','FontSize',fontSizeH);
@@ -790,12 +918,32 @@ function movieCallback(source,eventdata)
end
thisFolderPath = obj.inputFolders{folderIdx};
-
+ currentFolder = thisFolderPath;
% Update file list
try
folderFileListHere = ciapkg.api.getFileList(thisFolderPath,'.','addInputDirToPath',0);
folderFileListHere = folderFileListHere(3:end);
+ currentFolderFilesList = folderFileListHere;
+ for iz2 = 1:length(folderFileListHere)
+ thisFileTmp = folderFileListHere{iz2};
+ thisFileFullPath = fullfile(thisFolderPath,thisFileTmp);
+ if isfile(thisFileFullPath)==1
+ fileInfo2 = dir(thisFileFullPath);
+ fileSize = fileInfo2.bytes*9.53674e-7;
+ % Show in GB or Mb
+ if fileSize>2^10
+ thisFileTmp = sprintf('%s (%0.2f GB)',thisFileTmp,round(fileSize/2^10,2));
+ else
+ thisFileTmp = sprintf('%s (%0.2f MB)',thisFileTmp,round(fileSize,2));
+ end
+ elseif isfolder(thisFileFullPath)==1
+ thisFileTmp = ['->' thisFileTmp];
+ end
+ folderFileListHere{iz2} = thisFileTmp;
+ end
set(hListboxS.folderFiles,'string',folderFileListHere);
+
+ % Update selected files
set(hListboxS.folderFiles,'Value',1);
folderFilesHighlight = cellfun(@(x) ~isempty(cell2mat(regexp(x,{obj.fileFilterRegexp,obj.fileFilterRegexpRaw}))),folderFileListHere,'UniformOutput',0);
folderFilesHighlight = cell2mat(folderFilesHighlight);
@@ -996,8 +1144,16 @@ function movieCallback(source,eventdata)
while breakMovieLoop==0
if movieCheck{1}==1
if isvalid(movieImgHandle)
+ try
+ if inputMovieDims1(3)==inputMovieDims2(3)&&sliderLockState==2
+ set(frameSlider,'Value',i2);
+ end
+ catch
+
+ end
+
i = round(get(frameSlider,'Value'));
- [thisFrame,~,~] = ciapkg.io.readFrame(inputMoviePath{1},i,'inputDatasetName',obj.inputDatasetName);
+ [thisFrame,~,inputMovieDims1] = ciapkg.io.readFrame(inputMoviePath{1},i,'inputDatasetName',obj.inputDatasetName,'inputMovieDims',inputMovieDims1);
if ~isempty(boundaryIndices)&opts.overlayCellExtraction==1
thisFrame([boundaryIndices{:}]) = NaN;
@@ -1016,8 +1172,16 @@ function movieCallback(source,eventdata)
if movieCheck{2}==1
if isvalid(movieImgHandleTwo)
+ try
+ if inputMovieDims1(3)==inputMovieDims2(3)&&sliderLockState==1
+ set(frameSliderTwo,'Value',i);
+ end
+ catch
+
+ end
+
i2 = round(get(frameSliderTwo,'Value'));
- [thisFrame2,~,~] = ciapkg.io.readFrame(inputMoviePath{2},i2,'inputDatasetName',obj.inputDatasetName);
+ [thisFrame2,~,inputMovieDims2] = ciapkg.io.readFrame(inputMoviePath{2},i2,'inputDatasetName',obj.inputDatasetName,'inputMovieDims',inputMovieDims2);
if ~isempty(boundaryIndices)&opts.overlayCellExtraction==1
thisFrame2([boundaryIndices{:}]) = NaN;
@@ -1041,6 +1205,8 @@ function movieCallback(source,eventdata)
if breakMovieLoop==1
break;
end
+ % drawnow
+ drawnow limitrate
pause(1/FPShere);
end
warning on;
diff --git a/@ciatah/getRegistrationSettings.m b/@ciatah/getRegistrationSettings.m
index a8beb3e..4ff0e7c 100644
--- a/@ciatah/getRegistrationSettings.m
+++ b/@ciatah/getRegistrationSettings.m
@@ -26,6 +26,8 @@
% 2021.07.22 [12:10:10] - Added movie detrend options.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2021.12.01 [19:21:00] - Handle "User selects specific value" going into infinite loop. Allow cancel option to prevent loop.
+ % 2022.06.28 [15:28:13] - Add additional black background support.
+ % 2022.07.10 [17:21:21] - Added largeMovieLoad setting.
% TODO
% DONE: Allow user to input prior settings, indicate those changed from default by orange or similar color.
@@ -110,6 +112,10 @@
tS.REGISTRATION______________.val = {{'====================='}};
tS.REGISTRATION______________.str = {{'====================='}};
tS.REGISTRATION______________.tooltip = {{'====================='}};
+ tS.mcMethod = [];
+ tS.mcMethod.val = {{'turboreg','normcorre'}};
+ tS.mcMethod.str = {{'Turboreg motion correction','NoRMCorre (non-rigid) motion correction'}};
+ tS.mcMethod.tooltip = {{'Method to use for motion correction.'}};
tS.parallel = [];
tS.parallel.val = {{1,0}};
tS.parallel.str = {{'parallel processing','NO parallel processing'}};
@@ -303,6 +309,10 @@
tS.nParallelWorkers.val = {{nWorkersDefault, userSelectVal, nWorkersDefault*2}};
tS.nParallelWorkers.str = {{nWorkersDefault, userSelectStr, nWorkersDefault*2}};
tS.nParallelWorkers.tooltip = {{defaultTooltips}};
+ tS.largeMovieLoad = [];
+ tS.largeMovieLoad.val = {{0,1}};
+ tS.largeMovieLoad.str = {{'No','Yes'}};
+ tS.largeMovieLoad.tooltip = {{'Whether to load movie without pre-allocating matrix, useful for single large movie in each folder that needs to be processed.'}};
% ===================================
tS.STRIPE_REMOVAL______________ = [];
@@ -404,6 +414,7 @@
nGuiSubsetsTrue = (nGuiSubsets-1);
% nGuiSubsetsTrue = 1;
figure(figNoDefault);
+ set(gcf,'color',[0 0 0]);
for thisSet = 1:nGuiSubsetsTrue
% 1:nPropertiesToChange
% subsetStartTime = tic;
@@ -421,8 +432,8 @@
[figHandle figNo] = openFigure(figNoDefault, '');
clf
- uicontrol('Style','Text','String',inputTitleStr,'Units','normalized','Position',[uiXOffset uiYOffset+0.05 0.8 0.05],'BackgroundColor','white','HorizontalAlignment','Left','FontSize',uiFontSize);
- uicontrol('Style','Text','String',sprintf('Options page %d/%d: Mouse over each options for tips. To continue, press enter. Orange = previous non-default settings.\n>>>On older MATLAB versions, select the command window before pressing enter.',thisSet,nGuiSubsets-1),'Units','normalized','Position',[uiXOffset uiYOffset+0.02 uiTxtSize+uiBoxSize 0.05],'BackgroundColor','white','HorizontalAlignment','Left','FontSize',uiFontSize);
+ uicontrol('Style','Text','String',inputTitleStr,'Units','normalized','Position',[uiXOffset uiYOffset+0.05 0.8 0.05],'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontSize',uiFontSize);
+ uicontrol('Style','Text','String',sprintf('Options page %d/%d: Mouse over each options for tips. To continue, press enter. Orange = previous non-default settings.\n>>>On older MATLAB versions, select the command window before pressing enter.',thisSet,nGuiSubsets-1),'Units','normalized','Position',[uiXOffset uiYOffset+0.02 uiTxtSize+uiBoxSize 0.05],'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontSize',uiFontSize);
propNoDisp = 1;
for propertyNo = propertySubsetList
@@ -465,7 +476,7 @@
listPos = [uiXOffset+uiTxtSize+colNo uiYOffset-uiXIncrement*propNoDisp uiBoxSize 0.05];
uiListHandles{propertyNo} = uicontrol('Style', 'popup','String', propertySettingsStr.(property),'Units','normalized',...
- 'Position', listPos,...
+ 'Position', listPos,'BackgroundColor','black','ForegroundColor','white',...
'Callback',@subfxnInterfaceCallback,'FontSize',uiFontSize,'Tag',property);
% jEdit = findjobj(uiTextHandles{propertyNo});
@@ -482,15 +493,16 @@
% If property is non-default, set to orange to alert user.
if any(ismember(nonDefaultProperties,property))
- set(uiListHandles{propertyNo},'Backgroundcolor',[254 216 177]/255);
+ set(uiListHandles{propertyNo},'Backgroundcolor',[254 216 177]/255/2);
end
propNoDisp = propNoDisp+1;
end
startMethodHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[80 94 20 2]/100,'FontSize',9,'string','Click when done.','BackgroundColor',[153 255 153]/255,'callback',@subfxn_closeOptions);
- emptyBox = uicontrol('Style','Text','String',[''],'Units','normalized','Position',[1 1 1 1]/100,'BackgroundColor','white','HorizontalAlignment','Left','FontWeight','bold','FontSize',7);
+ emptyBox = uicontrol('Style','Text','String',[''],'Units','normalized','Position',[1 1 1 1]/100,'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontWeight','bold','FontSize',7);
set(gcf,'KeyPressFcn', @subfxn_closeOptionsKey);
+ ciapkg.view.changeFont('none','fontColor','w')
% waitfor(gcf,'Tag');
waitfor(emptyBox);
% pause
@@ -528,7 +540,7 @@
% preprocessSettingStruct.refCropFrame = str2num(movieSettings{1});
% end
function subfxnInterfaceCallback(hObject,callbackdata)
- set(hObject, 'Backgroundcolor', [208,229,180]/255);
+ set(hObject, 'Backgroundcolor', [208,229,180]/255/2);
% De-select the current option, allows user to press enter to continue.
set(hObject, 'Enable', 'off');
diff --git a/@ciatah/modelAddNewFolders.m b/@ciatah/modelAddNewFolders.m
index 3e67ba7..c0f4cfe 100644
--- a/@ciatah/modelAddNewFolders.m
+++ b/@ciatah/modelAddNewFolders.m
@@ -11,6 +11,8 @@
% 2021.02.02 [11:27:21] - 'Add CIAtah example folders.' now adds the absolute path to avoid path errors if user changes Matlab current directory.
% 2021.06.01 [??15:43:11] - Add 'next' button to initial menu, in part to add MATLAB Online support.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.04.10 [09:23:32] - Add additional check for when user adds blank folder paths (NOT empty folders) and remove them.
+ % 2022.07.05 [19:27:24] - Empty folder check update.
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
@@ -173,6 +175,16 @@
nNewFolders = length(newFolderListCell);
end
+ newFolderListCell
+ disp('Checking and removing blank folders')
+ emptyFolderIdx = cellfun(@isempty,newFolderListCell,'UniformOutput',false);
+ emptyFolderIdx = cell2mat(emptyFolderIdx);
+ if any(emptyFolderIdx)
+ newFolderListCell = newFolderListCell(~emptyFolderIdx);
+ end
+ newFolderListCell
+ nNewFolders = length(newFolderListCell);
+
fileIdxArray = (nExistingFolders+1):(nExistingFolders+nNewFolders);
% obj.foldersToAnalyze = fileIdxArray;
nFolders = length(fileIdxArray);
diff --git a/@ciatah/modelExtractSignalsFromMovie.m b/@ciatah/modelExtractSignalsFromMovie.m
index ff2784a..f3857a7 100644
--- a/@ciatah/modelExtractSignalsFromMovie.m
+++ b/@ciatah/modelExtractSignalsFromMovie.m
@@ -30,6 +30,9 @@
% 2021.11.08 [12:42:12] - Add nwbpkg support.
% 2021.11.09 [15:29:01] - Updated EXTRACT support.
% 2022.06.27 [15:33:57] - matlab.desktop.editor.openDocument no longer uses pwd since options.settingsPrivateSaveFolder is based on an absolute path.
+ % 2022.06.29 [11:25:57] - CELLMax support for loading prior settings.
+ % 2022.07.05 [20:12:34] - Update to EXTRACT support: added additional options, do not automatically eliminate summary section, and more.
+ % 2022.09.14 [10:52:43] - Switch order of mergeStructs and supplying cell radius to EXTRACT, else empty vector can be passed depending on user input.
% TODO
%
@@ -490,6 +493,16 @@ function getAlgorithmRootPath(algorithmFile,algorithmName,obj,rootFlag)
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
try
+ if exist(algorithmFile,'file')==2
+ fprintf('Found: %s\n',algorithmFile)
+ return;
+ elseif length(which('cellmax.runCELLMax'))>0
+ fprintf('Found: %s\n',algorithmFile)
+ return;
+ else
+ fprintf('Did not find: %s\n',algorithmFile)
+ end
+
% foundFiles = dir(fullfile([obj.defaultObjDir filesep obj.externalProgramsDir], ['**\' algorithmFile '']));
foundFiles = dir(fullfile([obj.externalProgramsDir], ['**' filesep algorithmFile '']));
pathToAdd = foundFiles.folder;
@@ -752,85 +765,121 @@ function getAlgorithmOptions()
options.PCAICA.max_iter = str2num(movieSettings{setNo});setNo = setNo+1;
options.PCAICA
case {'EM','CELLMax'}
+ % 'CELLMax | readMovieChunks | read movie chunks from disk? (1 = yes, 0 = no)',...
+
+ % Check if prior settings available.
+ options.CELLMax.readMovieChunks = '1';
+ options.CELLMax.percentFramesPerIteration = '0.3';
+ options.CELLMax.minIters = '50';
+ options.CELLMax.maxIters = '120';
+ options.CELLMax.gridSpacing = '';
+ options.CELLMax.gridWidth = '';
+ options.CELLMax.maxSqSize = '150';
+ options.CELLMax.percentFramesPCAICA = '0.5';
+ options.CELLMax.useGPU = '0';
+ options.CELLMax.subsampleMethod = 'random';
+ options.CELLMax.sizeThresh = '5';
+ options.CELLMax.sizeThreshMax = '250';
+ options.CELLMax.areaOverlapThresh = '0.65';
+ options.CELLMax.removeCorrProbs = '1';
+ options.CELLMax.distanceThresh = '3';
+ options.CELLMax.corrRemovalAreaOverlapThresh = '0.3';
+ options.CELLMax.threshForElim = '0.005';
+ options.CELLMax.scaledPhiCorrThresh = '0.3';
+ options.CELLMax.runMovieImageCorrThreshold = '1';
+ options.CELLMax.movieImageCorrThreshold = '0.15';
+ options.CELLMax.loadPreviousChunks = '0';
+ options.CELLMax.saveIterMovie = '0';
+ options.CELLMax.sqOverlap = '16';
+ options.CELLMax.downsampleFactorTime = '1';
+ options.CELLMax.downsampleFactorSpace = '1';
+ options.CELLMax.spatialFilterMovie = '0';
+ options.CELLMax.useSparseImageMatrix = '0';
+ options.CELLMax.exitEarlySaveSparse = '0';
+ options.CELLMax.numFramesSampleFitNoiseSigma = '1000';
+ options.CELLMax.recalculateFinalTraces = '1';
+ options.CELLMax.dsInitializeThreshold = '0.01';
+ options.CELLMax.numSigmasThresh = '0';
+ options.CELLMax.numPhotonsPerSigma = '10';
+ options.CELLMax.upsampleFullIters = '2';
+ options.CELLMax.removeAutoCorrThres = '0.65';
+ options.CELLMax.removeAutoCorrThres = '0.65';
+ options.CELLMax.saveChunksToRam = '1';
+ options.CELLMax.eccentricityThreshold = '0.99';
+ options.CELLMax.numObjThreshold = '3';
+
+ optsDefault = options.CELLMax;
+ try
+ optionsLoaded = obj.functionSettings.modelExtractSignalsFromMovie.options.CELLMax;
+ optsFn = fieldnames(optionsLoaded);
+ for iz = 1:length(optsFn)
+ options.CELLMax.(optsFn{iz}) = optionsLoaded.(optsFn{iz});
+ end
+ disp('Loaded prior settings!')
+ catch
+ end
+
+ % movieSettingsStrs = {...
+ mOpt = struct;
+ mOpt.readMovieChunks = 'read movie chunks from disk? (1 = yes, 0 = no)';
+ mOpt.percentFramesPerIteration = 'fraction of total frames subset each iteration? (Float 0->1)';
+ mOpt.minIters = 'number of min iterations? (Int)';
+ mOpt.maxIters = 'number of max iterations? (Int)';
+ mOpt.gridSpacing = '? (Int, leave blank for manual)';
+ mOpt.gridWidth = '? (Int, leave blank for manual)';
+ mOpt.maxSqSize = 'max square tile size? (Int)';
+ mOpt.percentFramesPCAICA = 'percent frames for PCA-ICA? (Float 0->1)';
+ mOpt.useGPU = 'use GPU? (1 = yes, 0 = no)';
+ mOpt.subsampleMethod = 'subsample method? (Str: random, resampleRemaining)';
+ mOpt.sizeThresh = '? (Int)';
+ mOpt.sizeThreshMax = '? (Int)';
+ mOpt.areaOverlapThresh = '?';
+ mOpt.removeCorrProbs = '? (1 = yes, 0 = no)';
+ mOpt.distanceThresh = '?';
+ mOpt.corrRemovalAreaOverlapThresh = '?';
+ mOpt.threshForElim = '? (elimination threshold scaled phi)';
+ mOpt.scaledPhiCorrThresh = '?';
+ mOpt.runMovieImageCorrThreshold = '?';
+ mOpt.movieImageCorrThreshold = '?';
+ mOpt.loadPreviousChunks = '?';
+ mOpt.saveIterMovie = '?';
+ mOpt.sqOverlap = '?';
+ mOpt.downsampleFactorTime = '?';
+ mOpt.downsampleFactorSpace = '?';
+ mOpt.spatialFilterMovie = ' (0 = no, 1 = yes, after loading)?';
+ mOpt.useSparseImageMatrix = ' (0 = no, 1 = yes)?';
+ mOpt.exitEarlySaveSparse = ' (0 = no, 1 = yes)?';
+ mOpt.numFramesSampleFitNoiseSigma = ' (Int, frames)?';
+ mOpt.recalculateFinalTraces = ' (0 = no, 1 = yes)?';
+ mOpt.dsInitializeThreshold = ' (Float, range 0:1)?';
+ mOpt.numSigmasThresh = ' (Float)?';
+ mOpt.numPhotonsPerSigma = ' (Int)?';
+ mOpt.upsampleFullIters = ' (Int)?';
+ mOpt.removeAutoCorrThres = ' (Float, range 0:1)?';
+ mOpt.saveChunksToRam = ' (0 = no, 1 = yes)?';
+ mOpt.eccentricityThreshold = 'Float: value 0 to 1, eccentricity (0 = more circular). By default is 0.99 so that dendrites are not excluded, lower if interested more in cells.';
+ mOpt.numObjThreshold = 'Int: Filter kept if number of objects is <= numObjThreshold. This generally filters for noise that does not produce a single object containing the signal.';
+ % };
+
+ % optsTmp = cellfun(@num2str,struct2cell(optsDefault),'UniformOutput',false);
+ % for iz = 1:length(movieSettingsStrs)
+ % movieSettingsStrs{iz} = [movieSettingsStrs{iz} ' | default: ' num2str(optsTmp{iz})];
+ % end
+
+ fnTmp = fieldnames(options.CELLMax);
+ movieSettingsStrs = {};
+ for iz = 1:length(fnTmp)
+ movieSettingsStrs{iz} = [fnTmp{iz} ' | ' mOpt.(fnTmp{iz}) ' | default: ' optsDefault.(fnTmp{iz})];
+ end
+
AddOpts.Resize='on';
AddOpts.WindowStyle='normal';
AddOpts.Interpreter='tex';
- % 'CELLMax | readMovieChunks | read movie chunks from disk? (1 = yes, 0 = no)',...
- movieSettings = inputdlgcol({...
- 'readMovieChunks | read movie chunks from disk? (1 = yes, 0 = no)',...
- 'percentFramesPerIteration | fraction of total frames subset each iteration? (Float 0->1)',...
- 'minIters | number of min iterations? (Int)',...
- 'maxIters | number of max iterations? (Int)',...
- 'gridSpacing? (Int, leave blank for manual)',...
- 'gridWidth? (Int, leave blank for manual)',...
- 'maxSqSize | max square tile size? (Int)',...
- 'percentFramesPCAICA | percent frames for PCA-ICA? (Float 0->1)',...
- 'useGPU | use GPU? (1 = yes, 0 = no)',...
- 'subsampleMethod | subsample method? (Str: random, resampleRemaining)',...
- 'sizeThresh? (Int)',...
- 'sizeThreshMax? (Int)',...
- 'areaOverlapThresh?',...
- 'removeCorrProbs? (1 = yes, 0 = no)',...
- 'distanceThresh?',...
- 'corrRemovalAreaOverlapThresh?',...
- 'threshForElim? (elimination threshold scaled phi)',...
- 'scaledPhiCorrThresh?',...
- 'runMovieImageCorrThreshold?',...
- 'movieImageCorrThreshold?',...
- 'loadPreviousChunks?',...
- 'saveIterMovie?',...
- 'sqOverlap?',...
- 'downsampleFactorTime?',...
- 'downsampleFactorSpace?',...
- 'spatialFilterMovie (0 = no, 1 = yes, after loading)?',...
- 'useSparseImageMatrix (0 = no, 1 = yes)?',...
- 'exitEarlySaveSparse (0 = no, 1 = yes)?',...
- 'numFramesSampleFitNoiseSigma (Int, frames)?',...
- 'recalculateFinalTraces (0 = no, 1 = yes)?',...
- 'dsInitializeThreshold (Float, range 0:1)?',...
- 'numSigmasThresh (Float)?',...
- 'numPhotonsPerSigma (Int)?',...
- 'upsampleFullIters (Int)?',...
- 'removeAutoCorrThres (Float, range 0:1)?',...
- },...
+ movieSettings = inputdlgcol(movieSettingsStrs,...
dlgStr,1,...
- {...
- '1',...
- '0.3',...
- '50',...
- '120',...
- '',...
- '',...
- '101',...
- '0.5',...
- '0',...
- 'random',...
- '5',...
- '250',...
- '0.65',...
- '1',...
- '3',...
- '0.3',...
- '0.005',...
- '0.3',...
- '1',...
- '0.15',...
- '0',...
- '0',...
- '16',...
- '1',...
- '1',...
- '0',...
- '0',...
- '0',...
- '1000',...
- '1',...
- '0.01',...
- '0',...
- '10',...
- '2',...
- '0.65'...
- },AddOpts,2);
+ cellfun(@num2str,struct2cell(options.CELLMax),'UniformOutput',false),...
+ AddOpts,2);
+
setNo = 1;
options.CELLMax.readMovieChunks = str2num(movieSettings{setNo});setNo = setNo+1;
options.CELLMax.percentFramesPerIteration = str2num(movieSettings{setNo});setNo = setNo+1;
@@ -868,39 +917,118 @@ function getAlgorithmOptions()
options.CELLMax.numPhotonsPerSigma = str2num(movieSettings{setNo});setNo = setNo+1;
options.CELLMax.upsampleFullIters = str2num(movieSettings{setNo});setNo = setNo+1;
options.CELLMax.removeAutoCorrThres = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.CELLMax.saveChunksToRam = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.CELLMax.eccentricityThreshold = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.CELLMax.numObjThreshold = str2num(movieSettings{setNo});setNo = setNo+1;
+
+ obj.functionSettings.modelExtractSignalsFromMovie.options.CELLMax = options.CELLMax;
case 'EXTRACT'
- movieSettings = inputdlg({...
- 'EXTRACT | Use GPU (''gpu'') or CPU (''cpu'')?',...
- 'EXTRACT | avg_cell_radius (Avg. cell radius, also controls cell elimination)? Leave blank for GUI to estimate radius.',...
- 'EXTRACT | cellfind_min_snr (threshold on the max instantaneous per-pixel SNR in the movie for searching for cells)?',...
- 'EXTRACT | preprocess? (1 = yes, 0 = no, [ONLY SELECT 1 on raw, motion-corrected movies])',...
- 'EXTRACT | num_partitions? Int: number of partitions in x and y.',...
- 'EXTRACT | compact_output? (1 = yes, 0 = no)',...
- 'EXTRACT | trace_output_option? Trace output type("nonneg" or "raw")',...
- 'EXTRACT | use_sparse_arrays? (1 = yes, 0 = no)'...
- },...
+
+ % Check if prior settings available.
+ options.EXTRACT.use_gpu = '0';
+ options.EXTRACT.parallel_cpu = '1';
+ options.EXTRACT.multi_gpu = '0';
+ options.EXTRACT.avg_cell_radius = '';
+ options.EXTRACT.cellfind_min_snr = '1';
+ options.EXTRACT.preprocess = '0';
+ options.EXTRACT.num_partitions_x = '2';
+ options.EXTRACT.num_partitions_y = '2';
+ options.EXTRACT.compact_output = '1';
+ options.EXTRACT.trace_output_option = 'nonneg';
+ options.EXTRACT.use_sparse_arrays = '0';
+ options.EXTRACT.T_min_snr = '10';
+ options.EXTRACT.cellfind_max_steps = '1000';
+ options.EXTRACT.temporal_corrupt_thresh = '0.7';
+ options.EXTRACT.spatial_corrupt_thresh = '0.7';
+ options.EXTRACT.size_upper_limit = '10';
+ options.EXTRACT.low_ST_index_thresh = '1e-2';
+ options.EXTRACT.low_ST_corr_thresh = '-Inf';
+ options.EXTRACT.init_with_gaussian = '0';
+ options.EXTRACT.downsample_time_by = '1';
+ options.EXTRACT.downsample_space_by = '1';
+ options.EXTRACT.T_dup_corr_thresh = '0.95';
+ options.EXTRACT.max_iter = '6';
+ options.EXTRACT.cellfind_filter_type = 'butter';
+
+ optsDefault = options.EXTRACT;
+ try
+ optionsLoaded = obj.functionSettings.modelExtractSignalsFromMovie.options.EXTRACT;
+ optsFn = fieldnames(optionsLoaded);
+ for iz = 1:length(optsFn)
+ options.EXTRACT.(optsFn{iz}) = optionsLoaded.(optsFn{iz});
+ end
+ disp('Loaded prior settings!')
+ catch
+ end
+
+ movieSettingsStrs = {...
+ 'use_gpu | Use GPU (1, ''gpu'') or CPU (0, ''cpu'')?',...
+ 'parallel_cpu | parallel processing in CPU mode? (1 = yes, 0 = no)',...
+ 'multi_gpu | parallel processing on multiple GPUs (1 = yes, 0 = no)',...
+ 'avg_cell_radius | Avg. cell radius (also controls cell elimination)? Leave blank for GUI to estimate radius.',...
+ 'cellfind_min_snr | Threshold on the max instantaneous per-pixel SNR in the movie for searching for cells?',...
+ 'preprocess | Preprocess movie? (1 = yes, 0 = no - ONLY SELECT 1 on raw, motion-corrected movies)',...
+ 'num_partitions | Int: number of partitions in x.',...
+ 'num_partitions | Int: number of partitions in y.',...
+ 'compact_output | Do not include bad components in output? (1 = yes, 0 = no)',...
+ 'trace_output_option | Trace output type ("nonneg" or "raw")',...
+ 'use_sparse_arrays | Save output as sparse arrays, to save memory for large FOV movies (1 = yes, 0 = no)',...
+ 'T_min_snr | Cells with temporal trace SNR below this value are eliminated (Int, e.g. 10)',...
+ 'cellfind_max_steps | Maximum number of cell candidate initialization during cell finding step. (Int, e.g. 100)',...
+ 'temporal_corrupt_thresh | Traces with index > value are eliminated (0 to 1, e.g. 0.7)',...
+ 'spatial_corrupt_thresh | Images with index > value are eliminated (0 to 1, e.g. 0.7)',...
+ 'size_upper_limit | any cell with an area outside of these will be eliminated during cell refinement',...
+ 'low_ST_index_thresh | threshold the ROIs, where the inferred traces do not explain the activity inside the filter well',...
+ 'low_ST_corr_thresh | threshold the ROIs, where the inferred traces do not explain the activity inside the filter well',...
+ 'init_with_gaussian | Initialize with a Gaussian shape prior to robust estimation? (1 = yes, 0 = no)',...
+ 'downsample_time_by | Downsampling factor time',...
+ 'downsample_space_by | Downsampling factor space',...
+ 'T_dup_corr_thresh | Duplicate removal correlation threshold',...
+ 'max_iter | Max iterations during cell finding step',...
+ 'cellfind_filter_type | Type of the spatial smoothing filter used for cell finding. Options: "butter" (IIR butterworth filter), "gauss" (FIR filter with a gaussian kernel), "wiener" (wiener filter), "movavg" (moving average in space), "median" (median filtering in 3D), "none" (no filtering).',...
+ };
+
+ optsTmp = cellfun(@num2str,struct2cell(optsDefault),'UniformOutput',false);
+ for iz = 1:length(movieSettingsStrs)
+ movieSettingsStrs{iz} = strrep([movieSettingsStrs{iz} ' | default: ' num2str(optsTmp{iz})],'_','\_');
+ end
+
+ AddOpts.Resize='on';
+ AddOpts.WindowStyle='normal';
+ AddOpts.Interpreter='tex';
+ movieSettings = inputdlgcol(movieSettingsStrs,...
dlgStr,1,...
- {...
- 'cpu',...
- '',...
- '1'...
- '0',...
- '2',...
- '0',...
- 'nonneg',...
- '0',...
- }...
- );setNo = 1;
- options.EXTRACT.gpuOrCPU = movieSettings{setNo};setNo = setNo+1;
+ cellfun(@num2str,struct2cell(options.EXTRACT),'UniformOutput',false),...
+ AddOpts,2);
+
+ setNo = 1;
+ options.EXTRACT.use_gpu = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.parallel_cpu = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.multi_gpu = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.avg_cell_radius = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.cellfind_min_snr = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.preprocess = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.num_partitions_x = str2num(movieSettings{setNo});setNo = setNo+1;
- options.EXTRACT.num_partitions_y = options.EXTRACT.num_partitions_x;
+ options.EXTRACT.num_partitions_y = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.compact_output = str2num(movieSettings{setNo});setNo = setNo+1;
options.EXTRACT.trace_output_option = movieSettings{setNo};setNo = setNo+1;
options.EXTRACT.use_sparse_arrays = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.T_min_snr = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.cellfind_max_steps = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.temporal_corrupt_thresh = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.spatial_corrupt_thresh = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.size_upper_limit = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.low_ST_index_thresh = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.low_ST_corr_thresh = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.init_with_gaussian = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.downsample_time_by = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.downsample_space_by = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.T_dup_corr_thresh = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.max_iter = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.EXTRACT.cellfind_filter_type = movieSettings{setNo};setNo = setNo+1;
+
+ obj.functionSettings.modelExtractSignalsFromMovie.options.EXTRACT = options.EXTRACT;
case 'CNMF'
movieSettings = inputdlg({...
@@ -957,7 +1085,8 @@ function getAlgorithmOptions()
'CNMF-E | iterate over parameter space? (1 = yes, 0 = no)',...
'CNMF-E | only run initialization algorithm? (1 = yes, 0 = no)',...
'CNMF-E | Spatial down-sampling factor (scalar >= 1)',...
- 'CNMF-E | Temporal down-sampling factor (scalar >= 1)'...
+ 'CNMF-E | Temporal down-sampling factor (scalar >= 1)',...
+ 'CNMF | Run CNMF output classifier (1 = yes, 0 = no)',...
},...
dlgStr,1,...
{...
@@ -968,7 +1097,8 @@ function getAlgorithmOptions()
'0',...
'0',...
'1',...
- '1'...
+ '1',...
+ '1',...
}...
);setNo = 1;
options.CNMFE.openEditor = str2num(movieSettings{setNo});setNo = setNo+1;
@@ -979,6 +1109,7 @@ function getAlgorithmOptions()
options.CNMFE.onlyRunInitialization = str2num(movieSettings{setNo});setNo = setNo+1;
options.CNMFE.ssub = str2num(movieSettings{setNo});setNo = setNo+1;
options.CNMFE.tsub = str2num(movieSettings{setNo});setNo = setNo+1;
+ options.CNMFE.classifyComponents = str2num(movieSettings{setNo});setNo = setNo+1;
if options.CNMFE.deleteTempFolders==1
display(repmat('*',1,21))
@@ -1293,6 +1424,9 @@ function runPCAICASignalFinder()
emOptions.CELLMaxoptions.numFramesSampleFitNoiseSigma = options.CELLMax.numFramesSampleFitNoiseSigma;
emOptions.CELLMaxoptions.recalculateFinalTraces = options.CELLMax.recalculateFinalTraces;
+ emOptions.CELLMaxoptions.eccentricityThreshold = options.CELLMax.eccentricityThreshold;
+ emOptions.CELLMaxoptions.numObjThreshold = options.CELLMax.numObjThreshold;
+
if options.defaultOptions==0
emOptions.CELLMaxoptions.localICimgs = [];
emOptions.CELLMaxoptions.localICtraces = [];
@@ -1408,115 +1542,42 @@ function runPCAICASignalFinder()
ciapkg.loadBatchFxns('loadEverything');
- movieList = getFileList(obj.inputFolders{obj.fileNum}, fileFilterRegexp);
- [inputMovie thisMovieSize Npixels Ntime] = loadMovieList(movieList,'convertToDouble',0,'inputDatasetName',obj.inputDatasetName,'treatMoviesAsContinuous',1);
- inputMovie(isnan(inputMovie)) = 0;
-
- % opts.movie_dataset = obj.inputDatasetName;
- % % opts.save_to_movie_dir = 1;
- % % % make larger if using 2x downsampled movie
- % % opts.spat_linfilt_halfwidth = 2;
- % % opts.ss_cell_size_threshold = 5;
- % % opts.spat_medfilt_enabled = 0;
- % % opts.trim_pixels = 0.4;
- % % opts.verbos = 2;
- % % opts.disableGPU = 1;
-
- % % options.turboreg = getFxnSettings();
- % % options.turboreg
- % % options.datasetName = options.turboreg.datasetName;
-
- % % settingDefaults = struct(...
- % % 'movie_dataset',{{'/1','/Movie','/movie'}},...
- % % 'save_to_movie_dir', {{1,0}},...
- % % 'spat_linfilt_halfwidth', {{2,5}},...
- % % 'ss_cell_size_threshold', {{5,10}},...
- % % 'spat_medfilt_enabled', {{0,1}},...
- % % 'trim_pixels', {{0.4,0.6}},...
- % % 'verbos', {{0,1}},...
- % % 'disableGPU', {{1,0}}...
- % % );
- % % settingStr = struct(...
- % % 'movie_dataset',{{'/1','/Movie','/movie'}},...
- % % 'save_to_movie_dir', {{1,0}},...
- % % 'spat_linfilt_halfwidth', {{2,5}},...
- % % 'ss_cell_size_threshold', {{5,10}},...
- % % 'spat_medfilt_enabled', {{0,1}},...
- % % 'trim_pixels', {{0.4,0.6}},...
- % % 'verbos', {{0,1}},...
- % % 'disableGPU', {{1,0}}...
- % % );
-
- % [h,w,t] = size(inputMovie);
-
- % opts.max_cell_radius=30;
- % opts.min_cell_spacing=5;
- % opts.remove_duplicate_cells = 0;
- % % Use GPU
- % opts.compute_device='gpu';
-
- % % This is how to call the function 'partition_helper()' to find out how many partitions are necessary:
- % num_parts = partition_helper(h,w,t,opts.min_cell_spacing,opts.max_cell_radius);
-
- % % Below call returned num_parts=20. We decide to partition x axis to 4, and y axis to 5. This makes 20 parititions overall.
- % nPlotsRoot = sqrt(num_parts);
- % if nPlotsRoot<2
- % nPlotsRoot = 2;
- % end
- % integ = fix(nPlotsRoot);
- % fract = abs(nPlotsRoot - integ);
- % opts.num_partition_y = ceil(nPlotsRoot);
- % opts.num_partition_x = floor(nPlotsRoot)+round(fract)
-
- % min_cell_spacing=3;
- % max_cell_radius=10;
- % num_partition_x=3;
- % num_partitiony=3;
- % cell_keep_tolerance=5;
- % subtract_background=1;
-
- % opts.config.diffuse_F=1;
- % opts.config.smooth_T = 0;
- % opts.config.smooth_F = 0;
- % opts.config.cell_keep_tolerance
-
- % [filters,traces,info,opts] = extractor(movieList{1},opts);
- % [filters,traces,info,opts] = extractor(inputMovie,opts);
- % outStruct = extractor(inputMovie,opts);
+ % Load default configuration
+ extractConfig = get_defaults([]);
- % switch pcaicaPCsICsSwitchStr
- % case 'Subject'
- % nPCsnICs = obj.numExpectedSignals.(obj.signalExtractionMethod).(obj.subjectStr{obj.fileNum})
- % case 'Folder'
- % nPCsnICs = obj.numExpectedSignals.(obj.signalExtractionMethod).Folders{obj.fileNum}
+ % switch options.EXTRACT.gpuOrCPU
+ % case 'gpu'
+ % extractConfig.use_gpu = 1;
+ % case 'cpu'
+ % extractConfig.use_gpu = 0;
+ % extractConfig.parallel_cpu = 1;
% otherwise
% % body
% end
- % extractConfig.num_estimated_cells = nPCsnICs(1);
-
- % Load default configuration
- extractConfig = get_defaults([]);
-
- extractConfig.avg_cell_radius = gridWidth.(obj.subjectStr{obj.fileNum});
- extractConfig.preprocess = options.EXTRACT.preprocess;
- switch options.EXTRACT.gpuOrCPU
- case 'gpu'
- extractConfig.use_gpu = 1;
- case 'cpu'
- extractConfig.use_gpu = 0;
- extractConfig.parallel_cpu = 1;
- otherwise
- % body
- end
% extractConfig.remove_static_background = false;
% extractConfig.skip_dff = true;
- extractConfig.cellfind_min_snr = options.EXTRACT.cellfind_min_snr;
extractConfig.num_partitions_x = options.EXTRACT.num_partitions_x;
extractConfig.num_partitions_y = options.EXTRACT.num_partitions_y;
- extractConfig.trace_output_option = options.EXTRACT.trace_output_option;
- extractConfig.use_sparse_arrays = options.EXTRACT.use_sparse_arrays;
+
+ % Merge user options and EXTRACT options
+ [extractConfig] = ciapkg.io.mergeStructs(extractConfig,options.EXTRACT,'showStack',0);
+ [extractConfig.thresholds] = ciapkg.io.mergeStructs(extractConfig.thresholds,options.EXTRACT,'showStack',0);
+
+ extractConfig.avg_cell_radius = gridWidth.(obj.subjectStr{obj.fileNum});
+
+ fn_structdisp(extractConfig);
+
+ % extractConfig.preprocess = options.EXTRACT.preprocess;
+ % extractConfig.cellfind_min_snr = options.EXTRACT.cellfind_min_snr;
+ % extractConfig.trace_output_option = options.EXTRACT.trace_output_option;
+ % extractConfig.use_sparse_arrays = options.EXTRACT.use_sparse_arrays;
% extractConfig.compact_output = options.EXTRACT.compact_output;
+ % extractConfig.T_min_SNR = options.EXTRACT.T_min_SNR;
+ % extractConfig.cellfind_max_steps = options.EXTRACT.cellfind_max_steps;
+ % extractConfig.temporal_corrupt_thresh = options.EXTRACT.temporal_corrupt_thresh;
+ % extractConfig.spatial_corrupt_thresh = options.EXTRACT.spatial_corrupt_thresh;
+ % extractConfig.multi_gpu = options.EXTRACT.multi_gpu;
% extractConfig.thresholds.T_min_snr = 3; % multiply with noise_std
% extractConfig.thresholds.size_lower_limit = 1/5; % multiply with avg_cell_area
@@ -1529,6 +1590,14 @@ function runPCAICASignalFinder()
% extractConfig.thresholds.low_ST_index_thresh = 1e-2;
% extractConfig.thresholds.high_ST_index_thresh = 0.8;
+ % disp(extractConfig)
+ % pause
+
+ % Load movie
+ movieList = getFileList(obj.inputFolders{obj.fileNum}, fileFilterRegexp);
+ [inputMovie thisMovieSize Npixels Ntime] = loadMovieList(movieList,'convertToDouble',0,'inputDatasetName',obj.inputDatasetName,'treatMoviesAsContinuous',1);
+ inputMovie(isnan(inputMovie)) = 0;
+
startTime = tic;
outStruct = extractor(inputMovie,extractConfig);
outStruct
@@ -1543,9 +1612,11 @@ function runPCAICASignalFinder()
try
extractAnalysisOutput.info = outStruct.info;
extractAnalysisOutput.config = outStruct.config;
- extractAnalysisOutput.info = outStruct.info;
+ % extractAnalysisOutput.info = outStruct.info;
+
% Remove the large summary field since takes up unnecessary space
- extractAnalysisOutput.info.summary = [];
+ % extractAnalysisOutput.info.summary = [];
+
extractAnalysisOutput.file = movieList{1};
extractAnalysisOutput.userInputConfig = extractConfig;
% for backwards compatibility
@@ -1844,7 +1915,8 @@ function runPCAICASignalFinder()
cnmfeOptions = cnmfeOpts;
end
- cnmfOptions.nonCNMF.inputDatasetName = obj.inputDatasetName;
+ cnmfeOptions.nonCNMF.inputDatasetName = obj.inputDatasetName;
+ cnmfeOptions.nonCNMF.classifyComponents = options.CNMFE.classifyComponents;
try
display(repmat('*',1,14))
@@ -2211,4 +2283,89 @@ function runPCAICASignalFinder()
turboregSettingStruct.(property) = turboregSettingDefaults.(property){uiListHandleData.Value};
end
close(1337)
-end
\ No newline at end of file
+end
+
+% function [extractAnalysisOutput] = runEXTRACTSignalFinder()
+
+ % opts.movie_dataset = obj.inputDatasetName;
+ % % opts.save_to_movie_dir = 1;
+ % % % make larger if using 2x downsampled movie
+ % % opts.spat_linfilt_halfwidth = 2;
+ % % opts.ss_cell_size_threshold = 5;
+ % % opts.spat_medfilt_enabled = 0;
+ % % opts.trim_pixels = 0.4;
+ % % opts.verbos = 2;
+ % % opts.disableGPU = 1;
+
+ % % options.turboreg = getFxnSettings();
+ % % options.turboreg
+ % % options.datasetName = options.turboreg.datasetName;
+
+ % % settingDefaults = struct(...
+ % % 'movie_dataset',{{'/1','/Movie','/movie'}},...
+ % % 'save_to_movie_dir', {{1,0}},...
+ % % 'spat_linfilt_halfwidth', {{2,5}},...
+ % % 'ss_cell_size_threshold', {{5,10}},...
+ % % 'spat_medfilt_enabled', {{0,1}},...
+ % % 'trim_pixels', {{0.4,0.6}},...
+ % % 'verbos', {{0,1}},...
+ % % 'disableGPU', {{1,0}}...
+ % % );
+ % % settingStr = struct(...
+ % % 'movie_dataset',{{'/1','/Movie','/movie'}},...
+ % % 'save_to_movie_dir', {{1,0}},...
+ % % 'spat_linfilt_halfwidth', {{2,5}},...
+ % % 'ss_cell_size_threshold', {{5,10}},...
+ % % 'spat_medfilt_enabled', {{0,1}},...
+ % % 'trim_pixels', {{0.4,0.6}},...
+ % % 'verbos', {{0,1}},...
+ % % 'disableGPU', {{1,0}}...
+ % % );
+
+ % [h,w,t] = size(inputMovie);
+
+ % opts.max_cell_radius=30;
+ % opts.min_cell_spacing=5;
+ % opts.remove_duplicate_cells = 0;
+ % % Use GPU
+ % opts.compute_device='gpu';
+
+ % % This is how to call the function 'partition_helper()' to find out how many partitions are necessary:
+ % num_parts = partition_helper(h,w,t,opts.min_cell_spacing,opts.max_cell_radius);
+
+ % % Below call returned num_parts=20. We decide to partition x axis to 4, and y axis to 5. This makes 20 parititions overall.
+ % nPlotsRoot = sqrt(num_parts);
+ % if nPlotsRoot<2
+ % nPlotsRoot = 2;
+ % end
+ % integ = fix(nPlotsRoot);
+ % fract = abs(nPlotsRoot - integ);
+ % opts.num_partition_y = ceil(nPlotsRoot);
+ % opts.num_partition_x = floor(nPlotsRoot)+round(fract)
+
+ % min_cell_spacing=3;
+ % max_cell_radius=10;
+ % num_partition_x=3;
+ % num_partitiony=3;
+ % cell_keep_tolerance=5;
+ % subtract_background=1;
+
+ % opts.config.diffuse_F=1;
+ % opts.config.smooth_T = 0;
+ % opts.config.smooth_F = 0;
+ % opts.config.cell_keep_tolerance
+
+ % [filters,traces,info,opts] = extractor(movieList{1},opts);
+ % [filters,traces,info,opts] = extractor(inputMovie,opts);
+ % outStruct = extractor(inputMovie,opts);
+
+ % switch pcaicaPCsICsSwitchStr
+ % case 'Subject'
+ % nPCsnICs = obj.numExpectedSignals.(obj.signalExtractionMethod).(obj.subjectStr{obj.fileNum})
+ % case 'Folder'
+ % nPCsnICs = obj.numExpectedSignals.(obj.signalExtractionMethod).Folders{obj.fileNum}
+ % otherwise
+ % % body
+ % end
+ % extractConfig.num_estimated_cells = nPCsnICs(1);
+% end
\ No newline at end of file
diff --git a/@ciatah/modelGetSignalsImages.m b/@ciatah/modelGetSignalsImages.m
index d32faee..74eea38 100644
--- a/@ciatah/modelGetSignalsImages.m
+++ b/@ciatah/modelGetSignalsImages.m
@@ -14,6 +14,7 @@
% 2020.12.08 [01:14:09] - Reorganized returnType to be outside raw signals flag, so common filtering mechanism regardless of variables loaded into RAM or not. This fixes if a user loads variables into RAM then uses cross-session alignment, the viewMatchObjBtwnSessions method may not display registered images (cross-session alignment is still fine and valid).
% 2021.06.30 [14:52:23] - Updated to allow loading raw files without calling modelVarsFromFiles.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.04.09 [20:30:52] - Ensure when loading cell extraction CIAtah-style that only MAT files are loaded.
% TODO
% Give a user a warning() output if there are no or empty cell-extraction outputs
@@ -256,7 +257,7 @@
end
else
while isempty(filesToLoad)
- filesToLoad = getFileList(obj.dataPath{thisFileNum},strrep(regexPairs{fileToLoadNo},'.mat',''));
+ filesToLoad = getFileList(obj.dataPath{thisFileNum},strrep(regexPairs{fileToLoadNo},'.mat','.*.mat$'));
fileToLoadNo = fileToLoadNo+1;
if fileToLoadNo>nRegExps
break;
@@ -264,7 +265,7 @@
end
end
if isempty(filesToLoad)
- else
+ else
[inputImages,inputSignals,infoStruct,algorithmStr,inputSignals2] = ciapkg.io.loadSignalExtraction(filesToLoad{1});
end
signalPeaks = [];
@@ -300,7 +301,7 @@
if loadCiaFiles==1
while isempty(filesToLoad)
- filesToLoad = getFileList(obj.dataPath{thisFileNum},strrep(regexPairs{fileToLoadNo},'.mat',''));
+ filesToLoad = getFileList(obj.dataPath{thisFileNum},strrep(regexPairs{fileToLoadNo},'.mat','.*.mat$'));
fileToLoadNo = fileToLoadNo+1;
if fileToLoadNo>nRegExps
break;
@@ -642,7 +643,11 @@
% if exist('inputSignals','var')
if ~isempty(inputSignals)
- if isempty(obj.signalPeaksArray{thisFileNum})
+ if isempty(obj.signalPeaksArray)
+ disp('Please run modelVarsFromFiles')
+ signalPeaks = [];
+ signalPeaksArray = [];
+ elseif isempty(obj.signalPeaksArray{thisFileNum})
signalPeaks = [];
signalPeaksArray = [];
else
diff --git a/@ciatah/modelModifyRegionAnalysis.m b/@ciatah/modelModifyRegionAnalysis.m
index ceed7e6..f03705c 100644
--- a/@ciatah/modelModifyRegionAnalysis.m
+++ b/@ciatah/modelModifyRegionAnalysis.m
@@ -11,7 +11,8 @@
% 2017.01.14 [20:06:04] - support switched from [nSignals x y] to [x y nSignals]
% 2021.06.18 [21:41:07] - added modelVarsFromFilesCheck() to check and load signals if user hasn't already.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
- % 2022.02.28 [13:01:20] - Fix modelVarsFromFilesCheck and modelVarsFromFiles recursion edge case.
+ % 2022.02.28 [13:01:20] - Fix modelVarsFromFilesCheck and modelVarsFromFiles recursion edge case.
+ % 2022.08.26 [15:00:28] - Misc code standard updates.
% TODO
%
@@ -26,7 +27,7 @@
if obj.guiEnabled==1
scnsize = get(0,'ScreenSize');
- [fileIdxArray idNumIdxArray nFilesToAnalyze nFiles] = obj.getAnalysisSubsetsToAnalyze();
+ [fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = obj.getAnalysisSubsetsToAnalyze();
% [fileIdxArray, ok] = listdlg('ListString',obj.fileIDNameArray,'ListSize',[scnsize(3)*0.2 scnsize(4)*0.25],'Name','which folders to analyze?');
scnsize = get(0,'ScreenSize');
@@ -59,12 +60,12 @@
regionFile = getFileList(obj.inputFolders{obj.fileNum},obj.regionModSaveStr);
% if no file, skip
if isempty(regionFile)
- display('No region analysis to load!')
+ disp('No region analysis to load!')
continue;
end
regionFile = regionFile{1};
fprintf('loading: %s',regionFile)
- loadFile = load(regionFile,'roipolyRegion','roipolyCoords')
+ loadFile = load(regionFile,'roipolyRegion','roipolyCoords');
obj.analysisROIArray{obj.fileNum} = loadFile.roipolyRegion;
obj.validRegionModPoly{obj.fileNum} = loadFile.roipolyCoords;
% NOT DONE!!!!!!!!
@@ -73,14 +74,14 @@
regionFile = getFileList(obj.inputFolders{obj.fileNum},obj.regionModSaveStr);
% if no file, skip
if isempty(regionFile)
- display('No region analysis to load!')
+ disp('No region analysis to load!')
continue;
end
- loadFile = load(regionFile,'roipolyRegion','roipolyCoords')
+ loadFile = load(regionFile,'roipolyRegion','roipolyCoords');
obj.analysisROIArray{obj.fileNum} = loadFile.roipolyRegion;
obj.validRegionModPoly{obj.fileNum} = loadFile.roipolyCoords;
end
- [inputSignals inputImages signalPeaks signalPeaksArray] = modelGetSignalsImages(obj,'returnType','raw');
+ [inputSignals, inputImages, signalPeaks, signalPeaksArray] = modelGetSignalsImages(obj,'returnType','raw');
% inputImages = thresholdImages(inputImages,'binary',0)/3;
% [inputSignals inputImages signalPeaks signalPeaksArray] = modelGetSignalsImages(obj);
@@ -147,9 +148,9 @@
h = impoly(gca,polyCoords);
h.wait
polyCoords = h.getPosition;
- [obj.analysisROIArray{obj.fileNum} xpoly ypoly] = roipoly(thisCellmap+0.1,polyCoords(:,1),polyCoords(:,2));
+ [obj.analysisROIArray{obj.fileNum}, xpoly, ypoly] = roipoly(thisCellmap+0.1,polyCoords(:,1),polyCoords(:,2));
else
- [obj.analysisROIArray{obj.fileNum} xpoly ypoly] = roipoly;
+ [obj.analysisROIArray{obj.fileNum}, xpoly, ypoly] = roipoly;
end
obj.validRegionModPoly{obj.fileNum} = [xpoly ypoly];
end
@@ -158,13 +159,13 @@
drawnow;
end
if strcmp(analysisToRun,'runAlreadySelectedRegions')
- display('Using previous ROI')
+ disp('Using previous ROI')
end
inputImagesROI = obj.analysisROIArray{obj.fileNum};
inputImagesThres = thresholdImages(inputImages,'waitbarOn',1,'binary',1);
% signalInROI = squeeze(nansum(nansum(bsxfun(@times,inputImages,permute(inputImages,[2 3 1])),1),2));
- display('finding ROIs inside region...')
- signalInROI = squeeze(nansum(nansum(bsxfun(@times,inputImagesThres,inputImagesROI),1),2));
+ disp('finding ROIs inside region...')
+ signalInROI = squeeze(sum(sum(bsxfun(@times,inputImagesThres,inputImagesROI),1,'omitnan'),2,'omitnan'));
% signalInROI = applyImagesToMovie(inputImages,permute(inputImages,[2 3 1]), 'alreadyThreshold',1);
signalsToKeep = signalInROI~=0;
diff --git a/@ciatah/modelPreprocessMovieFunction.m b/@ciatah/modelPreprocessMovieFunction.m
index 3fae594..1286f89 100644
--- a/@ciatah/modelPreprocessMovieFunction.m
+++ b/@ciatah/modelPreprocessMovieFunction.m
@@ -45,6 +45,14 @@
% 2022.01.25 [16:08:49] - Add `modelPreprocessMovieFunction` settings saved in CIAtah class to saved output file for later retrieval.
% 2022.01.26 [08:31:06] - For selecting turboreg crop coordinates, switched to ciapkg.io.readFrame when single frame is requested as this is faster and avoids long read times of all frame information as occurs in certain types of TIFF files or hard drives.
% 2022.02.09 [23:51:18] - Misc code fixes to conform to better Matlab language standards.
+ % 2022.03.03 [19:18:54] - Change all nested and local functions to start with "nestedfxn_" or "localfxn_" prefixes for clarity. Added option to fix "broken" frames, e.g. frames in which the camera gives out garbled or improperly formatted data. Fix with mean of the movie to reduce effect on downstream analysis. Disable local function "getMovieFileType" in favor or CIAtah package function.
+ % 2022.03.09 [16:50:36] - Added option to place prefix on all output files.
+ % 2022.03.12 [19:19:41] - Detrending now does everything within nested function instead of calling normalizeMovie() to reduce memory overhead.
+ % 2022.03.13 [23:20:08] - Added support for MAT-file based processing of movies.
+ % 2022.06.28 [15:28:13] - Add additional black background support.
+ % 2022.07.10 [17:21:21] - Added largeMovieLoad setting support, improve memory allocation for select movies.
+ % 2022.07.27 [12:45:08] - Make sure readFrame is compatible with all HDF5 dataset names.
+ % 2022.07.18 [10:30:01] - NormCorre integrated into the pipeline for end users.
% TODO
% Allow users to save out analysis options order and load it back in.
% Insert NaNs or mean of the movie into dropped frame location, see line 260
@@ -59,6 +67,12 @@
% load necessary functions and defaults
% loadBatchFxns();
%========================
+ % Str: path to MAT-file that will be used to temporarily store processed movie, to avoid Out of Memory errors for certain movies.
+ options.matfilePath = '';
+ % Str: Variable name for movie to store MAT-file in. DO NOT CHANGE for the moment.
+ options.matfileVarname = 'thisMovie';
+ % INTERNAL | Binary: 1 = running MAT-file based analysis, 0 = run normal analysis load in disk.
+ options.runMatfile = 0;
% Binary: 1 = show figures, 0 = disable showing figures
options.showFigures = 1;
% set the options, these can be modified by varargin
@@ -128,6 +142,12 @@
% Str: daset
options.nwbSettingsDatasetname = '/general/optophysiology/imaging_plane/description';
options.h5SettingsDatasetname = '/movie/preprocessingSettingsAll';
+ % Str: prefix to add to all saved files.
+ options.saveFilePrefix = '';
+ % Str: suffix to add to all saved files.
+ options.saveFileSuffix = '';
+ % Str: suffix to add to all saved files.
+ options.videoPlayer = 'matlab';
% ====
% get options
options = getOptions(options,varargin);
@@ -245,7 +265,10 @@
'str','Manually register movie images. (ignore)'),...
'turboreg',struct(...
'save','treg',...
- 'str','TurboReg (motion correction with option to spatially filter)'),...
+ 'str','Motion correction - TurboReg or NoRMCorre (with option to spatially filter)'),...
+ 'normcorre',struct(...
+ 'save','normcorre',...
+ 'str','NoRMCorre (motion correction with option to spatially filter)'),...
'fft_highpass',struct(...
'save','fftHp',...
'str','High-pass FFT (ignore most cases).'),...
@@ -296,6 +319,12 @@
% Do nothing
end
+ % Load existing settings
+ try
+ options.videoPlayer = obj.functionSettings.modelPreprocessMovieFunction.options.videoPlayer;
+ catch
+ end
+
analysisOptionListStr = analysisOptionList;
for optNoS3 = 1:length(analysisOptionListStr)
analysisOptionListStr{optNoS3} = analysisOptsInfo.(analysisOptionListStr{optNoS3}).str;
@@ -307,6 +336,8 @@
try
ok = 1;
[~, ~] = openFigure(1776, '');clf;
+
+ set(gcf,'color',[0 0 0]);
instructTextPos = [1 80 70 20]/100;
listTextPos = [1 40 98 28]/100;
listTextPos2 = [1 1 98 28]/100;
@@ -314,29 +345,30 @@
figNoList = struct;
figNoList.detrend = 102030;
- shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[1 75 30 3]/100,'FontSize',9,'string','Reset to default list order','callback',@subfxn_resetSetpsMenu);
- shortcutMenuHandle2 = uicontrol('style','pushbutton','Units','normalized','position',[32 75 30 3]/100,'FontSize',9,'string','Select default options (e.g. post-dragging)','callback',@subfxn_highlightDefault);
- shortcutMenuHandle3 = uicontrol('style','pushbutton','Units','normalized','position',[63 75 30 3]/100,'FontSize',9,'string','Finished','callback',@subfxn_closeOptions);
- % shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[32 62 30 3]/100,'FontSize',9,'string','Next screen','callback',@subfxn_highlightDefault);
+ shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[1 75 30 3]/100,'FontSize',9,'string','Reset to default list order','callback',@nestedfxn_resetSetpsMenu);
+ shortcutMenuHandle2 = uicontrol('style','pushbutton','Units','normalized','position',[32 75 30 3]/100,'FontSize',9,'string','Select default options (e.g. post-dragging)','callback',@nestedfxn_highlightDefault);
+ shortcutMenuHandle3 = uicontrol('style','pushbutton','Units','normalized','position',[63 75 30 3]/100,'FontSize',9,'string','Finished','callback',@nestedfxn_closeOptions);
+ % shortcutMenuHandle = uicontrol('style','pushbutton','Units','normalized','position',[32 62 30 3]/100,'FontSize',9,'string','Next screen','callback',@nestedfxn_highlightDefault);
- uicontrol('Style','Text','String','Analysis steps to perform.','Units','normalized','Position',[1 68 90 3]/100,'BackgroundColor','white','HorizontalAlignment','Left','FontWeight','bold');
+ uicontrol('Style','Text','String','Analysis steps to perform.','Units','normalized','Position',[1 68 90 3]/100,'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontWeight','bold');
[hListbox, jListbox, jScrollPane, jDND] = reorderableListbox('String',analysisOptionListStr,'Units','normalized','Position',listTextPos,'Max',Inf,'Min',0,'Value',defaultChoiceIdx,...
- 'MousePressedCallback',@subfxn_analysisOutputMenuChange,...
- 'MouseReleasedCallback',@subfxn_analysisOutputMenuChange,...
- 'DragOverCallback',@subfxn_analysisOutputMenuChange2,...
- 'DropCallback',@subfxn_analysisOutputMenuChange...
+ 'MousePressedCallback',@nestedfxn_analysisOutputMenuChange,...
+ 'MouseReleasedCallback',@nestedfxn_analysisOutputMenuChange,...
+ 'DragOverCallback',@nestedfxn_analysisOutputMenuChange2,...
+ 'DropCallback',@nestedfxn_analysisOutputMenuChange...
);
- uicontrol('Style','Text','String',['At which analysis step should files be saved to disk?'],'Units','normalized','Position',[1 30 90 3]/100,'BackgroundColor','white','HorizontalAlignment','Left','FontWeight','bold');
+ uicontrol('Style','Text','String',['At which analysis step should files be saved to disk?'],'Units','normalized','Position',[1 30 90 3]/100,'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontWeight','bold');
[hListboxS, jListboxS, jScrollPaneS, jDNDS] = reorderableListbox('String',analysisOptionListStr,'Units','normalized','Position',listTextPos2,'Max',Inf,'Min',0,'Value',defaultSaveIdx);
uicontrol('Style','Text','String',['Analysis step selection and ordering' 10 '======='...
10 'Gentlemen, you can not fight in here! This is the War Room.' 10 'We can know only that we know nothing.' 10 'And that is the highest degree of human wisdom.'...
- 10 10 '1: Click items to select.' 10 '2: Drag to re-order analysis.' 10 '3: Click command window and press ENTER to continue.'],'Units','normalized','Position',instructTextPos,'BackgroundColor','white','HorizontalAlignment','Left');
+ 10 10 '1: Click items to select.' 10 '2: Drag to re-order analysis.' 10 '3: Click command window and press ENTER to continue.'],...
+ 'Units','normalized','Position',instructTextPos,'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left');
- emptyBox = uicontrol('Style','Text','String','','Units','normalized','Position',[1 1 1 1]/100,'BackgroundColor','white','HorizontalAlignment','Left','FontWeight','bold','FontSize',7);
+ emptyBox = uicontrol('Style','Text','String','','Units','normalized','Position',[1 1 1 1]/100,'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Left','FontWeight','bold','FontSize',7);
if ismac
cmdWinEditorFont = 'Menlo-Regular';
@@ -348,15 +380,17 @@
cmdWinEditorFont = 'FixedWidth';
end
- usaFlagPic = @(x) uicontrol('Style','Text','String',x,'Units','normalized','Position',[0.7 0.85 0.28 0.14],'BackgroundColor','white','HorizontalAlignment','Right','FontName',cmdWinEditorFont,'FontSize',5);
+ usaFlagPic = @(x) uicontrol('Style','Text','String',x,'Units','normalized','Position',[0.7 0.85 0.28 0.14],'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Right','FontName',cmdWinEditorFont,'FontSize',5);
usaFlagPic(USAflagStr);
% exitHandle = uicontrol('style','pushbutton','Units', 'normalized','position',[5 85 50 3]/100,'FontSize',9,'string','Click here to finish','callback',@subfxnCloseFig,'HorizontalAlignment','Left');
% Wait for user input
- % set(gcf, 'WindowScrollWheelFcn', @mouseWheelChange);
+ % set(gcf, 'WindowScrollWheelFcn', @nestedfxn_mouseWheelChange);
% set(gcf,'KeyPressFcn', @(src,event) set(gcf,'Tag','next'));
- set(gcf,'KeyPressFcn', @subfxn_closeOptionsKey);
+ set(gcf,'KeyPressFcn', @nestedfxn_closeOptionsKey);
+ set(gcf,'color',[0 0 0]);
+ ciapkg.view.changeFont('none','fontColor','w')
% waitfor(gcf,'Tag');
waitfor(emptyBox);
% pause
@@ -456,7 +490,11 @@
'[optional, if using HDF5] Input HDF5 file dataset name (e.g. "/images" for raw Inscopix or "/1" for example data, sans quotes): ',...
'[optional, if using HDF5] Output HDF5 file dataset name (see above): ',...
'[optional] (0) Use default preprocessing settings, (1) Save settings or load previously saved settings: ',...
- '[optional] (1) Save movie to NWB format, (0) Save to HDF5 format: '...
+ '[optional] (1) Save movie to NWB format, (0) Save to HDF5 format: ',...
+ '[optional] Prefix to add to all processed movies (e.g. "concat_", "experiment_"): ',...
+ '[optional] Suffix to add to all processed movies (e.g. "concat_", "experiment_"): '...
+ '[optional] Default video player (imagej or matlab): ',...
+ '[optional] MAT-file path if processing large movie (e.g. getting Out of Memory errors), SSD drive preferred for speed: '...
},...
'Preprocessing settings',[1 100],...
{...
@@ -465,7 +503,11 @@
obj.inputDatasetName,...
obj.outputDatasetName,...
num2str(obj.saveLoadPreprocessingSettings),...
- num2str(obj.nwbLoadFiles)...
+ num2str(obj.nwbLoadFiles),...
+ '',...
+ '',...
+ options.videoPlayer,...
+ options.matfilePath...
}...
);
disp(movieSettings)
@@ -479,6 +521,12 @@
obj.outputDatasetName = movieSettings{4};
obj.saveLoadPreprocessingSettings = str2num(movieSettings{5});
obj.nwbLoadFiles = str2num(movieSettings{6});
+ options.saveFilePrefix = movieSettings{7};
+ options.saveFileSuffix = movieSettings{8};
+ options.videoPlayer = movieSettings{9};
+ options.matfilePath = movieSettings{10};
+
+ obj.functionSettings.modelPreprocessMovieFunction.options.videoPlayer = options.videoPlayer;
if obj.saveLoadPreprocessingSettings==1
currentDateTimeStr = datestr(now,'yyyymmdd_HHMMSS','local');
@@ -538,9 +586,20 @@
end
else
end
+
+ % Get movie processing settings, NOT just for motion correction
[options.turboreg, preprocessingSettingsAll] = obj.getRegistrationSettings('Processing options','inputSettings',previousPreprocessSettings);
fn_structdisp(options.turboreg)
+
+ % Get additional NormCorre settings
+ switch options.turboreg.mcMethod
+ case 'normcorre'
+ optsNoRMCorre = ciapkg.motion_correction.getNoRMCorreParams([400 400 1],'guiDisplay',1);
+ otherwise
+ optsNoRMCorre = [];
+ end
+
if obj.saveLoadPreprocessingSettings==1
obj.preprocessSettings = preprocessingSettingsAll;
try
@@ -575,7 +634,7 @@
if sum(strcmp(analysisOptionList(analysisOptionsIdx),'turboreg'))>0
if options.processMovies==1
try
- [turboRegCoords] = turboregCropSelection(options,folderList);
+ [turboRegCoords] = localfxn_turboregCropSelection(options,folderList);
catch err
disp(repmat('@',1,7))
disp(getReport(err,'extended','hyperlinks','on'));
@@ -673,7 +732,7 @@
% Get the list of movies
movieList = getFileList(thisDir, options.fileFilterRegexp);
- [movieList] = removeUnsupportedFiles(movieList,options);
+ [movieList] = localfxn_removeUnsupportedFiles(movieList,options);
% If there are no files in this folder to analyze, skip to the next folder.
if isempty(movieList)
@@ -685,7 +744,12 @@
disp(fileInfo)
% base string to save as
fileInfoSaveStr = [fileInfo.date '_' fileInfo.protocol '_' fileInfo.subject '_' fileInfo.assay];
- thisDirSaveStr = [thisDir filesep fileInfoSaveStr];
+ if isempty(options.saveFilePrefix)
+ thisDirSaveStr = [thisDir filesep fileInfoSaveStr];
+ else
+ thisDirSaveStr = [thisDir filesep options.saveFilePrefix fileInfoSaveStr];
+ end
+
thisProcessingDirFileInfoStr = [thisProcessingDir filesep currentDateTimeStr '_' fileInfoSaveStr];
saveStr = '';
% add the folder to the output structure
@@ -734,19 +798,34 @@
% options.frameList
options.frameList(options.frameList>sum(movieDims.z)) = [];
end
- if options.processMoviesSeparately==1
- thisMovie = loadMovieList(movieList{movieNo},'convertToDouble',0,'frameList',options.frameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',0,'loadSpecificImgClass','single');
- % playMovie(thisMovie);
- else
- % options.turboreg.treatMoviesAsContinuousSwitch = 1;
- if isempty(options.frameList)&&options.turboreg.loadMoviesFrameByFrame==1
- movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single','getMovieDims',1);
- sum(movieDims.z)
- thisFrameList = 1:sum(movieDims.z);
+ if isempty(options.matfilePath)
+ if options.processMoviesSeparately==1
+ thisMovie = loadMovieList(movieList{movieNo},'convertToDouble',0,'frameList',options.frameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',0,'loadSpecificImgClass','single','largeMovieLoad',options.turboreg.largeMovieLoad);
+ % playMovie(thisMovie);
else
- thisFrameList = options.frameList;
+ % options.turboreg.treatMoviesAsContinuousSwitch = 1;
+ if isempty(options.frameList)&&options.turboreg.loadMoviesFrameByFrame==1
+ movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single','getMovieDims',1,'largeMovieLoad',options.turboreg.largeMovieLoad);
+ sum(movieDims.z)
+ thisFrameList = 1:sum(movieDims.z);
+ else
+ thisFrameList = options.frameList;
+ end
+ thisMovie = loadMovieList(movieList,'convertToDouble',0,'frameList',thisFrameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single','largeMovieLoad',options.turboreg.largeMovieLoad);
end
- thisMovie = loadMovieList(movieList,'convertToDouble',0,'frameList',thisFrameList,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single');
+ else
+ % Write data to MAT-file to allow read-from-disk processing.
+ [success] = writeDataToMatfile(movieList{movieNo},options.matfileVarname,options.matfilePath,...
+ 'chunkSize',options.turboregNumFramesSubset,...
+ 'saveSpecificImgClass','single');
+ matObj = matfile(movieList{movieNo},'Writable',true);
+
+ % Make a dummy thisMovie for use in certain cases.
+ thisMovie = [];
+ if success==0
+ continue;
+ end
+ options.runMatfile = 1;
end
if options.processMoviesSeparately==1
@@ -756,7 +835,12 @@
end
[~, ~] = openFigure(4242, '');
- imagesc(squeeze(thisMovie(:,:,1)))
+ if options.runMatfile==1
+ tmpFrameHere = squeeze(matObj.(options.matfileVarname)(:,:,1));
+ else
+ tmpFrameHere = squeeze(thisMovie(:,:,1));
+ end
+ imagesc()
box off;
dispStr = [num2str(fileNumToRun) '/' num2str(nFilesToRun) ': ' 10 thisDirDispStr];
axis image; colormap gray;
@@ -774,7 +858,9 @@
inputMovieF0 = [];
% to improve memory usage, edit the movie in loops, at least until this is made object oriented.
for optionIdx = analysisOptionsIdx
- thisMovie = single(thisMovie);
+ if options.runMatfile==0
+ thisMovie = single(thisMovie);
+ end
optionName = analysisOptionList{optionIdx};
% Update the save string based on the analysis about to be run.
@@ -793,7 +879,7 @@
%% ADD BACK DROPPED FRAMES
% try
% if strcmp(optionName,'downsampleTime')
- % subfxnAddDroppedFrames();
+ % nestedfxn_addDroppedFrames();
% end
% catch err
% % save the location of the downsampled dfof for PCA-ICA identification
@@ -804,9 +890,9 @@
try
switch optionName
case 'fixDropFrames'
- subfxnAddDroppedFrames();
+ nestedfxn_addDroppedFrames();
case 'turboreg'
- subfxnPlotMotionCorrectionMetric('start');
+ subfxn_plotMotionCorrectionMetric('start');
pxToCropAll = 0;
ResultsOutOriginal = {};
@@ -817,7 +903,7 @@
% Miji;
manageMiji('startStop','start');
end
- turboregInputMovie();
+ subfxn_turboregInputMovie();
% Save output of translation.
save([thisProcessingDir filesep currentDateTimeStr '_turboregTranslationOutput.mat'],'ResultsOutOriginal');
@@ -827,16 +913,20 @@
end
% playMovie(thisMovie);
if options.turboreg.numTurboregIterations>1
- pxToCropTmp = getCropValues();
+ pxToCropTmp = subfxn_getCropValues();
pxToCropAll = max([pxToCropAll pxToCropTmp]);
fprintf('pxToCropAll: %d\n',pxToCropAll);
end
end
% Get the amount of motion and hence amount to crop, directly from turboreg output
- % gg = cellfun(@(z) cell2mat(cellfun(@(y) cell2mat(cellfun(@(x) max(abs(x.Translation)),y,'UniformOutput',false)),z,'UniformOutput',false)),ResultsOutOriginal,'UniformOutput',false);
- gg = cellfun(@(z) cell2mat(cellfun(@(y) cell2mat(cellfun(@(x) max(ceil(abs(x.Translation))),y,'UniformOutput',false)),z,'UniformOutput',false)),ResultsOutOriginal,'UniformOutput',false);
- pxToCropAllTmp = ceil(max(sum(abs(cat(1,gg{:})),1),[],'omitnan'));
+ try
+ % gg = cellfun(@(z) cell2mat(cellfun(@(y) cell2mat(cellfun(@(x) max(abs(x.Translation)),y,'UniformOutput',false)),z,'UniformOutput',false)),ResultsOutOriginal,'UniformOutput',false);
+ gg = cellfun(@(z) cell2mat(cellfun(@(y) cell2mat(cellfun(@(x) max(ceil(abs(x.Translation))),y,'UniformOutput',false)),z,'UniformOutput',false)),ResultsOutOriginal,'UniformOutput',false);
+ pxToCropAllTmp = ceil(max(sum(abs(cat(1,gg{:})),1),[],'omitnan'));
+ catch
+ pxToCropAllTmp = 0;
+ end
fprintf('pxToCropAllTmp: %d\n',pxToCropAllTmp);
if pxToCropAllTmp>pxToCropAll
disp('Adjusting pxToCropAll')
@@ -918,47 +1008,47 @@
clear tmpCropMovie;
end
- subfxnPlotMotionCorrectionMetric('end');
+ subfxn_plotMotionCorrectionMetric('end');
case 'crop'
if exist('pxToCropAll','var')==1&&pxToCropAll~=0
if pxToCropAll~=0
if pxToCropAll500
ostruct.movieFrames{fileNum} = 500;
else
@@ -1122,7 +1235,7 @@
% if options.inputPCAICA==0
% [ostruct options] = getPcaIcaParams(ostruct,options)
% end
- [ostruct, options] = playOutputMovies(ostruct,options);
+ [ostruct, options] = localfxn_playOutputMovies(ostruct,options);
toc(startTime)
@@ -1132,7 +1245,7 @@
disp(['Changing' ciapkg.pkgName 'input HDF5 dataset name "' obj.inputDatasetName '"->"' obj.outputDatasetName '"'])
obj.inputDatasetName = obj.outputDatasetName;
- function mouseWheelChange(hObject, callbackdata, handles)
+ function nestedfxn_mouseWheelChange(hObject, callbackdata, handles)
% Change keyIn to force while loop to exit, need for certain commands.
keyIn = 0;
@@ -1142,7 +1255,7 @@ function mouseWheelChange(hObject, callbackdata, handles)
set(gcf,'Tag','next')
end
end
- function subfxn_resetSetpsMenu(src,event)
+ function nestedfxn_resetSetpsMenu(src,event)
% analysisOptionListOrig = analysisOptionList;
% defaultChoiceListOrig = defaultChoiceList;
analysisOptionListStr = analysisOptionListOrig;
@@ -1156,7 +1269,7 @@ function subfxn_resetSetpsMenu(src,event)
hListboxS.String = analysisOptionListStr;
hListboxS.Value = defaultChoiceIdx(end);
end
- function subfxn_highlightDefault(src,event)
+ function nestedfxn_highlightDefault(src,event)
% analysisOptionListOrig = analysisOptionList;
% defaultChoiceListOrig = defaultChoiceList;
% analysisOptionListStr = analysisOptionListOrig;
@@ -1179,7 +1292,7 @@ function subfxn_highlightDefault(src,event)
hListbox.Value = defaultChoiceIdx;
hListboxS.Value = defaultChoiceIdx(end);
end
- function subfxn_analysisOutputMenuChange(src,event)
+ function nestedfxn_analysisOutputMenuChange(src,event)
% analysisOptionListOrig = analysisOptionList;
% defaultChoiceListOrig = defaultChoiceList;
% analysisOptionListStr = analysisOptionListOrig;
@@ -1210,7 +1323,7 @@ function subfxn_analysisOutputMenuChange(src,event)
% defaultSaveList = analysisOptionList{analysisOptionsIdx(end)};
% defaultSaveIdx = find(ismember(analysisOptionList,defaultSaveList));
end
- function subfxn_analysisOutputMenuChange2(src,event,PERMORDER)
+ function nestedfxn_analysisOutputMenuChange2(src,event,PERMORDER)
% analysisOptionListOrig = analysisOptionList;
% defaultChoiceListOrig = defaultChoiceList;
% analysisOptionListStr = analysisOptionListOrig;
@@ -1240,16 +1353,42 @@ function subfxn_analysisOutputMenuChange2(src,event,PERMORDER)
% defaultSaveList = analysisOptionList{analysisOptionsIdx(end)};
% defaultSaveIdx = find(ismember(analysisOptionList,defaultSaveList));
end
- function subfxn_closeOptions(src,event)
+ function nestedfxn_closeOptions(src,event)
delete(emptyBox);
end
- function subfxn_closeOptionsKey(src,event)
+ function nestedfxn_closeOptionsKey(src,event)
if event.Key=="return"
delete(emptyBox)
end
end
+ function nestedfxn_fixErrorFrames()
+ % Fixes frames in which the camera gives out garbled or improperly formatted data.
+
+ nRegions = 4;
+ nFramesHere = size(thisMovie,3);
+ metrixMatrix = zeros([nRegions nFrames]);
+
+ for subregionNo = 1:nRegions
+ % Select sub-regions to use for analysis.
+
+
+ % Calculate metric (e.g. difference, correlation, etc.) between
+
+
+ % Z-score metric by normalizing across all frames.
+ end
+
+
+ % Use threshold to determine which
- function subfxnAddDroppedFrames()
+
+ % Calculate replacement frame, e.g. mean
+
+
+ % Replace frames.
+
+ end
+ function nestedfxn_addDroppedFrames()
% TODO: to make this proper, need to verify that the log file names match those of movie files
display(repmat('-',1,7));
@@ -1406,7 +1545,7 @@ function subfxnAddDroppedFrames()
% end
end
- function downsampleTimeInputMovie()
+ function subfxn_downsampleTimeInputMovie()
options.downsampleZ = [];
options.waitbarOn = 1;
% thisMovie = single(thisMovie);
@@ -1436,7 +1575,7 @@ function downsampleTimeInputMovie()
reverseStr = cmdWaitbar(frame,downY,reverseStr,'inputStr','temporally downsampling matrix');
end
end
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% reverseStr = '';
% for frame = (downZ+1):size(thisMovie,3)
% thisMovie(:,:,1) = [];
@@ -1449,12 +1588,12 @@ function downsampleTimeInputMovie()
clear thisMovie;
thisMovie = thisMovieTmp;
clear thisMovieTmp;
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
drawnow;
% =====================
end
- function downsampleSpaceInputMovie()
+ function subfxn_downsampleSpaceInputMovie()
% Nested function to downsample input movie in space
options.downsampleZ = [];
options.waitbarOn = 1;
@@ -1492,7 +1631,7 @@ function downsampleSpaceInputMovie()
end
end
thisMovie = thisMovie(1:downX,1:downY,:);
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
if exist('turboRegCoords','var')
% Adjust crop coordinates if downsampling in space takes place before turboreg
@@ -1510,7 +1649,7 @@ function downsampleSpaceInputMovie()
% =====================
end
- function dfofInputMovie()
+ function subfxn_dfofInputMovie()
% dfof must have positive values
% thisMovieMin = nanmin(thisMovie(:));
if strcmp(options.turboreg.filterBeforeRegister,'bandpass')
@@ -1642,8 +1781,9 @@ function dfofInputMovie()
end
% =====================
end
- function subfxnPlotMotionCorrectionMetric(motionState)
+ function subfxn_plotMotionCorrectionMetric(motionState)
try
+ disp('Calculating and plotting correlation metric')
colorList = hsv(length(obj.inputFolders));
meanG = mean(thisMovie,3);
@@ -1651,7 +1791,12 @@ function subfxnPlotMotionCorrectionMetric(motionState)
corrMetric2 = NaN([1 size(thisMovie,3)]);
cc = turboRegCoords{fileNum}{movieNo};
meanG_cc = meanG(cc(2):cc(4),cc(1):cc(3));
- for i =1:size(thisMovie,3)
+ fprintf('Calculating correlation metric: ')
+ nFramesThis = size(thisMovie,3);
+ for i = 1:nFramesThis
+ if mod(i,500)==0
+ fprintf('%d | ',round((i/nFramesThis)*100));
+ end
thisFrame_cc = thisMovie(cc(2):cc(4),cc(1):cc(3),i);
corrMetric(i) = corr2(meanG_cc,thisFrame_cc);
corrMetric2(i) = corr(meanG_cc(:),thisFrame_cc(:),'Type','Spearman');
@@ -1706,15 +1851,17 @@ function subfxnPlotMotionCorrectionMetric(motionState)
disp(repmat('@',1,7))
end
end
- function turboregInputMovie()
+ function subfxn_turboregInputMovie()
+ disp(['Motion correction method: ' options.turboreg.mcMethod])
% number of frames to subset
subsetSize = options.turboregNumFramesSubset;
movieLength = size(thisMovie,3);
numSubsets = ceil(movieLength/subsetSize)+1;
subsetList = round(linspace(1,movieLength,numSubsets));
- display(['registering sublists: ' num2str(subsetList)]);
+ disp(['registering sublists: ' num2str(subsetList)]);
+ disp(options.turboreg.mcMethod)
% convert movie to single for turboreg
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% thisMovie = single(thisMovie);
% get reference frame before subsetting, so won't change
% thisMovieRefFrame = squeeze(thisMovie(:,:,options.refCropFrame));
@@ -1731,12 +1878,13 @@ function turboregInputMovie()
display(repmat('$',1,7))
if thisSet==nSubsets
movieSubset = subsetStartIdx:subsetEndIdx;
- display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+ % display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx) ' ' num2str(thisSet) '/' num2str(nSubsets)])
else
movieSubset = subsetStartIdx:(subsetEndIdx-1);
- display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+ % display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
end
- display(repmat('$',1,7))
+ disp([num2str(movieSubset(1)) '-' num2str(movieSubset(end)) ' ' num2str(thisSet) '/' num2str(nSubsets)])
+ disp(repmat('$',1,7))
%run with altered defaults
% ioptions.Levels = 2;
% ioptions.Lastlevels = 1;
@@ -1744,6 +1892,10 @@ function turboregInputMovie()
% ioptions.minGain=0.0;
% ioptions.SmoothX = 80;
% ioptions.SmoothY = 80;
+
+ % Set the motion correction algorithm
+ ioptions.mcMethod = options.turboreg.mcMethod;
+
ioptions.turboregRotation = options.turboreg.turboregRotation;
ioptions.RegisType = options.turboreg.RegisType;
ioptions.parallel = options.turboreg.parallel;
@@ -1785,6 +1937,10 @@ function turboregInputMovie()
% If multiple frames requested, only use the 1st
ioptions.refFrame = options.refCropFrame(1);
ioptions.refFrameMatrix = thisMovieRefFrame;
+
+ ioptions.optsNoRMCorre = optsNoRMCorre;
+ ioptions.displayOptions = 1;
+ ioptions
% for frameDftNo = movieSubset
% refFftFrame = fft2(thisMovieRefFrame);
% regFftFrame = fft2(squeeze(thisMovie(:,:,frameDftNo)));
@@ -1806,7 +1962,7 @@ function turboregInputMovie()
% dt=whos('VARIABLE_YOU_CARE_ABOUT'); MB=dt.bytes*9.53674e-7;
% thisMovie(:,:,movieSubset) = turboregMovie(thisMovie(:,:,movieSubset),'options',ioptions);
% j = whos('turboregThisMovie');j.bytes=j.bytes*9.53674e-7;j
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% [thisMovie(:,:,movieSubset), ResultsOutOriginal{thisSet}] = turboregMovie(thisMovie(:,:,movieSubset),'options',ioptions);
@@ -1830,7 +1986,7 @@ function turboregInputMovie()
% [tmpMovieNoFilter, ~] = turboregMovie(tmpMovieNoFilter,'options',ioptions);
% end
% Crop movie
- tmpMovieNoFilter = cropInputMovieSlice(tmpMovieNoFilter,options,ResultsOutOriginal);
+ tmpMovieNoFilter = localfxn_cropInputMovieSlice(tmpMovieNoFilter,options,ResultsOutOriginal);
% Add filtered movie to overall registered subset
thisMovie(:,:,movieSubset) = tmpMovieWithFilter;
@@ -1891,7 +2047,7 @@ function turboregInputMovie()
end
clear ioptions;
end
- function tmpPxToCrop = getCropValues()
+ function tmpPxToCrop = subfxn_getCropValues()
% Get values to use to add border and eliminate edge movement due to motion correction
thisMovieMinMask = zeros([size(thisMovie,1) size(thisMovie,2)]);
options.turboreg.registrationFxn
@@ -1918,7 +2074,7 @@ function turboregInputMovie()
tmpPxToCrop = max([topVal bottomVal leftVal rightVal]);
display(['[topVal bottomVal leftVal rightVal]: ' num2str([topVal bottomVal leftVal rightVal])])
end
- function cropInputMovie()
+ function subfxn_cropInputMovie()
% turboreg outputs 0s where movement goes off the screen
thisMovieMinMask = zeros([size(thisMovie,1) size(thisMovie,2)]);
options.turboreg.registrationFxn
@@ -1976,17 +2132,17 @@ function cropInputMovie()
if tmpPxToCrop~=0
if tmpPxToCrop=size(thisMovie,1)
% coords(1) = pxToCropPreprocess; %xmin
% coords(2) = pxToCropPreprocess; %ymin
@@ -2021,7 +2177,7 @@ function cropMatrixPreProcess(pxToCropPreprocess)
% set bottom rows to NaN
thisMovie(bottomRowCrop:end,1:end,:) = NaN;
end
- function medianFilterInputMovie()
+ function subfxn_medianFilterInputMovie()
% number of frames to subset
subsetSize = options.turboregNumFramesSubset;
movieLength = size(thisMovie,3);
@@ -2029,7 +2185,7 @@ function medianFilterInputMovie()
subsetList = round(linspace(1,movieLength,numSubsets));
display(['registering sublists: ' num2str(subsetList)]);
% convert movie to single for turboreg
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% get reference frame before subsetting, so won't change
nSubsets = (length(subsetList)-1);
for thisSet = 1:nSubsets
@@ -2045,13 +2201,13 @@ function medianFilterInputMovie()
display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
end
display(repmat('$',1,7))
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
thisMovie(:,:,movieSubset) = normalizeMovie(thisMovie(:,:,movieSubset),'normalizationType','medianFilter','medianFilterNeighborhoodSize',options.turboreg.medianFilterSize);
toc(subsetStartTime)
end
% thisMovie = normalizeMovie(thisMovie,'normalizationType','medianFilter');
end
- function detrendInputMovie()
+ function subfxn_detrendInputMovie()
% Plot trend before and after
openFigure(figNoList.detrend);
@@ -2066,8 +2222,22 @@ function detrendInputMovie()
disp(repmat('@',1,7))
end
+ frameMeanInputMovie = squeeze(mean(thisMovie,[1 2],'omitnan'));
+
+ trendVals = frameMeanInputMovie - detrend(frameMeanInputMovie,options.turboreg.detrendDegree);
+
+ % meanInputMovie = detrend(frameMeanInputMovie,0);
+ meanInputMovie = mean(frameMeanInputMovie,'omitnan');
+
+ nFramesToNormalize = size(thisMovie,3);
+
+ parfor frame = 1:nFramesToNormalize
+ thisFrame = thisMovie(:,:,frame);
+ thisMovie(:,:,frame) = thisFrame - trendVals(frame) + meanInputMovie;
+ end
+
% Takes an input movie and removes an underlying change in mean frame fluorescence.
- thisMovie = normalizeMovie(thisMovie,'normalizationType','detrend','detrendDegree',options.turboreg.detrendDegree);
+ % thisMovie = normalizeMovie(thisMovie,'normalizationType','detrend','detrendDegree',options.turboreg.detrendDegree);
openFigure(figNoList.detrend);
try
@@ -2083,7 +2253,7 @@ function detrendInputMovie()
disp(repmat('@',1,7))
end
end
- function spatialFilterInputMovie()
+ function subfxn_spatialFilterInputMovie()
% number of frames to subset
subsetSize = options.turboregNumFramesSubset;
movieLength = size(thisMovie,3);
@@ -2091,7 +2261,7 @@ function spatialFilterInputMovie()
subsetList = round(linspace(1,movieLength,numSubsets));
display(['filtering sublists: ' num2str(subsetList)]);
% convert movie to single for turboreg
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% get reference frame before subsetting, so won't change
nSubsets = (length(subsetList)-1);
for thisSet = 1:nSubsets
@@ -2107,7 +2277,7 @@ function spatialFilterInputMovie()
display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
end
display(repmat('$',1,7))
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% thisMovie(:,:,movieSubset) = normalizeMovie(thisMovie(:,:,movieSubset),'normalizationType','medianFilter','medianFilterNeighborhoodSize',options.turboreg.medianFilterSize);
@@ -2133,7 +2303,7 @@ function spatialFilterInputMovie()
end
% thisMovie = normalizeMovie(thisMovie,'normalizationType','medianFilter');
end
- function stripeRemovalInputMovie()
+ function subfxn_stripeRemovalInputMovie()
% number of frames to subset
subsetSize = options.turboregNumFramesSubset;
movieLength = size(thisMovie,3);
@@ -2141,7 +2311,7 @@ function stripeRemovalInputMovie()
subsetList = round(linspace(1,movieLength,numSubsets));
display(['Stripe removal sublists: ' num2str(subsetList)]);
% convert movie to single for turboreg
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
% get reference frame before subsetting, so won't change
nSubsets = (length(subsetList)-1);
for thisSet = 1:nSubsets
@@ -2157,7 +2327,7 @@ function stripeRemovalInputMovie()
display([num2str(subsetStartIdx) '-' num2str(subsetEndIdx-1) ' ' num2str(thisSet) '/' num2str(nSubsets)])
end
display(repmat('$',1,7))
- subfxn_dispMovieSize(thisMovie);
+ localfxn_dispMovieSize(thisMovie);
thisMovie(:,:,movieSubset) = removeStripsFromMovie(single(thisMovie(:,:,movieSubset)),'stripOrientation',options.turboreg.stripOrientationRemove,'meanFilterSize',options.turboreg.stripSize,'freqLowExclude',options.turboreg.stripfreqLowExclude,'bandpassType',options.turboreg.stripfreqBandpassType,'freqHighExclude',options.turboreg.stripfreqHighExclude,'waitbarOn',1);
@@ -2165,14 +2335,14 @@ function stripeRemovalInputMovie()
end
% thisMovie = normalizeMovie(thisMovie,'normalizationType','medianFilter');
end
- function movieProjectionsInputMovie()
+ function subfxn_movieProjectionsInputMovie()
% get the max projection
% get the mean projection
end
- function fftHighpassInputMovie()
+ function subfxn_fftHighpassInputMovie()
% do a highpass filter
ioptions.normalizationType = 'fft';
ioptions.freqLow = 7;
@@ -2192,7 +2362,7 @@ function fftHighpassInputMovie()
[thisMovie] = normalizeVector(thisMovie,'normRange','zeroToOne');
clear ioptions;
end
- function fftLowpassInputMovie()
+ function subfxn_fftLowpassInputMovie()
% do a lowpass filter
ioptions.normalizationType = 'fft';
ioptions.freqLow = 1;
@@ -2231,7 +2401,7 @@ function fftLowpassInputMovie()
end
end
-function [turboRegCoords] = turboregCropSelection(options,folderList)
+function [turboRegCoords] = localfxn_turboregCropSelection(options,folderList)
% Biafra Ahanonu
% 2013.11.10 [19:28:53]
@@ -2267,7 +2437,7 @@ function fftLowpassInputMovie()
movieList = regexp(folderList{fileNum},',','split');
% movieList = movieList{1};
movieList = getFileList(movieList, options.fileFilterRegexp);
- [movieList] = removeUnsupportedFiles(movieList,options);
+ [movieList] = localfxn_removeUnsupportedFiles(movieList,options);
if isempty(movieList)
continue;
end
@@ -2290,7 +2460,7 @@ function fftLowpassInputMovie()
display([num2str(fileNumIdx) '/' num2str(nFilesToRun) ': ' thisDir])
disp(['options.fileFilterRegexp: ' options.fileFilterRegexp])
movieList = getFileList(thisDir, options.fileFilterRegexp);
- [movieList] = removeUnsupportedFiles(movieList,options);
+ [movieList] = localfxn_removeUnsupportedFiles(movieList,options);
cellfun(@disp,movieList);
inputFilePath = movieList{movieNo};
if nMovies==1
@@ -2306,12 +2476,14 @@ function fftLowpassInputMovie()
end
if length(frameToGrabHere)==1&&options.turboreg.treatMoviesAsContinuousSwitch==0
- thisFrame = ciapkg.io.readFrame(inputFilePath,frameToGrabHere);
+ thisFrame = ciapkg.io.readFrame(inputFilePath,frameToGrabHere,'inputDatasetName',options.datasetName);
+ elseif length(frameToGrabHere)==1&&options.turboreg.treatMoviesAsContinuousSwitch==1&&nMovies==1
+ thisFrame = ciapkg.io.readFrame(inputFilePath,frameToGrabHere,'inputDatasetName',options.datasetName);
else
% for zFrame = 1:length(frameToGrabHere)
% end
- thisFrame = loadMovieList(inputFilePath,'convertToDouble',0,'frameList',frameToGrabHere,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single');
+ thisFrame = ciapkg.io.loadMovieList(inputFilePath,'convertToDouble',0,'frameList',frameToGrabHere,'inputDatasetName',options.datasetName,'treatMoviesAsContinuous',options.turboreg.treatMoviesAsContinuousSwitch,'loadSpecificImgClass','single');
end
if size(thisFrame,3)>1
@@ -2328,9 +2500,12 @@ function fftLowpassInputMovie()
colormap gray;
title(titleStr)
box off;
+ set(gcf,'color',[0 0 0]);
+ set(gca,'color',[0 0 0]);
+ ciapkg.view.changeFont('none','fontColor','w');
set(0,'DefaultTextInterpreter','none');
% ciapkg.overloaded.suptitle([num2str(fileNumIdx) '\' num2str(nFilesToRun) ': ' 10 strrep(thisDir,'\','/')],'fontSize',12,'plotregion',0.9,'titleypos',0.95);
- uicontrol('Style','Text','String',[num2str(fileNumIdx) '\' num2str(nFilesToRun) ': ' strrep(thisDir,'\','/')],'Units','normalized','Position',[0.1 0.9 0.8 0.10],'BackgroundColor','white','HorizontalAlignment','Center');
+ uicontrol('Style','Text','String',[num2str(fileNumIdx) '\' num2str(nFilesToRun) ': ' strrep(thisDir,'\','/')],'Units','normalized','Position',[0.1 0.9 0.8 0.10],'BackgroundColor','black','ForegroundColor','white','HorizontalAlignment','Center');
set(0,'DefaultTextInterpreter','latex');
% subplot(1,2,1);imagesc(thisFrame); axis image; colormap gray; title('Click to drag-n-draw region. Double-click region to continue.')
@@ -2340,17 +2515,17 @@ function fftLowpassInputMovie()
switch applyPreviousTurboreg
case -1 %'NO | do not duplicate coords across multiple folders'
% p = round(getrect);
- h = subfxn_getImRect(titleStr);
+ h = localfxn_getImRect(titleStr);
p = round(wait(h));
case 0 %'YES | duplicate coords across multiple folders'
% p = round(getrect);
- h = subfxn_getImRect(titleStr);
+ h = localfxn_getImRect(titleStr);
p = round(wait(h));
coordsStructure.(fileInfo.subject) = p;
case -2 %'YES | duplicate coords if subject the same'
if ~any(strcmp(fileInfo.subject,fieldnames(coordsStructure)))
% p = round(getrect);
- h = subfxn_getImRect(titleStr);
+ h = localfxn_getImRect(titleStr);
p = round(wait(h));
coordsStructure.(fileInfo.subject) = p;
else
@@ -2392,7 +2567,15 @@ function fftLowpassInputMovie()
% Display the subsetted image with appropriate axis ratio
[~, ~] = openFigure(9, '');
- subplot(1,2,2);imagesc(thisFrameCropped); axis image; colormap gray; title('cropped region');drawnow;
+ subplot(1,2,2);
+ imagesc(thisFrameCropped);
+ axis image;
+ colormap gray;
+ title('cropped region');
+ set(gcf,'color',[0 0 0]);
+ set(gca,'color',[0 0 0]);
+ ciapkg.view.changeFont('none','fontColor','w');
+ drawnow;
if applyPreviousTurboreg==0
if runAllFolders==1
@@ -2425,14 +2608,14 @@ function fftLowpassInputMovie()
end
end
end
-function h = subfxn_getImRect(titleStr)
+function h = localfxn_getImRect(titleStr)
h = imrect(gca);
addNewPositionCallback(h,@(p) title([titleStr 10 mat2str(p,3)]));
fcn = makeConstrainToRectFcn('imrect',get(gca,'XLim'),get(gca,'YLim'));
setPositionConstraintFcn(h,fcn);
end
% function [ostruct options] = getPcaIcaParams(ostruct,options)
-function [ostruct, options] = playOutputMovies(ostruct,options)
+function [ostruct, options] = localfxn_playOutputMovies(ostruct,options)
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
nFiles = length(ostruct.savedFilePaths);
@@ -2440,58 +2623,67 @@ function fftLowpassInputMovie()
movieFrameList = {};
numFramesPerPart = 50;
numParts = 10;
- disp('pre-allocating movies to display...')
- thisMovieArray = {};
- for fileNum=1:nFiles
- try
- disp('+++++++')
- if isempty(ostruct.savedFilePaths{fileNum})
- disp('no movie!')
- % display([num2str(fileNum) '/' num2str(nFiles) ' skipping: ' ostruct.savedFilePaths{fileNum}]);
- continue;
- else
- pathInfo = [num2str(fileNum) '/' num2str(nFiles) ': ' ostruct.savedFilePaths{fileNum}];
- % display(pathInfo);
- fprintf('%d/%d:%s\n',fileNum,nFiles,ostruct.savedFilePaths{fileNum})
- end
- movieList = {ostruct.savedFilePaths{fileNum}};
- % movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'getMovieDims',1);
- movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',[],'getMovieDims',1,'inputDatasetName',options.outputDatasetName);
+ switch options.videoPlayer
+ case 'imagej'
+ disp('pre-allocating movies to display...')
+ thisMovieArray = {};
+ for fileNum=1:nFiles
+ try
+ disp('+++++++')
+ if isempty(ostruct.savedFilePaths{fileNum})
+ disp('no movie!')
+ % display([num2str(fileNum) '/' num2str(nFiles) ' skipping: ' ostruct.savedFilePaths{fileNum}]);
+ continue;
+ else
+ pathInfo = [num2str(fileNum) '/' num2str(nFiles) ': ' ostruct.savedFilePaths{fileNum}];
+ % display(pathInfo);
+ fprintf('%d/%d:%s\n',fileNum,nFiles,ostruct.savedFilePaths{fileNum})
+ end
+ movieList = {ostruct.savedFilePaths{fileNum}};
- movieFrames = movieDims.three;
- if movieFrames>500
- movieFrames = 500;
- else
- % ostruct.movieFrames{fileNum} = movieFrames;
- end
- movieFrameList{fileNum} = movieFrames;
+ % movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'getMovieDims',1);
+ movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',[],'getMovieDims',1,'inputDatasetName',options.outputDatasetName);
- % options.frameList = [1:ostruct.movieFrames{fileNum}];
- options.frameList = [1:movieFrames];
+ movieFrames = movieDims.three;
+ if movieFrames>500
+ movieFrames = 500;
+ else
+ % ostruct.movieFrames{fileNum} = movieFrames;
+ end
+ movieFrameList{fileNum} = movieFrames;
- % get the movie
- % thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList);
- thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',[],'loadMovieInEqualParts',[numParts numFramesPerPart],'inputDatasetName',options.outputDatasetName);
- % thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'loadMovieInEqualParts',[numParts numFramesPerPart]);
+ % options.frameList = [1:ostruct.movieFrames{fileNum}];
+ options.frameList = [1:movieFrames];
- catch err
- thisMovieArray{fileNum} = [];
- display(repmat('@',1,7))
- disp(getReport(err,'extended','hyperlinks','on'));
- display(repmat('@',1,7))
- end
- end
- % inputdlg({'press OK to view a snippet of analyzed movies'},'...',1);
- % Miji;
- % MIJ.start
- manageMiji('startStop','start');
+ % get the movie
+ % thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList);
+ thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',[],'loadMovieInEqualParts',[numParts numFramesPerPart],'inputDatasetName',options.outputDatasetName,'largeMovieLoad',options.turboreg.largeMovieLoad);
+ % thisMovieArray{fileNum} = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList,'loadMovieInEqualParts',[numParts numFramesPerPart]);
- if isempty(thisMovieArray)||all(cellfun(@isempty,thisMovieArray))
- uiwait(ciapkg.overloaded.msgbox('No movies, check that processing ran successfully.','Success','modal'));
- else
- uiwait(ciapkg.overloaded.msgbox('Press OK to view a snippet of analyzed movies','Success','modal'));
+ catch err
+ thisMovieArray{fileNum} = [];
+ display(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ display(repmat('@',1,7))
+ end
+ end
+ % inputdlg({'press OK to view a snippet of analyzed movies'},'...',1);
+ % Miji;
+ % MIJ.start
+ manageMiji('startStop','start');
+
+ if isempty(thisMovieArray)||all(cellfun(@isempty,thisMovieArray))
+ uiwait(ciapkg.overloaded.msgbox('No movies, check that processing ran successfully.','Success','modal'));
+ else
+ uiwait(ciapkg.overloaded.msgbox('Press OK to view a snippet of analyzed movies','Success','modal'));
+ end
+ case 'matlab'
+ % Do nothing
+ otherwise
+ % Do nothing
end
+
% ask user for estimate of nPCs and nICs
for fileNum = 1:nFiles
try
@@ -2505,48 +2697,68 @@ function fftLowpassInputMovie()
display(pathInfo);
end
- % get the list of movies
- movieList = {ostruct.savedFilePaths{fileNum}};
+ trueFileNum = ostruct.fileNumList{fileNum};
- % options.frameList = [1:ostruct.movieFrames{fileNum}];
- options.frameList = [1:movieFrameList{fileNum}];
+ switch options.videoPlayer
+ case 'matlab'
+ thisFilePath = ostruct.savedFilePaths{fileNum};
+ [~,thisFileTitle,thisFileTitleExt] = fileparts(thisFilePath);
+ thisFileTitle = [thisFileTitle thisFileTitleExt];
+ titleStr = sprintf('%d/%d (%d/%d): \n %s \n %s',trueFileNum,length(ostruct.folderList),fileNum,nFiles,ostruct.folderList{trueFileNum},thisFileTitle);
- % get the movie
- % thisMovie = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList);
- thisMovie = thisMovieArray{fileNum};
+ if fileNum==1
+ ciapkg.overloaded.msgbox('Press E in movie GUI to move onto next movie, close this box to continue','Success','modal')
+ end
+ % playMovie(thisMovie);
- if isempty(thisMovie)
- continue;
- end
+ % Play the movie from disk, better preview.
+ playMovie(thisFilePath,'extraTitleText',titleStr,'colormapColor','gray');
+ case 'imagej'
+ % Try with Miji, else use built-in player
+ try
+ titleStr = sprintf('%d/%d (%d/%d): %s',trueFileNum,length(ostruct.folderList),fileNum,nFiles,ostruct.folderList{trueFileNum});
- trueFileNum = ostruct.fileNumList{fileNum};
+ % get the list of movies
+ movieList = {ostruct.savedFilePaths{fileNum}};
- % Try with Miji, else use built-in player
- try
- % playMovie(thisMovie,'fps',120,'extraTitleText',[10 pathInfo]);
- % MIJ.createImage([num2str(fileNum) '/' num2str(length(ostruct.folderList)) ': ' ostruct.folderList{fileNum}],thisMovie, true);
- % [num2str(trueFileNum) '/' num2str(length(ostruct.folderList)) ': ' ostruct.folderList{trueFileNum}]
- titleStr = sprintf('%d/%d (%d/%d): %s',trueFileNum,length(ostruct.folderList),fileNum,nFiles,ostruct.folderList{trueFileNum});
- ciapkg.overloaded.msgbox('Click movie to open next dialog box.','Success','normal')
- MIJ.createImage(titleStr,thisMovie, true);
- if size(thisMovie,1)<300
- % for foobar=1:2; MIJ.run('In [+]'); end
- for foobar=1:1; MIJ.run('In [+]'); end
- end
- for foobar=1:2; MIJ.run('Enhance Contrast','saturated=0.35'); end
- MIJ.run('Start Animation [\]');
- uiwait(ciapkg.overloaded.msgbox('press OK to move onto next movie','Success','modal'));
- % MIJ.run('Close All Without Saving');
- manageMiji('startStop','closeAllWindows');
- catch err
- disp(repmat('@',1,7))
- disp(getReport(err,'extended','hyperlinks','on'));
- disp(repmat('@',1,7))
- ciapkg.overloaded.msgbox('Press E in movie GUI to move onto next movie, close this box to continue','Success','modal')
- playMovie(thisMovie);
+ % options.frameList = [1:ostruct.movieFrames{fileNum}];
+ options.frameList = [1:movieFrameList{fileNum}];
- % Play the movie from disk, better preview.
- playMovie(ostruct.savedFilePaths{fileNum});
+ % get the movie
+ % thisMovie = loadMovieList(movieList,'convertToDouble',0,'frameList',options.frameList);
+ thisMovie = thisMovieArray{fileNum};
+
+ if isempty(thisMovie)
+ continue;
+ end
+
+ % playMovie(thisMovie,'fps',120,'extraTitleText',[10 pathInfo]);
+ % MIJ.createImage([num2str(fileNum) '/' num2str(length(ostruct.folderList)) ': ' ostruct.folderList{fileNum}],thisMovie, true);
+ % [num2str(trueFileNum) '/' num2str(length(ostruct.folderList)) ': ' ostruct.folderList{trueFileNum}]
+
+ ciapkg.overloaded.msgbox('Click movie to open next dialog box.','Success','normal')
+ MIJ.createImage(titleStr,thisMovie, true);
+ if size(thisMovie,1)<300
+ % for foobar=1:2; MIJ.run('In [+]'); end
+ for foobar=1:1; MIJ.run('In [+]'); end
+ end
+ for foobar=1:2; MIJ.run('Enhance Contrast','saturated=0.35'); end
+ MIJ.run('Start Animation [\]');
+ uiwait(ciapkg.overloaded.msgbox('press OK to move onto next movie','Success','modal'));
+ % MIJ.run('Close All Without Saving');
+ manageMiji('startStop','closeAllWindows');
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ ciapkg.overloaded.msgbox('Press E in movie GUI to move onto next movie, close this box to continue','Success','modal')
+ playMovie(thisMovie);
+
+ % Play the movie from disk, better preview.
+ playMovie(ostruct.savedFilePaths{fileNum});
+ end
+ otherwise
+ disp('Wrong video player option, skipping...')
end
if options.askForPCICs==1
@@ -2569,10 +2781,18 @@ function fftLowpassInputMovie()
display(repmat('@',1,7))
end
end
- % MIJ.exit;
- manageMiji('startStop','exit');
+
+ switch options.videoPlayer
+ case 'matlab'
+ % Do nothing
+ case 'imagej'
+ % MIJ.exit;
+ manageMiji('startStop','exit');
+ otherwise
+ % Do nothing
+ end
end
-function inputMovie = cropInputMovieSlice(inputMovie,options,ResultsOutOriginal)
+function inputMovie = localfxn_cropInputMovieSlice(inputMovie,options,ResultsOutOriginal)
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
% turboreg outputs 0s where movement goes off the screen
@@ -2637,7 +2857,7 @@ function fftLowpassInputMovie()
% set bottom rows to NaN
inputMovie(bottomRowCrop:end,1:end,:) = NaN;
end
-function [movieList] = removeUnsupportedFiles(movieList,options)
+function [movieList] = localfxn_removeUnsupportedFiles(movieList,options)
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
% Reject anything not HDF5, TIF, AVI, or ISXD
@@ -2657,32 +2877,36 @@ function fftLowpassInputMovie()
end
movieList = tmpMovieList;
end
-function [movieType, supported] = getMovieFileType(thisMoviePath)
- % determine how to load movie, don't assume every movie in list is of the same type
- supported = 1;
- try
- [~,~,ext] = fileparts(thisMoviePath);
- catch
- movieType = '';
- supported = 0;
- return;
- end
- % files are assumed to be named correctly (lying does no one any good)
- if strcmp(ext,'.h5')||strcmp(ext,'.hdf5')
- movieType = 'hdf5';
- elseif strcmp(ext,'.tif')||strcmp(ext,'.tiff')
- movieType = 'tiff';
- elseif strcmp(ext,'.avi')
- movieType = 'avi';
- elseif strcmp(ext,'.isxd')
- movieType = 'isxd';
- else
- movieType = '';
- supported = 0;
- end
-end
-function subfxn_dispMovieSize(thisMovie)
+% function [movieType, supported] = getMovieFileType(thisMoviePath)
+% % determine how to load movie, don't assume every movie in list is of the same type
+% supported = 1;
+% try
+% [~,~,ext] = fileparts(thisMoviePath);
+% catch
+% movieType = '';
+% supported = 0;
+% return;
+% end
+% % files are assumed to be named correctly (lying does no one any good)
+% if strcmp(ext,'.h5')||strcmp(ext,'.hdf5')
+% movieType = 'hdf5';
+% elseif strcmp(ext,'.tif')||strcmp(ext,'.tiff')
+% movieType = 'tiff';
+% elseif strcmp(ext,'.avi')
+% movieType = 'avi';
+% elseif strcmp(ext,'.isxd')
+% movieType = 'isxd';
+% else
+% movieType = '';
+% supported = 0;
+% end
+% end
+function localfxn_dispMovieSize(thisMovie)
j = whos('thisMovie');
j.bytes=j.bytes*9.53674e-7;
display(['Movie dims: ' num2str(size(thisMovie)) ' | Movie size: ' num2str(j.bytes) 'Mb | ' num2str(j.size) ' | ' j.class]);
+end
+function localfxn_changeFigName(hFig,titleStr)
+
+ set(hFig,'Name',[ciapkg.pkgName ': start-up GUI'],'NumberTitle','off')
end
\ No newline at end of file
diff --git a/@ciatah/modelSaveImgToFile.m b/@ciatah/modelSaveImgToFile.m
index 70a642c..ccb1f75 100644
--- a/@ciatah/modelSaveImgToFile.m
+++ b/@ciatah/modelSaveImgToFile.m
@@ -9,6 +9,8 @@
% changelog
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.07.28 [17:32:57] - Update PaperPosition to auto to avoid error "Positioning Figure for ResizeFcn. Set PaperPositionMode to 'auto' (match figure screen size) to avoid resizing and this warning."
+ % 2022.09.14 [11:13:12] - Further change to PaperPositionMode to avoid errors.
% TODO
%
@@ -60,10 +62,19 @@
if strcmp(class(thisFigNo),'char')&strcmp(thisFigNo,'current')
else
- set(thisFigNo,'PaperUnits',options.PaperUnits,'PaperPosition',options.PaperPosition)
- % set(thisFigNo,'PaperUnits','inches','PaperPosition',[0 0 20 20])
- set(0,'CurrentFigure',thisFigNo)
- % figure(thisFigNo)
+ try
+ % set(thisFigNo,'PaperUnits',options.PaperUnits,'PaperPosition',options.PaperPosition)
+ % set(thisFigNo,'PaperUnits',options.PaperUnits,'PaperPosition',options.PaperPosition)
+ % set(thisFigNo,'PaperUnits',options.PaperUnits,'PaperPosition','auto');
+ set(thisFigNo,'PaperUnits',options.PaperUnits,'PaperPositionMode','auto');
+ % set(thisFigNo,'PaperUnits','inches','PaperPosition',[0 0 20 20])
+ set(0,'CurrentFigure',thisFigNo)
+ % figure(thisFigNo)
+ catch err
+ display(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ display(repmat('@',1,7))
+ end
end
end
diff --git a/@ciatah/modelVarsFromFiles.m b/@ciatah/modelVarsFromFiles.m
index 67ef0b3..398c3f3 100644
--- a/@ciatah/modelVarsFromFiles.m
+++ b/@ciatah/modelVarsFromFiles.m
@@ -10,8 +10,10 @@
% changelog
% 2017.01.14 [20:06:04] - support switched from [nSignals x y] to [x y nSignals]
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.04.08 [16:41:27] - Enforce that NWB loading only looks for .nwb files and ensure that manual or automatic classifications are loaded when loading NWB files.
+ % 2022.04.09 [20:30:52] - Ensure when loading cell extraction CIAtah-style that only MAT files are loaded.
% TODO
- % ADD SUPPORT FOR EM ANALYSIS
+ % ADD SUPPORT FOR EM ANALYSIS - Done.
import ciapkg.api.* % import CIAtah functions in ciapkg package API.
@@ -20,7 +22,7 @@
% get options
% options = getOptions(options,varargin);
- % display(options)
+ % disp(options)
% unpack options into current workspace
% fn=fieldnames(options);
% for i=1:length(fn)
@@ -28,8 +30,8 @@
% end
%========================
- display(repmat('#',1,21))
- display('loading files...')
+ disp(repmat('#',1,21))
+ disp('loading files...')
for figNo = [98 1996 1997 95 99]
openFigure(figNo, '');
@@ -37,10 +39,6 @@
drawnow;
signalExtractionMethod = obj.signalExtractionMethod;
- % usrIdxChoiceStr = {'PCAICA','EM'};
- % [sel, ok] = listdlg('ListString',usrIdxChoiceStr);
- % usrIdxChoiceList = {2,1};
- % signalExtractionMethod = usrIdxChoiceStr{sel};
optFieldnames = fieldnames(obj.filterImageOptions);
if obj.guiEnabled==1
@@ -67,16 +65,16 @@
reportMidpoint = 0;
end
- [fileIdxArray idNumIdxArray nFilesToAnalyze nFiles] = obj.getAnalysisSubsetsToAnalyze();
+ [fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = obj.getAnalysisSubsetsToAnalyze();
for thisFileNumIdx = 1:nFilesToAnalyze
try
fileNum = fileIdxArray(thisFileNumIdx);
obj.fileNum = fileNum;
- display(repmat('=',1,21))
- % display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum}]);
+ disp(repmat('=',1,21))
+ % disp([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum}]);
% nFolders = length(obj.dataPath);
% for fileNum = 1:nFolders
- % display(repmat('-',1,7))
+ % disp(repmat('-',1,7))
% try
obj.rawSignals{fileNum} = [];
obj.rawImages{fileNum} = [];
@@ -88,20 +86,21 @@
obj.validManual{fileNum} = [];
obj.validAuto{fileNum} = [];
if strmatch('#',obj.dataPath{fileNum})
- % display([num2str(fileNum) '/' num2str(nFolders) ' | skipping: ' obj.dataPath{fileNum}]);
- % display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) ') | skipping: ' obj.fileIDNameArray{obj.fileNum}]);
+ % disp([num2str(fileNum) '/' num2str(nFolders) ' | skipping: ' obj.dataPath{fileNum}]);
+ % disp([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) ') | skipping: ' obj.fileIDNameArray{obj.fileNum}]);
fprintf('%d/%d (%d/%d) | skipping: %s\n',thisFileNumIdx,nFilesToAnalyze,fileNum,nFiles,obj.fileIDNameArray{obj.fileNum});
obj.rawSignals{fileNum} = [];
obj.rawImages{fileNum} = [];
continue;
else
- % display([num2str(fileNum) '/' num2str(nFolders) ': ' obj.dataPath{fileNum}]);
+ % disp([num2str(fileNum) '/' num2str(nFolders) ': ' obj.dataPath{fileNum}]);
% obj.fileIDNameArray{obj.fileNum}
- % display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum}]);
+ % disp([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum}]);
fprintf('%d/%d (%d/%d): %s\n',thisFileNumIdx,nFilesToAnalyze,fileNum,nFiles,obj.fileIDNameArray{obj.fileNum});
end
signalExtractionMethodOriginal = signalExtractionMethod;
+ nwbLoadLock = 0;
if obj.nwbLoadFiles==1
signalExtractionMethod = 'NWB';
end
@@ -109,7 +108,7 @@
case 'NWB'
% Check whether to use override NWB regular expression, else use calciumImagingAnalysis defaults.
if isempty(obj.nwbFileRegexp)
- filesToLoad = getFileList([obj.dataPath{fileNum} filesep obj.nwbFileFolder],obj.extractionMethodSaveStr.(obj.signalExtractionMethod));
+ filesToLoad = getFileList([obj.dataPath{fileNum} filesep obj.nwbFileFolder],[obj.extractionMethodSaveStr.(obj.signalExtractionMethod) '.nwb$']);
else
filesToLoad = getFileList([obj.dataPath{fileNum} filesep obj.nwbFileFolder],obj.nwbFileRegexp);
end
@@ -125,6 +124,25 @@
signalExtractionMethod = signalExtractionMethodOriginal;
end
rawFiles = 1;
+
+ % Load manual and classifier information if available.
+ regexPairs = {...
+ obj.extractionMethodSortedSaveStr.(obj.signalExtractionMethod);
+ obj.extractionMethodClassifierSaveStr.(obj.signalExtractionMethod);
+ };
+
+ % get list of files to load
+ filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
+ if isempty(filesToLoad)
+ disp('no files!');
+ continue
+ end
+ % load files in order
+ for i=1:length(filesToLoad)
+ disp(['loading: ' filesToLoad{i}]);
+ load(filesToLoad{i});
+ end
+
otherwise
end
@@ -142,13 +160,13 @@
% {obj.rawEMStructSaveStr},...
};
% get list of files to load
- filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
+ filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat','.*.mat$'));
rawFiles = 0;
filesToLoad = [];
fileToLoadNo = 1;
nRegExps = length(regexPairs);
while isempty(filesToLoad)
- filesToLoad = getFileList(obj.dataPath{obj.fileNum},strrep(regexPairs{fileToLoadNo},'.mat',''));
+ filesToLoad = getFileList(obj.dataPath{obj.fileNum},strrep(regexPairs{fileToLoadNo},'.mat','.*.mat$'));
fileToLoadNo = fileToLoadNo+1;
if fileToLoadNo>nRegExps
break;
@@ -160,7 +178,7 @@
cellfun(@(x) fprintf('Found: %s\n',x),filesToLoad)
if isempty(filesToLoad)
% if(~exist(filesToLoad{1}, 'file'))
- display('no files!');
+ disp('no files!');
continue
end
@@ -170,33 +188,35 @@
% rawFiles = 1;
% if isempty(filesToLoad)
% % if(~exist(filesToLoad{1}, 'file'))
- % display('no files!');
+ % disp('no files!');
% continue
% end
% end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
- if exist('pcaicaAnalysisOutput','var')
- signalTraces = double(pcaicaAnalysisOutput.IcaTraces);
- % signalImages = permute(double(pcaicaAnalysisOutput.IcaFilters),[3 1 2]);
- if strcmp(pcaicaAnalysisOutput.imageSaveDimOrder,'xyz')
- signalImages = double(pcaicaAnalysisOutput.IcaFilters);
- elseif strcmp(pcaicaAnalysisOutput.imageSaveDimOrder,'zxy')
- signalImages = permute(double(pcaicaAnalysisOutput.IcaFilters),[2 3 1]);
- % inputImages = pcaicaAnalysisOutput.IcaFilters;
+ if nwbLoadLock==0
+ if exist('pcaicaAnalysisOutput','var')
+ signalTraces = double(pcaicaAnalysisOutput.IcaTraces);
+ % signalImages = permute(double(pcaicaAnalysisOutput.IcaFilters),[3 1 2]);
+ if strcmp(pcaicaAnalysisOutput.imageSaveDimOrder,'xyz')
+ signalImages = double(pcaicaAnalysisOutput.IcaFilters);
+ elseif strcmp(pcaicaAnalysisOutput.imageSaveDimOrder,'zxy')
+ signalImages = permute(double(pcaicaAnalysisOutput.IcaFilters),[2 3 1]);
+ % inputImages = pcaicaAnalysisOutput.IcaFilters;
+ else
+ % inputImages = permute(double(pcaicaAnalysisOutput.IcaFilters));
+ signalImages = pcaicaAnalysisOutput.IcaFilters;
+ end
+
+ clear pcaicaAnalysisOutput;
else
- % inputImages = permute(double(pcaicaAnalysisOutput.IcaFilters));
- signalImages = pcaicaAnalysisOutput.IcaFilters;
+ signalImages = permute(IcaFilters,[2 3 1]);
+ signalTraces = IcaTraces;
+ clear IcaFilters IcaTraces;
end
-
- clear pcaicaAnalysisOutput;
- else
- signalImages = permute(IcaFilters,[2 3 1]);
- signalTraces = IcaTraces;
- clear IcaFilters IcaTraces;
end
rawFiles = 1;
case {'EM','CELLMax'}
@@ -210,12 +230,12 @@
% get list of files to load
filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
if isempty(filesToLoad)
- display('no files!');
+ disp('no files!');
continue
end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
if exist('cellmaxAnalysisOutput','var')
@@ -244,12 +264,12 @@
% get list of files to load
filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
if isempty(filesToLoad)
- display('no files!');
+ disp('no files!');
continue
end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
signalImages = double(extractAnalysisOutput.filters);
@@ -268,12 +288,12 @@
% get list of files to load
filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
if isempty(filesToLoad)
- display('no files!');
+ disp('no files!');
continue
end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
% signalImages = double(permute(cnmfAnalysisOutput.extractedImages,[3 1 2]));
@@ -288,12 +308,12 @@
% get list of files to load
filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
if isempty(filesToLoad)
- display('no files!');
+ disp('no files!');
continue
end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
% signalImages = double(permute(cnmfAnalysisOutput.extractedImages,[3 1 2]));
@@ -308,12 +328,12 @@
% get list of files to load
filesToLoad = getFileList(obj.dataPath{fileNum},strrep(regexPairs{1},'.mat',''));
if isempty(filesToLoad)
- display('no files!');
+ disp('no files!');
continue
end
% load files in order
for i=1:length(filesToLoad)
- display(['loading: ' filesToLoad{i}]);
+ disp(['loading: ' filesToLoad{i}]);
load(filesToLoad{i});
end
% signalImages = double(permute(cnmfAnalysisOutput.extractedImages,[3 1 2]));
@@ -327,18 +347,18 @@
signalExtractionMethod = signalExtractionMethodOriginal;
- display(['signalTraces: ' num2str(size(signalTraces))])
- display(['signalImages: ' num2str(size(signalImages))])
- % display(['signalPeaks: ' num2str(size(signalPeaks))])
- % display(['signalPeaksArray: ' num2str(size(signalPeaksArray))])
+ disp(['signalTraces: ' num2str(size(signalTraces))])
+ disp(['signalImages: ' num2str(size(signalImages))])
+ % disp(['signalPeaks: ' num2str(size(signalPeaks))])
+ % disp(['signalPeaksArray: ' num2str(size(signalPeaksArray))])
% if manually sorted signals, add
% if exist('valid','var')|exist('validCellMax','var')|exist('validEXTRACT','var')
varList = who;
% if length(intersect({obj.validPCAICAStructVarname,obj.validEMStructVarname,obj.validPCAICAStructVarname,obj.validEXTRACTStructVarname,obj.validCNMFStructVarname},varList))>0
if length(intersect({obj.extractionMethodValidVarname.(obj.signalExtractionMethod)},varList))>0
- display('adding manually sorted values...')
- display(['adding valid{' num2str(fileNum) '}.' obj.signalExtractionMethod '.manual identifications...'])
+ disp('adding manually sorted values...')
+ disp(['adding valid{' num2str(fileNum) '}.' obj.signalExtractionMethod '.manual identifications...'])
if exist(obj.validPCAICAStructVarname,'var')
obj.validManual{fileNum} = valid;
obj.valid{fileNum}.(obj.signalExtractionMethod).manual = valid;
@@ -369,12 +389,12 @@
obj.valid{fileNum}.(obj.signalExtractionMethod).manual = validROI;
clear validROI;
end
- display('clearing manual variable...')
+ disp('clearing manual variable...')
end
if exist('validClassifier','var')
- display('adding classifier annotation for signals...')
+ disp('adding classifier annotation for signals...')
obj.valid{fileNum}.(obj.signalExtractionMethod).classifier = validClassifier;
- display('clearing manual variable...')
+ disp('clearing manual variable...')
clear validClassifier;
end
@@ -389,7 +409,7 @@
% get the x/y coordinates
if isempty(signalImages);continue;end;
- [xCoords yCoords] = findCentroid(signalImages,'thresholdValue',0.4,'imageThreshold',0.4);
+ [xCoords, yCoords] = findCentroid(signalImages,'thresholdValue',0.4,'imageThreshold',0.4);
obj.objLocations{fileNum}.(obj.signalExtractionMethod) = [xCoords(:) yCoords(:)];
% rawFiles
@@ -408,8 +428,8 @@
% [signalImagesTmp(imageNo,:,:)] = viewMontage(inputMovie,signalImages(imageNo,:,:),signalTraces(imageNo,:),obj.signalPeaksArray{fileNum});
% end
% signalImages = signalImagesTmp;
- display(['traces dims: ' num2str(size(signalTracesTmp))])
- display(['images dims: ' num2str(size(signalImages))])
+ disp(['traces dims: ' num2str(size(signalTracesTmp))])
+ disp(['images dims: ' num2str(size(signalImages))])
[~, ~, validAuto, imageSizes, imgFeatures] = filterImages(signalImages, signalTracesTmp,'featureList',obj.classifierImageFeaturesNames,'options',obj.filterImageOptions,'testpeaks',testpeaks,'testpeaksArray',obj.signalPeaksArray{fileNum},'xCoords',xCoords,'yCoords',yCoords);
% obj.classifierFeatures{fileNum}.(obj.signalExtractionMethod).imageFeatures = imgFeatures;
@@ -429,7 +449,7 @@
% obj.rawImagesFiltered{fileNum} = createObjMap(filterImageGroups);
size(validAuto)
% validAuto
- display(['adding valid{' num2str(fileNum) '}.' obj.signalExtractionMethod '.auto identifications...'])
+ disp(['adding valid{' num2str(fileNum) '}.' obj.signalExtractionMethod '.auto identifications...'])
obj.validAuto{fileNum} = validAuto;
obj.valid{fileNum}.(obj.signalExtractionMethod).auto = validAuto;
clear validAuto
@@ -442,7 +462,7 @@
% add files
if obj.loadVarsToRam == 1
- display('Loading variables into ram.')
+ disp('Loading variables into ram.')
if exist('signalTraces','var')
obj.rawSignals{fileNum} = signalTraces;
end
@@ -455,9 +475,9 @@
end
clear signalTraces signalImages
catch err
- display(repmat('@',1,7))
+ disp(repmat('@',1,7))
disp(getReport(err,'extended','hyperlinks','on'));
- display(repmat('@',1,7))
+ disp(repmat('@',1,7))
end
end
obj.guiEnabled = 0;
diff --git a/@ciatah/viewMovie.m b/@ciatah/viewMovie.m
index 9dc64f1..dc56474 100644
--- a/@ciatah/viewMovie.m
+++ b/@ciatah/viewMovie.m
@@ -25,7 +25,7 @@
% fileFilterRegexp = obj.fileFilterRegexp;
FRAMES_PER_SECOND = obj.FRAMES_PER_SECOND;
% DOWNSAMPLE_FACTOR = obj.DOWNSAMPLE_FACTOR;
- options.videoPlayer = [];
+ options.videoPlayer = '';
options.settingsType = '';
% =====================
currentDateTimeStr = datestr(now,'yyyymmdd_HHMM','local');
diff --git a/@ciatah/viewMovieRegistrationTest.m b/@ciatah/viewMovieRegistrationTest.m
index 69de4a3..6cd95aa 100644
--- a/@ciatah/viewMovieRegistrationTest.m
+++ b/@ciatah/viewMovieRegistrationTest.m
@@ -16,6 +16,7 @@
% 2021.06.18 [21:41:07] - Added modelVarsFromFilesCheck() to check and load signals if user hasn't already.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
% 2021.12.08 [22:11:00] - Additional updates to handle CIAtah v4.0 API switch.
+ % 2022.05.11 [11:48:58] - Updated so more settings are pulled from CIAtah object settings.
% TODO
%
@@ -65,9 +66,9 @@
{...
'1:500',...
'3',...
- 'concat',...
+ obj.fileFilterRegexpRaw,...
obj.inputDatasetName,...
- '/1',...
+ obj.outputDatasetName,...
'1',...
'1',...
'1',...
@@ -77,9 +78,9 @@
% And so the kings battle in the darkness
frameList = str2num(movieSettings{1});
nTestToRun = str2num(movieSettings{2});
- fileFilterRegexp = movieSettings{3};
+ fileFilterRegexp = movieSettings{3}; obj.fileFilterRegexpRaw = fileFilterRegexp;
inputDatasetName = movieSettings{4}; obj.inputDatasetName = inputDatasetName;
- outputDatasetName = movieSettings{5};
+ outputDatasetName = movieSettings{5}; obj.outputDatasetName = outputDatasetName;
treatMoviesAsContinuousSwitch = str2num(movieSettings{6});
runMotionCorrection = str2num(movieSettings{7});
montageDownsampleFactorSpace = str2num(movieSettings{8});
diff --git a/@ciatah/viewObjmaps.m b/@ciatah/viewObjmaps.m
index 32262e5..d1c467b 100644
--- a/@ciatah/viewObjmaps.m
+++ b/@ciatah/viewObjmaps.m
@@ -290,7 +290,7 @@
set(gcf,'SizeChangedFcn',{@resizeui,axHandle});
linkaxes(linkAx);
- set(gcf,'PaperUnits','inches','PaperPosition',[0 0 22 16])
+ % set(gcf,'PaperUnits','inches','PaperPosition',[0 0 22 16])
obj.modelSaveImgToFile([],'objMapsGeneral_','current',[]);
% =======
diff --git a/@ciatah/viewSubjectMovieFrames.m b/@ciatah/viewSubjectMovieFrames.m
index 97e1cf1..2acf70a 100644
--- a/@ciatah/viewSubjectMovieFrames.m
+++ b/@ciatah/viewSubjectMovieFrames.m
@@ -11,6 +11,7 @@
% changelog
% 2021.06.18 [21:41:07] - added modelVarsFromFilesCheck() to check and load signals if user hasn't already.
% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
+ % 2022.03.16 [08:45:40] - Update code standards.
% TODO
%
@@ -164,37 +165,37 @@
% MIJ.run('Close All Without Saving');
manageMiji('startStop','closeAllWindows');
case 4
- [fileIdxArray idNumIdxArray nFilesToAnalyze nFiles] = obj.getAnalysisSubsetsToAnalyze();
+ [fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = obj.getAnalysisSubsetsToAnalyze();
for thisSubjectStr = subjectList
try
- display(repmat('=',1,7))
+ disp(repmat('=',1,7))
fprintf('Subject %s', thisSubjectStr{1});
validFoldersIdx = find(strcmp(thisSubjectStr,obj.subjectStr));
validFoldersIdx = intersect(fileIdxArray,validFoldersIdx);
if isempty(validFoldersIdx)
- display('Skipping...')
+ disp('Skipping...')
continue;
end
- subjectMovieFrames = [];
+ subjectMovieFrames = single([]);
for folderNo = 1:length(validFoldersIdx)
- display('===')
+ disp('===')
thisFileNum = validFoldersIdx(folderNo);
obj.fileNum = thisFileNum;
% Check that signal extraction information is loaded.
obj.modelVarsFromFilesCheck(thisFileNum);
- [inputSignals inputImages signalPeaks signalPeakIdx valid] = modelGetSignalsImages(obj,'returnType','raw');
- if isempty(inputSignals);display('no input signals');continue;end
+ [inputSignals, inputImages, signalPeaks, signalPeakIdx, valid] = modelGetSignalsImages(obj,'returnType','raw');
+ if isempty(inputSignals);disp('no input signals');continue;end
inputImages = thresholdImages(inputImages,'binary',0,'getBoundaryIndex',0,'threshold',0.4,'imageFilter','none');
goodImages = inputImages(:,:,logical(valid));
- goodImages = nanmax(goodImages,[],3);
+ goodImages = max(goodImages,[],3,'omitnan');
badImages = inputImages(:,:,~logical(valid));
- badImages = nanmax(badImages,[],3);
- [sum(valid) size(goodImages) NaN size(inputImages)]
+ badImages = max(badImages,[],3,'omitnan');
+ disp(num2str([sum(valid) size(goodImages) NaN size(inputImages)]))
goodImages = goodImages + 0.5*badImages;
% [goodImages] = viewAddTextToMovie(goodImages,obj.assay{obj.fileNum},12);
diff --git a/README.md b/README.md
index 5fcdce6..410f861 100644
--- a/README.md
+++ b/README.md
@@ -13,12 +13,12 @@
-`CIAtah` (pronounced cheetah; formerly calciumImagingAnalysis [ciapkg]) is a software package for analyzing one- and two-photon calcium imaging datasets.
+`CIAtah` (pronounced cheetah; formerly calciumImagingAnalysis [ciapkg]) is a software package for analyzing one- and two-photon calcium imaging datasets. It can also be used to process other imaging datasets (e.g. from non-calcium indicators and dyes).
`CIAtah` currently requires `MATLAB` and runs on all major operating systems (Windows, Linux [e.g. Ubuntu], and macOS).
-- Note: `CIAtah` version `v4` moves the remaining (i.e. all except external packages/software) CIAtah functions into the `ciapkg` package to improve namespace handling and requires MATLAB R2019b or above ([due to package import changes](https://www.mathworks.com/help/matlab/matlab_prog/upgrade-code-for-r2019b-changes-to-function-precedence-order.html#mw_2934c766-e115-4d22-9abf-eb46a1415f2c)). Users with earlier versions of MATLAB can download `CIAtah` version `v3` (see [Releases](https://github.com/bahanonu/ciatah/releases)) until pre-R2019b MATLAB support is fully integrated into v4.
+- Note: `CIAtah` version `v4` moves the remaining (i.e. all except external packages and software) CIAtah functions into the `ciapkg` package to improve namespace handling and requires MATLAB R2019b or above ([due to package import changes](https://www.mathworks.com/help/matlab/matlab_prog/upgrade-code-for-r2019b-changes-to-function-precedence-order.html#mw_2934c766-e115-4d22-9abf-eb46a1415f2c)). Users with earlier versions of MATLAB can download `CIAtah` version `v3` (see [Releases](https://github.com/bahanonu/ciatah/releases)) until pre-R2019b MATLAB support is fully integrated into v4.
## Full documentation at https://bahanonu.github.io/ciatah/.
@@ -68,14 +68,20 @@ Made in USA.
- A GUI, via `ciatah` class, with different modules for large-scale batch analysis.
- Includes all major calcium imaging analysis steps:
- Movie visualization (including reading from disk, for fast viewing of large movies);
- - pre-processing (motion correction, spatiotemporal downsampling, spatial filtering, relative fluorescence calculation, etc.);
+ - pre-processing (motion correction [e.g. TurboReg, NoRMCorre] , spatiotemporal downsampling, spatial filtering, relative fluorescence calculation, etc.);
- - support for multiple cell-extraction methods (CELLMax, PCA-ICA, CNMF, CNMF-E, EXTRACT, etc.);
+ - support for multiple cell-extraction methods:
+ - PCA-ICA
+ - CELLMax (additional)
+ - CNMF
+ - CNMF-E
+ - EXTRACT
+ - etc.
- manual classification of cells via GUIs;
- automated cell classification (i.e. CLEAN algorithm, coming soon!);
- cross-session cell alignment, and more.
- Includes example one- and two-photon calcium imaging datasets for testing `CIAtah`.
-- Supports a plethora of major imaging movie file formats: HDF5, NWB, AVI, ISXD [Inscopix], TIFF, and [Bio-Formats](https://www.openmicroscopy.org/bio-formats/) compatible formats (Olympus [OIR] and Zeiss [CZI and LSM] currently, additional support to be added or upon request).
+- Supports a plethora of major imaging movie file formats: HDF5, NWB, AVI, ISXD [Inscopix], TIFF, SLD [SlideBook], and [Bio-Formats](https://www.openmicroscopy.org/bio-formats/) compatible formats (Olympus [OIR] and Zeiss [CZI and LSM] currently, additional support to be added or upon request).
- Supports [Neurodata Without Borders](https://www.nwb.org/) data standard (see [calcium imaging tutorial](https://neurodatawithoutborders.github.io/matnwb/tutorials/html/ophys.html)) for reading/writing cell-extraction and imaging movie files.
- Animal position tracking (e.g. in open-field assay) via ImageJ plugin.
- Requires `MATLAB` and runs on all major operating systems (Windows, Linux [e.g. Ubuntu], and macOS).
diff --git a/docs/docs/help_cross_session_alignment.md b/docs/docs/help_cross_session_alignment.md
index 951a064..98d21d2 100644
--- a/docs/docs/help_cross_session_alignment.md
+++ b/docs/docs/help_cross_session_alignment.md
@@ -1,7 +1,26 @@
# Cross-day or -session cell alignment alignment
-The main function used to run cross-session analysis that allows users to align cells across sessions/days can be found at:
-- https://github.com/bahanonu/ciatah/blob/master/classification/matchObjBtwnTrials.m
+The purpose of cross-session alignment is to allow users to align cells across imaging sessions (e.g. those taken on different days) and thus compare coding of cells, changing in biological variables, and other parameters across conditions and time in their data.
+
+This page details additional notes on one of the algorithms used in CIAtah, usage outside the CIAtah GUI, and other tips. The main function used to run cross-session analysis that allows users to align cells across sessions/days can be found at:
+
+- https://github.com/bahanonu/ciatah/blob/master/+ciapkg/+classification/matchObjBtwnTrials.m
+
+
+ Stable cell alignment across imaging sessions.
+
+
+
+
+
+
+
+
+
+Below is an example of what the main output from cross-session alignment, a `globalIDs` matrix containing indices matches cells across days, looks like when visualized. Each column is an imaging session and each row is an individual global cell with the color indicating that global cell's within-session number. Any black cells indicate where no match was found for that global cell in that imaging day.
+
+
+
## Algorithm overview
@@ -9,6 +28,8 @@ For details, see __Cross-day analysis of BLA neuronal activity__ methods section
- http://science.sciencemag.org/content/sci/suppl/2019/01/16/363.6424.276.DC1/aap8586_Corder_SM.pdf#page=10.
+We will also have details in an forthcoming imaging experiments and analysis book chapter.
+
![image](https://user-images.githubusercontent.com/5241605/126744851-cd6e64ab-9b83-40bf-aa38-2301276f0ccf.png)
diff --git a/docs/docs/help_issues.md b/docs/docs/help_issues.md
index 5dbff89..37fdb89 100644
--- a/docs/docs/help_issues.md
+++ b/docs/docs/help_issues.md
@@ -21,6 +21,23 @@ Page outlines some common issues and fixes to them that may be encountered while
* [File or folder dialog box with no instructions](#file-or-folder-dialog-box-with-no-instructions)
***
+## Error opening AVI or other files due to codec issues
+
+If run into issues opening certain AVI files (e.g. due to codec issues) with CIAtah/MATLAB:
+
+```Matlab
+Error using VideoReader/initReader (line 734)
+Unable to determine the required codec.
+
+Error in audiovideo.internal.IVideoReader (line 136)
+ initReader(obj, fileName, currentTime);
+
+Error in VideoReader (line 104)
+ obj@audiovideo.internal.IVideoReader(varargin{:});
+```
+
+, install `K-Lite Codec Pack` (https://codecguide.com/download_kl.htm) or similar for added support.
+
## PCA-ICA, CNMF-E, or other cell extraction algorithm's don't produce sensible output.
- When running `modelPreprocessMovie` a dialog box appears showing the available analysis steps. Certain combinations of these steps make sense while others should be avoided.
diff --git a/docs/docs/help_temporal_downsampling.md b/docs/docs/help_temporal_downsampling.md
index ad5c295..6aaaebe 100644
--- a/docs/docs/help_temporal_downsampling.md
+++ b/docs/docs/help_temporal_downsampling.md
@@ -1,5 +1,7 @@
# Preprocessing: Temporal downsampling
+Next, temporally smooth each movie by downsampling from the original 20 or 30 Hz to 5 Hz
+
Example code to run the downsample test function with the following commands:
```Matlab
loadRepoFunctions;
@@ -13,3 +15,6 @@ Below is an example pixel from a cell in a BLA animal. Note:
- `imresize` using bilinear and bicubic produce similar results with bicubic having slower runtimes (e.g. on my machine 3.46 vs. 4.31 sec if set `cropSize` to 100).
- The number next to each name is the vector's variance.
![image](https://cloud.githubusercontent.com/assets/5241605/13099409/b85b119c-d4e6-11e5-91d4-f6f7c74fed18.png)
+
+![image](https://cloud.githubusercontent.com/assets/5241605/13099409/b85b119c-d4e6-11e5-91d4-f6f7c74fed18.png)
+img
\ No newline at end of file
diff --git a/docs/docs/install.md b/docs/docs/install.md
index adfee5d..1a56dc9 100644
--- a/docs/docs/install.md
+++ b/docs/docs/install.md
@@ -19,6 +19,13 @@ Below are steps needed to quickly get started using the `{{ site.name }}` softwa
cd('{{ code.mainclass }}-master')
```
+## Check required toolboxes installed
+
+`{{ site.name }}` depends on several MATLAB toolboxes to run properly. Run the below command to have `{{ site.name }}` check whether dependencies have been installed properly. If not use the `Add-Ons` (https://www.mathworks.com/help/matlab/matlab_env/get-add-ons.html) explorer to install each toolbox.
+
+```Matlab
+ciapkg.io.matlabToolboxCheck;`
+```
## Setup `{{ site.name }}`
diff --git a/docs/docs/pipeline_detailed_signal_extraction.md b/docs/docs/pipeline_detailed_signal_extraction.md
index c7e4fef..664e5bb 100644
--- a/docs/docs/pipeline_detailed_signal_extraction.md
+++ b/docs/docs/pipeline_detailed_signal_extraction.md
@@ -4,7 +4,14 @@ title: Automated cell extraction.
# Extracting cells with `modelExtractSignalsFromMovie`
-Users can run PCA-ICA, EXTRACT, CNMF, CNMF-E, and ROI cell extraction by following the below set of option screens. Details on running the new Schnitzer lab cell-extraction methods (e.g. CELLMax) will be added here after they are released.
+Users can run the following cell-extraction algorithms:
+ - CELLMax
+ - PCA-ICA
+ - CNMF
+ - CNMF-E
+ - EXTRACT
+ - etc.
+by following the below set of option screens. Details on running the new Schnitzer lab cell-extraction methods (e.g. CELLMax) will be added here after they are released.
We normally estimate the number of PCs and ICs on the high end, manually sort to get an estimate of the number of cells, then run PCA-ICA again with IC 1.5-3x the number of cells and PCs 1-1.5x number of ICs.
@@ -19,3 +26,51 @@ The resulting output (on _Figure 45+_) at the end should look something like:
+
+## PCA-ICA (Mukamel, 2009)
+
+There are several parameters for PCA-ICA that users can input, these are `µ`, `term_tol`, `max_iter`, and the number of PCs and ICs to request.
+
+### Mukamel, 2009 (`µ`)
+The original Mukamel, 2009 (https://doi.org/10.1016/j.neuron.2009.08.009) paper describing PCA-ICA gives an explanation of `µ`:
+
+![image](https://user-images.githubusercontent.com/5241605/180803955-55367e92-d1f6-494c-a78d-a1165da1b70a.png)
+
+`Fig. S3` also provides some information on the effects that varying `µ` from 0 to 1 have on cell extraction quality (we have often found lower values, e.g. 0.1, to work well in most cases):
+
+![image](https://user-images.githubusercontent.com/5241605/180803154-be738669-b90c-4cf3-850b-71441359bb25.png)
+
+### Ahanonu, 2022 (`µ` and # of PCs/ICs)
+
+We also describe `µ` in our recent calcium imaging experiments and analysis book chapter, see section `3.15 Extraction of Neuron Shapes, Locations, and Activity Traces`: https://link.springer.com/protocol/10.1007/978-1-0716-2039-7_13#Sec19.
+
+Further, we make a note about choosing the number
+
+![image](https://user-images.githubusercontent.com/5241605/180802392-b134fed1-c8ab-45b5-9ee6-814100e410ed.png)
+
+### term_tol
+
+The `term_tol` parameter is the ICA termination tolerance, e.g. when min difference between ICA iterations is below this value, the algorithm will exit (if it has not already reached `max_iter`).
+
+### max_iter
+
+The `max_iter` parameter determines how many iterations ICA will run before terminating.
+
+## CNMF (Pnevmatikakis et al. 2016)
+
+CNMF (Constrained Nonnegative Matrix Factorization) uses a modified version of NMF to reduce crosstalk between signals and also outputs model-based traces with reduced noise. It is recommended that users compare both the model-based, smoothed traces and the more noisy dF/F traces extracted from the movie as each can be useful for different types of analyses.
+
+A description of many of the parameters can be found at https://caiman.readthedocs.io/en/master/Getting_Started.html#parameters.
+
+## CNMF-e (Zhou et al. 2018)
+
+Use CNMF-e primarily on one-photon datasets or those with large background fluctuations, it will generally perform better than CNMF in those situations.
+
+- An overview of the CNMF-e model can be found at https://github.com/zhoupc/CNMF_E/wiki/Model-overview.
+- Inscopix provides a good description of parameters at: https://github.com/inscopix/inscopix-cnmfe/blob/main/docs/parameter_tuning.md.
+
+## EXTRACT (Inan et al. 2021)
+
+EXTRACT improves signal estimation via robust estimation to reduce contamination from surrounding noise sources (be they nearby cells or background activity).
+
+A description of EXTRACT parameters can be found at https://github.com/schnitzer-lab/EXTRACT-public#advanced-aspects.
\ No newline at end of file