diff --git a/src/MATLAB/+Dev/BuildDependencyGraph.m b/src/MATLAB/+Dev/BuildDependencyGraph.m new file mode 100644 index 0000000000000000000000000000000000000000..517cc444a11fedbf72eb5dafee55511f88c9e7a0 --- /dev/null +++ b/src/MATLAB/+Dev/BuildDependencyGraph.m @@ -0,0 +1,108 @@ +function graphStruct = BuildDependencyGraph(chkPath, bRecurseExternal) + if ( ~exist('chkPath','var') || isempty(chkPath) ) + chkPath = pwd(); + end + + if ( ~exist('bRecurseExternal','var')) + bRecurseExternal = true; + end + + %% Make sure we get back to our current dir even on error. + oldDir = cd(chkPath); + cleanupObj = onCleanup(@()(cd(oldDir))); + + %% + filenames = getAllFiles(chkPath); + + %% Initialize sparse matrix assuming a fanout of about 10x + n = length(filenames); + graphStruct = struct('nodes',{filenames}, 'graph',{sparse([],[],[],n,n,10*n)}); + + graphStruct = recursiveGetDeps(chkPath, graphStruct, filenames, bRecurseExternal); + graphStruct = sortNodes(chkPath,graphStruct); +end + +function graphStruct = sortNodes(localPath, graphStruct) + bLocal = strncmp(localPath,graphStruct.nodes, length(localPath)); + localIdx = find(bLocal); + externalIdx = find(~bLocal); + + %% Sort lexicographically, but all local functions are first. + [~,localSrt] = sort(graphStruct.nodes(bLocal)); + [~,externalSrt] = sort(graphStruct.nodes(~bLocal)); + + srtIdx = [localIdx(localSrt); externalIdx(externalSrt)]; + + graphStruct.nodes = graphStruct.nodes(srtIdx); + graphStruct.graph = graphStruct.graph(srtIdx,:); + graphStruct.graph = graphStruct.graph(:,srtIdx); +end + +function graphStruct = recursiveGetDeps(localPath,graphStruct, checkNames, bRecurseExternal) + if ( isempty(checkNames) ) + return; + end + + newEntries = {}; + + matRoot = matlabroot(); + % Get single-link dependencies + for i=1:length(checkNames) + [fList,pList] = matlab.codetools.requiredFilesAndProducts(checkNames{i}, 'toponly'); + toolboxes = arrayfun(@(x)(fullfile(matRoot,'toolbox',x.Name)),pList, 'UniformOutput',false); + + selfIdx = find(strcmp(checkNames{i},fList)); + if ( isempty(selfIdx) ) + selfIdx = 1; + fList = [checkNames(i) fList]; + end + + newNodes = [fList.'; toolboxes.']; + newGraph = createCallGraph(selfIdx, newNodes); + newStruct = struct('nodes',{newNodes},'graph',{newGraph}); + + [graphStruct,addedNodes] = Dev.MergeGraphStruct(graphStruct, newStruct); + newEntries = [newEntries; addedNodes]; + end + + % Don't recurse through external dependencies + bMatlab = strncmp(matRoot,newEntries, length(matRoot)); + newEntries = newEntries(~bMatlab); + if ( ~bRecurseExternal ) + bNewLocal = strncmp(localPath,newEntries, length(localPath)); + newEntries = newEntries(bNewLocal); + end + + graphStruct = recursiveGetDeps(localPath,graphStruct, newEntries, bRecurseExternal); +end + +function callGraph = createCallGraph(callerIdx,newNodes) + jIdx = setdiff(1:length(newNodes),callerIdx); + iIdx = repmat(callerIdx,1,length(jIdx)); + + callGraph = sparse(iIdx,jIdx, ones(1,length(jIdx)), length(newNodes),length(newNodes), length(jIdx)); +end + +function fullNames = getAllFiles(dirName) + matlabFiles = what(dirName); + + funcFileNames = vertcat(matlabFiles.m); + funcFileNames = [funcFileNames; vertcat(matlabFiles.mex)]; + + fullNames = cellfun(@(x)(fullfile(dirName,x)), funcFileNames, 'UniformOutput',false); + + for i=1:length(matlabFiles.packages) + pkgFullNames = getAllFiles(fullfile(dirName, ['+' matlabFiles.packages{i}])); + fullNames = [fullNames; pkgFullNames]; + end + + for i=1:length(matlabFiles.classes) + classDir = fullfile(dirName, ['@' matlabFiles.classes{i}]); + if ( ~exist(classDir,'dir') ) + continue; + end + + classFullNames = getAllFiles(classDir); + fullNames = [fullNames; classFullNames]; + end +end diff --git a/src/MATLAB/+Dev/BuildToolboxMap.m b/src/MATLAB/+Dev/BuildToolboxMap.m new file mode 100644 index 0000000000000000000000000000000000000000..b1f67fcbe607c9174cad82fe06653a3148cdfc9d --- /dev/null +++ b/src/MATLAB/+Dev/BuildToolboxMap.m @@ -0,0 +1,27 @@ +function toolboxMap = BuildToolboxMap() + toolboxRoot = toolboxdir(''); + toolboxMap = containers.Map('keyType','char', 'valueType','any'); + + % Ignore fixPoint, because + toolboxList = dir(toolboxRoot); + + bInvalidName = arrayfun(@(x)(strcmp(x.name,'.') || strcmp(x.name,'..') || strcmp(x.name,'fixpoint')), toolboxList); + bValidDir = ~bInvalidName & (vertcat(toolboxList.isdir) > 0); + toolboxList = toolboxList(bValidDir); + + % Always add local/shared directory to matlab + toolboxMap('MATLAB') = {fullfile(toolboxRoot,'local');fullfile(toolboxRoot,'shared')}; + + for i=1:length(toolboxList) + verStruct = ver(toolboxList(i).name); + if ( isempty(verStruct) ) + continue; + end + + if ( isKey(toolboxMap,verStruct.Name) ) + toolboxMap(verStruct.Name) = [toolboxMap(verStruct.Name); {fullfile(toolboxRoot,toolboxList(i).name)}]; + else + toolboxMap(verStruct.Name) = {fullfile(toolboxRoot,toolboxList(i).name)}; + end + end +end diff --git a/src/MATLAB/+Dev/GetCommitInfo.m b/src/MATLAB/+Dev/GetCommitInfo.m new file mode 100644 index 0000000000000000000000000000000000000000..cdfc4bbf7e354d40848a01ca698b25ccc50636f3 --- /dev/null +++ b/src/MATLAB/+Dev/GetCommitInfo.m @@ -0,0 +1,15 @@ +% [repoName,commitHash] = GetCommitInfo(inPath) +% + +function [repoName,commitHash] = GetCommitInfo(inPath) + remoteUrl = Dev.GitRemote(inPath,'origin'); + + repoName = ''; + + tokMatch = regexp(remoteUrl,'\w+@[\w\-.]+:[\w\-.]+/([\w\-]+\.git)', 'tokens','once'); + if ( ~isempty(tokMatch) ) + repoName = tokMatch{1}; + end + + commitHash = Dev.GitCommitHash(inPath); +end diff --git a/src/MATLAB/+Dev/GetVersion.m b/src/MATLAB/+Dev/GetVersion.m new file mode 100644 index 0000000000000000000000000000000000000000..e563019b7f900a6f58297480871853d1d86796fd --- /dev/null +++ b/src/MATLAB/+Dev/GetVersion.m @@ -0,0 +1,59 @@ +% version = GetVersion(command) +% Get a version string or number depending on the command argument. This +% m-file serves as a single repository for all version-related information. +% +% Note: Uses autogenerated version information in +Dev\VersionInfo.m + +function versionStr = GetVersion(command) + versionStr = []; + + verInfo = Dev.LoadVersion(); + + if ( ~exist('command','var') ) + command = 'string'; + end + + % Get rid of slashes in branch names + cleanBranch = strrep(verInfo.branchName, '/', '_'); + cleanBranch = strrep(cleanBranch, '\', '_'); + + primaryHash = ''; + matchTok = regexp(verInfo.commitHash{1},'.*:\s*(\w+)','tokens','once'); + if ( ~isempty(matchTok) ) + primaryHash = matchTok{1}; + end + + if ( strcmpi(command, 'string') || strcmpi(command, 'versionString') ) + versionStr = [num2str(verInfo.majorVersion) '.' num2str(verInfo.minorVersion) ' ' cleanBranch]; + return; + end + + if ( strcmpi(command, 'buildString') ) + versionStr = [verInfo.buildNumber '/' verInfo.buildMachine]; + return; + end + + if ( strcmpi(command, 'primaryHash') ) + versionStr = primaryHash; + return + end + + if ( strcmpi(command, 'buildHashes') ) + versionStr = verInfo.commitHash; + return + end + + if ( strcmpi(command, 'fullString') ) + versionStr = [verInfo.name ' v' num2str(verInfo.majorVersion) '.' num2str(verInfo.minorVersion) ' ' verInfo.buildNumber '/' verInfo.buildMachine ' ' cleanBranch ' ' primaryHash]; + return; + end + + if ( strcmpi(command, 'file') ) + minorStr = num2str(verInfo.minorVersion); + minorStr = strrep(minorStr, '.', '_'); + versionStr = [num2str(verInfo.majorVersion) minorStr '_' cleanBranch]; + return; + end + +end + diff --git a/src/MATLAB/+Dev/GitCommitHash.m b/src/MATLAB/+Dev/GitCommitHash.m new file mode 100644 index 0000000000000000000000000000000000000000..3548ce2344b3b9837be6440a1db0a9e64ac45246 --- /dev/null +++ b/src/MATLAB/+Dev/GitCommitHash.m @@ -0,0 +1,16 @@ +function commitHash = GitCommitHash(inPath) + bGit = Dev.SetupGit(); + if ( ~bGit ) + return; + end + + oldDir = cd(inPath); + cleanupObj = onCleanup(@()(cd(oldDir))); + + [status,hashOut] = system('git rev-parse HEAD'); + if ( status ~= 0 ) + return; + end + + commitHash = strtrim(hashOut); +end diff --git a/src/MATLAB/+Dev/GitRemote.m b/src/MATLAB/+Dev/GitRemote.m new file mode 100644 index 0000000000000000000000000000000000000000..fae8a87be8ac2859aceb2d211cfba370d7573a6a --- /dev/null +++ b/src/MATLAB/+Dev/GitRemote.m @@ -0,0 +1,18 @@ +function remoteURL = GitRemote(inPath,remoteName) + remoteURL = ''; + + bGit = Dev.SetupGit(); + if ( ~bGit ) + return; + end + + oldDir = cd(inPath); + cleanupObj = onCleanup(@()(cd(oldDir))); + + [status,urlOut] = system(['git remote get-url ' remoteName]); + if ( status ~= 0 ) + return; + end + + remoteURL = strtrim(urlOut); +end diff --git a/src/MATLAB/+Dev/GitStatus.m b/src/MATLAB/+Dev/GitStatus.m new file mode 100644 index 0000000000000000000000000000000000000000..90f0693283146a3e3a542455480a64b3d7be004f --- /dev/null +++ b/src/MATLAB/+Dev/GitStatus.m @@ -0,0 +1,67 @@ +function changeLines = GitStatus(chkPath,chkFiles) + changeLines = {}; + + if ( ~exist('chkFiles','var') ) + chkFiles = {}; + end + + bGit = Dev.SetupGit(); + if ( ~bGit ) + return; + end + + oldDir = cd(chkPath); + cleanupObj = onCleanup(@()(cd(oldDir))); + + [status,res] = system('git status -uall --porcelain'); + if ( status ~= 0 ) + return; + end + + statusLines = strsplit(res,'\n').'; + + bValid = cellfun(@(x)(~isempty(x)),statusLines); + statusLines = statusLines(bValid); + + changeTypes = cellfun(@(x)(8*changeMap(x(1)) + changeMap(x(2))), statusLines); + changeFiles = cellfun(@(x)(x(4:end)), statusLines, 'UniformOutput',false); + + if ( isempty(chkFiles) ) + changeLines = changeFiles(changeTypes > 0); + return; + end + + changeLines = {}; + + regexpFiles = regexptranslate('escape', strrep(chkFiles, '\','/')); + for i=1:length(changeFiles) + matchStarts = regexp(changeFiles{i},regexpFiles, 'start','once'); + bMatched = cellfun(@(x)(~isempty(x)), matchStarts); + + if ( any(bMatched) ) + changeLines = [changeLines; changeFiles(i)]; + end + end + + extCell = arrayfun(@(x)([ '.\' x.ext '|']), mexext('all'), 'UniformOutput',false); + extStr = [extCell{:}]; + bMexFiles = cellfun(@(x)(~isempty(x)), regexp(chkFiles,extStr(1:end-1),'once')); + if ( any(bMexFiles) ) + matchStarts = regexp(changeFiles,'^src/c/', 'start','once'); + bMatched = cellfun(@(x)(~isempty(x)), matchStarts); + + cFiles = changeFiles(bMatched); + for i=1:length(cFiles) + changeLines = [changeLines; {'Possible MEX dependency - ' cFiles{i}}]; + end + end +end + +function change = changeMap(c) + changeList = ['?','M','A','D']; + + change = find(c == changeList); + if ( isempty(change) ) + change = 0; + end +end diff --git a/src/MATLAB/+Dev/InitCompiler.m b/src/MATLAB/+Dev/InitCompiler.m new file mode 100644 index 0000000000000000000000000000000000000000..2a96f76da37295dd174aa6e37fd1775539051bf9 --- /dev/null +++ b/src/MATLAB/+Dev/InitCompiler.m @@ -0,0 +1,134 @@ +function initStruct = InitCompiler(productName,forceVersion) + if ( ~exist('forceVersion','var') ) + forceVersion = ''; + end + + rootDir = pwd(); + + initStruct = []; + + %% Build dependency graph for current directory + depGraph = Dev.BuildDependencyGraph(rootDir,true); + + %% Get list of matlab toolbox dependencies + toolboxRoot = toolboxdir(''); + bMatlab = strncmp(toolboxRoot,depGraph.nodes, length(toolboxRoot)); + + toolboxNodes = depGraph.nodes(bMatlab); + toolboxList = Dev.LoadToolboxes(toolboxNodes); + + %% Remove matlab paths/dependencies + depGraph.nodes = depGraph.nodes(~bMatlab); + depGraph.graph = depGraph.graph(~bMatlab,~bMatlab); + + %% Get rootPaths and local subdirectories for dependencies + [rootPaths,rootNames,localPaths] = Dev.SplitDependencyNames(depGraph.nodes); + + %% Verify no uncommited changes on dependencies + changeString = {}; + [chkPaths,ia,ic] = unique(rootPaths); + chkNames = rootNames(ia); + for i=1:length(chkPaths) + bInPath = (ic == i); + changeLines = Dev.GitStatus(chkPaths{i},localPaths(bInPath)); + + if ( ~isempty(changeLines) ) + changeString = [changeString; {[chkNames{i} ' (' chkPaths{i} '):']}]; + for j=1:length(changeLines) + changeString = [changeString; {[' ' changeLines{j}]}]; + end + end + end + + if ( ~isempty(changeString) ) + message = sprintf('The following dependencies have uncommitted changes, are you sure you wish to continue?\n\n'); + for i=1:length(changeString) + message = [message sprintf('%s\n',changeString{i})]; + end + + answer = questdlg(message, 'Uncommitted changes!', 'Continue','Cancel', 'Cancel'); + if ( strcmpi(answer,'Cancel') ) + initStruct = []; + return; + end + end + + %% Make full version string and fallback version file + Dev.MakeVersion(productName,forceVersion,chkPaths); + + %% Copy the external dependencies to local paths + bExternal = ~strncmp(rootDir,rootPaths,length(rootDir)); + externalPaths = rootPaths(bExternal); + externalDeps = localPaths(bExternal); + + copyPaths = {}; + for i=1:length(externalPaths) + localDir = fileparts(externalDeps{i}); + if ( ~exist(fullfile(rootDir,localDir),'dir') ) + mkdir(fullfile(rootDir,localDir)); + end + + copyPaths = [copyPaths; fullfile(rootDir,externalDeps{i})]; + copyfile(fullfile(externalPaths{i},externalDeps{i}), fullfile(rootDir,externalDeps{i})); + end + + initStruct.toolboxList = toolboxList; + initStruct.cleanupObj = onCleanup(@()(compilerCleanup(copyPaths))); + + % temporarily remove any startup scripts that would normally be run by matlabrc + enableStartupScripts(false); +end + +function compilerCleanup(copyPaths) + % Remove all copied dependencies + for i=1:length(copyPaths) + if ( exist(copyPaths{i},'file') ) + delete(copyPaths{i}); + end + end + + % Re-enable any disabled startup scripts + enableStartupScripts(true); +end + +function enableStartupScripts(bEnable) + searchPrefix = ''; + renamePrefix = 'disabled_'; + if ( bEnable ) + searchPrefix = 'disabled_'; + renamePrefix = ''; + end + + searchName = [searchPrefix 'startup.m']; + newName = [renamePrefix 'startup.m']; + + startupScripts = findFilesInPath(searchName, userpath); + for i=1:length(startupScripts) + scriptPath = fileparts(startupScripts{i}); + movefile(startupScripts{i}, fullfile(scriptPath,newName)); + end +end + +function fullNames = findFilesInPath(filename, searchPaths) + fullNames = {}; + + chkPaths = []; + while ( ~isempty(searchPaths) ) + [newPath remainder] = strtok(searchPaths, pathsep); + if ( isempty(newPath) ) + searchPaths = remainder; + continue; + end + + chkPaths = [chkPaths; {newPath}]; + searchPaths = remainder; + end + + for i=1:length(chkPaths) + chkFullPath = fullfile(chkPaths{i}, filename); + if ( exist(chkFullPath, 'file') ) + fullNames = [fullNames; {chkFullPath}]; + end + end +end + diff --git a/src/MATLAB/+Dev/LoadToolboxes.m b/src/MATLAB/+Dev/LoadToolboxes.m new file mode 100644 index 0000000000000000000000000000000000000000..5f93c35aff9dfce6dfde80b07b7da5f306709a32 --- /dev/null +++ b/src/MATLAB/+Dev/LoadToolboxes.m @@ -0,0 +1,15 @@ +function toolboxPaths = LoadToolboxes(toolboxNodes) + toolboxMap = Dev.BuildToolboxMap(); + + toolboxRoot = toolboxdir(''); + toolboxNames = cellfun(@(x)(x(length(toolboxRoot)+2:end)), toolboxNodes, 'UniformOutput',false); + + toolboxPaths = {}; + for i=1:length(toolboxNames) + if ( ~isKey(toolboxMap,toolboxNames{i}) ) + continue; + end + + toolboxPaths = [toolboxPaths; toolboxMap(toolboxNames{i})]; + end +end diff --git a/src/MATLAB/+Dev/LoadVersion.m b/src/MATLAB/+Dev/LoadVersion.m new file mode 100644 index 0000000000000000000000000000000000000000..e9a39d08dd39d0c0665ebcfa7d7695c924ef481f --- /dev/null +++ b/src/MATLAB/+Dev/LoadVersion.m @@ -0,0 +1,82 @@ +function verInfo = LoadVersion() + %% Use compiled VersionInfo function when deployed + if ( isdeployed ) + verInfo = Dev.VersionInfo(); + return; + end + + %% Try to load version tag and branch using git + bFoundGit = Dev.SetupGit(); + verInfo = []; + if ( bFoundGit ) + chkVerInfo = gitVersionInfo(); + end + + %% Use fallback file for name (set by Dev.MakeVersion) + fallbackFile = 'version.json'; + + fallbackVerInfo = loadFallbackInfo(fallbackFile); + if ( isempty(fallbackVerInfo) ) + fprintf('WARNING: Invalid fallback file, unable to load version information.\n'); + return; + end + + if ( isempty(chkVerInfo) ) + fprintf('WARNING: Could not find git directory, using fallback %s\n', fallbackFile); + chkVerInfo = fallbackVerInfo; + end + + % Always use the name from fallback file + chkVerInfo.name = fallbackVerInfo.name; + + verInfo = chkVerInfo; +end + +%% Read fallback json version file +function verInfo = loadFallbackInfo(fallbackFile) + verInfo = []; + + fid = fopen(fallbackFile); + if ( fid <= 0 ) + return; + end + + jsonVer = fread(fid, '*char').'; + fclose(fid); + + verInfo = Utils.ParseJSON(jsonVer); +end + +%% Use git tags to get version information +function verInfo = gitVersionInfo() + verInfo = []; + + [verStatus,verString] = system('git describe --tags --match v[0-9]*.[0-9]* --abbrev=0'); + [branchStatus,branchString] = system('git rev-parse --abbrev-ref HEAD'); + + [majorVer,minorVer] = Dev.ParseVerTag(verString); + + if ( verStatus ~= 0 || isempty(majorVer) || isempty(minorVer) ) + fprintf('WARNING: There was an error retrieving tag from git:\n %s\n', verString); + return; + end + + branchName = strtrim(branchString); + if ( branchStatus ~= 0 ) + fprintf('WARNING: There was an error retrieving branch name from git:\n %s\n', branchName); + return; + end + + [repoName,commitHash] = Dev.GetCommitInfo(pwd()); + commitString = [repoName ' : ' commitHash]; + + %% VersionInfo default structure + verInfo = struct(... + 'name',{repoName},... + 'majorVersion',{majorVer},... + 'minorVersion',{minorVer+1},... + 'branchName',{branchName},... + 'buildNumber',{'UNKNOWN'},... + 'buildMachine',{'UNKNOWN'},... + 'commitHash',{{commitString}}); +end diff --git a/src/MATLAB/+Dev/MakeVersion.m b/src/MATLAB/+Dev/MakeVersion.m new file mode 100644 index 0000000000000000000000000000000000000000..4c91fd3699249b5c4a8ba7d88fa671ac331a8a11 --- /dev/null +++ b/src/MATLAB/+Dev/MakeVersion.m @@ -0,0 +1,107 @@ + + +function verInfo = MakeVersion(productName, forceVersion, dependencyPaths) + + %% Template for the Dev.VersionInfo command + funcString = { + '%% versionInfo = VersionInfo()' + '%% Return the version info structure' + '%%' + '%% Note: This file is autogenerated by build script DO NOT MODIFY!!' + '' + 'function versionInfo = VersionInfo()' + ' jsonVer = ''%s'';' + ' ' + ' versionInfo = Utils.ParseJSON(jsonVer);' + 'end'}; + + if ( ~exist('forceVersion', 'var') ) + forceVersion = ''; + end + + if ( ~exist('dependencyPaths', 'var') ) + dependencyPaths = {}; + end + + fallbackFile = 'version.json'; + + %% Load version info from git tags + verInfo = Dev.LoadVersion(); + if ( isempty(verInfo) ) + error('Unable to load git version information'); + end + + % Force name to be the passed in product name + verInfo.name = productName; + + %% If we are forcing a specific version number + if ( ~isempty(forceVersion) ) + verString = ['v' forceVersion]; + + [majorVer,minorVer] = Dev.ParseVerTag(verString); + if ( isempty(majorVer) ) + error('Invalid version string %s', verString); + end + + verInfo.majorVersion = majorVer; + verInfo.minorVersion = minorVer; + end + + %% Get a timestamp build-number + c = clock(); + verInfo.buildNumber = sprintf('%d.%02d.%02d.%02d', c(1), c(2), c(3), c(4)); + + %% Get machine ID + [status,buildMachine] = system('hostname'); + if ( status ~= 0 ) + fprintf('WARNING: There was an error retrieving hostname:\n %s\n', buildMachine); + else + verInfo.buildMachine = strtrim(buildMachine); + end + + %% Make sure local path is not in the list + localPath = pwd(); + bHasLocal = strcmp(localPath,dependencyPaths); + dependencyPaths = dependencyPaths(~bHasLocal); + + %% Add all other dependent commit hashes to list + hashStrings = cell(length(dependencyPaths),1); + for i=1:length(dependencyPaths) + [repoName,commitHash] = Dev.GetCommitInfo(dependencyPaths{i}); + hashStrings{i} = [repoName ' : ' commitHash]; + end + + verInfo.commitHash = [verInfo.commitHash; hashStrings]; + + %% Create +Dev/VersionInfo.m for use in compiled files + % Concatenate the template function lines into one giant string + templateString = []; + for i=1:length(funcString) + templateString = [templateString funcString{i} '\n']; + end + + % Now insert all our arguments into the template and write to a file. + if ( ~exist('+Dev','dir') ) + mkdir('+Dev'); + end + + fid = fopen(fullfile('+Dev','VersionInfo.m'), 'wt'); + if ( fid <= 0 ) + error('Unable to open +Dev/VersionInfo.m for writing'); + end + + jsonVer = Utils.CreateJSON(verInfo,false); + fprintf(fid, templateString, jsonVer); + + fclose(fid); + + %% Update fallback file if we used git to retrieve version info. + jsonStr = Utils.CreateJSON(verInfo); + fid = fopen(fallbackFile, 'wt'); + if ( fid <= 0 ) + return; + end + + fprintf(fid, '%s\n', jsonStr); + fclose(fid); +end diff --git a/src/MATLAB/+Dev/MergeGraphStruct.m b/src/MATLAB/+Dev/MergeGraphStruct.m new file mode 100644 index 0000000000000000000000000000000000000000..c8dcab1c81d9c9dea391115a403cf66f7cca8c30 --- /dev/null +++ b/src/MATLAB/+Dev/MergeGraphStruct.m @@ -0,0 +1,18 @@ +function [graphStruct,newNodes] = MergeGraphStruct(graphStruct, newStruct) + oldIdx = cellfun(@(x)(find(strcmp(x,graphStruct.nodes),1,'first')), newStruct.nodes, 'UniformOutput',0); + bNew = cellfun(@(x)(isempty(x)), oldIdx); + + newNodes = newStruct.nodes(bNew); + + p = length(newNodes); + [m,n] = size(graphStruct.graph); + + idxMap = zeros(1,length(newStruct.nodes)); + idxMap(~bNew) = [oldIdx{~bNew}]; + idxMap(bNew) = (1:p) + n; + + graphStruct.nodes = [graphStruct.nodes; newNodes]; + graphStruct.graph = [graphStruct.graph zeros(n,p); zeros(p,n+p)]; + + graphStruct.graph(idxMap,idxMap) = (graphStruct.graph(idxMap,idxMap) | newStruct.graph); +end \ No newline at end of file diff --git a/src/MATLAB/+Dev/ParseVerTag.m b/src/MATLAB/+Dev/ParseVerTag.m new file mode 100644 index 0000000000000000000000000000000000000000..278ecaa61b879af28761ec7d4c6b4c2fd95031d0 --- /dev/null +++ b/src/MATLAB/+Dev/ParseVerTag.m @@ -0,0 +1,15 @@ +% [majorVer,minorVer] = ParseVerTag(verString) +% + +function [majorVer,minorVer] = ParseVerTag(verString) + majorVer = []; + minorVer = []; + + verString = strtrim(verString); + + numTok = regexp(verString, '[Vv](\d+)\.(\d+(?:\.\d+)?).*', 'tokens', 'once'); + if ( length(numTok) >= 2 ) + majorVer = str2double(numTok{1}); + minorVer = str2double(numTok{2}); + end +end diff --git a/src/MATLAB/+Dev/SetupGit.m b/src/MATLAB/+Dev/SetupGit.m new file mode 100644 index 0000000000000000000000000000000000000000..aece550b17f59bdfd74b87a7b3614ed789303da7 --- /dev/null +++ b/src/MATLAB/+Dev/SetupGit.m @@ -0,0 +1,70 @@ +function bFoundGit = SetupGit() + bFoundGit = 0; + + [bGitError gitVer] = system('git version'); + if ( ~bGitError ) + bFoundGit = 1; + return; + end + + gitPath = findGitPath(); + if ( isempty(gitPath) ) + return; + end + + pathEnv = getenv('PATH'); + idx = strfind(pathEnv,gitPath); + if ( isempty(idx) ) + pathEnv = [gitPath pathsep pathEnv]; + setenv('PATH', pathEnv); + end + + [bGitError gitVer] = system('git version'); + if ( ~bGitError ) + bFoundGit = 1; + return; + end +end + +function gitPath = findGitPath() + gitPath = ''; + + comparch = computer('arch'); + progFilesPath64 = 'C:\Program Files'; + if ( strcmpi(comparch,'win64') ) + progFilesPath = getenv('ProgramFiles(x86)'); + progFilesPath64 = getenv('ProgramFiles'); + elseif ( strcmpi(comparch,'win32') ) + progFilesPath = getenv('ProgramFiles'); + else + return; + end + + tryPaths = {fullfile(progFilesPath, 'Git'); + fullfile(progFilesPath64, 'Git'); + fullfile(progFilesPath, 'msysgit'); + fullfile(progFilesPath64, 'msysgit'); + 'C:\Git'; + 'C:\msysgit'}; + + trySubdir = {'bin';'cmd'}; + + foundPath = ''; + for i=1:length(tryPaths) + if ( exist(tryPaths{i}, 'dir') ) + foundPath = tryPaths{i}; + break; + end + end + + if ( isempty(foundPath) ) + return; + end + + for i=1:length(trySubdir) + if ( exist(fullfile(foundPath,trySubdir{i},'git.exe'),'file') || exist(fullfile(foundPath,trySubdir{i},'git.cmd'),'file') ) + gitPath = fullfile(foundPath,trySubdir{i}); + return; + end + end +end diff --git a/src/MATLAB/+Dev/SplitDependencyNames.m b/src/MATLAB/+Dev/SplitDependencyNames.m new file mode 100644 index 0000000000000000000000000000000000000000..6cf65d26a984843f085c59872b023989e11b83e6 --- /dev/null +++ b/src/MATLAB/+Dev/SplitDependencyNames.m @@ -0,0 +1,61 @@ +function [rootPath,rootName,localPath] = SplitDependencyNames(depList) + rootPath = cell(length(depList),1); + rootName = cell(length(depList),1); + localPath = cell(length(depList),1); + + dropPostfix = {'src/MATLAB'}; + localMatches = '^private$|^\+.+$|^@.+$|^.+\.(m|mexw64|mexw32|fig)$'; + + for i=1:length(depList) + qualPath = splitQualifiedPath(depList{i}); + + bLocalPath = cellfun(@(x)(~isempty(x)),regexp(qualPath,localMatches, 'start','once')); + endRoot = find(diff(bLocalPath),1,'last'); + if ( isempty(endRoot) ) + % This shouldn't happen if matlab toolboxes are handled first. + continue; + end + + chkPath = qualPath(1:endRoot); + for j=1:size(dropPostfix,1) + chkPostfix = strsplit(dropPostfix{j},'/'); + cmpIdx = length(chkPath)-length(chkPostfix) + 1; + bDropMatch = strcmp(chkPostfix,chkPath(cmpIdx:end)); + + if ( all(bDropMatch) ) + chkPath = chkPath(1:(cmpIdx-1)); + break; + end + end + + rootName{i} = chkPath{end}; + rootPath{i} = fullfile(qualPath{1:endRoot}); + localPath{i} = fullfile(qualPath{(endRoot+1):end}); + end +end + +function qualPath = splitQualifiedPath(inPath) + chkPath = strrep(inPath, '\','/'); + + qualPath = {}; + splitRoot = regexp(chkPath,'^(/{1,2}.+?|.+?:)/(.*?)$','tokens','once'); + if ( ~isempty(splitRoot) ) + qualPath = splitRoot(1); + chkPath = splitRoot{2}; + end + + splitPath = strsplit(chkPath,'/'); + while ( ~isempty(splitPath) ) + popPath = splitPath{1}; + splitPath = splitPath(2:end); + + if ( strcmp('.',popPath) ) + continue; + elseif ( strcmp('..',popPath) ) + qualPath = qualPath(1:end-1); + continue; + end + + qualPath = [qualPath {popPath}]; + end +end diff --git a/src/MATLAB/+ImUtils/ConvertType.m b/src/MATLAB/+ImUtils/ConvertType.m new file mode 100644 index 0000000000000000000000000000000000000000..01756425f863f3ede570b0939b441431438f33d2 --- /dev/null +++ b/src/MATLAB/+ImUtils/ConvertType.m @@ -0,0 +1,117 @@ +% [ imageOut ] = ConvertType(IMAGEIN, OUTCLASS, NORMALIZE) +% ConvertType converts image from current type into the specified type +% OUTCLASS +% If normalize==true then each channel/frame will be set between [0,1] prior +% to conversion, meaning that normalization happens on a frame by frame as +% well as a channel by channel bases. +% Assumes a 5D image of (rows,col,z,channels,time). Non-existent dimensions +% should be singleton. + +function [ imageOut ] = ConvertType(imageIn, typ, normalize) + +if (~exist('normalize','var') || isempty(normalize)) + normalize = 0; +end + +if (~strcmpi(typ,'logical')) + imageOut = zeros(size(imageIn),typ); +else + imageOut = false(size(imageIn)); +end + +if (normalize) + for t=1:size(imageIn,5) + for c=1:size(imageIn,4) + inType = class(imageIn); + if (strcmpi(inType,'double') || strcmpi(inType,'uint64') || strcmpi(inType,'int64')) + imTemp = double(imageIn(:,:,:,c,t)); + else + imTemp = single(imageIn(:,:,:,c,t)); + end + imTemp = imTemp-min(imTemp(:)); + imTemp = imTemp./max(imTemp(:)); + + switch typ + case 'uint8' + imageOut(:,:,:,c,t) = im2uint8(imTemp); + case 'uint16' + imageOut(:,:,:,c,t) = im2uint16(imTemp); + case 'int16' + imageOut(:,:,:,c,t) = im2int16(imTemp); + case 'uint32' + imageOut(:,:,:,c,t) = im2uint32(imTemp); + case 'int32' + imageOut(:,:,:,c,t) = im2int32(imTemp); + case 'single' + imageOut(:,:,:,c,t) = im2single(imTemp); + case 'double' + imageOut(:,:,:,c,t) = imTemp; + case 'logical' + imageOut(:,:,:,c,t) = imTemp>min(imTemp(:)); + otherwise + error('Unkown type of image to convert to!'); + end + end + end +else + w = whos('imageIn'); + switch w.class + case 'single' + imageIn = convertToMaxOfOne(imageIn,w.class); + case 'double' + imageIn = convertToMaxOfOne(imageIn,w.class); + end + if (strcmpi(w.class,typ)) + imageOut = imageIn; + else + switch typ + case 'uint8' + imageOut = im2uint8(imageIn); + case 'uint16' + imageOut = im2uint16(imageIn); + case 'int16' + imageOut = im2int16(imageIn); + case 'uint32' + imageOut = im2uint32(imageIn); + case 'int32' + imageOut = im2int32(imageIn); + case 'single' + imageOut = im2single(imageIn); + case 'double' + imageOut = im2double(imageIn); + case 'logical' + imageOut = imageIn>min(imageIn(:)); + otherwise + error('Unkown type of image to convert to!'); + end + end +end +end + +function im = convertToMaxOfOne(im,outTyp) +switch outTyp + case 'uint8' + im = im./2^8; + case 'uint16' + if (max(im(:))<2^12+1) + im = im./2^12; + else + im = im./2^16; + end + case 'int16' + im = im./2^15-1; + case 'uint32' + im = im./2^32; + case 'int32' + im = im./2^32-1; + case 'single' + im = im./max(im(:)); + case 'double' + im = im./max(im(:)); + case 'logical' + % im = im; + otherwise + error('Unkown type of image to convert to!'); +end +end + diff --git a/src/MATLAB/+MicroscopeData/+Helper/CreateUniqueWordedPath.m b/src/MATLAB/+MicroscopeData/+Helper/CreateUniqueWordedPath.m new file mode 100644 index 0000000000000000000000000000000000000000..80c66aa3ba2f292c38e7c5f95da703710b5387f8 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Helper/CreateUniqueWordedPath.m @@ -0,0 +1,64 @@ +function newFilePath = CreateUniqueWordedPath(fPath) +%newFilePath = CreateUniqueWordedPath(fPath) +% Pass in a path with at least two directories with '\' seperating the +% direcories, and a new path will be generated that removes the words that +% are repeated. This forces the unique words closer to the file name or +% most deep directory. + +driveIdx = find(fPath==':',1,'first'); +if (~isempty(driveIdx)) + tmpPath = strtrim(MicroscopeData.Helper.SanitizeString(fPath(driveIdx+1:end))); + fPath = [fPath(1:driveIdx),tmpPath]; +else + fPath = strtrim(MicroscopeData.Helper.SanitizeString(fPath)); +end + +dirs = strsplit(fPath,'\'); + +newDirs = {}; +for i=1:length(dirs) + if (strcmp(dirs{i},'.')) + continue + end + if (strcmp(dirs{i},'..')) + newDirs = newDirs(1:end-1); + continue + end + if (isempty(newDirs)) + newDirs = dirs(i); + else + newDirs{end+1} = dirs{i}; + end +end + +dirs = newDirs; + +wordList = strsplit(dirs{end},' '); + +newFilePath = dirs{end}; + +for i=length(dirs)-1:-1:1 + curDir = dirs{i}; + curWords = strsplit(curDir,' '); + keepWords = true(1,length(curWords)); + for j=1:length(curWords) + if (any(strcmpi(curWords{j},wordList))) + keepWords(j) = false; + else + wordList{end+1} = curWords{j}; + end + end + + newDir = ''; + for j=1:length(keepWords) + if (keepWords(j)) + if (isempty(newDir)) + newDir = strtrim(curWords{j}); + else + newDir = sprintf('%s %s',newDir,strtrim(curWords{j})); + end + end + end + newFilePath = fullfile(newDir,newFilePath); +end +end diff --git a/src/MATLAB/+MicroscopeData/+Helper/GetPixelTypeTIF.m b/src/MATLAB/+MicroscopeData/+Helper/GetPixelTypeTIF.m new file mode 100644 index 0000000000000000000000000000000000000000..25247b9e1d4018aa97238dfa3930e8fe61a2fabf --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Helper/GetPixelTypeTIF.m @@ -0,0 +1,37 @@ +% [pixelType,dataTypeLookup] = GetPixelTypeTIF(tifFile) + +function [pixelType,imInfo] = GetPixelTypeTIF(tifFile) + pixelType = []; + + dataTypeLookup = {'uint8';'uint16';'uint32';'uint64'; + 'int8';'int16';'int32';'int64'; + 'single';'double'; + 'logical'}; + + dataTypeSize = [1;2;4;8; + 1;2;4;8; + 4;8; + 1]; + + dataTypeFormat = {'Unsigned integer';'Unsigned integer';'Unsigned integer';'Unsigned integer'; + 'Integer';'Integer';'Integer';'Integer'; + 'IEEE floating point';'IEEE floating point'; + 'Unsigned Integer'}; + + imInfo = imfinfo(tifFile,'tif'); + sampleFormat = 'Unsigned integer'; + if ( isfield(imInfo,'SampleFormat') ) + sampleFormat = imInfo.SampleFormat; + end + bitDepth = imInfo.BitDepth; + + bSizeMatch = (dataTypeSize == floor(bitDepth/8)); + bSampleMatch = strcmpi(sampleFormat,dataTypeFormat); + + formatIdx = find(bSizeMatch & bSampleMatch); + if ( isempty(formatIdx) ) + return; + end + + pixelType = dataTypeLookup{formatIdx}; +end diff --git a/src/MATLAB/+MicroscopeData/+Helper/ParseReaderInputs.m b/src/MATLAB/+MicroscopeData/+Helper/ParseReaderInputs.m new file mode 100644 index 0000000000000000000000000000000000000000..3e70080448e0f95ba717fd9cd8148fa7ab9b1eb8 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Helper/ParseReaderInputs.m @@ -0,0 +1,43 @@ +function argStruct = ParseReaderInputs(varargin) + dataTypeLookup = {'uint8';'uint16';'uint32';'uint64'; + 'int8';'int16';'int32';'int64'; + 'single';'double'; + 'logical'}; + + dataTypeSize = [1;2;4;8; + 1;2;4;8; + 4;8; + 1]; + + p = inputParser(); + p.StructExpand = false; + + % This is ridiculous, but we assume that the optional path is specified if + % length(varargin) is odd + if ( mod(length(varargin),2) == 1 ) + addOptional(p,'path','', @ischar); + else + addParameter(p,'path','', @ischar); + end + + addParameter(p,'imageData',[], @(x)(validOrEmpty(@isstruct,x))); + + addParameter(p,'chanList',[], @(x)(validOrEmpty(@isvector,x))); + addParameter(p,'timeRange',[], @(x)(validOrEmpty(@(y)(numel(y)==2),x))); + addParameter(p,'roi_xyz',[], @(x)(validOrEmpty(@(y)(all(size(y)==[2,3])),x))); + + addParameter(p,'outType',[], @(x)(validOrEmpty(@(y)(any(strcmp(y,dataTypeLookup))),x))); + addParameter(p,'normalize',false,@islogical); + + addParameter(p,'verbose',false, @islogical); + addParameter(p,'prompt',[], @(x)(validOrEmpty(@islogical,x))); + addParameter(p,'promptTitle','', @ischar); + + parse(p,varargin{:}); + argStruct = p.Results; +end + +% Inputs are valid if they are empty or if they satisfy their validity function +function bValid = validOrEmpty(validFunc,x) + bValid = (isempty(x) || validFunc(x)); +end diff --git a/src/MATLAB/+MicroscopeData/+Helper/SanitizeString.m b/src/MATLAB/+MicroscopeData/+Helper/SanitizeString.m new file mode 100644 index 0000000000000000000000000000000000000000..43140f0e05b44030b01a2b61922b48b60c6ff507 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Helper/SanitizeString.m @@ -0,0 +1,24 @@ +function [ str ] = SanitizeString( str ) +%SANITIZESTRING Summary of this function goes here +% Detailed explanation goes here +str = strrep(str, '!', ''); +str = strrep(str, '*', ''); +str = strrep(str, '''', ''); +str = strrep(str, '(', ''); +str = strrep(str, ')', ''); +str = strrep(str, ';', ''); +str = strrep(str, ':', ''); +str = strrep(str, '@', ''); +str = strrep(str, '&', ''); +str = strrep(str, '=', ''); +str = strrep(str, '+', ''); +str = strrep(str, '$', ''); +str = strrep(str, ',', ''); +str = strrep(str, '/', ''); +str = strrep(str, '?', ''); +str = strrep(str, '#', ''); +str = strrep(str, '[', ''); +str = strrep(str, ']', ''); + +str = strtrim(str); +end diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJarPath.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJarPath.m new file mode 100644 index 0000000000000000000000000000000000000000..42ae0e4ee556b23c135446e4ecbc4693ab8ad5d8 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJarPath.m @@ -0,0 +1,24 @@ +function CheckJarPath() +%CHECKJARPATH Summary of this function goes here +% Detailed explanation goes here + +%% ensure that the bioformats jar file is on the path +dynamicPaths = javaclasspath('-dynamic'); +bfIsLoaded = false; +if (~isempty(dynamicPaths)) + for i=1:length(dynamicPaths) + [~,name,~] = fileparts(dynamicPaths{i}); + if (strcmpi('bioformats_package',name)) + bfIsLoaded = true; + break + end + end +end + +if (~bfIsLoaded) + curPath = mfilename('fullpath'); + [pathstr,~,~] = fileparts(curPath); + javaaddpath(fullfile(pathstr,'bioformats_package.jar'),'-end'); +end +end + diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJavaMemory.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJavaMemory.m new file mode 100644 index 0000000000000000000000000000000000000000..92a2223060a7723abedfcaf22a295809a3ecb124 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/CheckJavaMemory.m @@ -0,0 +1,54 @@ +function [] = bfCheckJavaMemory(varargin) +% bfCheckJavaMemory warn if too little memory is allocated to Java +% +% SYNOPSIS bfCheckJavaMemory() +% +% Input +% +% minMemory - (Optional) The minimum suggested memory setting in MB. +% Default: 512 +% +% Output +% +% A warning message is printed if too little memory is allocated. + +% OME Bio-Formats package for reading and converting biological file formats. +% +% Copyright (C) 2014 Open Microscopy Environment: +% - Board of Regents of the University of Wisconsin-Madison +% - Glencoe Software, Inc. +% - University of Dundee +% +% This program is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as +% published by the Free Software Foundation, either version 2 of the +% License, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License along +% with this program; if not, write to the Free Software Foundation, Inc., +% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +runtime = java.lang.Runtime.getRuntime(); +maxMemory = runtime.maxMemory() / (1024 * 1024); + +ip = inputParser; +ip.addOptional('minMemory', 512, @isscalar); +ip.parse(varargin{:}); +minMemory = ip.Results.minMemory; + +warningID = 'BF:lowJavaMemory'; + +if maxMemory < minMemory - 64 + warning_msg = [... + '*** Insufficient memory detected. ***\n'... + '*** %dm found ***\n'... + '*** %dm or greater is recommended ***\n'... + '*** See http://www.mathworks.com/matlabcentral/answers/92813 ***\n'... + '*** for instructions on increasing memory allocation. ***\n']; + warning(warningID, warning_msg, round(maxMemory), minMemory); +end diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetImages.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetImages.m new file mode 100644 index 0000000000000000000000000000000000000000..4ab4ffa76d764824df513dd1e15d88472cff579d --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetImages.m @@ -0,0 +1,117 @@ +function [ seriesImages ] = GetImages( bfReader, seriesNum ) +%GETIMAGES Summary of this function goes here +% Detailed explanation goes here + +numSeries = bfReader.getSeriesCount(); + +omeMetadata = bfReader.getMetadataStore(); +prgs = Utils.CmdlnProgress(1,true); + +if (exist('seriesNum','var') && ~isempty(seriesNum) && numSeries>=seriesNum) + seriesImages = readSeriesImage(bfReader, seriesNum-1, omeMetadata, true, prgs); +else + if (bfReader.getSeriesCount()>1) + prgs.SetMaxIterations(bfReader.getSeriesCount()); + + onlyOneSeries = false; + else + prgs.SetMaxIterations(numSeries); + onlyOneSeries = true; + end + + for series=0:numSeries-1; + im = readSeriesImage(bfReader, series, omeMetadata, onlyOneSeries, prgs); + + seriesImages{series+1} = im; + + prgs.PrintProgress(series+1); + end +end + +prgs.ClearProgress(); +end + +function im = readSeriesImage(bfReader, series, omeMetadata, onlyOneSeries, prgs) + bfReader.setSeries(series); + + imageData = []; + + imageData.Dimensions = [safeGetValue(omeMetadata.getPixelsSizeX(series));... + safeGetValue(omeMetadata.getPixelsSizeY(series));... + safeGetValue(omeMetadata.getPixelsSizeZ(series))]; + + imageData.NumberOfChannels = omeMetadata.getChannelCount(series); + imageData.NumberOfFrames = safeGetValue(omeMetadata.getPixelsSizeT(series)); + + clss = char(omeMetadata.getPixelsType(series)); + if (strcmpi(clss,'float')) + clss = 'single'; + end + im = zeros([Utils.SwapXY_RC(imageData.Dimensions'),imageData.NumberOfChannels,imageData.NumberOfFrames],clss); + + order = char(omeMetadata.getPixelsDimensionOrder(series)); + + if (onlyOneSeries) + prgs.SetMaxIterations(imageData.NumberOfFrames*imageData.NumberOfChannels*imageData.Dimensions(3)); + i = 1; + end + + for t=1:imageData.NumberOfFrames + for z=1:imageData.Dimensions(3) + for c=1:imageData.NumberOfChannels + ind = calcPlaneInd(order,z,c,t,imageData); + im(:,:,z,c,t) = MicroscopeData.Original.BioFormats.GetPlane(bfReader,ind); + + if (onlyOneSeries) + prgs.PrintProgress(i); + i = i+1; + end + end + end + end +end + +function ind = calcPlaneInd(order,z,c,t,imageData) +switch order(3) + case 'Z' + ind = z-1; + mul = imageData.Dimensions(3); + case 'C' + ind = c-1; + mul = imageData.NumberOfChannels; + case 'T' + ind = t-1; + mul = imageData.NumberOfFrames; +end + +switch order(4) + case 'Z' + ind = ind + (z-1)*mul; + mul = imageData.Dimensions(3)*mul; + case 'C' + ind = ind + (c-1)*mul; + mul = imageData.NumberOfChannels*mul; + case 'T' + ind = ind + (t-1)*mul; + mul = imageData.NumberOfFrames*mul; +end + +switch order(5) + case 'Z' + ind = ind + (z-1)*mul; + case 'C' + ind = ind + (c-1)*mul; + case 'T' + ind = ind + (t-1)*mul; +end + +ind = ind +1; +end + +function val = safeGetValue(varIn) +if (isempty(varIn)) + val = 0; + return +end +val = varIn.getValue(); +end diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetMetadata.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetMetadata.m new file mode 100644 index 0000000000000000000000000000000000000000..decc62a844aa6268d813654caf1ad3b0808abf02 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetMetadata.m @@ -0,0 +1,171 @@ +function [seriesMetadata, varargout] = GetMetadata( bfReader, datasetExt ) +%GETMETADATA Summary of this function goes here +% Detailed explanation goes here + +seriesMetadata = {}; + +if (~exist('datasetExt','var') || isempty(datasetExt)) + datasetExt = ''; +end + +orgMetadata = bfReader.getSeriesMetadata(); +omeMetadata = bfReader.getMetadataStore(); + +onlyOneSeries = true; +if (bfReader.getSeriesCount()>1) + prgs = Utils.CmdlnProgress(bfReader.getSeriesCount(),true); + onlyOneSeries = false; +end + +for series=0:bfReader.getSeriesCount()-1; + bfReader.setSeries(series); + + imageData = []; + + [~,imageData.DatasetName,~] = fileparts(char(omeMetadata.getImageName(series))); + + imageData.Dimensions = [safeGetValue(omeMetadata.getPixelsSizeX(series)),... + safeGetValue(omeMetadata.getPixelsSizeY(series)),... + safeGetValue(omeMetadata.getPixelsSizeZ(series))]; + + imageData.NumberOfChannels = omeMetadata.getChannelCount(series); + imageData.NumberOfFrames = safeGetValue(omeMetadata.getPixelsSizeT(series)); + + xPixelPhysicalSize = safeGetValue(omeMetadata.getPixelsPhysicalSizeX(series)); + if xPixelPhysicalSize==0 + xPixelPhysicalSize = 1; + end + + yPixelPhysicalSize = safeGetValue(omeMetadata.getPixelsPhysicalSizeY(series)); + if yPixelPhysicalSize==0 + yPixelPhysicalSize = 1; + end + + zPixelPhysicalSize = safeGetValue(omeMetadata.getPixelsPhysicalSizeZ(series)); + if zPixelPhysicalSize==0 + zPixelPhysicalSize = 1; + end + + imageData.PixelPhysicalSize = [xPixelPhysicalSize, yPixelPhysicalSize, zPixelPhysicalSize]; + + if (strcmp(datasetExt,'.czi')) + imageData.Position = [orgMetadata.get('Global Information|Image|S|Scene|Position|X #1'),... + orgMetadata.get('Global Information|Image|S|Scene|Position|Y #1'),... + orgMetadata.get('Global Information|Image|S|Scene|Position|Z #1')]; + elseif (omeMetadata.getPlaneCount(series)>0) + imageData.Position = [double(omeMetadata.getPlanePositionX(series,0)),... + double(omeMetadata.getPlanePositionY(series,0)),... + double(omeMetadata.getPlanePositionZ(series,0))]; + end + + imageData.ChannelNames = cell(imageData.NumberOfChannels,1); + for c=1:imageData.NumberOfChannels + colr = char(omeMetadata.getChannelName(series,c-1)); + + if (isempty(colr)) + colr = sprintf('Channel:%d',c); + end + + imageData.ChannelNames{c} = colr; + end + + imageData.StartCaptureDate = safeGetValue(omeMetadata.getImageAcquisitionDate(series)); + ind = strfind(imageData.StartCaptureDate,'T'); + if (~isempty(ind)) + imageData.StartCaptureDate(ind) = ' '; + end + + imageData.TimeStampDelta = 0; + + order = char(omeMetadata.getPixelsDimensionOrder(series)); + + if (onlyOneSeries) + prgs = Utils.CmdlnProgress(imageData.NumberOfFrames*imageData.NumberOfChannels*imageData.Dimensions(3),true); + i = 1; + end + + for t=1:imageData.NumberOfFrames + for z=1:imageData.Dimensions(3) + for c=1:imageData.NumberOfChannels + ind = calcPlaneInd(order,z,c,t,imageData); + try + delta = omeMetadata.getPlaneDeltaT(series,ind-1); + catch er + delta = []; + end + if (~isempty(delta)) + imageData.TimeStampDelta(z,c,t) = delta.floatValue; + end + if (onlyOneSeries) + prgs.PrintProgress(i); + i = i+1; + end + end + end + end + + if (size(imageData.TimeStampDelta,1)~=imageData.Dimensions(3) ||... + size(imageData.TimeStampDelta,2)~=imageData.NumberOfChannels || ... + size(imageData.TimeStampDelta,3)~=imageData.NumberOfFrames) + imageData = rmfield(imageData,'TimeStampDelta'); + end + + seriesMetadata{series+1} = imageData; + + prgs.PrintProgress(series+1); +end + +prgs.ClearProgress(); + +if (nargout>1) + varargout{1} = omeMetadata; +end +if (nargout>2) + varargout{2} = orgMetadata; +end +end + +function ind = calcPlaneInd(order,z,c,t,imageData) +switch order(3) + case 'Z' + ind = z-1; + mul = imageData.Dimensions(3); + case 'C' + ind = c-1; + mul = imageData.NumberOfChannels; + case 'T' + ind = t-1; + mul = imageData.NumberOfFrames; +end + +switch order(4) + case 'Z' + ind = ind + (z-1)*mul; + mul = imageData.Dimensions(3)*mul; + case 'C' + ind = ind + (c-1)*mul; + mul = imageData.NumberOfChannels*mul; + case 'T' + ind = ind + (t-1)*mul; + mul = imageData.NumberOfFrames*mul; +end + +switch order(5) + case 'Z' + ind = ind + (z-1)*mul; + case 'C' + ind = ind + (c-1)*mul; + case 'T' + ind = ind + (t-1)*mul; +end + +ind = ind +1; +end + +function val = safeGetValue(varIn) +if (isempty(varIn)) + val = 0; + return +end +val = varIn.getValue(); +end diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetPlane.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetPlane.m new file mode 100644 index 0000000000000000000000000000000000000000..8794cc475b4a03b382b45c0a2dbbeb966be96e0e --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetPlane.m @@ -0,0 +1,110 @@ +function I = GetPlane(r, iPlane, varargin) +% BFGETPLANE Retrieve the plane data from a reader using Bio-Formats +% +% I = bfGetPlane(r, iPlane) returns a specified plane from the input +% format reader. The index specifying the plane to retrieve should be +% contained between 1 and the number of planes for the series. +% +% I = bfGetPlane(r, iPlane, x, y, width, height) only returns the tile +% which origin is specified by (x, y) and dimensions are specified by +% (width, height). +% +% Examples +% +% I = bfGetPlane(r, 1) % First plane of the series +% I = bfGetPlane(r, r.getImageCount()) % Last plane of the series +% I = bfGetPlane(r, 1, 1, 1, 20, 20) % 20x20 tile originated at (0, 0) +% +% See also: BFGETREADER + +% OME Bio-Formats package for reading and converting biological file formats. +% +% Copyright (C) 2012 - 2014 Open Microscopy Environment: +% - Board of Regents of the University of Wisconsin-Madison +% - Glencoe Software, Inc. +% - University of Dundee +% +% This program is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as +% published by the Free Software Foundation, either version 2 of the +% License, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License along +% with this program; if not, write to the Free Software Foundation, Inc., +% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +% Input check +ip = inputParser; +ip.addRequired('r', @(x) isa(x, 'loci.formats.IFormatReader') && ... + ~isempty(x.getCurrentFile())); +ip.parse(r); + +% Plane check +isValidPlane = @(x) isscalar(x) && ismember(x, 1 : r.getImageCount()); +ip.addRequired('iPlane', isValidPlane); + +% Optional tile arguments check +isValidX = @(x) isscalar(x) && ismember(x, 1 : r.getSizeX()); +isValidY = @(x) isscalar(x) && ismember(x, 1 : r.getSizeY()); +ip.addOptional('x', 1, isValidX); +ip.addOptional('y', 1, isValidY); +ip.addOptional('width', r.getSizeX(), isValidX); +ip.addOptional('height', r.getSizeY(), isValidY); +ip.parse(r, iPlane, varargin{:}); + +% Additional check for tile size +assert(ip.Results.x - 1 + ip.Results.width <= r.getSizeX(),... + 'MATLAB:InputParser:ArgumentFailedValidation',... + 'Invalid tile size'); +assert(ip.Results.y - 1 + ip.Results.height <= r.getSizeY(),... + 'MATLAB:InputParser:ArgumentFailedValidation',... + 'Invalid tile size'); + +% Get pixel type +pixelType = r.getPixelType(); +bpp = loci.formats.FormatTools.getBytesPerPixel(pixelType); +fp = loci.formats.FormatTools.isFloatingPoint(pixelType); +sgn = loci.formats.FormatTools.isSigned(pixelType); +little = r.isLittleEndian(); + +plane = r.openBytes(iPlane - 1, ip.Results.x - 1, ip.Results.y - 1, ... + ip.Results.width, ip.Results.height); + +% convert byte array to MATLAB image +if sgn + % can get the data directly to a matrix + I = loci.common.DataTools.makeDataArray2D(plane, ... + bpp, fp, little, ip.Results.height); +else + % get the data as a vector, either because makeDataArray2D + % is not available, or we need a vector for typecast + I = loci.common.DataTools.makeDataArray(plane, ... + bpp, fp, little); +end + +% Java does not have explicitly unsigned data types; +% hence, we must inform MATLAB when the data is unsigned +if ~sgn + % NB: arr will always be a vector here + switch class(I) + case 'int8' + I = typecast(I, 'uint8'); + case 'int16' + I = typecast(I, 'uint16'); + case 'int32' + I = typecast(I, 'uint32'); + case 'int64' + I = typecast(I, 'uint64'); + end +end + +if isvector(I) + % convert results from vector to matrix + shape = [ip.Results.width ip.Results.height]; + I = reshape(I, shape)'; +end diff --git a/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetReader.m b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetReader.m new file mode 100644 index 0000000000000000000000000000000000000000..80d105a429965219a77bda69109675aa5b95a359 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/+BioFormats/GetReader.m @@ -0,0 +1,22 @@ +function [ bfReader ] = GetReader( fullPath ) +%GETSERIESDATA Summary of this function goes here +% Detailed explanation goes here + +%% read data using bioformats +MicroscopeData.Original.BioFormats.CheckJavaMemory(); +loci.common.DebugTools.enableLogging('INFO'); + +bfReader = loci.formats.ChannelFiller(); +bfReader = loci.formats.ChannelSeparator(bfReader); +OMEXMLService = loci.formats.services.OMEXMLServiceImpl(); +bfReader.setMetadataStore(OMEXMLService.createOMEXMLMetadata()); + +try + bfReader.setId(fullPath); +catch err + warning(err.message); + bfReader = []; +end + +end + diff --git a/src/MATLAB/+MicroscopeData/+Original/CanExportFormat.m b/src/MATLAB/+MicroscopeData/+Original/CanExportFormat.m new file mode 100644 index 0000000000000000000000000000000000000000..6ad68667b7a1b8a6012d8322de3dc7b913dc4fb8 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/CanExportFormat.m @@ -0,0 +1,31 @@ +% [bCanExport,guessType] = CanExportFormat(filenames) +% +% bCanExport - True for all filenames that match a bioformats supported extension +% guessType - Cell arry of strings representing a guess at the associated file type + +function [bCanExport,guessType] = CanExportFormat(filenames) + exportFormats = MicroscopeData.Original.GetSupportedFormats(); + formatIdx = arrayfun(@(x,y)(repmat(y,length(x{1}),1)),exportFormats(:,2),(1:size(exportFormats,1)).', 'UniformOutput',false); + + allExt = vertcat(exportFormats{:,2}); + allIdx = vertcat(formatIdx{:}); + + [chkExt,idxExt] = unique(allExt); + chkIdx = allIdx(idxExt); + + % Can use strncmpi to quickly check extension matches on reversed filenames + revExt = cellfun(@(x)(x(end:-1:1)),chkExt, 'UniformOutput',false); + revNames = cellfun(@(x)(x(end:-1:1)),filenames, 'UniformOutput',false); + + bCanExport = false(length(filenames),1); + guessType = cell(length(filenames),1); + + matchExt = cellfun(@(x)(find(strncmpi(revNames,x,length(x)))),revExt,'UniformOutput',false); + matchFormatIdx = arrayfun(@(x,y)(repmat(y,length(x{1}),1)),matchExt,chkIdx,'UniformOutput',false); + + matchedIdx = vertcat(matchExt{:}); + matchedFormats = vertcat(matchFormatIdx{:}); + + bCanExport(matchedIdx) = true; + guessType(matchedIdx) = exportFormats(matchedFormats,1); +end diff --git a/src/MATLAB/+MicroscopeData/+Original/Convert2Tiffs.m b/src/MATLAB/+MicroscopeData/+Original/Convert2Tiffs.m new file mode 100644 index 0000000000000000000000000000000000000000..b3863dfb33c7f16166d2e484f03195b4f515ddd2 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/Convert2Tiffs.m @@ -0,0 +1,75 @@ +function [ im, imD ] = Convert2Tiffs( imDir, imName, outDir, overwrite, quiet, cleanName) +%[ im, imD ] = Convert2Tiffs( imDir, imName, outDir, overwrite, quiet, cleanName) + +im = []; +imD = []; + +if (~exist('overwrite','var') || isempty(overwrite)) + overwrite = false; +end +if (~exist('quiet','var') || isempty(quiet)) + quiet = false; +end + +if (~exist('imDir','var') || isempty(imDir)) + imDir = '.'; +end + +if (~exist('imName','var') || isempty(imName)) + [imName,imDir,~] = uigetfile('*.*','Choose a Microscope File to Convert'); + if (imName==0) + warning('Nothing read'); + return + end +end + +if (~exist('outDir','var') || isempty(outDir)) + outDir = uigetdir('.','Choose a folder to output to'); + if (outDir==0) + warning('No where to write!'); + return + end +end + +if (~exist('cleanName','var') || isempty(cleanName)) + cleanName = false; +end + +if (cleanName) + outDir = MicroscopeData.Helper.CreateUniqueWordedPath(outDir); +end + +[~,name,~] = fileparts(imName); + +if (~exist(fullfile(outDir,name),'dir') || overwrite) + + imD = MicroscopeData.Original.ReadMetadata(imDir,imName); + if ( isempty(imD) ) + return; + end + + if (length(imD)>1) + [~,datasetName,~] = fileparts(imName); + if (cleanName) + datasetName = MicroscopeData.Helper.SanitizeString(datasetName); + end + outDir = fullfile(outDir,datasetName); + end + + % Don't overwrite images that already exist + if (exist(fullfile(outDir,imD{1}.DatasetName),'dir') && ~overwrite) + return; + end + + im = MicroscopeData.Original.ReadImages(imDir,imName); + for i=1:length(imD) + if (cleanName) + imD{i}.DatasetName = MicroscopeData.Helper.SanitizeString(imD{i}.DatasetName); + end + if (~exist(fullfile(outDir,imD{i}.DatasetName),'dir') || overwrite) + MicroscopeData.Writer(im{i},fullfile(outDir,imD{i}.DatasetName),imD{i},[],[],[],quiet); + end + end +end +end + diff --git a/src/MATLAB/+MicroscopeData/+Original/GetSupportedFormats.m b/src/MATLAB/+MicroscopeData/+Original/GetSupportedFormats.m new file mode 100644 index 0000000000000000000000000000000000000000..aef1a75495a2ff02f6f7858499b0c87a6923fa47 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/GetSupportedFormats.m @@ -0,0 +1,294 @@ +% This is just a big list of names and associated extensions supported by bioformats; based on their website: +% https://www.openmicroscopy.org/site/support/bio-formats5.1/supported-formats.html. + +function formats = GetSupportedFormats() + extensions = {{'.gif'}; + {'.jpg'}; + {'.jp2'}; + {'.png'}; + {'.txt'}; + {'.tif';'.tiff'}; + {'.bmp'}; + + {'.sld'}; + {'.tif'}; + {'.aim'}; + {'.al3d'}; + {'.gel'}; + {'.am';'.amiramesh';'.grey';'.hx';'.labels'}; + {'.cif'}; + {'.img';'.hdr'}; + {'.png'}; + {'.afi';'.svs'}; + {'.svs'}; + {'.htd';'.pnl'}; + {'.avi'}; + {'.arf'}; + {'.exp';'.tif'}; + {'.sdt'}; + {'.1sc'}; + {'.pic';'.raw';'.xml'}; + {'.scn'}; + {'.ims'}; + { }; + {'.img'}; + {'.cr2';'.crw'}; + {'.ch5'}; + {'.c01';'.dib'}; + {'.vsi'}; + {'.xml';'.tif'}; + {'.dv';'.r3d'}; + {'.dcm';'.dicom'}; + {'.v'}; + {'.eps';'.epsi';'.ps'}; + {'.flex';'.mea';'.res'}; + {'.img'}; + {'.tiff'}; + {'.fits'}; + {'.dm3';'.dm4'}; + {'.dm2'}; + {'.naf'}; + {'.his'}; + {'.ndpi';'.ndpis'}; + {'.vms'}; + {'.txt';'.tif';'.bmp';'.jpg'}; + {'.i2i'}; + {'.ics';'.ids'}; + {'.fff'}; + {'.seq'}; + {'.ipw'}; + {'.hed';'.img'}; + {'.mod'}; + {'.liff'}; + {'.raw'}; + {'.tif'}; + {'.obf';'.msr'}; + {'.xdce';'.tif'}; + {'.frm'}; + {'.inr'}; + {'.hdr'}; + {'.ipl'}; + {'.ipm'}; + {'.dat';'.img';'.par'}; + {'.jpk'}; + {'.jpx'}; + {'.xv'}; + {'.bip'}; + {'.fli'}; + {'.msr'}; + {'.lei';'.tif'}; + {'.lif'}; + {'.scn'}; + {'.sxm'}; + {'.l2d';'.tif';'.scn'}; + {'.lim'}; + {'.tiff'}; + {'.stk';'.nd'}; + {'.tif'}; + {'.tif';'.txt';'.xml'}; + {'.mnc'}; + {'.mrw'}; + {'.mng'}; + {'.stp'}; + {'.mrc'}; + {'.nef';'.tif'}; + {'.img';'.hdr'}; + {'.tiff'}; + {'.tiff'}; + {'.nd2'}; + {'.nrrd';'.nhdr';'.raw';'.txt'}; + {'.apl';'.mtb';'.tnb';'.tif';'.obsep'}; + {'.oib';'.oif'}; + {'.tif'}; + {'.xml';'.dat';'.tif'}; + {'.tiff'}; + {'.ome.tiff'}; + {'.ome';'.ome.xml'}; + {'.top'}; + {'.pcoraw';'.rec'}; + {'.pcx'}; + {'.pds'}; + {'.im3'}; + {'.tiff';'.xml'}; + {'.tif';'.2';'.3';'.4'}; + {'.pbm';'.pgm';'.ppm'}; + {'.psd'}; + {'.tif';'.tiff'}; + {'.bin'}; + {'.pict'}; + {'.tif';'.xml';'.cfg'}; + {'.afm'}; + {'.mov'}; + {'.sm2';'.sm3'}; + { }; + {'.xqd';'.xqf'}; + {'.cxd'}; + {'.tiff'}; + { }; + {'.spi';'.stk'}; + {'.tga'}; + {'.vws'}; + {'.tfr';'.ffr';'.zfr';'.zfp';'.2fl'}; + {'.tif';'.sld';'.jpg'}; + {'.pr3'}; + {'.dat';'.hdr'}; + {'.fdf'}; + {'.hdf'}; + {'.dti'}; + {'.xys';'.html'}; + {'.mvd2'}; + {'.acff'}; + {'.wat'}; + {'.wlz'}; + {'.lms'}; + {'.xml';'.tiff'}; + {'.zvi'}; + {'.czi'}; + {'.lsm';'.mdb'}}; + + names = {'GIF (Graphics Interchange Format)'; + 'JPEG'; + 'JPEG 2000'; + 'PNG (Portable Network Graphics)'; + 'Text'; + 'TIFF (Tagged Image File Format)'; + 'Windows Bitmap'; + + '3i SlideBook'; + 'Andor Bio-Imaging Division (ABD) TIFF'; + 'AIM'; + 'Alicona 3D'; + 'Amersham Biosciences Gel'; + 'Amira Mesh'; + 'Amnis FlowSight'; + 'Analyze 7.5'; + 'Animated PNG'; + 'Aperio AFI'; + 'Aperio SVS TIFF'; + 'Applied Precision CellWorX'; + 'AVI (Audio Video Interleave)'; + 'Axon Raw Format'; + 'BD Pathway'; + 'Becker & Hickl SPCImage'; + 'Bio-Rad Gel'; + 'Bio-Rad PIC'; + 'Bio-Rad SCN'; + 'Bitplane Imaris'; + 'Bruker MRI'; + 'Burleigh'; + 'Canon DNG'; + 'CellH5'; + 'Cellomics'; + 'cellSens VSI'; + 'CellVoyager'; + 'DeltaVision'; + 'DICOM'; + 'ECAT7'; + 'EPS (Encapsulated PostScript)'; + 'Evotec/PerkinElmer Opera Flex'; + 'FEI'; + 'FEI TIFF'; + 'FITS (Flexible Image Transport System)'; + 'Gatan Digital Micrograph'; + 'Gatan Digital Micrograph 2'; + 'Hamamatsu Aquacosmos NAF'; + 'Hamamatsu HIS'; + 'Hamamatsu ndpi'; + 'Hamamatsu VMS'; + 'Hitachi S-4800'; + 'I2I'; + 'ICS (Image Cytometry Standard)'; + 'Imacon'; + 'ImagePro Sequence'; + 'ImagePro Workspace'; + 'IMAGIC'; + 'IMOD'; + 'Improvision Openlab LIFF'; + 'Improvision Openlab Raw'; + 'Improvision TIFF'; + 'Imspector OBF'; + 'InCell 1000/2000'; + 'InCell 3000'; + 'INR'; + 'Inveon'; + 'IPLab'; + 'IVision'; + 'JEOL'; + 'JPK'; + 'JPX'; + 'Khoros VIFF (Visualization Image File Format) Bitmap'; + 'Kodak BIP'; + 'Lambert Instruments FLIM'; + 'LaVision Imspector'; + 'Leica LCS LEI'; + 'Leica LAS AF LIF (Leica Image File Format)'; + 'Leica SCN'; + 'LEO'; + 'Li-Cor L2D'; + 'LIM (Laboratory Imaging/Nikon)'; + 'MetaMorph 7.5 TIFF'; + 'MetaMorph Stack (STK)'; + 'MIAS (Maia Scientific)'; + 'Micro-Manager'; + 'MINC MRI'; + 'Minolta MRW'; + 'MNG (Multiple-image Network Graphics)'; + 'Molecular Imaging'; + 'MRC (Medical Research Council)'; + 'NEF (Nikon Electronic Format)'; + 'NIfTI'; + 'Nikon Elements TIFF'; + 'Nikon EZ-C1 TIFF'; + 'Nikon NIS-Elements ND2'; + 'NRRD (Nearly Raw Raster Data)'; + 'Olympus CellR/APL'; + 'Olympus FluoView FV1000'; + 'Olympus FluoView TIFF'; + 'Olympus ScanR'; + 'Olympus SIS TIFF'; + 'OME-TIFF'; + 'OME-XML'; + 'Oxford Instruments'; + 'PCORAW'; + 'PCX (PC Paintbrush)'; + 'Perkin Elmer Densitometer'; + 'PerkinElmer Nuance'; + 'PerkinElmer Operetta'; + 'PerkinElmer UltraVIEW'; + 'Portable Any Map'; + 'Adobe Photoshop PSD'; + 'Photoshop TIFF'; + 'PicoQuant Bin'; + 'PICT (Macintosh Picture)'; + 'Prairie Technologies TIFF'; + 'Quesant'; + 'QuickTime Movie'; + 'RHK'; + 'SBIG'; + 'Seiko'; + 'SimplePCI & HCImage'; + 'SimplePCI & HCImage TIFF'; + 'SM Camera'; + 'SPIDER'; + 'Targa'; + 'TillPhotonics TillVision'; + 'Topometrix'; + 'Trestle'; + 'UBM'; + 'Unisoku'; + 'Varian FDF'; + 'Veeco AFM'; + 'VG SAM'; + 'VisiTech XYS'; + 'Volocity'; + 'Volocity Library Clipping'; + 'WA-TOP'; + 'Woolz'; + 'Zeiss Axio CSM'; + 'Zeiss AxioVision TIFF'; + 'Zeiss AxioVision ZVI (Zeiss Vision Image)'; + 'Zeiss CZI'; + 'Zeiss LSM (Laser Scanning Microscope) 510/710'}; + + formats = [names extensions]; +end diff --git a/src/MATLAB/+MicroscopeData/+Original/ReadImages.m b/src/MATLAB/+MicroscopeData/+Original/ReadImages.m new file mode 100644 index 0000000000000000000000000000000000000000..70732a9ceafa2e4164e09e56fce0c206bc236db1 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/ReadImages.m @@ -0,0 +1,37 @@ +function [ seriesImages ] = ReadImages( dirIn, fileNameIn, seriesNum ) +%READIMAGES [seriesImages] = ReadImages(dirIn, fileNameIn) +%This function will read in data generated by a microscope and return the +%image data along with the metadata associated with each of the images. +% dirIn and fileNameIn are both optional arguments where if either are +% empty, a get file dialog will appear. +% +% Microscope data may contain more than one set of images thus the output +% of this function will each be cells. Each cell will contain the image +% data for each microscope run. There are more than one "series" typically +% when there are multiple stage positions that are being captured over an +% experiment. + +%% get file and properties +if (~exist('dirIn','var') || ~exist('fileNameIn','var') || isempty(dirIn) || isempty(fileNameIn)) + [fileNameIn,dirIn,~] = uigetfile('*.*','Select Microscope Data'); + if (fileNameIn==0) + warning('No images read'); + return + end +end + +if (~exist('seriesNum','var')) + seriesNum = []; +end + +[datasetPath,datasetName,datasetExt] = fileparts(fullfile(dirIn,fileNameIn)); + +MicroscopeData.Original.BioFormats.CheckJarPath(); + +bfReader = MicroscopeData.Original.BioFormats.GetReader(fullfile(datasetPath,[datasetName,datasetExt])); + +seriesImages = MicroscopeData.Original.BioFormats.GetImages(bfReader,seriesNum); + +bfReader.close(); +end + diff --git a/src/MATLAB/+MicroscopeData/+Original/ReadMetadata.m b/src/MATLAB/+MicroscopeData/+Original/ReadMetadata.m new file mode 100644 index 0000000000000000000000000000000000000000..bebfdb2e1b8aeb0f05acef01b3975c05b26036ce --- /dev/null +++ b/src/MATLAB/+MicroscopeData/+Original/ReadMetadata.m @@ -0,0 +1,46 @@ +function [ seriesMetaData, varargout ] = ReadMetadata( dirIn, fileNameIn ) +%READMETADATA [seriesMetadata] = ReadData(dirIn, fileNameIn) +%This function will read in data generated by a microscope and return the +%image data along with the metadata associated with each of the images. +% dirIn and fileNameIn are both optional arguments where if either are +% empty, a get file dialog will appear. +% +% Microscope data may contain more than one set of images thus the output +% of this function will each be cells. Each cell will contain the associated +% metadata for each microscope run. There are more than one "series" +% typically when there are multiple stage positions that are being captured +% over an experiment. + +%% get file and properties +if (~exist('dirIn','var') || ~exist('fileNameIn','var') || isempty(dirIn) || isempty(fileNameIn)) + [fileNameIn,dirIn,~] = uigetfile('*.*','Select Microscope Data'); + if (fileNameIn==0) + warning('No images read'); + return + end +end + +[datasetPath,datasetName,datasetExt] = fileparts(fullfile(dirIn,fileNameIn)); + +MicroscopeData.Original.BioFormats.CheckJarPath(); + +bfReader = MicroscopeData.Original.BioFormats.GetReader(fullfile(datasetPath,[datasetName,datasetExt])); + +if (~isempty(bfReader)) + [seriesMetaData, omeMetaData, orgMetaData] = MicroscopeData.Original.BioFormats.GetMetadata(bfReader,datasetExt); + bfReader.close(); +else + seriesMetaData = []; + omeMetaData = []; + orgMetaData = []; +end + +if (nargout>1) + varargout{1} = omeMetaData; +end +if (nargout>2) + varargout{2} = orgMetaData; +end + +end + diff --git a/src/MATLAB/+MicroscopeData/CreateMetadata.m b/src/MATLAB/+MicroscopeData/CreateMetadata.m new file mode 100644 index 0000000000000000000000000000000000000000..d163e93a166e0052a4085b7a0b95734930d9c240 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/CreateMetadata.m @@ -0,0 +1,43 @@ +function CreateMetadata(root,imageData,quiet) + +if (isempty(root)) + if (isfield(imageData,'imageDir')) + openDir = imageData.imageDir; + else + openDir = []; + end + root = uigetdir(openDir,'Directory to Place Metadata'); + if (root==0) + return + end +end + +if (~exist(root,'dir')) + mkdir(root); +end + +if (~exist('quiet','var') || isempty(quiet)) + quiet = 0; +end + +fileName = fullfile(root,[imageData.DatasetName '.json']); + +if (~quiet) + fprintf('Creating Metadata %s...',fileName); +end + +if (isfield(imageData,'imageDir')) + imageData = rmfield(imageData,'imageDir'); +end + +jsonMetadata = Utils.CreateJSON(imageData); +fileHandle = fopen(fileName,'wt'); + +fwrite(fileHandle, jsonMetadata, 'char'); + +fclose(fileHandle); + +if (~quiet) + fprintf('Done\n'); +end +end \ No newline at end of file diff --git a/src/MATLAB/+MicroscopeData/GetEmptyMetadata.m b/src/MATLAB/+MicroscopeData/GetEmptyMetadata.m new file mode 100644 index 0000000000000000000000000000000000000000..5431d4e2aa83307b2f50041d123431f479bbe18c --- /dev/null +++ b/src/MATLAB/+MicroscopeData/GetEmptyMetadata.m @@ -0,0 +1,21 @@ +function imageData = GetEmptyMetadata() +imageData = struct('DatasetName',{''},... + 'Dimensions',{[0,0,0]},... + 'NumberOfChannels',{0},... + 'NumberOfFrames',{0},... + 'PixelPhysicalSize',{[1,1,1]},... + 'ChannelNames',{''},... + 'StartCaptureDate',{},... + 'ChannelColors',{[]},... + 'PixelFormat',{''}); + +imageData(1).DatasetName = ''; +imageData.Dimensions = [0,0,0]; +imageData.NumberOfChannels = 0; +imageData.NumberOfFrames = 0; +imageData.PixelPhysicalSize = [0,0,0]; +imageData.ChannelNames = ''; +imageData.StartCaptureDate = ''; +imageData.ChannelColors = []; +imageData.PixelFormat = 'none'; +end \ No newline at end of file diff --git a/src/MATLAB/+MicroscopeData/MakeMetadataFromFolder.m b/src/MATLAB/+MicroscopeData/MakeMetadataFromFolder.m new file mode 100644 index 0000000000000000000000000000000000000000..5bff00d2817e5fb6e9fd82e664ca101adfa98999 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/MakeMetadataFromFolder.m @@ -0,0 +1,42 @@ +function imageData = MakeMetadataFromFolder(rootDir,datasetName) + imageData = MicroscopeData.GetEmptyMetadata(); + + tifList = dir(fullfile(rootDir,[datasetName '*.tif'])); + nameList = {tifList.name}.'; + + prefixStr = regexptranslate('escape',datasetName); + + tokMatch = regexp(nameList, [prefixStr '_c(\d+)_t(\d+)_z(\d+)\.tif'], 'tokens','once'); + + bValidTok = cellfun(@(x)(length(x)==3), tokMatch); + chkTok = vertcat(tokMatch{bValidTok}); + + ctzVals = cellfun(@(x)(str2double(x)), chkTok); + + ctzMax = max(ctzVals,[],1); + + chkFilename = sprintf('%s_c%02d_t%04d_z%04d.tif', datasetName,1,1,1); + imInfo = imfinfo(fullfile(rootDir,chkFilename)); + + imageData.DatasetName = datasetName; + imageData.Dimensions = [imInfo.Width, imInfo.Height, ctzMax(3)]; + imageData.NumberOfChannels = ctzMax(1); + imageData.NumberOfFrames = ctzMax(2); + imageData.PixelPhysicalSize = [1,1,1]; + + colors = [1,0,0;... + 0,1,0;... + 0,0,1;... + 0,1,1;... + 1,0,1;... + 1,1,0]; + + for c=1:imageData.NumberOfChannels + imageData.ChannelNames = [imageData.ChannelNames; {sprintf('Channel %d',c)}]; + colidx = mod(c,size(colors,1)); + imageData.ChannelColors = vertcat(imageData.ChannelColors,colors(colidx,:)); + end + + imageData.StartCaptureDate = datestr(now,'yyyy-mm-dd HH:MM:SS'); + imageData.PixelFormat = MicroscopeData.Helper.GetPixelTypeTIF(fullfile(rootDir,chkFilename)); +end diff --git a/src/MATLAB/+MicroscopeData/ReadMetadata.m b/src/MATLAB/+MicroscopeData/ReadMetadata.m new file mode 100644 index 0000000000000000000000000000000000000000..708499193262c55613445264ce5c89a3e23bfdfa --- /dev/null +++ b/src/MATLAB/+MicroscopeData/ReadMetadata.m @@ -0,0 +1,46 @@ +function [imageData,jsonDir,jsonFile] = ReadMetadata(root,forcePrompt,promptTitle) + +imageData = []; +jsonDir = []; +jsonFile = []; + +if ( ~exist('promptTitle','var') ) + promptTitle = []; +end + +if (~exist('forcePrompt','var')) + forcePrompt = []; +end + +if (~exist('root','var') || isempty(root)) + root = ''; +end + +if (~isempty(root) && ~any(strcmp(root(end),{'\','/'})) && exist(root,'dir')) + root = fullfile(root, filesep); +end + +% This is to help when the filename might have '.' whithin them +% TODO rework this logic %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if (exist([root,'.json'],'file')) + root = [root,'.json']; +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +bAlwaysPrompt = (~isempty(forcePrompt) && forcePrompt); +if ( ~bAlwaysPrompt ) + [imageData,jsonDir,jsonFile] = MicroscopeData.ReadMetadataFile(root); +end + +bNeedData = (isempty(forcePrompt) && isempty(imageData)); +if ( bAlwaysPrompt || bNeedData ) + [fileName,rootDir,filterIndex] = uigetfile({'*.json','Metadata files (*.json)';'*.*','All Files (*.*)'},promptTitle,root); + if (filterIndex==0) + return + end + + root = fullfile(rootDir,fileName); + [imageData,jsonDir,jsonFile] = MicroscopeData.ReadMetadataFile(root); +end + +end diff --git a/src/MATLAB/+MicroscopeData/ReadMetadataFile.m b/src/MATLAB/+MicroscopeData/ReadMetadataFile.m new file mode 100644 index 0000000000000000000000000000000000000000..19c3b5fc2358b368d6b5f668fc683fa3de098479 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/ReadMetadataFile.m @@ -0,0 +1,64 @@ +function [imageData,jsonDir,jsonFile] = ReadMetadataFile(metadataPath) + imageData = []; + jsonDir = []; + jsonFile = []; + + if ( isempty(metadataPath) ) + metadataPath = ''; + end + + chkPath = findValidJSON(metadataPath); + if ( isempty(chkPath) ) + return; + end + + fileHandle = fopen(chkPath); + + jsonData = fread(fileHandle,'*char').'; + imageData = Utils.ParseJSON(jsonData); + + fclose(fileHandle); + + [rootDir,fileName] = fileparts(chkPath); + + jsonDir = rootDir; + jsonFile = [fileName '.json']; + + imageData.imageDir = jsonDir; +end + +function jsonPath = findValidJSON(chkPath) + jsonPath = []; + + [rootDir,fileName,ext] = fileparts(chkPath); + if ( ~isempty(ext) ) + if ( ~strcmpi(ext,'.json') ) + return; + end + chkPath = fullfile(rootDir,[fileName,'.json']); + elseif (~isempty(fileName)) + % case root has a file name + if (~exist(fullfile(rootDir,[fileName,'.json']),'file')) + return; + end + chkPath = fullfile(rootDir,[fileName,'.json']); + elseif (~isempty(rootDir)) + % case root is a path (e.g. \ terminated) + jsonList = dir(fullfile(rootDir,'*.json')); + if (isempty(jsonList)) + return; + end + chkPath = fullfile(rootDir,jsonList(1).name); + end + + [~,~,ext] = fileparts(chkPath); + if ( ~strcmpi(ext,'.json') ) + return; + end + + if (~exist(chkPath,'file')) + return + end + + jsonPath = chkPath; +end diff --git a/src/MATLAB/+MicroscopeData/Reader.m b/src/MATLAB/+MicroscopeData/Reader.m new file mode 100644 index 0000000000000000000000000000000000000000..abafe06ba52e612c88a0e13efd75e934c17b3476 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/Reader.m @@ -0,0 +1,62 @@ +% [IM, IMAGEDATA] = MicroscopeData.Reader([path], varargin) +% +% Optional Parameters (Key,Value pairs): +% +% imageData - Input metadata, if specified, the optional path argument is ignored +% chanList - List of channels to read +% timeRange - Range min and max times to read +% roi_xyz - x,y,z min and max roi to read +% outType - Desired output type, conversion is applied if different from image +% normalize - Normalize images on [0,1] per frame before conersion to output type +% verbose - Display verbose output and timing information +% prompt - False to completely disable prompts, true to force prompt, leave unspecified or empty for default prompt behavior +% promptTitle - Open dialog title in the case that prompting is required + +function [im, imD] = Reader(varargin) +im = []; +imD = []; + +dataTypeLookup = {'uint8';'uint16';'uint32';'uint64'; + 'int8';'int16';'int32';'int64'; + 'single';'double'; + 'logical'}; + +args = MicroscopeData.Helper.ParseReaderInputs(varargin{:}); + +loadPath = ''; +if ( ~isempty(args.imageData) ) + loadPath = args.imageData.imageDir; +elseif ( ~isempty(args.path) ) + loadPath = args.path; +end + +if ( args.prompt ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +elseif ( isempty(args.imageData) ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +else + imD = args.imageData; +end + +if (isempty(imD)) + warning('No image read!'); + return +end + +imPath = imD.imageDir; +hdf5File = fullfile(imPath,[imD.DatasetName '.h5']); +if ( exist(hdf5File,'file') ) + [im,imD] = MicroscopeData.ReaderH5('imageData',imD, 'chanList',args.chanList, 'timeRange',args.timeRange, 'roi_xyz',args.roi_xyz,... + 'outType',args.outType, 'normalize',args.normalize, 'verbose',args.verbose, 'prompt',false); + return; +end + +tifFile = fullfile(imPath,sprintf('%s_c%02d_t%04d_z%04d.tif',imD.DatasetName,1,1,1)); +if ( exist(tifFile,'file') ) + [im,imD] = MicroscopeData.ReaderTIF('imageData',imD, 'chanList',args.chanList, 'timeRange',args.timeRange, 'roi_xyz',args.roi_xyz,... + 'outType',args.outType, 'normalize',args.normalize, 'verbose',args.verbose, 'prompt',false); + return; +end + +warning('No supported image type found!'); +end diff --git a/src/MATLAB/+MicroscopeData/ReaderH5.m b/src/MATLAB/+MicroscopeData/ReaderH5.m new file mode 100644 index 0000000000000000000000000000000000000000..54a6227016c043339967af3c17cbae517867db57 --- /dev/null +++ b/src/MATLAB/+MicroscopeData/ReaderH5.m @@ -0,0 +1,141 @@ +% [IM, IMAGEDATA] = MicroscopeData.Sandbox.ReaderH5([path], varargin) +% +% Optional Parameters (Key,Value pairs): +% +% imageData - Input metadata, if specified, the optional path argument is ignored +% chanList - List of channels to read +% timeRange - Range min and max times to read +% roi_xyz - x,y,z min and max roi to read +% outType - Desired output type, conversion is applied if different from image +% normalize - Normalize images on [0,1] per frame before conersion to output type +% verbose - Display verbose output and timing information +% prompt - False to completely disable prompts, true to force prompt, leave unspecified or empty for default prompt behavior +% promptTitle - Open dialog title in the case that prompting is required + +function [im, imD] = ReaderH5(varargin) +im = []; + +dataTypeLookup = {'uint8';'uint16';'uint32';'uint64'; + 'int8';'int16';'int32';'int64'; + 'single';'double'; + 'logical'}; + +dataTypeSize = [1;2;4;8; + 1;2;4;8; + 4;8; + 1]; + +args = MicroscopeData.Helper.ParseReaderInputs(varargin{:}); + +loadPath = ''; +if ( ~isempty(args.imageData) ) + loadPath = args.imageData.imageDir; +elseif ( ~isempty(args.path) ) + loadPath = args.path; +end + +if ( args.prompt ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +elseif ( isempty(args.imageData) ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +else + imD = args.imageData; +end + +imPath = imD.imageDir; + +if (isempty(args.chanList)) + args.chanList = 1:imD.NumberOfChannels; +end + +if (isempty(args.timeRange)) + args.timeRange = [1 imD.NumberOfFrames]; +end + +if (isempty(args.roi_xyz)) + args.roi_xyz = [1 1 1; imD.Dimensions]; +end + +if (~exist(fullfile(imPath,[imD.DatasetName '.h5']),'file')) + warning('No image to read!'); + return +end + +inType = class(h5read(fullfile(imPath,[imD.DatasetName '.h5']),'/Data',[1 1 1 1 1],[1 1 1 1 1])); +inIdx = find(strcmp(inType,dataTypeLookup)); +if ( ~isempty(inIdx) ) + inBytes = dataTypeSize(inIdx); +else + error('Unsupported image type!'); +end + +if (~isfield(imD,'PixelFormat')) + imD.PixelFormat = inType; +end + +if ( isempty(args.outType) ) + if (strcmp(imD.PixelFormat,'logical')) + args.outType = 'logical'; + else + args.outType = inType; + end +elseif ( ~any(strcmp(args.outType,dataTypeLookup)) ) + error('Unsupported output type!'); +end + +outIdx = find(strcmp(args.outType,dataTypeLookup)); +if ( ~isempty(outIdx) ) + outBytes = dataTypeSize(outIdx); +end + +convert = ~strcmpi(inType,args.outType) || args.normalize; +imSize = [diff(Utils.SwapXY_RC(args.roi_xyz),1)+1,length(args.chanList),(args.timeRange(2)-args.timeRange(1)+1)]; +if (~strcmpi(args.outType,'logical')) + im = zeros(imSize, args.outType); +else + im = false(imSize); +end + +if ( args.verbose ) + orgSize = [imD.Dimensions(1),imD.Dimensions(2),imD.Dimensions(3),imD.NumberOfChannels,imD.NumberOfFrames]; + fprintf('Reading (%d,%d,%d,%d,%d) %s %5.2fMB --> Into (%d,%d,%d,%d,%d) %s %5.2fMB,',... + orgSize(2),orgSize(1),orgSize(3),orgSize(4),orgSize(5),inType,... + (prod(orgSize)*inBytes)/(1024*1024),... + imSize(1),imSize(2),imSize(3),imSize(4),imSize(5),args.outType,... + (prod(imSize)*outBytes)/(1024*1024)); +end + +tic +if ( convert ) + for c=1:length(args.chanList) + for t=1:imSize(5) + tempIm = h5read(fullfile(imPath,[imD.DatasetName '.h5']),'/Data', [Utils.SwapXY_RC(args.roi_xyz(1,:)) args.chanList(c) t+args.timeRange(1)-1], [imSize(1:3) 1 1]); + im(:,:,:,c,t) = ImUtils.ConvertType(tempIm,args.outType,args.normalize); + end + end + + clear tempIm; +else + for c=1:length(args.chanList) + im(:,:,:,c,:) = h5read(fullfile(imPath,[imD.DatasetName '.h5']),'/Data', [Utils.SwapXY_RC(args.roi_xyz(1,:)) args.chanList(c) args.timeRange(1)], [imSize(1:3) 1 imSize(5)]); + end +end +if (args.verbose) + fprintf(' took:%s\n',Utils.PrintTime(toc)); +end + +imD.Dimensions = Utils.SwapXY_RC(imSize(1:3)); +imD.NumberOfChannels = size(im,4); +imD.NumberOfFrames = size(im,5); + +if (isfield(imD,'ChannelNames') && ~isempty(imD.ChannelNames)) + imD.ChannelNames = imD.ChannelNames(args.chanList)'; +else + imD.ChannelNames = {}; +end +if (isfield(imD,'ChannelColors') && ~isempty(imD.ChannelColors)) + imD.ChannelColors = imD.ChannelColors(args.chanList,:); +else + imD.ChannelColors = []; +end +end diff --git a/src/MATLAB/+MicroscopeData/ReaderTIF.m b/src/MATLAB/+MicroscopeData/ReaderTIF.m new file mode 100644 index 0000000000000000000000000000000000000000..8141f486e07bb4d1ab1f7c40d20f400e3c6aa3fc --- /dev/null +++ b/src/MATLAB/+MicroscopeData/ReaderTIF.m @@ -0,0 +1,175 @@ +% [IM, IMAGEDATA] = MicroscopeData.Reader([path], varargin) +% +% Optional Parameters (Key,Value pairs): +% +% imageData - Input metadata, if specified, the optional path argument is ignored +% chanList - List of channels to read +% timeRange - Range min and max times to read +% roi_xyz - x,y,z min and max roi to read +% outType - Desired output type, conversion is applied if different from image +% normalize - Normalize images on [0,1] per frame before conersion to output type +% verbose - Display verbose output and timing information +% prompt - False to completely disable prompts, true to force prompt, leave unspecified or empty for default prompt behavior +% promptTitle - Open dialog title in the case that prompting is required + +function [im, imD] = ReaderTIF(varargin) +im = []; + +dataTypeLookup = {'uint8';'uint16';'uint32';'uint64'; + 'int8';'int16';'int32';'int64'; + 'single';'double'; + 'logical'}; + +dataTypeSize = [1;2;4;8; + 1;2;4;8; + 4;8; + 1]; + +args = MicroscopeData.Helper.ParseReaderInputs(varargin{:}); + +loadPath = ''; +if ( ~isempty(args.imageData) ) + loadPath = args.imageData.imageDir; +elseif ( ~isempty(args.path) ) + loadPath = args.path; +end + +if ( args.prompt ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +elseif ( isempty(args.imageData) ) + imD = MicroscopeData.ReadMetadata(loadPath,args.prompt,args.promptTitle); +else + imD = args.imageData; +end + +if (isempty(imD)) + warning('No image read!'); + return +end + +imPath = imD.imageDir; + +if (isempty(args.chanList)) + args.chanList = 1:imD.NumberOfChannels; +end + +if (isempty(args.timeRange)) + args.timeRange = [1 imD.NumberOfFrames]; +end + +if (isempty(args.roi_xyz)) + args.roi_xyz = [1 1 1; imD.Dimensions]; +end + +if (isempty(args.normalize)) + args.normalize = false; +end + +if (~exist(fullfile(imPath,sprintf('%s_c%02d_t%04d_z%04d.tif',imD.DatasetName,1,1,1)),'file')) + warning('No image to read!'); + return +end + +useROI = (nnz(args.roi_xyz(:,1:2) ~= [1 1;imD.Dimensions(1:2)]) > 0); + +chkFilename = fullfile(imPath,sprintf('%s_c%02d_t%04d_z%04d.tif',imD.DatasetName,1,1,1)); +inType = MicroscopeData.Helper.GetPixelTypeTIF(chkFilename); +inIdx = find(strcmp(inType,dataTypeLookup)); +if ( ~isempty(inIdx) ) + inBytes = dataTypeSize(inIdx); +else + error('Unsupported image type!'); +end + +if (~isfield(imD,'PixelFormat')) + imD.PixelFormat = inType; +end + +if ( isempty(args.outType) ) + if (strcmp(imD.PixelFormat,'logical')) + args.outType = 'logical'; + else + args.outType = inType; + end +elseif ( ~any(strcmp(args.outType,dataTypeLookup)) ) + error('Unsupported output type!'); +end + +outIdx = find(strcmp(args.outType,dataTypeLookup)); +if ( ~isempty(outIdx) ) + outBytes = dataTypeSize(outIdx); +end + +convert = ~strcmpi(inType,args.outType) || args.normalize; +imSize = [diff(Utils.SwapXY_RC(args.roi_xyz),1)+1,length(args.chanList),(args.timeRange(2)-args.timeRange(1)+1)]; +if (~strcmpi(args.outType,'logical')) + im = zeros(imSize, args.outType); +else + im = false(imSize); +end + +if ( args.verbose ) + orgSize = [imD.Dimensions(1),imD.Dimensions(2),imD.Dimensions(3),imD.NumberOfChannels,imD.NumberOfFrames]; + fprintf('Reading (%d,%d,%d,%d,%d) %s %5.2fMB --> Into (%d,%d,%d,%d,%d) %s %5.2fMB,',... + orgSize(1),orgSize(2),orgSize(3),orgSize(4),orgSize(5),inType,... + (prod(orgSize)*inBytes)/(1024*1024),... + imSize(1),imSize(2),imSize(3),imSize(4),imSize(5),args.outType,... + (prod(imSize)*outBytes)/(1024*1024)); +end + +if ( args.verbose ) + iter = imSize(5)*length(args.chanList)*imSize(3); + cp = Utils.CmdlnProgress(iter,true,sprintf('Reading %s...',imD.DatasetName)); + i=1; +end + +tic +for t=1:imSize(5) + timeVal = t+args.timeRange(1)-1; + for c=1:length(args.chanList) + for z=1:imSize(3) + zVal = z+args.roi_xyz(1,3)-1; + + tifName = fullfile(imPath,sprintf('%s_c%02d_t%04d_z%04d.tif',imD.DatasetName,args.chanList(c),timeVal,zVal)); + if (convert || useROI) + tempIm(:,:,z) = imread(tifName,'TIF'); + else + im(:,:,z,c,t) = imread(tifName,'TIF'); + end + + if ( args.verbose ) + cp.PrintProgress(i); + i = i+1; + end + end + + if (convert) + im(:,:,:,c,t) = ImUtils.ConvertType(... + tempIm(args.roi_xyz(1,2):args.roi_xyz(2,2),args.roi_xyz(1,1):args.roi_xyz(2,1),:),... + args.outType,args.normalize); + elseif (useROI) + im(:,:,:,c,t) = tempIm(args.roi_xyz(1,2):args.roi_xyz(2,2),args.roi_xyz(1,1):args.roi_xyz(2,1),:); + end + end +end + +if ( args.verbose ) + cp.ClearProgress(true); + fprintf(' took:%s\n',Utils.PrintTime(toc)); +end + +imD.Dimensions = Utils.SwapXY_RC(imSize(1:3)); +imD.NumberOfChannels = size(im,4); +imD.NumberOfFrames = size(im,5); + +if (isfield(imD,'ChannelNames') && ~isempty(imD.ChannelNames)) + imD.ChannelNames = imD.ChannelNames(args.chanList)'; +else + imD.ChannelNames = {}; +end +if (isfield(imD,'ChannelColors') && ~isempty(imD.ChannelColors)) + imD.ChannelColors = imD.ChannelColors(args.chanList,:); +else + imD.ChannelColors = []; +end +end diff --git a/src/MATLAB/+MicroscopeData/Writer.m b/src/MATLAB/+MicroscopeData/Writer.m new file mode 100644 index 0000000000000000000000000000000000000000..a92373d112b18ce3f5cf365aec09100039c6a08f --- /dev/null +++ b/src/MATLAB/+MicroscopeData/Writer.m @@ -0,0 +1,192 @@ +% TIFFWRITER(IM, PREFIX, IMAGEDATA, TIMELIST, CHANLIST, ZLIST, QUIET) +% TIMELIST, CHANLIST, and ZLIST are optional; pass in empty [] for the +% arguments that come prior to the one you would like to populate. +% +% IM = the image data to write. Assumes a 5-D image in the format +% (X,Y,Z,channel,time). If the image already exists and TIMELIST, CHANLIST, +% and ZLIST are populated with less then the whole image, the existing +% image is updated. If the file does not exist and the image data doesn't +% fill the whole image, the rest will be filled in with black (zeros) +% frames. +% +% PREFIX = filepath in the format ('c:\path\FilePrefix') unless there is no +% imagedata in which case it should be ('c:\path) +% IMAGEDATA = metadata that will be written to accompany the image. If you +% want this generated from the image data only, this paramater should be +% just a string representing the dataset name. See PREFIX above in such +% case. +% TIMELIST = a list of frames that the fifth dimention represents +% CHANLIST = the channels that the input image represents +% ZLIST = the z slices that the input image represents +% QUITE = suppress printing out progress + +function Writer(im, outDir, imageData, timeList, chanList, zList, quiet) +if (exist('tifflib') ~= 3) + tifflibLocation = which('/private/tifflib'); + if (isempty(tifflibLocation)) + error('tifflib does not exits on this machine!'); + end + copyfile(tifflibLocation,'.'); +end + +if (~exist('quiet','var') || isempty(quiet)) + quiet = 0; +end + +if (exist('imageData','var') && ~isempty(imageData) && isfield(imageData,'DatasetName')) + idx = strfind(imageData.DatasetName,'"'); + imageData.DatasetName(idx) = []; +else + if isstruct(imageData) + error('ImageData struct is malformed!'); + end + dName = imageData; + imageData = []; + imageData.DatasetName = dName; + + imageData.Dimensions = Utils.SwapXY_RC(size(im)); + imageData.NumberOfChannels = size(im,4); + imageData.NumberOfFrames = size(im,5); + + imageData.PixelPhysicalSize = [1.0, 1.0, 1.0]; +end + +w = whos('im'); +switch w.class + case 'uint8' + tags.SampleFormat = Tiff.SampleFormat.UInt; + tags.BitsPerSample = 8; + case 'uint16' + tags.SampleFormat = Tiff.SampleFormat.UInt; + tags.BitsPerSample = 16; + case 'uint32' + tags.SampleFormat = Tiff.SampleFormat.UInt; + tags.BitsPerSample = 32; + case 'int8' + tags.SampleFormat = Tiff.SampleFormat.Int; + tags.BitsPerSample = 8; + case 'int16' + tags.SampleFormat = Tiff.SampleFormat.Int; + tags.BitsPerSample = 16; + case 'int32' + tags.SampleFormat = Tiff.SampleFormat.Int; + tags.BitsPerSample = 32; + case 'single' + tags.SampleFormat = Tiff.SampleFormat.IEEEFP; + tags.BitsPerSample = 32; + case 'double' + tags.SampleFormat = Tiff.SampleFormat.IEEEFP; + tags.BitsPerSample = 64; + case 'logical' + imtemp = zeros(size(im),'uint8'); + imtemp(im) = 255; + im = imtemp; + clear imtemp + tags.SampleFormat = Tiff.SampleFormat.UInt; + tags.BitsPerSample = 8; + otherwise + error('Image type unsupported!'); +end + +if (~isfield(imageData,'PixelFormat')) + imageData.PixelFormat = w.class; +end + +if (exist('outDir','var') && ~isempty(outDir)) + idx = strfind(outDir,'"'); + outDir(idx) = []; +elseif (isfield(imageData,'imageDir') && ~isempty(imageData.imageDir)) + outDir = imageData.imageDir; +else + outDir = fullfile('.',imageData.DatasetName); +end + +MicroscopeData.CreateMetadata(outDir,imageData,quiet); + +if (~exist('timeList','var') || isempty(timeList)) + timeList = 1:imageData.NumberOfFrames; +else + if (max(timeList(:))>imageData.NumberOfFrames) + error('A value in timeList is greater than the number of frames in the image data!'); + end +end +if (size(im,5)~=length(timeList)) + error('There are %d frames and %d frames to be written!',size(im,5),length(timeList)); +end + +if (~exist('chanList','var') || isempty(chanList)) + chanList = 1:imageData.NumberOfChannels; +else + if (max(chanList(:))>imageData.NumberOfChannels) + error('A value in chanList is greater than the number of channels in the image data!'); + end +end +if (size(im,4)~=length(chanList)) + error('There are %d channels and %d channels to be written!',size(im,4),length(chanList)); +end + +if (~exist('zList','var') || isempty(zList)) + zList = 1:imageData.Dimensions(3); +else + if (max(zList(:))>imageData.Dimensions(3)) + error('A value in zList is greater than the z dimension in the image data!'); + end +end +if (size(im,3)~=length(zList)) + error('There are %d z images and %d z images to be written!',size(im,3),length(zList)); +end + +tags.ImageLength = size(im,1); +tags.ImageWidth = size(im,2); +tags.RowsPerStrip = size(im,2); +tags.Photometric = Tiff.Photometric.MinIsBlack; +tags.ExtraSamples = Tiff.ExtraSamples.Unspecified; +tags.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky; +tags.SamplesPerPixel = 1; +tags.Compression = Tiff.Compression.LZW; +tags.Software = 'MATLAB'; + +if (~quiet) + iter = length(timeList)*length(chanList)*length(zList); + cp = Utils.CmdlnProgress(iter,true,sprintf('Writing %s...',imageData.DatasetName)); + i=1; +end + +isBig = false; +if (tags.BitsPerSample/8 * prod(imageData.Dimensions) > 0.95*2^32) + isBig = true; +end + +tic +for t=1:length(timeList) + for c=1:length(chanList) + for z=1:length(zList) + if (isBig) + tiffObj = Tiff(fullfile(outDir,[imageData.DatasetName,sprintf('_c%02d_t%04d_z%04d.tif',chanList(c),timeList(t),zList(z))]),'w8'); + else + tiffObj = Tiff(fullfile(outDir,[imageData.DatasetName,sprintf('_c%02d_t%04d_z%04d.tif',chanList(c),timeList(t),zList(z))]),'w'); + end + tiffObj.setTag(tags); + tiffObj.write(im(:,:,z,c,t),tags); + tiffObj.close(); + +% fname = fullfile(outDir,[imageData.DatasetName,sprintf('_c%02d_t%04d_z%04d.tif',chanList(c),timeList(t),zList(z))]); +% imwrite(im(:,:,z,c,t),fname,'Compression','lzw'); + + if (~quiet) + cp.PrintProgress(i); + i = i+1; + end + + end + end +end + +if (~quiet) + cp.ClearProgress(); + fprintf('Wrote %.0fMB in %s\n',... + ((tags.BitsPerSample/8)*prod(imageData.Dimensions)*imageData.NumberOfChannels*imageData.NumberOfFrames)/(1024*1024),... + Utils.PrintTime(toc)); +end +end + diff --git a/src/MATLAB/+Utils/CmdlnProgress.m b/src/MATLAB/+Utils/CmdlnProgress.m new file mode 100644 index 0000000000000000000000000000000000000000..320d8a5f59495a18b1b8d8038ec49b0ee39b4de3 --- /dev/null +++ b/src/MATLAB/+Utils/CmdlnProgress.m @@ -0,0 +1,130 @@ +classdef CmdlnProgress<handle + %PRINTPROGRESS prints the progress and the estimated time of completion on + %the commandline. + %ENSURE that the code that is being monitored does not have internal + %printing to the commandline. + % INIT - if init is true, then the val passed in will be used as the + % denominator when figuring out the percent completed. + % When init is false, the progress is deleted from the command line and the + % internal valuse reset. + % VAL - when updating the progress enter the number that will be used as + % the numerator for the percent completed and ensure that the second + % paramater is not used or empty. + % + % Usage -- Initalize by using CmdlnProgress(number of iterations, true, optionalTitle); + % Update by using PrintProgress(current iteration); + % Clean up by using PrintProgress(0,false); + + properties + backspaces + firstTime + total + useBs + titleText + end + + methods + function obj = CmdlnProgress(iterations,useBackspace,optionalTitle) + if(~exist('useBackspace', 'var') || isempty(useBackspace)) + obj.useBs = true; + else + obj.useBs = useBackspace; + end + + if (~exist('optionalTitle','var') || isempty(optionalTitle)) + obj.titleText = ''; + else + obj.titleText = optionalTitle; + end + + obj.total = iterations; + + obj.backspaces = []; + obj.firstTime = now; + end + + function SetMaxIterations(obj,iterations) + obj.total = iterations; + end + + function PrintProgress(obj,val) + cur = now; + + prcntDone = val / obj.total; + elpsTime = (cur - obj.firstTime) * 86400; + totalSec = elpsTime / prcntDone; + finDate = obj.firstTime + (totalSec / 86400); + timeLeft = (finDate - cur)*86400; + + if (~isempty(obj.titleText)) + doneStr = sprintf('%s: %5.2f%%%% est. %s @ %s\n',... + obj.titleText,... + prcntDone*100,... + Utils.PrintTime(timeLeft),... + datestr(finDate,'HH:MM:SS dd-mmm-yy')); + else + doneStr = sprintf('%5.2f%%%% est. %s @ %s\n',... + prcntDone*100,... + Utils.PrintTime(timeLeft),... + datestr(finDate,'HH:MM:SS dd-mmm-yy')); + end + fprintf([obj.backspaces,doneStr]); + + if(obj.useBs) + obj.backspaces = repmat(sprintf('\b'),1,length(doneStr)-1); + else + fprintf('\n'); + end + end + + function ReprintProgress(obj,val) + cur = now; + + prcntDone = val / obj.total; + elpsTime = (cur - obj.firstTime) * 86400; + totalSec = elpsTime / prcntDone; + finDate = obj.firstTime + (totalSec / 86400); + timeLeft = (finDate - cur)*86400; + + if (~isempty(obj.titleText)) + doneStr = sprintf('%s: %5.2f%%%% est. %s @ %s\n',... + obj.titleText,... + prcntDone*100,... + Utils.PrintTime(timeLeft),... + datestr(finDate,'HH:MM:SS dd-mmm-yy')); + else + doneStr = sprintf('%5.2f%%%% est. %s @ %s\n',... + prcntDone*100,... + Utils.PrintTime(timeLeft),... + datestr(finDate,'HH:MM:SS dd-mmm-yy')); + end + + fprintf(doneStr); + + if(obj.useBs) + obj.backspaces = repmat(sprintf('\b'),1,length(doneStr)-1); + else + fprintf('\n'); + end + end + + function ClearProgress(obj,printTotal) + if (~isempty(obj.backspaces)) + fprintf(obj.backspaces); + end + if (exist('printTotal','var') && ~isempty(printTotal) && printTotal) + cur = now; + elpsTime = (cur - obj.firstTime) * 86400; + if (~isempty(obj.titleText)) + fprintf('%s took: %s\n',obj.titleText,Utils.PrintTime(elpsTime)) + else + fprintf('Took: %s\n',Utils.PrintTime(elpsTime)) + end + end + obj.backspaces = []; + obj.firstTime = 0; + obj.total = 0; + obj.useBs = false; + end + end +end diff --git a/src/MATLAB/+Utils/CoordToInd.m b/src/MATLAB/+Utils/CoordToInd.m new file mode 100644 index 0000000000000000000000000000000000000000..0a5ae3e55574b389e66c73e2ee1a2b5036d6387c --- /dev/null +++ b/src/MATLAB/+Utils/CoordToInd.m @@ -0,0 +1,7 @@ +% CoordToInd - Convert subscript indices to linear array indices. +% +% arrayIdx = CoordToInd(arraySize, coords) +function arrayIdx = CoordToInd(arraySize, coords) + linSize = [1 cumprod(arraySize(1:end-1))]; + arrayIdx = sum((coords-1) .* repmat(linSize, size(coords,1),1), 2) + 1; +end diff --git a/src/MATLAB/+Utils/CreateJSON.m b/src/MATLAB/+Utils/CreateJSON.m new file mode 100644 index 0000000000000000000000000000000000000000..6d9d970d990a85353036fb8f11955bced226e586 --- /dev/null +++ b/src/MATLAB/+Utils/CreateJSON.m @@ -0,0 +1,155 @@ +function json = CreateJSON(data,bWhitespace) + if ( ~exist('bWhitespace','var') ) + bWhitespace = true; + end + + spaceStruct = struct('line',{''}, 'space',{''}, 'indent',{''}); + if ( bWhitespace ) + spaceStruct = struct('line',{'\n'}, 'space',{' '}, 'indent',{' '}); + end + + if ( isstruct(data) ) + json = writeObject(data,'', spaceStruct); + else + json = writeArray(data,'', spaceStruct); + end +end + +function json = writeObject(data, spacePrefix, spaceStruct) + fields = fieldnames(data); + + fieldSep = ','; + objJSON = ''; + + fieldPattern = ['%s"%s"' spaceStruct.space ':' spaceStruct.space '%s%s']; + patternPad = length(sprintf(fieldPattern,'','','','')); + + fieldPrefix = [spacePrefix spaceStruct.indent]; + for i=1:length(fields) + if ( i == length(fields) ) + fieldSep = ''; + end + + elemPad = repmat(spaceStruct.space, 1,patternPad+length(fields{i})); + elemPrefix = [fieldPrefix elemPad]; + + valJSON = writeValue(data.(fields{i}), elemPrefix, spaceStruct); + fieldJSON = sprintf([spaceStruct.line fieldPattern], fieldPrefix, fields{i}, valJSON,fieldSep); + + objJSON = [objJSON fieldJSON]; + end + + objectPattern = ['{%s' spaceStruct.line '%s}']; + json = sprintf(objectPattern, objJSON,spacePrefix); +end + +function [json,bSingleLine] = writeArray(data, spacePrefix, spaceStruct) + bSingleLine = false; + if ( isnumeric(data) && (size(data,1) == numel(data)) ) + bSingleLine = true; + json = writeSingleLineArray(data, spaceStruct); + return; + end + + valSep = ','; + arrayJSON = ''; + + valPattern = '%s%s%s'; + + valuePrefix = [spacePrefix spaceStruct.indent]; + for i=1:size(data,1) + if ( i == size(data,1) ) + valSep = ''; + end + + arrayEntry = squashSelect(data, i); + if ( numel(arrayEntry) == 1 ) + if ( iscell(arrayEntry) ) + valJSON = writeValue(arrayEntry{1}, valuePrefix, spaceStruct); + else + valJSON = writeValue(arrayEntry, valuePrefix, spaceStruct); + end + else + [valJSON,bSingleLine] = writeArray(arrayEntry, valuePrefix, spaceStruct); + end + + % Combine brackets on one line if all the root array is a single line + if ( bSingleLine && (size(data,1) == 1) ) + json = sprintf('[%s]',valJSON); + return + else + arrayJSON = [arrayJSON sprintf([spaceStruct.line valPattern], valuePrefix, valJSON, valSep)]; + end + end + + arrayPattern = ['[%s' spaceStruct.line '%s]']; + json = sprintf(arrayPattern, arrayJSON,spacePrefix); +end + +function arrayEntry = squashSelect(arrayData, i) + if ( ndims(arrayData) == 1 ) + arrayEntry = arrayData(i); + return; + end + + dimSizes = size(arrayData); + if ( length(dimSizes) < 3 ) + dimSizes = [dimSizes 1]; + end + + arrayEntry = reshape(arrayData(i,:), dimSizes(2:end)); +end + +function json = writeSingleLineArray(data, spaceStruct) + valSep = [',' spaceStruct.space]; + arrayJSON = ''; + for i=1:length(data) + if ( i == length(data) ) + valSep = ''; + end + + valJSON = writeValue(data(i),'', spaceStruct); + arrayJSON = [arrayJSON sprintf('%s%s', valJSON,valSep)]; + end + json = sprintf('[%s]', arrayJSON); +end + +function json = writeValue(data, spacePrefix, spaceStruct) + if ( ischar(data) ) + json = sprintf('"%s"',escapeString(data)); + elseif ( iscell(data) || any(size(data) > 1) ) + json = writeArray(data, spacePrefix, spaceStruct); + elseif ( isstruct(data) ) + json = writeObject(data, spacePrefix, spaceStruct); + elseif ( islogical(data) ) + if ( data ) + json = 'true'; + else + json = 'false'; + end + elseif ( isempty(data) ) + json = 'null'; + elseif ( isnumeric(data) ) + json = num2str(data); + else + ME = MException('json:save','Cannot save unsupported type'); + ME.throw; + end +end + +function quotedStr = escapeString(inStr) + escChars = {'\' '"' char(8) char(12) char(10) char(13) char(9)}; + escStr = {'\\' '\"','\b','\f','\n','\r','\t'}; + + escMap = containers.Map(escChars,escStr); + + quotedStr = ''; + for i=1:length(inStr) + nextChar = inStr(i); + if ( isKey(escMap,nextChar) ) + nextChar = escMap(nextChar); + end + + quotedStr = [quotedStr nextChar]; + end +end diff --git a/src/MATLAB/+Utils/IndToCoord.m b/src/MATLAB/+Utils/IndToCoord.m new file mode 100644 index 0000000000000000000000000000000000000000..c9edc7b69d15af289c85832d03b66bc343b169a2 --- /dev/null +++ b/src/MATLAB/+Utils/IndToCoord.m @@ -0,0 +1,17 @@ +% IndToCoord - Convert linear array indices into a list of subscript +% indices. +% +% coords = IndToCoord(arraySize, arrayIdx) +function coords = IndToCoord(arraySize, arrayIdx) + coords = zeros(length(arrayIdx),length(arraySize)); + + linSize = [1 cumprod(arraySize)]; + partialIdx = arrayIdx; + for i = length(arraySize):-1:1 + r = rem(partialIdx-1, linSize(i)) + 1; + q = floor((partialIdx-r) / linSize(i)) + 1; + + coords(:,i) = q; + partialIdx = r; + end +end diff --git a/src/MATLAB/+Utils/ParseJSON.m b/src/MATLAB/+Utils/ParseJSON.m new file mode 100644 index 0000000000000000000000000000000000000000..b4e9f740fc105268e78cf9ed992a04e454e3e22e --- /dev/null +++ b/src/MATLAB/+Utils/ParseJSON.m @@ -0,0 +1,308 @@ +function data = ParseJSON(json) + quoteIdx = regexp(json,'\"', 'start'); + escStart = regexp(json,'\\([/\"\\bfnrt]|u[a-fA-F\d]{4})', 'start'); + + strQuotes = setdiff(quoteIdx,escStart+1); + quoteMap = containers.Map(strQuotes(1:end-1),strQuotes(2:end)); + + parsePos = ignoreSpace(json,1); + assertChar('parse', {'{','['}, json,parsePos); + + if ( json(parsePos) == '{' ) + [data parsePos] = parseObjectJSON(json,parsePos+1, quoteMap); + elseif ( json(parsePos) == '[' ) + [data parsePos] = parseArrayJSON(json,parsePos+1, quoteMap); + end + + data = postprocessObjects(data); +end + +function [objData parsePos] = parseObjectJSON(json, startPos, quoteMap) + parsePos = ignoreSpace(json,startPos); + + objData = []; + assertInStr('parseObject', '''STRING'' or ''}''', json, parsePos); + if ( json(parsePos) == '}' ) + parsePos = ignoreSpace(json,parsePos+1); + return; + end + + while ( parsePos <= length(json) ) + assertChar('parseObject', '"', json,parsePos); + [keyStr parsePos] = parseStringJSON(json,parsePos, quoteMap); + + assertChar('parseObject', ':', json,parsePos); + [value parsePos] = parseValueJSON(json,parsePos+1, quoteMap); +% if ( isempty(value) ) +% throwError('parseObject','Expecting ''STRING'', ''NUMBER'', ''NULL'', ''TRUE'', ''FALSE'', ''{'', ''[''', json, parsePos); +% end + + objData.(validFieldName(keyStr)) = value; + + assertInStr('parseObject', '''}''', json, parsePos); + if ( json(parsePos) == '}' ) + parsePos = ignoreSpace(json,parsePos+1); + return; + end + + assertChar('parseObject', {',','}'}, json,parsePos); + parsePos = ignoreSpace(json,parsePos+1); + end + + throwError('parseObject','Expecting closing ''}''', json, parsePos); +end + +function [arrayData parsePos] = parseArrayJSON(json, startPos, quoteMap) + parsePos = ignoreSpace(json,startPos); + + arrayData = cell(0,1); + if ( json(parsePos) == ']' ) + parsePos = ignoreSpace(json,parsePos+1); + return; + end + + while ( parsePos <= length(json) ) + [value parsePos] = parseValueJSON(json,parsePos, quoteMap); +% if ( isempty(value) ) +% throwError('parseArray','Expecting ''STRING'', ''NUMBER'', ''NULL'', ''TRUE'', ''FALSE'', ''{'', ''[''', json, parsePos); +% end + + arrayData{end+1} = value; + + assertInStr('parseArray', ''']''', json, parsePos); + if ( json(parsePos) == ']' ) + parsePos = ignoreSpace(json,parsePos+1); + return; + end + + assertChar('parseArray', {',',']'}, json,parsePos); + + parsePos = ignoreSpace(json,parsePos+1); + end + + throwError('parseArray','Expecting closing '']''', json, parsePos); +end + +function [valueData parsePos] = parseValueJSON(json, startPos, quoteMap) + parsePos = ignoreSpace(json,startPos); + + assertInStr('parseValue', '''STRING'', ''NUMBER'', ''NULL'', ''TRUE'', ''FALSE'', ''{'', ''[''', json, parsePos); + chkChar = json(parsePos); + + keywordMap = {'true',true; 'false',false; 'null',[]}; + + switch(chkChar) + case '[' + [valueData parsePos] = parseArrayJSON(json,parsePos+1, quoteMap); + case '{' + [valueData parsePos] = parseObjectJSON(json,parsePos+1, quoteMap); + case '"' + [valueData parsePos] = parseStringJSON(json,parsePos, quoteMap); + case {'-','0','1','2','3','4','5','6','7','8','9'} + [valueData parsePos] = parseNumberJSON(json,parsePos); + otherwise + for i=1:size(keywordMap,1) + [bMatched parsePos] = matchKeyword(json,parsePos,keywordMap{i,1}); + if ( bMatched ) + valueData = keywordMap{i,2}; + return; + end + end + throwError('parseValue','Expecting ''STRING'', ''NUMBER'', ''NULL'', ''TRUE'', ''FALSE'', ''{'', ''[''', json, parsePos); + end +end + +function [stringData parsePos] = parseStringJSON(json, startPos, quoteMap) + if ( ~isKey(quoteMap,startPos) ) + throwError('parseString','Expecting ''STRING''', json, parsePos); + end + + startQuote = startPos; + endQuote = quoteMap(startPos); + escapedStr = json(startQuote+1:endQuote-1); + + stringData = validExpandString(escapedStr, json, startPos); + parsePos = ignoreSpace(json,endQuote + 1); +end + +function [numberData parsePos] = parseNumberJSON(json, startPos) + numPad = 20; + chkEnd = min(startPos+numPad,length(json)); + + matchStr = regexp(json(startPos:chkEnd),'^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?', 'once','match'); + if ( isempty(matchStr) ) + throwError('parseNumber','Expecting ''NUMBER''', json, startPos); + end + + numberData = str2double(matchStr); + parsePos = ignoreSpace(json,startPos+length(matchStr)); +end + +function [bMatched parsePos] = matchKeyword(json,parsePos, keywordStr) + bMatched = false; + if ( length(json) < parsePos+length(keywordStr) ) + return; + end + + keywordEnd = parsePos+length(keywordStr)-1; + if ( ~strcmpi(json(parsePos:keywordEnd), keywordStr) ) + return; + end + + parsePos = ignoreSpace(json,keywordEnd+1); + bMatched = true; +end + +function fieldStr = validFieldName(inStr) + fieldStr = inStr; + bAlphaNum = isstrprop(inStr,'alphanum'); + fieldStr(~bAlphaNum) = '_'; + + if ( ~isletter(fieldStr(1)) ) + fieldStr = ['s_' fieldStr]; + end +end + +function expandStr = validExpandString(escapedStr, json,strPos) + + expandSeq = {'\"','\b','\f','\n','\r','\t'}; + unescStart = regexp(escapedStr,['[' [expandSeq{:}] ']'], 'start'); + + if ( ~isempty(unescStart) ) + throwError('parseString', 'Invalid character in string', json, strPos+unescStart(1)); + end + + expandStr = ''; + bEscape = false; + for i=1:length(escapedStr) + expandChar = escapedStr(i); + + if ( bEscape ) + bEscape = false; + if ( expandChar == 'u' ) + continue; + end + + if ( ~any(expandChar == '\"bfnrt/') ) + throwError('parseString', 'Invalid escape sequence in string', json, strPos+i); + end + + expandChar = sprintf(['\' expandChar]); + elseif ( expandChar == '\' ) + bEscape = true; + continue; + end + + expandStr(end+1) = expandChar; + end +end + +function dataOut = postprocessObjects(dataEntry) + if ( isstruct(dataEntry) ) + fields = fieldnames(dataEntry); + for i=1:length(fields) + dataEntry.(fields{i}) = postprocessObjects(dataEntry.(fields{i})); + end + elseif ( iscell(dataEntry) ) + dataEntry = postprocessArrays(dataEntry); + end + + dataOut = dataEntry; +end + +function dataEntry = postprocessArrays(dataEntry) + finalDims = length(dataEntry); + + [bCanExpand expandDims expandTypes] = canExpandArray(dataEntry); + if ( ~bCanExpand ) + finalDims = [finalDims 1]; + end + + while ( bCanExpand ) + dataEntry = reshape(dataEntry, numel(dataEntry),1); + dataEntry = vertcat(dataEntry{:}); + + finalDims = [finalDims expandDims]; + [bCanExpand expandDims expandTypes] = canExpandArray(dataEntry); + end + + dataEntry = reshape(dataEntry, finalDims); + if ( length(expandDims) > 1 ) + for i=1:numel(dataEntry) + dataEntry{i} = postprocessObjects(dataEntry{i}); + end + elseif ( length(expandTypes) == 1 && strcmpi(expandTypes{1},'double') ) + dataEntry = cell2mat(dataEntry); + end +end + +function [bCanExpand expandDims expandTypes] = canExpandArray(cellArray) + chkType = unique(cellfun(@(x)(class(x)), cellArray(:), 'UniformOutput',0)); + chkDims = unique(cellfun(@(x)(length(x)), cellArray(:))); + + bCanExpand = (length(chkDims) == 1) && (length(chkType) == 1) && strcmpi(chkType{1},'cell'); + + expandTypes = chkType; + expandDims = chkDims; +end + +function parsePos = ignoreSpace(json,startPos) + for parsePos=startPos:length(json) + if ( ~isspace(json(parsePos)) ) + return; + end + end +end + +function assertInStr(type, expect, json, parsePos) + if ( parsePos > length(json) ) + if ( isempty(expect) ) + throwError(type, 'Unexpected end of file', json, parsePos); + else + throwError(type, ['Unexpected end of file, expecting ' expect], json, parsePos); + end + end +end + +function assertChar(type, charValue, json, parsePos) + if ( ischar(charValue) ) + charValue = {charValue}; + end + + if ( parsePos > length(json) ) + throwError(type, makeAssertStr(charValue), json, parsePos) + end + + for i=1:length(charValue) + if ( json(parsePos) == charValue{i} ) + return; + end + end + + throwError(type,makeAssertStr(charValue), json, parsePos) +end + +function errStr = makeAssertStr(charValues) + errStr = 'Expecting'; + + for i=1:length(charValues)-1 + errStr = [errStr ' ''' charValues{i} ''' or']; + end + + errStr = [errStr ' ''' charValues{end} '''']; +end + +function throwError(type,message, json, pos) + contextPad = 5; + contextStart = max(pos-contextPad,1); + contextEnd = min(pos+3*contextPad,length(json)); + + arrowPos = pos-contextStart; + arrowSpace = repmat(' ',1,arrowPos); + + contextStr = json(contextStart:contextEnd); + contextStr = strtok(contextStr,sprintf('\n')); + + ME = MException(['json:' type],'Parse error: %s\n\n%s\n%s^', message,contextStr,arrowSpace); + ME.throw; +end diff --git a/src/MATLAB/+Utils/PrintTime.m b/src/MATLAB/+Utils/PrintTime.m new file mode 100644 index 0000000000000000000000000000000000000000..5060ef568877fb436d364ac24ad4675e684eea9e --- /dev/null +++ b/src/MATLAB/+Utils/PrintTime.m @@ -0,0 +1,12 @@ +function [ strOut ] = printTime( timeIn ) +%PRINTTIME takes time in sec and outputs a string in the form HH:MM:SS.ss + +hr = floor(timeIn/3600); +tmNew = timeIn - hr*3600; +mn = floor(tmNew/60); +tmNew = tmNew - mn*60; +sc = tmNew; + +strOut = sprintf('%02dh:%02dm:%05.2fs',hr,mn,sc); +end + diff --git a/src/MATLAB/+Utils/SwapXY_RC.m b/src/MATLAB/+Utils/SwapXY_RC.m new file mode 100644 index 0000000000000000000000000000000000000000..f3d1304a3b7f0e81c10b001dcd4ba414a166500a --- /dev/null +++ b/src/MATLAB/+Utils/SwapXY_RC.m @@ -0,0 +1,9 @@ +function swapCoords = SwapXY_RC(coords) + swapCoords = coords; + + if ( isvector(coords) ) + swapCoords([1 2]) = swapCoords([2 1]); + elseif ( ismatrix(coords) ) + swapCoords(:,[1 2]) = swapCoords(:,[2 1]); + end +end