diff --git a/dicominfoSCase.m b/dicominfoSCase.m index 06ea9d6..bb2bff7 100755 --- a/dicominfoSCase.m +++ b/dicominfoSCase.m @@ -1,541 +1,545 @@ function outputArg = dicominfoSCase(varargin) %DICOMINFOSCASE Get info from dicom fields and save in csv, xls, mat, and SQL % This function should be started from /shoulder/methods/matlab/database % A logfile is written during execution in % /shoulder/methods/matlab/database/log/dicominfoSCase.log % If the function is called without arguments the csv file is overwritten, % otherwise it is updated. The same applies for xls, mat and SQL. % The fonction can be run from server (lbovenus) with % cd /home/shoulder/methods/matlab/database; matlab -nojvm -nodisplay -nosplash -r "dicominfoSCase;quit" % For 700 cases, it takes less than 60 seconds (when executed from server) % Progress can be be checked throuhth log file with following shell command % cd /home/shoulder/methods/matlab/database;tail -f log/dicominfoSCase.log % This funtion replaces % /home/shoulder/methods/matlab/database/CTinfoCT.m % which only works matlab R2015a on a Windows system % Syntax: outputArg = dicominfoSCase(varargin) % % Input: % varargin: can be nothing (all cases), 'N', 'P', or SCase(s) % % Output: % outputArg: 1 if ok and 0 otherwise % % Example: CTdicomInfo, CTdicominfo('P'), CTdicominfo('P315') % Other M-files required: listSCase % Subfunctions: % See also: measureSCase % % Author: Alexandre Terrier % EPFL-LBO % Creation date: 2018-07-01 % Revision date: 2018-12-30 % % TO DO: +% When list of SCases as arguments it doesn't work properly because it +% overwrites the CSV with only the listed SCased (by JSM 2019-02-13) % Re-check for double slash in paths % We should implement the ShoulderCase class % We should save data in SCase/matlab directory % We might add family name, given name (to check anonymity) % We should first read csv and then update % We should update MySQL % Check 'Index exceeds matrix dimensions' when 'using dicomdisp' % Structure SCaseDicom should match csv (and xlx aand sql) firld names % rather than dicom %% Open log file % Check if log dir exists in working directory logDir = 'log'; if ~exist('log', 'dir') % If not create dir log mkdir('log') end logFile = [logDir '/dicominfoSCase.log']; logFileID = fopen(logFile, 'w'); fprintf(logFileID, 'dicominfoSCase log file'); fprintf(logFileID, '\nDate: '); fprintf(logFileID, datestr(datetime)); %% Set the data directory from the configuration file config.txt configFile = 'config.txt'; if ~exist(configFile, 'file') error([configFile, ' required']); end fileID = fopen(configFile,'r'); dataDir = fscanf(fileID, '%s'); % Read config file without spaces fclose(fileID); k = strfind(dataDir, 'dataDir='); % Find definition of dataDir k = k + 8; % Remove 'dataDir=' dataDir = dataDir(k:end); % Assume that config file ends with dataDir content % Check if dataDir exists if ~exist(dataDir, 'dir') fprintf(logFileID, ['Data directory not found, check ' configFile]); %error(['Data directory not found, check ' configFile]); % Select the \data directory try dataDir = uigetdir(pwd); if ~exist(dataDir, 'dir') errordlg(sprintf("%s",... "Run 'dicominfoSCase.m' manually and select the data directory"),... "Error: data directory not found.") end catch mistake errordlg(sprintf("%s\n%s",... "Run 'dicominfoSCase.m' manually and select the data directory",... mistake.message),"Error: data directory not found.") end end % Location of the XLS ShoulderDatabase xlsDataDir = strcat(dataDir, '/Excel/xlsFromMatlab'); %% Get list of SCases if nargin == 0 % dicominfoSCase called without argument - SCaseList = listSCase; % All SCases in data dir with valid dicom CT dir + SCaseList = listSCase(dataDir); % All SCases in data dir with valid dicom CT dir update = 0; elseif nargin == 1 update = 1; inputArg = varargin{1}; - SCaseList = listSCase(inputArg); % SCase related to inputArg ('N', 'P', or list) + SCaseList = listSCase(dataDir,inputArg); % SCase related to inputArg ('N', 'P', or list) end %% Read dicom info tic; % Start stopwatch timer fprintf(logFileID, '\n\nRead dicom meta data\n'); % log file message % Struct below correponds to regular dicom field names, except SCase_ID SCaseDicom = struct(... 'SCase_id' ,[],... 'PatientID' ,[],... 'PatientSex' ,[],... 'PatientAge' ,[],... 'PatientBirthDate' ,[],... 'PatientSize' ,[],... 'PatientWeight' ,[],... 'PatientFamilyName' ,[],... 'PatientGivenName' ,[],... 'AcquisitionDate' ,[],... 'ConvolutionKernel' ,[],... 'PixelSpacing' ,[],... 'SpacingBetweenSlices' ,[],... 'SliceThickness' ,[],... 'KVP' ,[],... 'XrayTubeCurrent' ,[],... 'InstitutionName' ,[],... 'StudyDescription' ,[],... 'SeriesDescription' ,[],... % Might describe shoulder side 'Manufacturer' ,[],... 'ManufacturerModelName' ,[],... 'ProtocolName' ,[]... ); % Loop over all SCases collected above for SCase_idx=1:1:numel(SCaseList) SCase_id = SCaseList(SCase_idx).id; dicomDir = [SCaseList(SCase_idx).dir '/dicom']; dicomFiles = dir([dicomDir, '/*.dcm']); dicomFile1 = [dicomDir, '/', dicomFiles(10).name]; % seems better to check 10th than 1st ! dicomInfoError = false; fprintf(logFileID, '\n'); fprintf(logFileID, SCaseList(SCase_idx).id); fprintf(logFileID, ': '); % Try first with matlab function dicominfo try dicomInfo1 = dicominfo(dicomFile1, 'UseVRHeuristic', true); fprintf(logFileID, 'dicominfo ok'); % If dicominfo didn't worked, try with matlab function dicomdisp catch ME dicomInfoError = true; fprintf(logFileID, '\n'); fprintf(logFileID, 'dicominfo error --> using dicomdisp'); fprintf(logFileID, ['\n' ME.message]); dicomdispStr = evalc('dicomdisp(dicomFile1)'); end % If dicominfo didn't worked, continue with output of dicomdisp if dicomInfoError % get dicom info from dicomdisp() PatientID = extractAfter(dicomdispStr,'PatientID'); PatientID = extractAfter(PatientID,'['); PatientID = extractBefore(PatientID,']'); PatientSex = extractAfter(dicomdispStr,'PatientSex'); PatientSex = extractAfter(PatientSex,'['); PatientSex = extractBefore(PatientSex,']'); PatientAge = extractAfter(dicomdispStr,'PatientAge'); PatientAge = extractAfter(PatientAge,'['); PatientAge = extractBefore(PatientAge,']'); PatientBirthDate = extractAfter(dicomdispStr,'PatientBirthDate'); PatientBirthDate = extractAfter(PatientBirthDate,'['); PatientBirthDate = extractBefore(PatientBirthDate,']'); PatientSize = extractAfter(dicomdispStr,'PatientSize'); PatientSize = extractAfter(PatientSize,'['); PatientSize = extractBefore(PatientSize,']'); PatientSize = str2double(PatientSize); PatientWeight = extractAfter(dicomdispStr,'PatientWeight'); PatientWeight = extractAfter(PatientWeight,'['); PatientWeight = extractBefore(PatientWeight,']'); PatientWeight = str2double(PatientWeight); AcquisitionDate = extractAfter(dicomdispStr,'AcquisitionDate'); AcquisitionDate = extractAfter(AcquisitionDate,'['); AcquisitionDate = extractBefore(AcquisitionDate,']'); ConvolutionKernel = extractAfter(dicomdispStr,'ConvolutionKernel'); ConvolutionKernel = extractAfter(ConvolutionKernel,'['); ConvolutionKernel = extractBefore(ConvolutionKernel,']'); PixelSpacing = extractAfter(dicomdispStr,'PixelSpacing'); PixelSpacing = extractAfter(PixelSpacing,'['); PixelSpacing = extractBefore(PixelSpacing,'\'); SpacingBetweenSlices = extractAfter(dicomdispStr,'SpacingBetweenSlices'); SpacingBetweenSlices = extractAfter(SpacingBetweenSlices,'['); SpacingBetweenSlices = extractBefore(SpacingBetweenSlices,']'); SpacingBetweenSlices = str2double(SpacingBetweenSlices); SliceThickness = extractAfter(dicomdispStr,'SliceThickness'); SliceThickness = extractAfter(SliceThickness,'['); SliceThickness = extractBefore(SliceThickness,']'); SliceThickness = str2double(SliceThickness); KVP = extractAfter(dicomdispStr,'KVP'); KVP = extractAfter(KVP,'['); KVP = extractBefore(KVP,']'); KVP = str2double(KVP); XrayTubeCurrent = extractAfter(dicomdispStr,'XrayTubeCurrent'); XrayTubeCurrent = extractAfter(XrayTubeCurrent,'['); XrayTubeCurrent = extractBefore(XrayTubeCurrent,']'); XrayTubeCurrent = str2double(XrayTubeCurrent); InstitutionName = extractAfter(dicomdispStr,'InstitutionName'); InstitutionName = extractAfter(InstitutionName,'['); InstitutionName = extractBefore(InstitutionName,']'); Manufacturer = extractAfter(dicomdispStr,'Manufacturer'); Manufacturer = extractAfter(Manufacturer,'['); Manufacturer = extractBefore(Manufacturer,']'); ManufacturerModelName = extractAfter(dicomdispStr,'ManufacturerModelName'); ManufacturerModelName = extractAfter(ManufacturerModelName,'['); ManufacturerModelName = extractBefore(ManufacturerModelName,']'); ProtocolName = extractAfter(dicomdispStr,'ProtocolName'); ProtocolName = extractAfter(ProtocolName,'['); ProtocolName = extractBefore(ProtocolName,']'); SeriesDescription = extractAfter(dicomdispStr,'SeriesDescription'); SeriesDescription = extractAfter(SeriesDescription,'['); SeriesDescription = extractBefore(SeriesDescription,']'); else % dicomInfo exists if isfield(dicomInfo1,'PatientID') PatientID = dicomInfo1.PatientID; else PatientID = ''; end if isfield(dicomInfo1,'PatientSex') PatientSex = dicomInfo1.PatientSex; else PatientSex = ''; end if isfield(dicomInfo1,'PatientBirthDate') PatientBirthDate = dicomInfo1.PatientBirthDate; else PatientBirthDate = ''; end if isfield(dicomInfo1,'PatientAge') PatientAge = dicomInfo1.PatientAge; else PatientAge = ''; end if isfield(dicomInfo1,'PatientSize') PatientSize = dicomInfo1.PatientSize; else PatientSize = ''; end if isfield(dicomInfo1,'PatientWeight') PatientWeight = dicomInfo1.PatientWeight; else PatientWeight=''; end if isfield(dicomInfo1,'AcquisitionDate') AcquisitionDate = dicomInfo1.AcquisitionDate; else AcquisitionDate = ''; end if isfield(dicomInfo1,'ConvolutionKernel') ConvolutionKernel = dicomInfo1.ConvolutionKernel; else ConvolutionKernel = ''; end if isfield(dicomInfo1,'PixelSpacing') PixelSpacing = dicomInfo1.PixelSpacing(1); PixelSpacing = num2str(PixelSpacing); % PixelSpacing = [,PixelSpacing,]; % Commented because I (AT) don't % understand the effect else PixelSpacing=''; end if isfield(dicomInfo1,'SpacingBetweenSlices') SpacingBetweenSlices = dicomInfo1.SpacingBetweenSlices; else % SpacingBetweenSlices estimated from 2 slices % fprintf(logFileID, '\n'); fprintf(logFileID, ', SpacingBetweenSlices estimated from 2 slices'); dicomFile2 = [dicomDir, '/', dicomFiles(11).name]; dicomInfo2 = dicominfo(dicomFile2, 'UseVRHeuristic', true); ImagePositionPatient1 = dicomInfo1.ImagePositionPatient(3); ImagePositionPatient2 = dicomInfo2.ImagePositionPatient(3); posdDiff = abs(ImagePositionPatient2 - ImagePositionPatient1); InstanceNumber1 = dicomInfo1.InstanceNumber; InstanceNumber2 = dicomInfo2.InstanceNumber; InstanceNumberDiff = abs(InstanceNumber2 - InstanceNumber1); if InstanceNumberDiff == 0 InstanceNumberDiff=1; end SpacingBetweenSlices = posdDiff/InstanceNumberDiff; end if isfield(dicomInfo1,'SliceThickness') SliceThickness = dicomInfo1.SliceThickness; else SliceThickness=''; end if isfield(dicomInfo1,'KVP') KVP = dicomInfo1.KVP; else KVP= ''; end if isfield(dicomInfo1,'XrayTubeCurrent') XrayTubeCurrent = dicomInfo1.XrayTubeCurrent; else XrayTubeCurrent = ''; end if isfield(dicomInfo1,'InstitutionName') InstitutionName = dicomInfo1.InstitutionName; else InstitutionName = ''; end if isfield(dicomInfo1,'Manufacturer') Manufacturer = dicomInfo1.Manufacturer; else Manufacturer =''; end if isfield(dicomInfo1,'ManufacturerModelName') ManufacturerModelName = dicomInfo1.ManufacturerModelName; else ManufacturerModelName = ''; end if isfield(dicomInfo1,'ProtocolName') ProtocolName = dicomInfo1.ProtocolName; else ProtocolName = ''; end if isfield(dicomInfo1,'SeriesDescription') SeriesDescription = dicomInfo1.SeriesDescription; else SeriesDescription = ''; end % End of reading dimcom info end %% Format data PatientBirthDate = datetime(PatientBirthDate,'InputFormat','yyyyMMdd'); PatientBirthDate = datestr(PatientBirthDate, 'yyyy-mm-dd'); PatientAge = str2double(strtok(PatientAge,'Y')); PatientSize = round(100 * PatientSize); if PatientSize == 0 PatientSize = ''; end PatientWeight = round(PatientWeight); if PatientWeight == 0 PatientWeight = ''; end AcquisitionDate = datetime(AcquisitionDate,'InputFormat','yyyyMMdd'); AcquisitionDate = datestr(AcquisitionDate, 'yyyy-mm-dd'); PixelSpacing = str2double(PixelSpacing); PixelSpacing = round(PixelSpacing, 3); SpacingBetweenSlices = round(SpacingBetweenSlices, 3); SliceThickness = round(SliceThickness, 3); KVP = round(KVP); XrayTubeCurrent = round(XrayTubeCurrent); % Replace any ',' by ' ' to avoid problem in csv InstitutionName = strrep(InstitutionName,',',' '); Manufacturer = strrep(Manufacturer,',',' '); ManufacturerModelName = strrep(ManufacturerModelName,',',' '); ProtocolName = strrep(ProtocolName,',',' '); SeriesDescription = strrep(SeriesDescription,',',' '); %% Add data in SCaseDicom structure % This could be directly in SCase.CT object SCaseDicom(SCase_idx).id = SCase_id; SCaseDicom(SCase_idx).PatientID = PatientID; SCaseDicom(SCase_idx).PatientSex = PatientSex; SCaseDicom(SCase_idx).PatientBirthDate = PatientBirthDate; SCaseDicom(SCase_idx).PatientAge = PatientAge; SCaseDicom(SCase_idx).PatientSize = PatientSize; SCaseDicom(SCase_idx).PatientWeight = PatientWeight; SCaseDicom(SCase_idx).AcquisitionDate = AcquisitionDate; SCaseDicom(SCase_idx).ConvolutionKernel = ConvolutionKernel; SCaseDicom(SCase_idx).PixelSpacing = PixelSpacing; SCaseDicom(SCase_idx).SpacingBetweenSlices = SpacingBetweenSlices; SCaseDicom(SCase_idx).SliceThickness = SliceThickness; SCaseDicom(SCase_idx).KVP = KVP; SCaseDicom(SCase_idx).XrayTubeCurrent = XrayTubeCurrent; SCaseDicom(SCase_idx).InstitutionName = InstitutionName; SCaseDicom(SCase_idx).Manufacturer = Manufacturer; SCaseDicom(SCase_idx).ManufacturerModelName = ManufacturerModelName; SCaseDicom(SCase_idx).ProtocolName = ProtocolName; SCaseDicom(SCase_idx).SeriesDescription = SeriesDescription; end fprintf(logFileID, '\n\nElapsed time (s): %s', num2str(toc)); % stopwatch timer %% Write csv file % This might be a function. The input would be a filename and a structure % data dataHeader = [... 'SCase_ID,' ... 'patient_IPP,' ... 'patient_gender,' ... 'patient_birthDate,' ... 'patient_age,' ... 'patient_height,' ... 'patient_weight,' ... 'CT_date,' ... 'CT_kernel,' ... 'CT_pixelSize,' ... 'CT_sliceSpacing,' ... 'CT_sliceThickness,' ... 'CT_tension,' ... 'CT_current,' ... 'CT_institution,' ... 'CT_manufacturer,' ... 'CT_model,' ... 'CT_protocol,' ... 'CT_SeriesDescription' ... ]; csvFilename = strcat(xlsDataDir, '/CTdicomInfo.csv'); % If dicominfoSCase is called with argument, we have to update the csv. if update % Read the csv and put data in a structure like SCaseDicom % We should chech that file exists csvTable = readtable(csvFilename,'DatetimeType', 'text'); % If 'DatetimeType' is specified as 'text', then the type for imported % date and time data depends on the value specified in the 'TextType' % parameter: If 'TextType' is 'char', then readtable returns dates as a % cell array of character vectors. If 'TextType' is 'string', then % readtable returns dates as an array of strings. % Set csv data in temporary SCaseDicomTmp structure SCaseDicom_csv = table2struct(csvTable); % Update the SCaseDicomTmp structure with new data for iSCaseDicom = 1:length(SCaseDicom) % loop on new SCase(s) iSCaseDicomTmp = strcmp({SCaseDicom_csv.SCase_ID}, SCaseDicom(iSCaseDicom).id); % 0/1 array iSCaseDicomTmp = find(iSCaseDicomTmp); % index with non-zero, correponding to SCaseID - % We should test iSCaseDicomTmp + % We should test iSCaseDicomTmp => is overwriting the whole array + % Must be changed by last SCase position +1 + SCaseDicom_csv(iSCaseDicomTmp).patient_IPP = SCaseDicom(iSCaseDicom).PatientID; SCaseDicom_csv(iSCaseDicomTmp).patient_gender = SCaseDicom(iSCaseDicom).PatientSex; SCaseDicom_csv(iSCaseDicomTmp).patient_birthDate = SCaseDicom(iSCaseDicom).PatientBirthDate; SCaseDicom_csv(iSCaseDicomTmp).patient_age = SCaseDicom(iSCaseDicom).PatientAge; SCaseDicom_csv(iSCaseDicomTmp).patient_height = SCaseDicom(iSCaseDicom).PatientSize; SCaseDicom_csv(iSCaseDicomTmp).patient_weight = SCaseDicom(iSCaseDicom).PatientWeight; SCaseDicom_csv(iSCaseDicomTmp).CT_date = SCaseDicom(iSCaseDicom).AcquisitionDate; SCaseDicom_csv(iSCaseDicomTmp).CT_kernel = SCaseDicom(iSCaseDicom).ConvolutionKernel; SCaseDicom_csv(iSCaseDicomTmp).CT_pixelSize = SCaseDicom(iSCaseDicom).PixelSpacing; SCaseDicom_csv(iSCaseDicomTmp).CT_sliceSpacing = SCaseDicom(iSCaseDicom).SpacingBetweenSlices; SCaseDicom_csv(iSCaseDicomTmp).CT_sliceThickness = SCaseDicom(iSCaseDicom).SliceThickness; SCaseDicom_csv(iSCaseDicomTmp).CT_tension = SCaseDicom(iSCaseDicom).KVP; SCaseDicom_csv(iSCaseDicomTmp).CT_current = SCaseDicom(iSCaseDicom).XrayTubeCurrent; SCaseDicom_csv(iSCaseDicomTmp).CT_institution = SCaseDicom(iSCaseDicom).InstitutionName; SCaseDicom_csv(iSCaseDicomTmp).CT_manufacturer = SCaseDicom(iSCaseDicom).Manufacturer; SCaseDicom_csv(iSCaseDicomTmp).CT_model = SCaseDicom(iSCaseDicom).ManufacturerModelName; SCaseDicom_csv(iSCaseDicomTmp).CT_protocol = SCaseDicom(iSCaseDicom).ProtocolName; SCaseDicom_csv(iSCaseDicomTmp).CT_SeriesDescription = SCaseDicom(iSCaseDicom).SeriesDescription; end % SCaseDicom must now be updated for iSCaseDicom = 1:length(SCaseDicom) SCaseDicom(iSCaseDicom).id = SCaseDicom_csv(iSCaseDicom).SCase_ID; SCaseDicom(iSCaseDicom).PatientID = SCaseDicom_csv(iSCaseDicom).patient_IPP; SCaseDicom(iSCaseDicom).PatientSex = SCaseDicom_csv(iSCaseDicom).patient_gender; SCaseDicom(iSCaseDicom).PatientBirthDate = SCaseDicom_csv(iSCaseDicom).patient_birthDate; SCaseDicom(iSCaseDicom).PatientAge = SCaseDicom_csv(iSCaseDicom).patient_age; SCaseDicom(iSCaseDicom).PatientSize = SCaseDicom_csv(iSCaseDicom).patient_height; SCaseDicom(iSCaseDicom).PatientWeight = SCaseDicom_csv(iSCaseDicom).patient_weight; SCaseDicom(iSCaseDicom).AcquisitionDate = SCaseDicom_csv(iSCaseDicom).CT_date; SCaseDicom(iSCaseDicom).ConvolutionKernel = SCaseDicom_csv(iSCaseDicom).CT_kernel; SCaseDicom(iSCaseDicom).PixelSpacing = SCaseDicom_csv(iSCaseDicom).CT_pixelSize; SCaseDicom(iSCaseDicom).SpacingBetweenSlices = SCaseDicom_csv(iSCaseDicom).CT_sliceSpacing; SCaseDicom(iSCaseDicom).SliceThickness = SCaseDicom_csv(iSCaseDicom).CT_sliceThickness; SCaseDicom(iSCaseDicom).KVP = SCaseDicom_csv(iSCaseDicom).CT_tension; SCaseDicom(iSCaseDicom).XrayTubeCurrent = SCaseDicom_csv(iSCaseDicom).CT_current; SCaseDicom(iSCaseDicom).InstitutionName = SCaseDicom_csv(iSCaseDicom).CT_institution; SCaseDicom(iSCaseDicom).Manufacturer = SCaseDicom_csv(iSCaseDicom).CT_manufacturer; SCaseDicom(iSCaseDicom).ManufacturerModelName = SCaseDicom_csv(iSCaseDicom).CT_model; SCaseDicom(iSCaseDicom).ProtocolName = SCaseDicom_csv(iSCaseDicom).CT_protocol; SCaseDicom(iSCaseDicom).SeriesDescription = SCaseDicom_csv(iSCaseDicom).CT_SeriesDescription; end end % Write csv file csvFileId = fopen(csvFilename, 'w'); fprintf(csvFileId,dataHeader); for SCase_idx=1:1:numel(SCaseDicom) % Loop on all SCase provided by listSCase() fprintf(csvFileId,'\n%s,%s,%s,%s,%f,%f,%f,%s,%s,%f,%f,%f,%f,%f,%s,%s,%s,%s,%s',... SCaseDicom(SCase_idx).id,... SCaseDicom(SCase_idx).PatientID,... SCaseDicom(SCase_idx).PatientSex,... SCaseDicom(SCase_idx).PatientBirthDate,... SCaseDicom(SCase_idx).PatientAge,... SCaseDicom(SCase_idx).PatientSize,... SCaseDicom(SCase_idx).PatientWeight,... SCaseDicom(SCase_idx).AcquisitionDate,... SCaseDicom(SCase_idx).ConvolutionKernel,... SCaseDicom(SCase_idx).PixelSpacing,... SCaseDicom(SCase_idx).SpacingBetweenSlices,... SCaseDicom(SCase_idx).SliceThickness,... SCaseDicom(SCase_idx).KVP,... SCaseDicom(SCase_idx).XrayTubeCurrent,... SCaseDicom(SCase_idx).InstitutionName,... SCaseDicom(SCase_idx).Manufacturer,... SCaseDicom(SCase_idx).ManufacturerModelName,... SCaseDicom(SCase_idx).ProtocolName,... SCaseDicom(SCase_idx).SeriesDescription... ); end fclose(csvFileId); fprintf(logFileID, ['\n\nSaved csv file: ' csvFilename]); %% Write xls file (Windows only) [~,message] = csv2xlsSCase(csvFilename); fprintf(logFileID, message); %% Update MySQL % To be done %{ addpath('XLS_MySQL_DB'); MainExcel2SQL; %} %% Final log fprintf(logFileID, '\n\nSCases treated: %s', num2str(numel(SCaseDicom))); % Number of SCases treated fprintf(logFileID, '\nElapsed time (s): %s', num2str(toc)); % stopwatch timer fclose(logFileID); % Close log file outputArg = 1; % Might be extended end \ No newline at end of file diff --git a/listSCase.m b/listSCase.m index 42b252d..f2b8cac 100755 --- a/listSCase.m +++ b/listSCase.m @@ -1,262 +1,263 @@ function SCaseList = listSCase(varargin) %LISTSCASE output list of sCases (id and path), accordind to inputList or %filter arguments. % Each sCase directory should at least contain at % directory with a name staring with 'CT' and ending with either % '-1' : preop/shoulder/sharp/noPhantom % '-2' : preop/shoulder/smooth/noPhantom % '-3' : preop/shoulder/sharp/phantom % '-4' : preop/shoulder/smooth/phantom % and containing a 'dicom' subdirectory. The priority is '1', '2', '3', '4', % unless there is a amira folder already present in one of these diretories. % % The optional input argument inputList if an array of SCaseID, or 'N' or % 'P'. If empty, listSCase will search for all SCaseID. The optional % input argument filter can restrict le list. % The fonction can be run from server (lbovenus) with % cd /home/shoulder/methods/matlab/database; matlab -nojvm -nodisplay -nosplash -r "listSCase;quit" % For 700 cases, it takes less than 60 seconds (when executed from server) % Progress can be be checked throuhth log file with following shell command % cd /home/shoulder/methods/matlab/database;tail -f log/listSCase.log % Input: % inputArg: can be nothing (=all), 'N', 'P', or SCase(s) % % Output: % outputArg: 1 if ok and 0 otherwise % % Example: listSCase, listSCase('P'), listSCase('P001','P002') % Other M-files required: None % Subfunctions: None % See also: Used by dicominfoSCase, measureSCase, % % Author: Alexandre Terrier % EPFL-LBO % Creation date: 2018-07-01 % Revision date: 2018-12-30 % % TODO % Two main looops might be merged in one, to avoid duplicagte post % treatment %% Open log file logFileID = openLogFile('listSCase.log'); % %% Set the data directory from the configuration file config.txt % dataDir = openConfigFile('config.txt', logFileID); %% Struct contains id and associated directory SCaseList = struct(... 'id' ,[],... 'dir' ,[],... 'comment' ,[]... % Might be used later ); %% Check varargin dirL0Array = ''; SCaseListIn = {}; switch nargin case 0 % All SCase, default when no argument dirL0Array = ['N';'P']; fprintf(logFileID, '\nGet list of all SCase with a valid dicom folder'); % Set the data directory from the configuration file config.txt dataDir = openConfigFile('config.txt', logFileID); case 1 if exist(varargin{1},'dir')==7 % Assumes the input is the data directory dataDir = varargin{1}; % All SCase as default when no more arguments dirL0Array = ['N';'P']; % All SCase, default when no argument fprintf(logFileID, '\nGet list of all SCase with a valid dicom folder'); else % When the only argument is not a directory % Set the data directory from the configuration file config.txt dataDir = openConfigFile('config.txt', logFileID); % Assumes the argument is 'N', 'P' or a list of SCases switch varargin{1} case 'N' % All normal SCase dirL0Array = 'N'; fprintf(logFileID, '\nGet list of all normal SCase with a valid dicom folder'); case 'P' % All pathological SCase dirL0Array = 'P'; fprintf(logFileID, '\nGet list of all pathological SCase with a valid dicom folder'); otherwise % A specific SCase fprintf(logFileID, '\nGet list of a SCase with a valid dicom folder'); SCaseListIn = varargin{1}; end end otherwise if exist(varargin{1},'dir')==7 % Assumes the input is the data directory dataDir = varargin{1}; switch varargin{2} case 'N' % All normal SCase dirL0Array = 'N'; fprintf(logFileID, '\nGet list of all normal SCase with a valid dicom folder'); case 'P' % All pathological SCase dirL0Array = 'P'; fprintf(logFileID, '\nGet list of all pathological SCase with a valid dicom folder'); - otherwise % A specific SCase + otherwise % Some specific SCase (wrong) fprintf(logFileID, '\nGet list of a SCase with a valid dicom folder'); - SCaseListIn = varargin{2:end}; + SCaseListIn = varargin; + SCaseListIn(1) = []; % removing the dataDir from the argument list end else % When the first argument is not a directory, assumes it is a list of SCases % Set the data directory from the configuration file config.txt dataDir = openConfigFile('config.txt', logFileID); % list of multiple SCase given in varargin fprintf(logFileID, '\nGet list of a SCase with a valid dicom folder'); SCaseListIn = varargin; end end %% Only one of the two loops below are performed SCaseListIdx = 0; % Used in one of two loops below %% Loop over SCase (N, P, or both), if length(dirL0Array) > 1 for dirL0idx = 1:length(dirL0Array) % N or P for dirL1idx = 0:9 % Hunderts dir for dirL2idx = 0:9 % Tens dir dirL0Str = dirL0Array(dirL0idx); dirL1Str = num2str(dirL1idx); dirL2Str = num2str(dirL2idx); dirTens = [dataDir '/' dirL0Str '/' dirL1Str '/' dirL2Str]; dirTensList = dir(dirTens); % List all directories in tens dir dirTensList = dirTensList(~ismember({dirTensList.name},{'.','..'})); % Remove . and .. % We might add here a filter (only diretories, {N/P} followed % by digits for dirL3idx = 1:numel(dirTensList) sCaseDirName = dirTensList(dirL3idx).name; % Root directory of this case sCaseDirPath = dirTensList(dirL3idx).folder; % Check if this SCaseID has a CT dir [CT?*-?] with a valid preoprative CT SCaseID = strtok(sCaseDirName, '-'); % Get chars before '-' CTdirList = dir([sCaseDirPath '/' sCaseDirName '/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 fprintf(logFileID, ['\n', SCaseID, ' has no valid dicom directory']); else if iCTdirAmira % If amira dir exist, use it iCTdir2use = iCTdirAmira; end CTdir = CTdirList(iCTdir2use); SCaseListIdx = SCaseListIdx + 1; SCaseList(SCaseListIdx).id = SCaseID; CTdirPath = [CTdir.folder '/' CTdir.name]; SCaseList(SCaseListIdx).dir = CTdirPath; if CTnum ~= 1 fprintf(logFileID, ['\n', SCaseID, ' dicom directory is CT-' num2str(CTnum)]); end end end end end end %% Loop over input SCase list (given as input argument), if length(SCaseListIn) > 0 for iSCaseListIn = 1:length(SCaseListIn) % Check if there is a valid dicom directory for this SCaseID SCaseID = char(SCaseListIn{iSCaseListIn}); % Build directory from SCaseID (as in function ShoulderCase.path) % The validity of the format should be either Pnnn or Nnnn. if (numel(regexp(SCaseID,'^[PN]\d{1,3}$')) == 0) error(['Invalid format of SCaseID: ' SCaseID{1} '. 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)]; % Check if a (!unique! to be done) directory exists for this SCaseID 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]; % Check if this SCaseID has a CT directory with a valid preoprative CT 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 fprintf(logFileID, ['\n', SCaseID, ' has no valid dicom directory']); else if iCTdirAmira % If amira dir exist, use it iCTdir2use = iCTdirAmira; end CTdir = CTdirList(iCTdir2use); SCaseListIdx = SCaseListIdx + 1; SCaseList(SCaseListIdx).id = SCaseID; CTdirPath = [CTdir.folder '/' CTdir.name]; SCaseList(SCaseListIdx).dir = CTdirPath; if CTnum ~= 1 fprintf(logFileID, ['\n', SCaseID, ' dicom directory is CT-' num2str(CTnum)]); end end end fprintf(logFileID, ['\nNumber of SCase: ' num2str(length(SCaseList))]); fclose(logFileID); % Close log file end diff --git a/openConfigFile.m b/openConfigFile.m index d21d038..6052c5d 100644 --- a/openConfigFile.m +++ b/openConfigFile.m @@ -1,46 +1,47 @@ function dataDir = openConfigFile(configFile, logFileID) %OPENCONFIGFILE open file config.txt and read dataDir % Config file must follow specific/restrictive rules % The file config.txt should be writen as below. % One line should contain dataDir = %{ % This configuration file contains the definition of variable dataDir for database access. % It is used by functions: listSCase.m, measureSCase.m, plotScase.m % /home/shoulder/data <-- acces data dir from lbovenus or lbomars (default) % /home/shoulder/dataDev <-- acces dataDev dir from lbovenus or lbomars % /Volumes/shoulder/data <-- acces data dir from lbovenus/lbomars mounted on macos % /Volumes/shoulder/dataDev <-- acces dataDev dir from lbovenus mounted on macos % Z:\data <-- acces data dir from lbovenus/lbomars mounted on windows % Z:\dataDev <-- acces dataDev dir from lbovenus mounted on windows dataDir = /Volumes/shoulder/dataDev %} % Author: AT % Date: 2019-01-15 +% Modified by: JSM, 2019-02-13 % TODO: Less restrict rules for writting config.txt dataDir = []; if nargin==2 if exist(configFile, 'file') fileID = fopen(configFile,'r'); dataDir = fscanf(fileID, '%s'); % Read config file without spaces fclose(fileID); k = strfind(dataDir, 'dataDir='); % Find definition of dataDir k = k + 8; % Remove 'dataDir=' % Check if dataDir exists dataDir = dataDir(k:end); % Assume that config file ends with dataDir content if ~exist(dataDir, 'dir') fprintf(logFileID, ['Data directory not found, check ' configFile]); % error(['Data directory not found, check ' configFile]); end end end if ~exist(dataDir, 'dir') errordlg(sprintf("%s",... "Pleas select manually the data directory"),... "Error: ConfigFile not provided or not found") dataDir = uigetdir(pwd); end end