diff --git a/README.md b/README.md index dc1f2b3..27d794a 100755 --- a/README.md +++ b/README.md @@ -1,146 +1,146 @@ # Import and update shoulderDB Author: Alexandre Terrier, EPFL-LBO Last update: 2019-02-19 ## Project description This repository contains Matlab codes to import anonymised clinical cases in a shoulder database (shoulderDB), and update this database with complementary clinical, radiological, morphological, or biomechanical data. This project is a collaboration between Laboratory of Biomechanical Orthopedics of the Ecole Polytechnique Federal de Lausanne (EPFL-LBO), the Orthopedic Service of the University Hospital of Lausanne (CHUV-OTR), and the Radiological Department of the University Hospital of Lausanne (CHUV-RAD). Matlab codes are tested with version 2018a. A config.txt file can be add in the main directory to locate the shoulderDB, which should be mounted locally. The config file is described and used by function openConfigFile. ## ImportSCase The import code is located in the sub-directory **importSCase**. Its description is detailed there. It currently only work for Windows. ## Update The update of the shoulderDB is performed by the function measureSCase. This function should be run from current directory. The update function is based on the matlab class ShoulderCase. **How to call the function measureSCase:** Input arguments can be one or several among: - {Empty,'GlenoidDensity', 'Update', '[NP][000-999]', '[NP]'} + {Empty,'GlenoidDensity', 'Update', '[NP][1-999]', '[NP]'} If there is no argument the function will measure every new case. The first 'N' or 'P' given as an argument will force the function to be executed on all the corresponding 'N' or 'P' cases. This will overwrite - every other ShoulderCase argument '[NP][000-999]' given before and will + every other ShoulderCase argument '[NP][1-999]' given before and will not take the next arguments into account. Examples: measureSCase('GlenoidDensity') is a valid instruction where every new case is measured including its glenoid density. measureSCase('P400','N520') is a valid instruction where the cases 'P400' and 'N520' are measured only if they have not already been measured yet (new cases). measureSCase('P400','N520','Update') is a valid instruction where the cases 'P400' and 'N520' are measured whether they have already been measured or not. measureSCase('GlenoidDensity','P400','N520','P','Update') is a valid instruction where all the 'P' cases are measured including their glenoid density only if they have not been measured yet (new P cases). measureSCase('GlenoidDensity','P400','N520','Update','P') is a valid instruction where all the 'P' cases are measured including their glenoid density whether they have been measured or not. measureSCase('Z1590','trollingArgumentLol') is a valid instruction which raises a warning about the arguments and will result in the same as executing measureSCase() i.e. every new case is calculated. ## Analyse Currently, the analysis is limited to the graphical display of an entry (SCase) of the shoulderDB, with the function plotSCase. ## Updated in 2018 * measureSCase.m: function to measure (and save) SCase (all or part of database) * dicominfoSCase.m: function to get dicom info if SCase, to replace CT/infoCT * listSCase.m: funcion to list SCase in /shoulder/data/ * plotSCase.m: function to plot SCase * csv2xlsSCase.m: fonction to write a xls file from a csv file (for SCase) * statSCase.m: script to perform statistical analyses on SCaseDB * ShoulderCase: directory with classes related to the shoulder cases * log: Directory with log files * segmentation: Directory with functions related to the automatic segementaton of the CT ## Oldies to be rewritten and/or removed * anatomy: Directory with functions related to the anatomical analysis of the shoulder CT * CT: Directory with functions for the reading of dicom field * Generated_Amira_TCL_Scripts: Ditrectory with tcl scripts for Amira, created by matlab functions * muscles: Directory with functions related to the analysis of muscle degeneration * XLS_MySQL_DB: Directory with function related to the access to the MySQL database * CylinderReferences_P403_AmiraCode.dat: To be checked ## To be cleaned * imageToolsExperimentation.m: ?? * Button 1 pushed.ps: ?? * LBODicomViewer.fig: ?? * LBODicomViewer.m: ?? * tempInfo.mat: ?? ## To do * Link to the automated segmentation * R/L identification at import * Glenoid type automated identification * Batch update * Automated muscle degeneration * Humerus bone quality * Deltoid Tuberosity Index * Restore link with MySQL * Link with musculoskeletal model * Web interface for import, update, and analyse ## Main repository should not be divided (it seems to increase complexity without gain) It might still be organized as folder, well documented in a README * One folder for class ShoulderCase * One folder for importSCase * One folder for measureSCase * One folder for anatomy (to be removed) * One folder for CT (to be removed) * One folder for muscles (integrated to class and measure, and removed) * One folder for XLS_MySQL_DB (to be integrated to import and measure, and removed) * One folder for other common files ## Contributors * Julien Ston * Antoine Dewarrat * Raphael Obrist * Killian Cosendey * Valérie Malfroy Camine * Jorge Solana-Muñoz * Bharath Narayanan * Paul Cimadomo ## Current Files and Folders at 2019.04.03, explained by JSM ## Files - *config.txt*: used by some Matlab scripts to find the working directory (usually data or dataDev). Needed when running - *csv2xlxSCase.m*: (not mine) Matlab script to transform CSV files into XLS files - *dicominfoSCase.m*: Matlab script used after new SCase importations. Check utility in ImportSCase SOP - *LBODicomViewer.fig*: (don't know this file) - *LBODicomViewer.m*: (don't know this file) - *list.txt*: temporary text file summarizing this directory content - *listSCase.m*: Matlab script that create a list of SCases - *listSCase_before20190211.m*: version of *listSCase.m* previous to 2019.02.11 (don't know the differences) - *loadDicomCTscan*: (don't know this file) - *measureSCase.m*: Matlab Script used at the end of Anatomical Measurement process (check SOP for more details) - *openConfigFile.m*: Matlab Script to read and use the text file *configFile* - *openConfigFile_before20190211*: version of *openConfigFile.m* previous to 2019.02.11 (don't know the differences) - *openLogFile.m*: (don't know this file) - *plotSCase.m*: (don't know this file) - *readDicomMetaData*: (maybe old file not longer needed) not used in *importSCase.m*, neither in *measureSCase.m* - *README.md*: This is this current file - *README_importSCaseDev_fServer.md*: Temp notes, removable - *statSCase.m*: (don't know this file) - *tempInfo.mat*: temporal file created by ImportSCase (removable) ## Directories - **anatomy**: folder containing *anatomy* measurement scripts - **CT**: rests of old *dicomCopier.m*. Old versions of current *importSCase.m*. Removable - **Generated_Amira_TCL_Scripts** this directory is used to store TCL scripts - **ImportSCase**: contains scripts needed to import new SCases into the shoulder database - **log**: contains *log* text files from some of the Matlab scripts in the current directory (*dicominfoSCase*, *listSCase*, *measureSCase* and *segmentScapula*) for debugging purposes - **muscles**: contains Matlab scripts for muscles calculations - **ShoulderCase**: contains Matlab scripts, unkown to JSM - **upsert**: I don't recognize this folder and I don't understand why it shows that I have modify it - **XLS_MySQL_DB**: contains Matlab scripts, unkown to JSM diff --git a/ShoulderCase/@ShoulderCase/ShoulderCase.m b/ShoulderCase/@ShoulderCase/ShoulderCase.m index 23999fc..a17e172 100755 --- a/ShoulderCase/@ShoulderCase/ShoulderCase.m +++ b/ShoulderCase/@ShoulderCase/ShoulderCase.m @@ -1,499 +1,499 @@ classdef ShoulderCase < handle % Properties and methods associated to the SoulderCase object. % Author: Alexandre Terrier, EPFL-LBO % Creation date: 2018-07-01 % Revision date: 2018-12-31 % TODO: % Need method to load properties (from amira, matlab, excel, SQL) % Need method to save properties (in matlab, excel, SQL) % Remove datapath from constants % properties id % id of the shoulder case, as it appears in the database (Pnnn) diagnosis treatment outcome patient shoulder study comment end % properties (Constant, Hidden = true) % dataPath = '/Volumes/shoulder/dataDev'; % path to data folder from package folder % % This should be done differently. Check where we are, and where we % % should go. % end properties (Hidden = true) dataPath dataCTPath dataAmiraPath dataMatlabPath id4C % SCaseId with P/N followed by 3 digits --> 4 char end methods function obj = ShoulderCase(SCaseId) %SHOULDERCASE Construct an instance of this class % If called with argument SCaseId, set id and path of CT if nargin > 0 obj.id = SCaseId; %{ % SCaseId must be P or N followed by 1-3 digit. if ischar(SCaseId) if strlength(SCaseId) == 4 obj.id = SCaseId; else error('id must have 4 char'); end else % error('id must be a string'); end %} else % error('id is required'); end % Initialsation obj.diagnosis = ''; obj.treatment = ''; obj.outcome = ''; obj.patient = Patient(obj); obj.shoulder = Shoulder(obj); obj.study = ''; obj.comment = ''; obj.dataCTPath = ''; obj.dataAmiraPath = ''; obj.dataMatlabPath = ''; obj.id4C = ''; % Set path of the SCase % obj.path; To be set only if varargin ~isempty end function outputArg = path(obj) % Should be private % PATH Summary of this method goes here % Detailed explanation goes here SCase = obj; SCaseId = SCase.id; % The validity of the format should be checked Pnnn or Nnnn. if (numel(regexp(SCaseId,'^[PN]\d{1,3}$')) == 0) error(['Invalid format of SCaseId: ' SCaseId{1i} '. CaseID must start with "P" or "N" and be followed by 1 to 3 digits.']); end % Set the folder of the SCaseId SCaseDirLevelPN = SCaseId(1); % Either 'P' or 'N' strLengthSCaseId = strlength(SCaseId(2:end)); if (strLengthSCaseId < 2) SCaseDirLevel2 = '0'; % Hunderets SCaseDirLevel1 = '0'; % Dozent elseif (strLengthSCaseId < 3) SCaseDirLevel2 = '0'; % Hunderets SCaseDirLevel1 = SCaseId(2); % Dozent else SCaseDirLevel2 = SCaseId(2); % Hunderets SCaseDirLevel1 = SCaseId(3); % Dozent end % Set SCaseId with fixed 3 digits after P/N (needed when id < % 10 inloading of data in amira directory. SCaseId4C = [SCaseDirLevelPN SCaseDirLevel2 SCaseDirLevel1 SCaseId(end)]; obj.id4C = SCaseId4C; % Check if a (!unique! to be done) directory exists for this SCaseId dataDir = obj.dataPath; FindSCaseIdFolder = dir([dataDir '/' SCaseDirLevelPN '/' SCaseDirLevel2 '/' SCaseDirLevel1 '/' SCaseId '*']); if (isempty(FindSCaseIdFolder)) % No directory for this SCaseId error(['Missing directory for SCaseId: ' SCaseId]); end SCaseDirLevel0 = FindSCaseIdFolder.name; SCaseDir = [dataDir '/' SCaseDirLevelPN '/' SCaseDirLevel2 '/' SCaseDirLevel1 '/' SCaseDirLevel0]; %{ %8 lines below to remove when -1 consrain avoided % Check if this SCaseId has a CT directory with '-1' postfix (preoperative % sharp CT FindCTFolder = dir([SCaseDir '/' 'CT*-1']); if (isempty(FindCTFolder)) % No CT directory for this SCaseId error(['Missing directory for SCaseId: ' SCaseId]); % ! should exist script here ! end CTDir = [SCaseDir '/' FindCTFolder.name]; %} CTdirList=dir([SCaseDir '/CT-*']); % List directories with CT iCTdirAmira = 0; iCTdir2use = 0; for iCTdirList = length(CTdirList):-1:1 % Loop from last to first (4 to 1) CTdir = CTdirList(iCTdirList); % Check that dir name ends with -1,-2,-3,-4 dirName = CTdir.name; if strcmp(dirName(end-1),'-') % Exclude postoperative 'p' CT CTnum = str2num(dirName(end)); if CTnum <= 4 % Exlude non shoulder (elbow) CT % Check that the dir contains a dicom dir CTdir = [CTdir.folder '/' CTdir.name]; dicomDir = [CTdir '/dicom']; if exist(dicomDir, 'dir') % Chech if amira dir exist amiraDir = [CTdir '/amira']; if exist(amiraDir, 'dir') % This is the CT folder to use iCTdirAmira = iCTdirList; end iCTdir2use = iCTdirList; end end end end if iCTdir2use == 0 error(['\n', SCaseID, ' has no valid dicom directory']); else if iCTdirAmira % If amira dir exist, use it iCTdir2use = iCTdirAmira; end CTdir = CTdirList(iCTdir2use); CTdirPath = [CTdir.folder '/' CTdir.name]; end CTDir = CTdirPath; amiraDir = [CTDir '/amira']; % we should check for existance matlabDir = [CTDir '/matlab']; % we should check for existance obj.dataCTPath = CTDir; obj.dataAmiraPath = amiraDir; obj.dataMatlabPath = matlabDir; outputArg = 1; % Should report on directories existence if exist(amiraDir, 'dir') ~= 7 outputArg = 0; end end function outputArg = output(obj, varargin) % Below is copy/past from previous version % This function is used to output the variable Results, used % by scapula_measurePKG the modified funcion scapula_measure. % inputArg could be as in scapula_calculation % 'density', 'References', 'obliqueSlice', 'display' Result.SCase_id = obj.id; % 1 Result.glenoid_Radius = obj.shoulder.scapula.glenoid.radius; % 2 Result.glenoid_radiusRMSE = obj.shoulder.scapula.glenoid.sphereRMSE; % 3 % 3 %Result.glenoidSphericity = ' '; %Result.glenoid_biconcave = ' '; Result.glenoid_depth = obj.shoulder.scapula.glenoid.depth; % 4 Result.glenoid_width = obj.shoulder.scapula.glenoid.width; % 5 Result.glenoid_height = obj.shoulder.scapula.glenoid.height; % 6 Result.glenoid_center_PA = obj.shoulder.scapula.glenoid.centerLocal(1); % 7 Result.glenoid_center_IS = obj.shoulder.scapula.glenoid.centerLocal(2); % 8 Result.glenoid_center_ML = obj.shoulder.scapula.glenoid.centerLocal(3); % 9 Result.glenoid_version_ampl = obj.shoulder.scapula.glenoid.versionAmpl; % 10 Result.glenoid_version_orient = obj.shoulder.scapula.glenoid.versionOrient; % 11 Result.glenoid_Version = obj.shoulder.scapula.glenoid.version; % 12 Result.glenoid_Inclination = obj.shoulder.scapula.glenoid.inclination; % 13 Result.humerus_joint_radius = ' '; % 14 Result.humeral_head_radius = obj.shoulder.humerus.radius; % 15 % Result.humerus_GHsublux_2D = ' '; % Result.humerus_SHsublux_2D = ' '; Result.humerus_GHsubluxation_ampl = obj.shoulder.humerus.GHSAmpl; % 16 Result.humerus_GHsubluxation_orient = obj.shoulder.humerus.GHSOrient; % 17 Result.humerus_SHsubluxation_ampl = obj.shoulder.humerus.SHSAmpl; % 18 Result.humerus_SHsubluxation_orient = obj.shoulder.humerus.SHSOrient; % 19 Result.scapula_CSA = obj.shoulder.scapula.acromion.criticalShoulderAngle; % radCSA; % 5 Lines below should be updated Result.scapula_CTangle = 0; %CTorientation; %20 Result.scapula_CTangleVersion = 0; % WearPlaneAngle; %21 Result.scapula_CTangleSHS = 0; % SHSPlaneAngle; % 22 Result.scapula_CTangleGHS = 0; % GHSPlaneAngle; % 23 Result.scapula_PlaneRMSE = 0; %PlaneRMSE;%24 Result.scapula_AI = obj.shoulder.scapula.acromion.acromionIndex; outputArg = Result; end function outputArg = saveMatlab(obj) % Save SCase to matlab file dir = obj.dataMatlabPath; % Create dir if not exist try if ~exist(dir, 'dir') % create directory if it does not exist mkdir(dir); end catch fprintf('Error creating the matlab directory \n'); % Should be in log end % Save SCase in matlab directoty, in a file named SCaseCNNN.m filename = 'SCase'; filename = [dir '/' filename '.mat']; try SCase = obj; save(filename, 'SCase'); outputArg = 1; catch fprintf('Error creating SCase matlab file \n'); % Should be in log outputArg = -1; end end function outputArg = appendToCSV(obj,filename) % Save SCase to csv file logFid = fopen('log/measureSCase.log', 'a'); dataDir = openConfigFile('config.txt', logFid); xlsDir = [dataDir '/Excel/xlsFromMatlab']; fid = fopen([xlsDir '/' filename],'a'); fprintf(fid,[... - obj.id ','... % SCase_id - obj.shoulder.side ','... % shoulder_side - num2str(obj.shoulder.scapula.glenoid.radius) ','... % glenoid_radius - num2str(obj.shoulder.scapula.glenoid.sphereRMSE) ','... % glenoid_sphereRMSE - num2str(obj.shoulder.scapula.glenoid.depth) ','... % glenoid_depth - num2str(obj.shoulder.scapula.glenoid.width) ','... % glenoid_width - num2str(obj.shoulder.scapula.glenoid.height) ','... % glenoid_height - num2str(obj.shoulder.scapula.glenoid.centerLocal.x) ','... % glenoid_centerPA - num2str(obj.shoulder.scapula.glenoid.centerLocal.y) ','... % glenoid_centerIS - num2str(obj.shoulder.scapula.glenoid.centerLocal.z) ','... % glenoid_centerML - num2str(obj.shoulder.scapula.glenoid.versionAmpl) ','... % glenoid_versionAmpl - num2str(obj.shoulder.scapula.glenoid.versionOrient) ','... % glenoid_versionOrient - num2str(obj.shoulder.scapula.glenoid.version) ','... % glenoid_version - num2str(obj.shoulder.scapula.glenoid.inclination) ','... % glenoid_inclination - num2str(obj.shoulder.humerus.jointRadius) ','... % humerus_jointRadius - num2str(obj.shoulder.humerus.radius) ','... % humerus_headRadius - num2str(obj.shoulder.humerus.GHSAmpl) ','... % humerus_GHSAmpl - num2str(obj.shoulder.humerus.GHSOrient) ','... % humerus_GHSOrient - num2str(obj.shoulder.humerus.SHSAmpl) ','... % humerus_SHSAmpl - num2str(obj.shoulder.humerus.SHSOrient) ','... % humerus_SHSOrient - num2str(obj.shoulder.humerus.SHSAngle) ','... % humerus_SHSAgle - num2str(obj.shoulder.humerus.SHSPA) ','... % humerus_SHSPA - num2str(obj.shoulder.humerus.SHSIS) ','... % humerus_SHSIS - num2str(obj.shoulder.scapula.acromion.AI) ','... % acromion_AI - num2str(obj.shoulder.scapula.acromion.CSA) ','... % acromion_CSA - num2str(obj.shoulder.scapula.acromion.PSA) ','... % acromion_PSA + obj.id ';'... % SCase_id + obj.shoulder.side ';'... % shoulder_side + num2str(obj.shoulder.scapula.glenoid.radius) ';'... % glenoid_radius + num2str(obj.shoulder.scapula.glenoid.sphereRMSE) ';'... % glenoid_sphereRMSE + num2str(obj.shoulder.scapula.glenoid.depth) ';'... % glenoid_depth + num2str(obj.shoulder.scapula.glenoid.width) ';'... % glenoid_width + num2str(obj.shoulder.scapula.glenoid.height) ';'... % glenoid_height + num2str(obj.shoulder.scapula.glenoid.centerLocal.x) ';'... % glenoid_centerPA + num2str(obj.shoulder.scapula.glenoid.centerLocal.y) ';'... % glenoid_centerIS + num2str(obj.shoulder.scapula.glenoid.centerLocal.z) ';'... % glenoid_centerML + num2str(obj.shoulder.scapula.glenoid.versionAmpl) ';'... % glenoid_versionAmpl + num2str(obj.shoulder.scapula.glenoid.versionOrient) ';'... % glenoid_versionOrient + num2str(obj.shoulder.scapula.glenoid.version) ';'... % glenoid_version + num2str(obj.shoulder.scapula.glenoid.inclination) ';'... % glenoid_inclination + num2str(obj.shoulder.humerus.jointRadius) ';'... % humerus_jointRadius + num2str(obj.shoulder.humerus.radius) ';'... % humerus_headRadius + num2str(obj.shoulder.humerus.GHSAmpl) ';'... % humerus_GHSAmpl + num2str(obj.shoulder.humerus.GHSOrient) ';'... % humerus_GHSOrient + num2str(obj.shoulder.humerus.SHSAmpl) ';'... % humerus_SHSAmpl + num2str(obj.shoulder.humerus.SHSOrient) ';'... % humerus_SHSOrient + num2str(obj.shoulder.humerus.SHSAngle) ';'... % humerus_SHSAgle + num2str(obj.shoulder.humerus.SHSPA) ';'... % humerus_SHSPA + num2str(obj.shoulder.humerus.SHSIS) ';'... % humerus_SHSIS + num2str(obj.shoulder.scapula.acromion.AI) ';'... % acromion_AI + num2str(obj.shoulder.scapula.acromion.CSA) ';'... % acromion_CSA + num2str(obj.shoulder.scapula.acromion.PSA) ';'... % acromion_PSA num2str(obj.shoulder.scapula.acromion.AAA) '\n'... % acromion_AAA ]); fclose(fid); fclose(logFid); outputArg = 1; end function outputArg = saveExcel(~) % Save SCase to Excel file outputArg = 1; end function outputArg = saveSQL(~) % Save SCase to MySQL database outputArg = 1; end %{ function outputArg = plot(obj) % code moved to file plot.m % Plot SCase figure; % Create figure hold on; % Superpose objects on the same figure %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Scapula % Plot the scapula surface as points % If exists, plot the segemented surface (manual) if strcmp(obj.shoulder.scapula.segmentation, 'M') points = obj.shoulder.scapula.surface.points; faces = obj.shoulder.scapula.surface.faces; x = points(:,1); y = points(:,2); z = points(:,3); trisurf(faces,x,y,z,'Facecolor','yellow','FaceAlpha',1,'EdgeColor','none','FaceLighting','gouraud'); %scatter3(x,y,z,1,'black'); % Point points end % If exists, plot the segemented surface (auto) if strcmp(obj.shoulder.scapulaAuto.segmentation, 'A') points = obj.shoulder.scapulaAuto.surface.points; faces = obj.shoulder.scapulaAuto.surface.faces; x = points(:,1); y = points(:,2); z = points(:,3); trisurf(faces,x,y,z,'Facecolor','red','FaceAlpha',0.25,'EdgeColor','none','FaceLighting','gouraud'); end % Plot scapula groove (manual) points = obj.shoulder.scapula.groove; x = points(:,1); y = points(:,2); z = points(:,3); scatter3(x,y,z,100,'blue'); % If exists, plot segments between manual and auto % Plot scapula markers points = [... obj.shoulder.scapula.angulusInferior; obj.shoulder.scapula.trigonumSpinae; obj.shoulder.scapula.processusCoracoideus; obj.shoulder.scapula.acromioClavicular; obj.shoulder.scapula.angulusAcromialis; obj.shoulder.scapula.spinoGlenoidNotch... ]; x = points(:,1); y = points(:,2); z = points(:,3); scatter3(x,y,z,100,'blue','LineWidth',2); % Plot scapular plane axisLength = 50; pt0 = obj.shoulder.scapula.coordSys.origin; pt1 = pt0 + (obj.shoulder.scapula.coordSys.ML + obj.shoulder.scapula.coordSys.IS) * axisLength ; pt2 = pt1 - obj.shoulder.scapula.coordSys.IS * pdist([pt1 ; obj.shoulder.scapula.angulusInferior]); pt3 = pt2 - obj.shoulder.scapula.coordSys.ML * pdist([pt1 ; obj.shoulder.scapula.trigonumSpinae] ); pt4 = pt3 + obj.shoulder.scapula.coordSys.IS * pdist([pt1 ; obj.shoulder.scapula.angulusInferior]); X = [pt1(1) pt2(1) pt3(1) pt4(1)]; Y = [pt1(2) pt2(2) pt3(2) pt4(2)]; Z = [pt1(3) pt2(3) pt3(3) pt4(3)]; patch(X,Y,Z,'black','FaceAlpha',0.3); % Transparent plane % Plot scapular axis pt1 = obj.shoulder.scapula.coordSys.origin; axisLength = 10 + pdist([pt1 ; obj.shoulder.scapula.trigonumSpinae]); pt2 = pt1 - obj.shoulder.scapula.coordSys.ML * axisLength; x = [pt1(1), pt2(1)]; y = [pt1(2), pt2(2)]; z = [pt1(3), pt2(3)]; plot3(x,y,z,'Color','blue','LineWidth', 5); % Plot scapular coordinate system axisLength = 50; % Length of the cood. sys. axes pt0 = obj.shoulder.scapula.coordSys.origin; pt1 = pt0 + obj.shoulder.scapula.coordSys.PA*axisLength; % X pt2 = pt0 + obj.shoulder.scapula.coordSys.IS*axisLength; % Y pt3 = pt0 + obj.shoulder.scapula.coordSys.ML*axisLength; % Z x = [pt0(1), pt1(1)]; y = [pt0(2), pt1(2)]; z = [pt0(3), pt1(3)]; plot3(x,y,z,'Color','red','LineWidth', 5); x = [pt0(1), pt2(1)]; y = [pt0(2), pt2(2)]; z = [pt0(3), pt2(3)]; plot3(x,y,z,'Color','green','LineWidth', 5); % Not plotting ML axis to avoid its superposition with scapular axis on glenoid center % x = [pt0(1), pt3(1)]; % y = [pt0(2), pt3(2)]; % z = [pt0(3), pt3(3)]; % plot3(x,y,z,'Color','blue','LineWidth', 5); % plot scapular axis on glenoid center axisLength = 50; pt1 = obj.shoulder.scapula.glenoid.center; pt2 = pt1 + obj.shoulder.scapula.coordSys.ML*axisLength; x = [pt1(1), pt2(1)]; y = [pt1(2), pt2(2)]; z = [pt1(3), pt2(3)]; plot3(x,y,z,'Color','blue','LineWidth', 5); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Glenoid % Plot the glenoid surface % We might move them in the direction of th glenoid center % instead of the ML axis points = obj.shoulder.scapula.glenoid.surface.points +... obj.shoulder.scapula.coordSys.ML * 0.2; faces = obj.shoulder.scapula.glenoid.surface.faces; x = points(:,1); y = points(:,2); z = points(:,3); %scatter3(x,y,z,1,'blach'); trisurf(faces,x,y,z,'Facecolor','none','FaceAlpha',1,'EdgeColor','yellow','FaceLighting','none'); % plot glenoid centerline pt1 = obj.shoulder.scapula.glenoid.center; pt2 = pt1 + obj.shoulder.scapula.glenoid.centerLine; x = [pt1(1), pt2(1)]; y = [pt1(2), pt2(2)]; z = [pt1(3), pt2(3)]; plot3(x,y,z,'Color','yellow','LineWidth', 5); % We might add spherical cap %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Humerus % plot humeral head sphere n = 20; % number of lines [X,Y,Z] = sphere(n); X = X*obj.shoulder.humerus.radius; Y = Y*obj.shoulder.humerus.radius; Z = Z*obj.shoulder.humerus.radius; Xc = obj.shoulder.humerus.center(1); Yc = obj.shoulder.humerus.center(2); Zc = obj.shoulder.humerus.center(3); mesh(X+Xc,Y+Yc,Z+Zc,'LineStyle','none','FaceColor','magenta','FaceAlpha',0.3,'FaceLighting','gouraud'); % hidden off; % Transparent sphere % check 'FaceLighting','gouraud', % plot humeral head centerline pt1 = obj.shoulder.scapula.glenoid.center; pt2 = obj.shoulder.humerus.center; x = [pt1(1), pt2(1)]; y = [pt1(2), pt2(2)]; z = [pt1(3), pt2(3)]; plot3(x,y,z,'Color','magenta','LineWidth', 5); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Graph properties % axis off; % remove axis lightPosition = obj.shoulder.scapula.glenoid.center + ... (... obj.shoulder.scapula.coordSys.ML + ... obj.shoulder.scapula.coordSys.IS + ... obj.shoulder.scapula.coordSys.PA ... ) * 100; light('Position',lightPosition,'Style','local'); grid on; xlabel('X (CT)'); ylabel('Y (CT)'); zlabel('Z (CT)'); axis square; axis vis3d; % fixed limits and scaling view(obj.shoulder.scapula.coordSys.IS); % view from top % Align ML axis with window horizontal axis !to do % obj.shoulder.scapula.coordSys.ML %angleDegrees = rad2deg(obj.shoulder.scapula.coordSys.ML % camroll(angleDegrees); figureHandle = gca; figureHandle.DataAspectRatio = [1 1 1]; % Same relative length of data units along each axis figureHandle.Box = 'On'; outputArg = 1; end %} end end diff --git a/measureSCase.m b/measureSCase.m index 0a7dc42..d87777b 100755 --- a/measureSCase.m +++ b/measureSCase.m @@ -1,475 +1,485 @@ function [status,message] = measureSCase(varargin) % MEASURESCASE Update the entire SCase DB with anatomical measurements. % The function can be run from (lbovenus) server with the following shell command % cd /home/shoulder/methods/matlab/database; matlab -nodisplay -nosplash -r "measureSCase;quit" % Progress can be checked through log file with following shell command % cd /home/shoulder/methods/matlab/database;tail -f log/measureSCase.log % It take ~30 min for 700 cases when run from server. % After run from server, the output file % /shoulder/data/Excel/xlsFromMatlab/anatomy.csv % must be open from Excel and saved % as xls at the same location. Excel file % /shoulder/data/Excel/ShoulderDataBase.xlsx should also be open, updated, and saved. % The script measureSCase was replacing 3 previous functions % 1) rebuildDatabaseScapulaMeasurements.m --> list of all SCases --> execute scapula_measure.m % 2) scapula_measure.m --> execute scapula_calculation and save results in Excel and SQL % 3) scapula_calculation.m --> perform anatomical analysis --> results varaiable % Input arguments can be one or several among: -% {Empty,'GlenoidDensity', 'Update', '[NP][000-999]', '[NP]'} +% {Empty,'GlenoidDensity', 'Update', '[NP][1-999]', '[NP]'} % % If there is no argument the function will measure every new case. % The first 'N' or 'P' given as an argument will force the function to be % executed on all the corresponding 'N' or 'P' cases. This will overwrite -% every other ShoulderCase argument '[NP][000-999]' given before and will +% every other ShoulderCase argument '[NP][1-999]' given before and will % not take the next arguments into account. % % Examples: % measureSCase('GlenoidDensity') % is a valid instruction where every new case is measured % including its glenoid density. % measureSCase('P400','N520') % is a valid instruction where the cases 'P400' and 'N520' are % measured only if they have not already been measured yet (new % cases). % measureSCase('P400','N520','Update') % is a valid instruction where the cases 'P400' and 'N520' are % measured whether they have already been measured or not. % measureSCase('GlenoidDensity','P400','N520','P','Update') % is a valid instruction where all the 'P' cases are measured % including their glenoid density only if they have not been % measured yet (new P cases). % measureSCase('GlenoidDensity','P400','N520','Update','P') % is a valid instruction where all the 'P' cases are measured % including their glenoid density whether they have been % measured or not. % measureSCase('Z1590','trollingArgumentLol') % is a valid instruction which raises a warning about the % arguments and will result in the same as executing % measureSCase() i.e. every new case is calculated. % Output % File /shoulder/data/Excel/xlsFromMatlab/anatomy.csv % File /shoulder/data/Excel/xlsFromMatlab/anatomy.xls (only windows) % File /home/shoulder/data/matlab/SCaseDB.mat % File {SCaseId}.mat in each {SCaseId}/{CT}/matlab directory % logs in /log/measureSCase.log % Example: measureSCase % Author: Alexandre Terrier, EPFL-LBO % Creation date: 2018-07-01 % Revision date: 2019-06-29 % TO DO: % Read first last anatomy.csv and/or SCaseDB.mat % Fill with patient and clinical data, from excel or MySQL % use argument to redo or update % Save csv within the main loop (not sure it's a good idea) % Add more message in log % Add more test to assess validity of data % Update MySQL % Rename all objects with a capital % Make it faster by not loading scapula surface if already in SCaseDB.mat % Add argument 'load' to force reload files even if they are alerady in DB % Add argument 'measure' to force re-measuring event if measurement is already in DB % Add "scapula_addmeasure.m" --> should be coded in ShoulderCase class tic; % Start stopwatch timer %% Open log file logFileID = openLogFile('measureSCase.log'); %% Set the data directory from the configuration file config.txt dataDir = openConfigFile('config.txt', logFileID); %% Location of the XLS ShoulderDatabase xlsDir = [dataDir '/Excel/xlsFromMatlab']; matlabDir = [dataDir '/matlab']; %% Get list of all SCase % Get list of SCases from varargin % Delault value for inputArg calcGlenoidDensity = false; updateCalculations = false; fprintf(logFileID, '\n\nList of SCase'); cases = {}; for argument = 1:nargin switch varargin{argument} % Test every argument in varargin. case 'GlenoidDensity' calcGlenoidDensity = true; case 'Update' updateCalculations = true; case regexp(varargin{argument},'[NP]','match') % Detect if argument is 'N' or 'P'. cases = {}; % If it's the case the for loop cases{end+1} = varargin{argument}; % ends here. break % - case regexp(varargin{argument},'[NP]\d{3}','match') % Test for a correct argument [NP]+[000-999] - cases{end+1} = varargin{argument}; + case regexp(varargin{argument},'[NP][1-9][0-9]{0,2}','match') % Test for a correct argument [NP][1-999] + cases{end+1} = varargin{argument}; % + for element = 1:length(cases)-1 % Check if the case has already been added to the case list before + if varargin{argument} == cases{element} % + cases = cases(1:end-1); % Delete the last element added to cases if it is already in the list + end + end otherwise warning(['\n "%s" is not a valid argument and has not been added to the'... ' list of cases.\n measureSCase arguments format must be "[NP]",'... - ' "[NP][000-999]", "GlenoidDensity", or "Update"'],varargin{argument}) + ' "[NP][1-999]", "GlenoidDensity", or "Update"'],varargin{argument}) end end SCaseList = listSCase(cases); %% Load current xls database (for clinical data) fprintf(logFileID, '\nLoad xls database'); addpath('XLS_MySQL_DB'); filename = [dataDir '/Excel/ShoulderDataBase.xlsx']; excelRaw = rawFromExcel(filename); % cell array excelSCaseID = excelRaw(2:end, 1); excelDiagnosis = excelRaw(2:end, 4); excelPatientHeight = excelRaw(2:end, 23); excelGlenoidWalch = excelRaw(2:end, 55); % Lines below adapted from XLS_MySQL_DB/MainExcel2XLS % Excel.diagnosisList = diagnosisListFromExcel(Excel.Raw); % Excel.treatmentList = treatmentListFromExcel(Excel.Raw); % ExcelData.Patient = patientFromExcel(excelRaw); % Structure with patient data % Excel.shoulder = shoulderFromExcel(Excel.patient, Excel.Raw); % [Excel.SCase, Excel.diagnosis, Excel.treatment, Excel.outcome, Excel.study] = SCaseFromExcel(... % Excel.shoulder, Excel.patient, Excel.diagnosisList, Excel.treatmentList, Excel.Raw); % fprintf(logFileID, ': OK'); %% Add path to ShoulderCase class addpath('ShoulderCase'); %% Instance of a ShoulderCase object if (exist('SCase','var') > 0) clear SCase; % Check for delete method end SCase = ShoulderCase; % Instanciate a ShoulderCase object +SCaseDB = ShoulderCase; SCase.dataPath = dataDir; % Set dataDir for SCase %% Start loop over SCases in SCaseList nSCase = length(SCaseList); % Number of SCases for iSCaseID = 1:nSCase SCaseID = SCaseList(iSCaseID).id; SCase(iSCaseID).id = SCaseID; SCase(iSCaseID).dataPath = dataDir; % Set dataDir for SCase percentProgress = num2str(iSCaseID/nSCase*100, '%3.1f'); fprintf(logFileID, ['\n\nSCaseID: ' SCaseID ' (' percentProgress '%%)']); % There are 3 parts within this SCase loop: % 1) Load & analyses manual data from amira % 2) Load & analyses auto data from matlab (Statistical Shape Model) % 3) Load clinical data from Excel database, and set them to SCase % 4) Save SCase in file SCaseID/matlab/SCase.mat % 1) Load & analyses manual data from amira if (~exist([SCase(iSCaseID).dataMatlabPath '\SCase.mat'],'file') || updateCalculations) % New calculation of SCase.mat or update the current calculation fprintf(logFileID, '\n Segmentation manual '); output = SCase(iSCaseID).path; % Set data path of this SCase if output % Continue if amira dir exists in SCase fprintf(logFileID, '\n Load scapula surface and landmarks'); output = SCase(iSCaseID).shoulder.scapula.load; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Set coord. syst.'); output = SCase(iSCaseID).shoulder.scapula.coordSysSet; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Load glenoid surface'); output = SCase(iSCaseID).shoulder.scapula.glenoid.load; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Measure glenoid anatomy'); output = SCase(iSCaseID).shoulder.scapula.glenoid.morphology; if output fprintf(logFileID, ': OK'); if calcGlenoidDensity fprintf(logFileID, '\n Measure glenoid density'); output = SCase(iSCaseID).shoulder.scapula.glenoid.calcDensity; if output fprintf(logFileID, ': OK'); else fprintf(logFileID, ': Could no read dicom'); end end fprintf(logFileID, '\n Load humerus data'); output = SCase(iSCaseID).shoulder.humerus.load; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Measure humerus subluxation'); output = SCase(iSCaseID).shoulder.humerus.subluxation; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Measure acromion anatomy'); % Should be run after SCase.shoulder.humerus (for AI) output = SCase(iSCaseID).shoulder.scapula.acromion.morphology; if output fprintf(logFileID, ': OK'); else fprintf(logFileID, '\n: Measure acromion anatomy error'); end % Acromion anatomy else fprintf(logFileID, '\n: Measure humerus subluxation error'); end % Subluxation humerus else fprintf(logFileID, '\n: Load humerus data error'); end % Load humerus else fprintf(logFileID, '\n: Measure glenoid anatomy error'); end % Anatomy glenoid else fprintf(logFileID, '\n: Density calculus error'); end % Load glenoid else fprintf(logFileID, '\n Scapula coord syst error '); end % Set coord. syst. else fprintf(logFileID, '\n Scapula loading error'); end % Load scapula else fprintf(logFileID, '\n No amira folder'); end % Amira path exist end % 2) Load & analyses auto data from matlab (Statistical Shape Model) fprintf(logFileID, '\n Segmentation auto '); % Load scapula surface and landmarks fprintf(logFileID, '\n Load scapula surface and landmarks'); output = SCase(iSCaseID).shoulder.scapulaAuto.loadAuto; % Load auto data from matlab directory if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Set coord. syst.'); output = SCase(iSCaseID).shoulder.scapulaAuto.coordSysSet; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Load glenoid surface'); output = SCase(iSCaseID).shoulder.scapulaAuto.glenoid.loadAuto; if output fprintf(logFileID, ': OK'); fprintf(logFileID, '\n Measure glenoid anatomy'); output = SCase(iSCaseID).shoulder.scapulaAuto.glenoid.morphology; if output fprintf(logFileID, ': OK'); else fprintf(logFileID, '\n: Error in measuring glenoid anatomy'); end else fprintf(logFileID, '\n: Error in loading glenoid surface'); end else fprintf(logFileID, '\n: Error in setting the coordonnate System'); end else fprintf(logFileID, '\n: Error in loading scapula surface and landmarks'); end % 3) Set clinical data from Excel database to SCase fprintf(logFileID, '\n Set clinical data from Excel'); % Get idx of excelRaw for SCaseID % Use cell2struct when dots are removed from excel headers idx = find(strcmp(excelRaw, SCaseID)); % Index of SCaseID in excelRaw SCase(iSCaseID).diagnosis = excelRaw{idx,4}; SCase(iSCaseID).treatment = excelRaw{idx,9}; SCase(iSCaseID).patient.gender = excelRaw{idx,19}; SCase(iSCaseID).patient.age = excelRaw{idx,21}; SCase(iSCaseID).patient.height = excelRaw{idx,23}; SCase(iSCaseID).patient.weight = excelRaw{idx,24}; SCase(iSCaseID).patient.BMI = excelRaw{idx,25}; SCase(iSCaseID).shoulder.scapula.glenoid.walch = excelRaw{idx,55}; % Patient data --> TO DO % find patient id (in SQL) corresponding to iSCaseID % % % idxSCase = find(contains(ExcelData, SCaseID)); % SCase(iSCaseID).patient.id = ExcelData(idxSCase).patient.patient_id; % SQL patient id % SCase(iSCaseID).patient.idMed = ExcelData(idxSCase).patient.IPP; % IPP to be replace by coded (anonymized) IPP from SecuTrial % SCase(iSCaseID).patient.gender = ExcelData(idxSCase).patient.gender; % SCase(iSCaseID).patient.age = []; % Age (years) at treatment, or preop CT (we might also add birthdate wi day et at 15 of the month) % SCase(iSCaseID).patient.ethnicity = ExcelData(idxSCase).patient.IPP; % SCase(iSCaseID).patient.weight = ExcelData(idxSCase).patient.weight; % SCase(iSCaseID).patient.height = ExcelData(idxSCase).patient.height; % SCase(iSCaseID).patient.BMI = ExcelData(idxSCase).pateint.BMI; % SCase(iSCaseID).patient.comment = ExcelData(idxSCase).patient.comment; % 4) Save SCase in file SCaseID/matlab/SCase.mat fprintf(logFileID, '\n Save SCase'); output = SCase(iSCaseID).saveMatlab; if output fprintf(logFileID, ': OK'); else fprintf(logFileID, ': Error'); end end % End of loop on SCaseList -%% Save the entire SCase array as a matlab file -fprintf(logFileID, '\n\nSave SCase database'); -filename = 'SCaseDB'; -filename = [matlabDir '/' filename '.mat']; -try - SCaseDB = SCase; - save(filename, 'SCaseDB'); - fprintf(logFileID, ': OK'); -catch - fprintf(logFileID, ': Error'); -end - %% Write csv file % This might be a function (SCase.csvSave()). The input would be a filename and a structure % data % Replace header and data by structure. Currently not working %{ txtFilename = [xlsDir, '/anatomy.txt']; % Name of the txt file DataStruc = struc(... 'SCase_id', SCase.id, ... 'shoulder_side', SCase.shoulder.side,... 'glenoid_radius', SCase.shoulder.scapula.glenoid.radius,... 'glenoid_sphereRMSE', SCase.shoulder.scapula.glenoid.sphereRMSE,... 'glenoid_depth', SCase.shoulder.scapula.glenoid.depth,... 'glenoid_width', SCase.shoulder.scapula.glenoid.width,... 'glenoid_height', SCase.shoulder.scapula.glenoid.height,... 'glenoid_centerPA', SCase.shoulder.scapula.glenoid.centerLocal(1),... 'glenoid_centerIS', SCase.shoulder.scapula.glenoid.centerLocal(2),... 'glenoid_centerML', SCase.shoulder.scapula.glenoid.centerLocal(3),... 'glenoid_versionAmpl', SCase.shoulder.scapula.glenoid.versionAmpl,... 'glenoid_versionOrient', SCase.shoulder.scapula.glenoid.versionOrient,... 'glenoid_version', SCase.shoulder.scapula.glenoid.version,... 'glenoid_inclination', SCase.shoulder.scapula.glenoid.inclination,... 'humerus_jointRadius', SCase.shoulder.humerus.jointRadius,... 'humerus_headRadius', SCase.shoulder.humerus.radius,... 'humerus_GHSAmpl', SCase.shoulder.humerus.GHSAmpl,... 'humerus_GHSOrient', SCase.shoulder.humerus.GHSOrient,... 'humerus_SHSAmpl', SCase.shoulder.humerus.SHSAmpl,... 'humerus_SHSOrient', SCase.shoulder.humerus.SHSOrient,... 'humerus_SHSAngle', SCase.shoulder.humerus.SHSAngle,... 'humerus_SHSPA', SCase.shoulder.humerus.SHSPA,... 'humerus_SHSIS', SCase.shoulder.humerus.SHSIS,... 'acromion_AI', SCase.shoulder.scapula.acromion.AI,... 'acromion_CSA', SCase.shoulder.scapula.acromion.CSA,... 'acromion_PS', SCase.shoulder.scapula.acromion.PS... ); DataTable = strct2table(Data); writetable(DataTable,filename); %} % Header of the csv file -fprintf(logFileID, '\n\nSave csv database'); - dataHeader = [... - 'SCase_id,' ... - 'shoulder_side,' ... - 'glenoid_radius,' ... - 'glenoid_sphereRMSE,' ... - 'glenoid_depth,' ... - 'glenoid_width,' ... - 'glenoid_height,' ... - 'glenoid_centerPA,' ... - 'glenoid_centerIS,' ... - 'glenoid_centerML,' ... - 'glenoid_versionAmpl,' ... - 'glenoid_versionOrient,' ... - 'glenoid_version,' ... - 'glenoid_inclination,' ... - 'humerus_jointRadius,' ... - 'humerus_headRadius,' ... - 'humerus_GHSAmpl,' ... - 'humerus_GHSOrient,' ... - 'humerus_SHSAmpl,' ... - 'humerus_SHSOrient,' ... - 'humerus_SHSAngle,' ... - 'humerus_SHSPA,' ... - 'humerus_SHSIS,' ... - 'acromion_AI,' ... - 'acromion_CSA,' ... - 'acromion_PSA,'... + 'SCase_id;' ... + 'shoulder_side;' ... + 'glenoid_radius;' ... + 'glenoid_sphereRMSE;' ... + 'glenoid_depth;' ... + 'glenoid_width;' ... + 'glenoid_height;' ... + 'glenoid_centerPA;' ... + 'glenoid_centerIS;' ... + 'glenoid_centerML;' ... + 'glenoid_versionAmpl;' ... + 'glenoid_versionOrient;' ... + 'glenoid_version;' ... + 'glenoid_inclination;' ... + 'humerus_jointRadius;' ... + 'humerus_headRadius;' ... + 'humerus_GHSAmpl;' ... + 'humerus_GHSOrient;' ... + 'humerus_SHSAmpl;' ... + 'humerus_SHSOrient;' ... + 'humerus_SHSAngle;' ... + 'humerus_SHSPA;' ... + 'humerus_SHSIS;' ... + 'acromion_AI;' ... + 'acromion_CSA;' ... + 'acromion_PSA;'... 'acromion_AAA\n'... ]; -updateSCase = listSCase; -fid = fopen([xlsDir, '/anatomy.csv'],'w'); %Last csv is deleted and a new one, updated is being created +updateList = listSCase; % The list of SCase to use to construct anatomy.csv and SCaseDB.mat + +fprintf(logFileID, '\n\nSave anatomy.csv file'); +% fid = fopen([xlsDir, '/anatomy.csv'],'w'); %Last csv is deleted and a new one, updated is being created +fid = fopen('Y:\dataDev/Excel/xlsFromMatlab/anatomy.csv','w') %Last csv is deleted and a new one, updated is being created fprintf(fid,dataHeader); fclose(fid); -for iSCaseID=1:length(updateSCase) - SCaseID = updateSCase(iSCaseID).id; - SCase(iSCaseID).id = SCaseID; - SCase(iSCaseID).dataPath = dataDir; - SCase(iSCaseID).path; +% The following lines re-create the SCaseDB.mat and contruct the anatomy.csv file - loadingVariable = load([SCase(iSCaseID).dataMatlabPath '/SCase.mat']); %Loading current SCase.mat content in loadingVariable - loadingVariable(1,1).SCase.appendToCSV('anatomy.csv'); +S = ShoulderCase; +for Case=1:length(updateList) + S(Case).id = updateList(Case).id; % + S(Case).dataPath = dataDir; % + S(Case).path; % Compute the SCase.dataMatlabPath + + loaded = load([S(Case).dataMatlabPath '/SCase.mat']); % Load SCase.mat + SCaseDB(Case) = loaded.SCase; + SCaseDB(Case).appendToCSV('anatomy.csv'); end fprintf(logFileID, ': OK'); + +%% Save the entire SCaseDB array as a matlab file +fprintf(logFileID, '\n\nSave SCase database'); +filename = 'SCaseDB'; +filename = [matlabDir '/' filename '.mat']; +try + save(filename, 'SCaseDB'); + fprintf(logFileID, ': OK'); +catch + fprintf(logFileID, ': Error'); +end + %fprintf(logFileID, [': ' csvFilename]); %% Write xls file (Windows only) % [~,message] = csv2xlsSCase(csvFilename); % fprintf(logFileID, message); % If run from non-windows system, only csv will be produced. The xls can be % produced by opening the csv from Excel and save as xls. % Could be a function with 1 input (cvsFilenames %{ xlsFilename = strrep(csvFilename, '.csv', '.xls'); % Replace .csv by .xls if ispc % Check if run from windows! % Read cvs file as table and write the table as xls cvsTable = readtable(csvFilename); % Get table from csv (only way to read non-numeric data) cvsCell = table2cell(cvsTable); % Tranform table cell array dataHeader = cvsTable.Properties.VariableNames; % Get header cvsCell = [dataHeader; cvsCell]; % Add header sheet = 'anatomy'; % Sheet name of the xls file [status,message] = xlswrite(xlsFilename, cvsCell, sheet); % Save xls if status fprintf(logFileId, ['\nSaved ' xlsFilename]); else fprintf(logFileId, ['\nCould not save ' xlsFilename]); fprintf(logFileId, ['\n' message.identifier]); fprintf(logFileId, ['\n' message.message]); end else fprintf(logFileId, ['\n' xlsFilename ' not saved. Open and save as xls from Excel']); end %} %% Close log file fprintf(logFileID, '\n\nSCase measured: %s', num2str(length(SCaseList))); % Number of SCases measured fprintf(logFileID, '\nElapsed time (s): %s', num2str(toc)); % stopwatch timer fprintf(logFileID, '\n'); fclose(logFileID); % Close log file %% Output of the SCase if required by argument % SCase.output % inputArg = abaqus/density/references/ % update mySQL % SCase.sqlSave status = 1; message = 'OK'; end diff --git a/test_measureSCase.m b/test_measureSCase.m index 877ffcc..9d60d1f 100755 --- a/test_measureSCase.m +++ b/test_measureSCase.m @@ -1,22 +1,28 @@ function output = test_measureSCase(varargin) -disp('Test: measureSCase("Z100","P1500","trollingArgumentLol","P400");') -measureSCase('Z100','P1500','trollingArgumentLol','P400'); +disp('Test: measureSCase("Z100","P1500","P001","trollingArgumentLol","N29");') +measureSCase('Z100','P1500','P001','trollingArgumentLol','N29'); %disp('Test: measureSCase("GlenoidDensity");') %measureSCase('GlenoidDensity'); -disp('Test: measureSCase("P400","N520");') -measureSCase('P400','N520'); +disp('Test: measureSCase("N29","N32");') +measureSCase('N29','N32'); -disp('Test: measureSCase("P400","N520","Update");') -measureSCase('P400','N520','Update'); +disp('Test: measureSCase("N29","N29");') +measureSCase('N29','N29'); -disp('Test: measureSCase("GlenoidDensity","P400","Update");') -measureSCase('GlenoidDensity','P400','Update'); +disp('Test: measureSCase("N29","N32","N34","N37");') +measureSCase('N29','N32','N34','N37'); -% disp('Test: measureSCase("GlenoidDensity","P400","N520","Update","P");') -% measureSCase('GlenoidDensity','P400','N520','Update','P'); +disp('Test: measureSCase("N29","N32","Update");') +measureSCase('N29','N32','Update'); -disp('Test: measureSCase("P400","N520","P");') -measureSCase('P400','N520','P'); +disp('Test: measureSCase("GlenoidDensity","N29","Update");') +measureSCase('GlenoidDensity','N29','Update'); + +% disp('Test: measureSCase("GlenoidDensity","N29","N32","Update","P");') +% measureSCase('GlenoidDensity','N29','N32','Update','P'); + +disp('Test: measureSCase("N29","N32","P");') +measureSCase('N29','N32','P');