diff --git a/Import/importSCase.m b/Import/importSCase.m index 7244967..318198a 100644 --- a/Import/importSCase.m +++ b/Import/importSCase.m @@ -1,1983 +1,1984 @@ function varargout = importSCase(varargin) % IMPORTSCASE MATLAB code for importSCase.fig % IMPORTSCASE, by itself, creates a new IMPORTSCASE or raises the existing % singleton*. % % H = IMPORTSCASE returns the handle to a new IMPORTSCASE or the handle to % the existing singleton*. % % IMPORTSCASE('CALLBACK',hObject,eventData,handles,...) calls the local % function named CALLBACK in IMPORTSCASE.M with the given input arguments. % % IMPORTSCASE('Property','Value',...) creates a new IMPORTSCASE or raises the % existing singleton*. Starting from the left, property value pairs are % applied to the GUI before importSCase_OpeningFcn gets called. An % unrecognized property name or invalid value makes property application % stop. All inputs are passed to importSCase_OpeningFcn via varargin. % % *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one % instance to run (singleton)". % % See also: GUIDE, GUIDATA, GUIHANDLES % Edit the above text to modify the response to help importSCase % Last Modified by GUIDE v2.5 14-Feb-2019 17:34:15 %% Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @importSCase_OpeningFcn, ... 'gui_OutputFcn', @importSCase_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function importSCase_OpeningFcn(hObject, eventdata, handles, varargin) %% --- Executes just before importSCase is made visible. % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to importSCase (see VARARGIN) %-------------------------------------------------------------------------- % Choose default command line output for importSCase handles.output = hObject; % Update handles structure guidata(hObject, handles); % UIWAIT makes importSCase wait for user response (see UIRESUME) % uiwait(handles.figure1); movegui('center') %-------------------------------------------------------------------------- %% Customized by JSM 2018/10/26 % Setting default values for some global variables % Default directory = the current directory handles.script_dir = pwd; % cd([pwd '/../../../../']); % handles.home_path = pwd; handles.home_path = uigetdir(handles.script_dir,"Please select the home directory containing folder 'data'"); cd(handles.home_path) -cd([pwd '/data/']); -handles.base_dir = pwd; +% cd([pwd '/data/']); +% handles.base_dir = pwd; +handles.base_dir = uigetdir(handles.home_path,"Please select the main directory 'data or dataDev'"); cd(handles.script_dir); % Radiobuttons % *handles.acquisition_stage* => might be 'Normal' or 'Pathologic' set(handles.radiobutton_Pathologic,'Value',1); handles.patient_group="P"; % *handles.shoulder_side* => might be 'Right' or 'Left' or 'Both' set(handles.radiobutton_right,'Value',1); % *handles.acquisition_stage* => might be 'Preop' or 'Postop' set(handles.radiobutton_preop,'Value',1); % *handles.output_dir* => importation folder. By default the current dir. set(handles.output_dir,'String',pwd); % *handles.body* => might be 'Shoulder', 'Elbow' or 'Other[]' set(handles.radiobutton_shoulder,'Value',1); % *handles.CTlabel_text* => might be '-1', '-2', ..., '-1p', '-2p', ... set(handles.CTlabel_text,'String','-xxx'); guidata(hObject,handles); function varargout = importSCase_OutputFcn(hObject, eventdata, handles) %% --- Outputs from this function are returned to the command line. % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output; %% ====================================================================== %% "Create Functions" execute during object creation, after setting all properties. function patient_num_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to patient_num (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end %% --- Executes during object creation, after setting all properties. function slider_2Dslice_CreateFcn(hObject, eventdata, handles) % hObject handle to slider_2Dslice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end guidata(hObject,handles); function edit_otherBody_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to edit_otherBody (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end guidata(hObject,handles); function edit_extra_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to edit_extra (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end set(hObject,'String','Write here every extra info...'); % update_infoFields(handles); guidata(hObject,handles); %% ====================================================================== %% "Callback Functions" execute on hObject's action events %% ************** Patient Type (group) ************** function radiobutton_Pathologic_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_Pathologic. % hObject handle to radiobutton_Pathologic (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_Pathologic handles.patient_group="P"; set(handles.act3Dview,'value',1) act3Dview_Callback(handles.act3Dview,eventdata,handles) patient_num = get(handles.patient_num,'String'); set(handles.patient_num,'String',['P' patient_num(2:end)]); function radiobutton_Normal_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_Normal. % hObject handle to radiobutton_Normal (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_Normal handles.patient_group="N"; set(handles.act3Dview,'value',0) act3Dview_Callback(handles.act3Dview,eventdata,handles) patient_num = get(handles.patient_num,'String'); set(handles.patient_num,'String',['N' patient_num(2:end)]); %% ************** Start new Patient / sCase ************** function newPatient_Callback(hObject, eventdata, handles) %% --- Executes on button press in newPatient. % hObject handle to newPatient (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % This button lunch the initial check of a folder containing a group of % sets of CTs. It counts the files per subfolder to clasify them % Select a limit of minimun dicom files to discard scout scans & other % small scans as elbows scoutScanLimit = 30; smallScanLimit = 90; % Choose the main folder for the desired case newPath = uigetdir(handles.base_dir,"Chose the folder containing the all the CT sets"); % handles.patient_dir = newPath; current_dir = pwd; cd([newPath '/..']); handles.patient_dir = pwd; cd(current_dir); % Check the given path and extract data from every folder and file inside dirList = dir(newPath); % Select only the folders from the current path --> folderList folderN = 0; for k=3:length(dirList) folderN = folderN + dirList(k).isdir if folderN * dirList(k).isdir >0 folderList(folderN) = dirList(k); folderN,folderList(folderN).name end end % Check how many dicom files are in each folder (to discard ScoutScans) wb = waitbar(0,'Checking folders size'); for k=1:folderN % will contain a struct with data of files in the desired % folder waitbar(k/folderN,wb,sprintf('%s%s%s%i%s%i',"Checking directory ",... string(folderList(k).name),": ",k,"/",folderN)); files=dir([folderList(k).folder '/' folderList(k).name]); filesDicom = 0; % Initialitation of dicom files counter for n=1:length(files) % if is a file and this file is a dicom file, note it % (files.isdicom) and count it if isfile([files(n).folder '/' files(n).name]) & ... isdicom([files(n).folder '/' files(n).name]) files(n).isdicom = 1; % noting down that it is a dicom file filesDicom = filesDicom +1; % counting the dicom files % Next line builds a list of dicom file names in cell mode folderList(k).dicomList(filesDicom) = cellstr(files(n).name); else % Otherwise, is not a dicom file and we note it like that files(n).isdicom = 0; end end % Each folder has it own counter of dicom files folderList(k).dicomN = filesDicom; % We add a prefix with the number of files per CT, helping to priorize % the importation order if ~contains(folderList(k).name,['s' char(string(folderList(k).dicomN))]) movefile([folderList(k).folder '/' folderList(k).name],... [folderList(k).folder '/' 's'... char(string(folderList(k).dicomN)) '-' folderList(k).name]); folderList(k).name = ['s' char(string(folderList(k).dicomN))... '-' folderList(k).name]; end %% From here it could be commented and eliminated (JSM 25102018) % When a folder has few dicom files we can consider that it was a scan % scout instead of an interesting CT if folderList(k).dicomN < scoutScanLimit folderList(k).scanMode = 'scoutScan'; % Changing the folder name to mark it as scoutScan (if not marked % before) if ~contains(folderList(k).name,'scout') movefile([folderList(k).folder '/' folderList(k).name],... [folderList(k).folder '/' folderList(k).name '_scoutScan']); end elseif folderList(k).dicomN < smallScanLimit folderList(k).scanMode = 'smallScan'; % Changing the folder name to mark it as scoutScan (if not marked % before) if ~contains(folderList(k).name,'small') movefile([folderList(k).folder '/' folderList(k).name],... [folderList(k).folder '/' folderList(k).name '_smallScan']); end else folderList(k).scanMode = 'CT'; end % Until here end % Makes handles variable visible to other functions in this script % hanldes.dicomdir = newPath; guidata(hObject,handles); %% ************** Load next CT set ************** function Load_Next_CT_Callback(hObject, eventdata, handles) %% --- Executes on button press in Load_Next_CT. % hObject handle to Load_Next_CT (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Before loading a new set of CTs we should remove the last one clear handles.data % Output Directory update % if isfield(handles,'patient_dir') % handles.base_dir = handles.patient_dir; % else % handles.base_dir = handles.home_path; % end % Also remove the fields 'myinfo' and 'mydicom' (they could be just % cleared?) if isfield(handles, 'myinfo'); handles = rmfield(handles, 'myinfo'); % Removing 'myinfo' field from structure in handles else if isfield(handles, 'mydicom'); handles = rmfield(handles, 'mydicom');% Removing 'mydicom' field from structure in handles end end if isfield(handles,'dicomSet') handles = rmfield(handles,'dicomSet'); end if isfield(handles,'dicomSetSorted') handles = rmfield(handles,'dicomSetSorted'); end %% Loading CT files % Using last directory if isfield(handles,'patient_dir') [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',handles.patient_dir,'MultiSelect', 'on'); elseif isfield(handles,'base_dir') [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',handles.base_dir,'MultiSelect', 'on'); else [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',home_path,'MultiSelect', 'on'); end % Showing the dicom name in the interface set(handles.text_dicom_name,'String',strcat(string(handles.dicomdir), string(handles.dicomlist(1)))); % Number of dicom files handles.N = length(handles.dicomlist); % Set parametres for 2D visualization set(handles.slider_2Dslice, 'Max', handles.N); set(handles.slider_2Dslice, 'Value', int32(handles.N/2)); set(handles.slider_2Dslice, 'SliderStep',[1/handles.N 10/handles.N]); set(handles.slider_2Dslice, 'Min', 1); set(handles.info_text, 'ForegroundColor', 'black', 'FontWeight', 'normal'); % Read dicom data try % This loop reads all the dicom files and builds a new struct with the % information from each row and the data (the images) wb = waitbar(0,'Loading DICOM files'); %This creates a waitbar or progressbar L = length(handles.dicomlist); % number of dicom files to load for n=1:L waitbar(n/L,wb,sprintf('%s%i%s%i',"Loading DICOM metadata ",n,"/",L)); % Reads the info from each dicomfile handles.dicomSet(n) = dicominfo(sprintf('%s%s',... handles.dicomdir, char(handles.dicomlist(n))),'UseDictionaryVR',true); end for n=1:L waitbar(n/L,wb,sprintf('%s%i%s%i',"Loading DICOM images ",n,"/",L)); handles.dicomSet(n).data = dicomread(sprintf('%s%s', ... handles.dicomdir, char(handles.dicomlist(int32(n))))); end handles.myinfo = handles.dicomSet(1); % To be removed (JSM 25102018) % % 'handles.myinfo' will keep the metadata of the dicom files loaded % handles.myinfo(1) = dicominfo(sprintf('%s%s', handles.dicomdir, ... % char(handles.dicomlist(1))),'UseDictionaryVR',true); % Temporal storing of metadata tempInfo=handles.dicomSet(1); save('tempInfo.mat','tempInfo'); % Shows few metadata in the interface: % - IPP identification number and Patient Initials set(handles.IPP_text, 'String', handles.dicomSet(1).PatientID) set(handles.initials_text,'String',sprintf('(%s%s)',... handles.dicomSet(1).PatientName.FamilyName(1),... handles.dicomSet(1).PatientName.GivenName(1))); set(handles.birthdate_text,'String',handles.dicomSet(1).PatientBirthDate); set(handles.gender_text,'String',handles.dicomSet(1).PatientSex); % - CT resolution in mm set(handles.Resolution_text, 'String', sprintf('%f mm', handles.dicomSet(1).PixelSpacing(1))); % - CT date set(handles.CTdate_text, 'String', sprintf('%s', handles.dicomSet(1).AcquisitionDate)); % The following function sort the dicomFiles by their slice location % handles.dicomSetSorted=sortStructByField(handles.dicomSet,{'SliceLocation'}); handles.dicomSetSorted=sortStructByField(handles.dicomSet,{'InstanceNumber'},handles); handles.dicomSetOld=handles.dicomSet; handles.dicomSet=handles.dicomSetSorted; 'load finished' catch except except except.identifier if strcmp(except.identifier,'Error identifier: MATLAB:nonExistentField') errordlg(except.message,'Loading Error') % return % elseif or(strcmp(except.identifier,'MATLAB:heterogeneousStrucAssignment'),... % strcmp(except.identifier,'MATLAB:heterogeneousStrucAssignment')) % f = errordlg(sprintf('%s\n\n%s\n%s', ' CT SCAN WITH UNEXPECTED ORIENTATION.',... % 'Suggestion: mark the current folder as "Wrong_Orientation"',... % 'and try with another set of CTs'),'Loading Error'); % return else errordlg(sprintf('%s%s','Error identifier: ',except.identifier)) end end %% Updating the radioButtons "NONE", "BONE"('BONE'), "SOFT"('STANDARD') if isfield(handles.dicomSet(1), 'ConvolutionKernel') if strfind(handles.dicomSet(1).ConvolutionKernel, 'BONE') set(handles.radiobutton_none, 'Value', 0); set(handles.radiobutton_sharp, 'Value', 1); set(handles.radiobutton_smooth, 'Value', 0); elseif strfind(handles.dicomSet(1).ConvolutionKernel, 'STANDARD') set(handles.radiobutton_none, 'Value', 0); set(handles.radiobutton_sharp, 'Value', 0); set(handles.radiobutton_smooth, 'Value', 1); else set(handles.radiobutton_none, 'Value', 1); set(handles.radiobutton_sharp, 'Value', 0); set(handles.radiobutton_smooth, 'Value', 0); end else set(handles.radiobutton_none, 'Value', 1); set(handles.radiobutton_sharp, 'Value', 0); set(handles.radiobutton_smooth, 'Value', 0); end %% Extracting data for interface representation handles.slice = int32(handles.N / 2); % Chooses the central slice % handles.mydicom(:,:,handles.slice) = dicomread(sprintf('%s%s', handles.dicomdir, char(handles.dicomlist(handles.slice)))); if isfield(handles,'mydicom') handles=rmfield(handles,'mydicom'); end handles.mydicom(:,:,handles.slice) = handles.dicomSet(handles.slice).data; handles.CTmin = -200; handles.CTmax = 2000; handles.bigmax = 3000; handles.bigmin = -2000; set(handles.slider_2Dcontrast, 'Max', handles.bigmax - handles.bigmin - 1); set(handles.slider_2Dcontrast, 'Min', 0); set(handles.slider_2Dcontrast, 'Value', 900); set(handles.slider_2Dbrightness, 'Max', handles.bigmax); set(handles.slider_2Dbrightness, 'Min', handles.bigmin); set(handles.slider_2Dbrightness, 'Value', 1100); set(handles.slice_num, 'String', handles.slice); axes(handles.axes_2Dviewer); % Set current axes % imshow(handles.mydicom(:,:,handles.slice), [handles.CTmin handles.CTmax]); % imshow(handles.dicomSet(handles.slice).data, [handles.CTmin handles.CTmax]); % trying dicomreadVolume % [V, SPATIAL, DIM] = dicomreadVolume(handles.dicomdir); % imshow(V(:,:,1,handles.slice), [handles.CTmin handles.CTmax]); imshow(handles.dicomSet(handles.slice).data, [handles.CTmin handles.CTmax]); %% ISOSURFACE section ==================================================== act3Dview_Callback(handles.act3Dview, eventdata, handles); axes(handles.axes_2Dviewer); % Set current axes guidata(hObject,handles); % handles if and(get(handles.patient_num,'String') ~="P###",... exist([char(get(handles.output_dir,'String')) '/readme'])==2) handles.text_readme.String="listo para leer"; end % update_infoFields(handles); % Makes handles variable visible to other functions in this script %% Reset the DB chech result info box set(handles.text_coincidences_info,'String',"Check DB for coincidences..."); guidata(hObject,handles); function act3Dview_Callback(hObject, eventdata, handles) %% --- Executes on button press in act3Dview. % hObject handle to act3Dview (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of act3Dview % handles.enable_3Dview = get(hObject,'Value') if get(hObject,'Value') C=questdlg(['The 3D viewer might create memory problems when the CT'... ' contains more than 300 slices. Do you want to activate it?'],... '3D viewer activation','Yes, Display 3D view','Abort','Abort') switch C case 'Yes, Display 3D view' handles.enable_3Dview = 1 otherwise handles.enable_3Dview = 0 set(hObject,'Value',0) end else handles.enable_3Dview = 0 end % clear handles.data axes(handles.isosurface); % Set current axes cla if isfield(handles,'N') & handles.enable_3Dview if handles.N>1 directory=handles.dicomdir; % Preparing list of dicom full names (including address) for k=1:handles.N dicomListFullName(k)=""; dicomListFullName(k)=[directory char(handles.dicomlist(k))]; end try [V,spatial,dim] = dicomreadVolume(dicomListFullName); v = squeeze(V); v = double(v); v2= imresize3(v,0.2); handles.v2 = v2; handles.isoValue = 1400; % default limit for isosurface rendering p = patch(isosurface(v2,handles.isoValue)); Sv2 = size(v2); M = max(max(max(v2))); m = min(min(min(v2))); handles.bigmax = M; handles.bigmin = m; set(handles.slider_3Dviewer, 'Max', handles.bigmax); set(handles.slider_3Dviewer, 'Min', handles.bigmin); set(handles.slider_3Dviewer, 'Value', handles.isoValue); d1=(M-m)/Sv2(1); d2=(M-m)/Sv2(2); d3=(M-m)/Sv2(3); [x,y,z]=meshgrid(m:d1:M,m:d2:M,m:d3:M); handles.x=x(1:Sv2(1),1:Sv2(2),1:Sv2(3)); handles.y=y(1:Sv2(1),1:Sv2(2),1:Sv2(3)); handles.z=z(1:Sv2(1),1:Sv2(2),1:Sv2(3)); isocolors(handles.x,handles.y,handles.z,v2,p); p.FaceColor='interp'; p.EdgeColor='none'; view(-20, 10); % isosurface(v2,1400); rotate3d on; catch except if strcmp(except.identifier,'images:dicomread:differentPatientOrientations') f = errordlg(sprintf('%s\n\n%s\n%s', ' CT SCAN WITH UNEXPECTED ORIENTATION.',... 'Suggestion: mark the current folder as "Wrong_Orientation"',... 'and try with another set of CTs'),'Loading Error'); % We change the folder name to mark it as scoutScan (if not marked before) if ~contains(handles.dicomdir,'wrong') movefile([handles.dicomdir(1:end-1)],... [handles.dicomdir(1:end-1) '_wrongOrientatedScan']); end return else errordlg(sprintf('%s%s','Error identifier: ',except.identifier)); % rethrow(except); end end end end guidata(hObject,handles); function slider_3Dviewer_Callback(hObject, eventdata, handles) %% --- Executes on slider movement. % hObject handle to slider_3Dviewer (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider guidata(hObject,handles); function radiobutton_left_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_left. % hObject handle to radiobutton_left (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_left handles.shoulder_side = 'Left'; update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); % --- Executes on button press in radiobutton_both. function radiobutton_both_Callback(hObject, eventdata, handles) % hObject handle to radiobutton_both (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_both handles.shoulder_side = 'Both'; update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); function radiobutton_right_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_right. % hObject handle to radiobutton_right (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_right handles.shoulder_side = 'Right'; update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); %% --- Executes on button press in radiobutton_preop. function radiobutton_preop_Callback(hObject, eventdata, handles) % hObject handle to radiobutton_preop (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_preop handles.acquisition_stage = 'Preop'; update_readme_Callback(handles.update_readme,eventdata,handles); % update_infoFields(handles); guidata(hObject,handles); function radiobutton_postop_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_postop. % hObject handle to radiobutton_postop (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_postop handles.acquisition_stage = 'Postop'; update_readme_Callback(handles.update_readme,eventdata,handles); % update_infoFields(handles); guidata(hObject,handles); function Check_Database_Callback(hObject, eventdata, handles) %% --- Executes on button press in Check_Database. % hObject handle to Check_Database (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) wb = waitbar(0,'Checking Excel Database'); "Openning Excel Database" % Checking Database ----------------------------------- % Importing database from excel file depending on computer system % if computer('arch')=="win64" % handles.data=importDataBaseTab('../../../../data/Excel/ShoulderDataBase.xlsx','sCase',2,2000); % excelDB = '../../../../data/Excel/ShoulderDataBase.xlsx'; % else % % path2DB=[ '/home/shoulder/data/Excel/ShoulderDataBase.xlsx']; % system('ls /home/shoulder/data/Excel/'); % path2DB=['/home/shoulder/data/Excel/ShoulderDataBase.xlsx']; % excelDB = '/home/shoulder/data/Excel/ShoulderDataBase.xlsx'; % % ls('/home/shoulder/data/Excel/') % % f=importDataBaseTab_script; % handles.data=importDataBaseTab(path2DB,'sCase',2,2000); % % end % New way of reading the Excel Database: [excelDB, DBpath, ~] = uigetfile('*.xlsx','Select the Excel Database File'); tic E = exist(sprintf('%s%s',DBpath,excelDB)); % We store the Excel file adress and name for further use handles.ExcelDB_path = DBpath; handles.ExcelDB_fullname = sprintf('%s%s',DBpath,excelDB); if E == 2 % Only if the file exist as a known format (XLS) [~,txt,raw,dates]=xlsread(handles.ExcelDB_fullname,'sCase','','',@convertSpreadsheetExcelDates); % The dates cell array will have NaN in every cell not containing a "date" dates(~cellfun(@(x) isnumeric(x) || islogical(x), dates)) = {NaN}; handles.data.headers = string(txt(1,:)); % Conversion of dates's cell array to numeric matrix dates=cell2mat(dates); % Finding columns with dates dates_CperR=find(~isnan(dates(1,:))); date_Cells{1}= dates_CperR; txt_Cells{1} = []; for p=2:size(txt,1) dates_CperR=find(~isnan(dates(p,:))); date_Cells{p}= dates_CperR; txt_CperR = find(~ismissing(txt(p,:))); txt_Cells{p} = txt_CperR; end date_columns = []; txt_columns = []; for p=1:length(date_Cells) date_columns= union(date_columns,date_Cells{p}); txt_columns = union(txt_columns,txt_Cells{p}); end num_columns = setdiff(1:size(raw,2),txt_columns); txt_columns = setdiff(txt_columns,date_columns); % Inverting the "date to number" convertion done during the importation dates_only = datetime(dates(2:end,[date_columns]),'ConvertFrom','Excel','Format','dd-MM-yyy'); % num_only = cell2mat(raw(2:end,num_columns)); for n=1:size(txt,2) % for each text headers (column) [data.(raw{1,n})]=raw(2:end,n); end k=1; for n=date_columns' % for the date columns [data.(raw{1,n})]=dates_only(:,k); k=k+1; end elseif E == 0 warndlg("Excel file not found!") else warndlg("Database file has unrecognized format. It should be an Excel file") end toc handles.data=data; % ***Obtain the first row available for a new case*** % Get the colum of sCase.id, including those from rows available sCase_list = handles.data.SCase_ID; % Cut the list from the end untill found an existing sCases [s1,s2]=size(sCase_list); s0=0; while sCase_list(end,:)=="" % Last position empty... sCase_list = sCase_list(1:end-1,:); s0=s0+1; waitbar(s0/s1,wb,'Checking Excel Database'); end sCase_N=[]; sCase_P=[]; % sCase_list_sorted=sort(sCase_list); % This helps to avoid errors if a row is eventually void sCase_list_sorted=string(sCase_list); % This have to be cleaned for n=1:s1-s0 if sCase_list_sorted(n,:)~="" L = length(char(extractAfter(sCase_list_sorted(n,:),1))); while L<3 sCase_list_sorted(n,:)=sprintf('%s%s%s',... extractBefore(sCase_list_sorted(n,:),2),... '0',extractAfter(sCase_list_sorted(n,:),1)); L = length(char(extractAfter(sCase_list_sorted(n,:),1))); end if extractBefore(sCase_list_sorted(n,:),2)=="N" sCase_N=[sCase_N; sCase_list_sorted(n,:)]; elseif extractBefore(sCase_list_sorted(n,:),2)=="P" sCase_P=[sCase_P; sCase_list_sorted(n,:)]; else "there is a row without identification (N or P) in the DataBase" end waitbar(s1-s0+n/s1,wb,'Checking Excel Database'); end end sCase_N=sort(sCase_N); sCase_P=sort(sCase_P); % Build the next sCase.id for a potential new sCase % by extracting the 'id' number of the last sCase and adding '1' if handles.patient_group=="N" new_sCase = str2num(extractAfter(char(sCase_N(end)),1))+1; % Finaly convert to string and add a 'P' for 'Pathological' new_sCase = string(['N' num2str(new_sCase)]); elseif handles.patient_group=="P" new_sCase = str2num(extractAfter(char(sCase_P(end)),1))+1; % Finaly convert to string and add a 'N' for 'Normal' new_sCase = string(['P' num2str(new_sCase)]); end handles.data.anonymity_IPP=cell2mat(handles.data.anonymity_IPP); handles.data.shoulder_side=cell2mat(handles.data.shoulder_side); % *** Q1: Does the new IPP exist in the Database yet? *** % Extract the list of patient IDs from Excel database IPP_list_DB = handles.data.anonymity_IPP; % Extracting patient ID from dicom-set info IPP_sCase = str2num(handles.myinfo(1).PatientID) % If the IPP of the current case already exist in the database, % it finds the position of the current patient ID in the list. % And for this position it extracts the case name "sCase", otherwise it % leave the variables empty % handles.patientIndex=find(handles.data.anonymityIPP==str2num(handles.myinfo(1).PatientID)) handles.patientIndex=find(IPP_list_DB==IPP_sCase); handles.sCase = handles.data.SCase_ID(handles.patientIndex); % Determine how many sCases exits with the same IPP as the current CT Ncoincidences=length(handles.sCase); % *** We start the decision tree *** if Ncoincidences==0 % This is a new sCase!! So we write a short message and set the sCase set(handles.dataCheck_text,'String', sprintf('This is a new case')); set(handles.dataCheck_panel,'BackgroundColor','g'); handles.current_sCase=new_sCase; set(handles.patient_num,'String',sprintf('%s',handles.current_sCase)); set(handles.text_coincidences_info,'String',"Current IPP not found in DB"); % If the IPP exists in the Excel Database in more than one sCase we first % can discriminate by the shoulder side (rigth or left) elseif Ncoincidences>1 % Launch a warning message set(handles.dataCheck_text,'String',... ['Patient found in ' num2str(Ncoincidences) ' sCase(s)']); set(handles.dataCheck_panel,'BackgroundColor','y'); % Shows information of the 2 first coincidences coincidences = sprintf("%s \t%s \t%s \t%s \n%s \t%s \t%s \t\t%s \n%s \t%s \t%s \t\t%s",... "SCase"," IPP "," CT_date ","Side",... string(handles.sCase(1)),... string(IPP_list_DB(handles.patientIndex(1))),... handles.data.CT_date(handles.patientIndex(1)),... handles.data.shoulder_side(handles.patientIndex(1)),... string(handles.sCase(2)),... string(IPP_list_DB(handles.patientIndex(2))),... handles.data.CT_date(handles.patientIndex(2)),... handles.data.shoulder_side(handles.patientIndex(2))); set(handles.text_coincidences_info,'String',coincidences); % *** Q2: is the current CT from the same side of one of the existing? % Get the side of the coincident sCases side_sCase = handles.data.shoulder_side(handles.patientIndex); % Launch 'question dialog' to let the user choose if one the coincident % sCases matches with the side (rigth or left) of the current CT question = sprintf("%s %s %s \n%s\n%s",... "The SCase",string(IPP_sCase),... "was found in the more than one case in the database.",... "Please, check the shoulder side of the currently loaded CT",... "and choose between the following options"); title = "SCase found in database"; btn1 = ['Existing case:' char(handles.sCase(1)) ' side:' char(side_sCase(1))]; btn2 = ['Existing case:' char(handles.sCase(2)) ' side:' char(side_sCase(2))]; btn3 = ['New case:' char(new_sCase) ' side: ?']; dfltbtn = btn3; answer = questdlg(question,title,btn1,btn2,btn3,dfltbtn); switch answer case btn1 handles.current_sCase=handles.sCase(1); answerN = 1; case btn2 handles.current_sCase=handles.sCase(2); answerN = 2; case btn3 handles.current_sCase=string(new_sCase); answerN = 3; otherwise disp("Auto Check DB -ABORTED-"); return; end disp(sprintf("%s %s",string(handles.current_sCase),"chosen")); % Update the list of sCases after selection handles.sCase=handles.current_sCase; Ncoincidences=1; % *** Q3: is the current CT date similar to the CT_date from any of the % concident sCases in the Database? *** % Get the CT's date from the coincident sCase from the Database try CTdate_DB = datestr(handles.data.CT_date(handles.patientIndex),'dd-mmm-yyyy'); catch error message = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s",... "Suggested solution:",... "Opt1. Update InfoCT.xlsx and repeat the 'Auto Check DB'",... "Opt2. Check the CT_date yourself and use 'Manual sCase'",... "Error details:",... error.identifier,error.message); errordlg(message,"Not valid CT_date found in Database"); CTdate_DB = NaT; end % Get the CT's date from the new CT set current_CTdate = handles.myinfo.AcquisitionDate % Adapt date format to make it comparable current_CTdate=datestr([current_CTdate(1:4),'-',... current_CTdate(5:6),'-',current_CTdate(7:8)],'dd-mmm-yyyy') % Compare CT's dates if isequal(current_CTdate,CTdate_DB) % The sCase already exits so the user will have to decide if the CT % must be added to the case of it is already added. set(handles.dataCheck_text,'String',... ['Current CT matches in IPP, side and CTdate with others in the database']); set(handles.dataCheck_panel,'BackgroundColor','r'); set(handles.patient_num,'String',string(handles.current_sCase)); else % The user must check if the current CT set corresponds to a % post-op shoulder set(handles.dataCheck_text,'String',... ['Check if the CT is from a post-op. Current CT matches in IPP, side']); set(handles.dataCheck_panel,'BackgroundColor','r'); set(handles.patient_num,'String',string(handles.current_sCase)); end % If only one sCase was found in the Database elseif Ncoincidences==1 % Launch a warning message set(handles.dataCheck_text,'String',... ['Patient found in one sCase(s)']); set(handles.dataCheck_panel,'BackgroundColor','r'); handles.current_sCase=handles.sCase; coincidences = sprintf("%s \t%s \t%s \t%s \n%s \t%s \t%s \t\t%s",... "SCase"," IPP "," CT_date ","Side",... string(handles.sCase(1)),... string(IPP_list_DB(handles.patientIndex(1))),... handles.data.CT_date(handles.patientIndex(1)),... handles.data.shoulder_side(handles.patientIndex(1))); set(handles.text_coincidences_info,'String',coincidences); side_sCase=handles.data.shoulder_side(handles.patientIndex); % Creating question dialog to let the user choose between the found % SCase and a new SCase label question = sprintf("%s %s %s \n%s\n%s",... "The loaded SCase",string(IPP_sCase),... "was found in the SCase list in the database.",... "Please, check the shoulder side of the currently loaded CT",... "and choose between the following options"); title = "SCase found in database"; btn1 = ['Existing case:' char(handles.sCase(1)) ' side:' char(side_sCase(1))]; btn2 = ['New case:' char(new_sCase) ' side: ?']; dfltbtn = btn2; answer= questdlg(question,title,btn1,btn2,dfltbtn); switch answer case btn1 handles.current_sCase=handles.sCase(1) answerN=1; case btn2 handles.current_sCase=string(new_sCase); answerN=2; otherwise disp("Auto Check DB -ABORTED-"); return; end disp(sprintf("%s %s",string(handles.current_sCase),"chosen")); if answerN==1 % *** Q3: is the current CT date similar to the CT_date from any of the % concident sCases in the Database? *** % Get the CT's date from the coincident sCase from the Database try CTdate_DB = datestr(handles.data.CT_date(handles.patientIndex),'dd-mmm-yyyy'); catch error message = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s",... "Suggested solution:",... "Opt1. Update InfoCT.xlsx and repeat the 'Auto Check DB'",... "Opt2. Check the CT_date yourself and use 'Manual sCase'",... "Error details:",... error.identifier,error.message); errordlg(message,"Not valid CT_date found in Database"); CTdate_DB = NaT; end % Get the CT's date from the new CT set current_CTdate = handles.myinfo.AcquisitionDate % Adapt date format to make it comparable current_CTdate=datestr([current_CTdate(1:4),'-',... current_CTdate(5:6),'-',current_CTdate(7:8)],'dd-mmm-yyyy') % Compare CT's dates if isequal(current_CTdate,CTdate_DB) % The sCase already exits so the user will have to decide if the CT % must be added to the case of it is already added. set(handles.dataCheck_text,'String',... ['Current CT matches in IPP, side and CTdate with others in the database']); set(handles.dataCheck_panel,'BackgroundColor','r'); set(handles.patient_num,'String',sprintf('%s',string(handles.current_sCase))); else % The user must check if the current CT set corresponds to a % post-op shoulder set(handles.dataCheck_text,'String',... ['Check if the CT is from a post-op. Current CT matches in IPP, side']); set(handles.dataCheck_panel,'BackgroundColor','r'); set(handles.patient_num,'String',sprintf('%s',string(handles.current_sCase))); end else % This is a new sCase!! So we write a short message and set the sCase set(handles.dataCheck_text,'String', sprintf('This is a new case')); set(handles.dataCheck_panel,'BackgroundColor','g'); handles.current_sCase=new_sCase; set(handles.patient_num,'String',sprintf('%s',string(handles.current_sCase))); end end F1=extractBefore(char(handles.current_sCase),2); F2=extractBetween(char(handles.current_sCase),2,2); F3=extractBetween(char(handles.current_sCase),3,3); current_dir = cd(char(strcat(handles.base_dir,'/',F1,'/',F2,'/',F3))); % saving current directory and changing to the output dir output_dir = pwd; % needed to update the output directory cd(current_dir); % back to the current directory set(handles.output_dir,'String',output_dir); % set(handles.output_dir,'String',strcat(handles.base_dir,F1,'/',F2,'/',F3));%sprintf('%s%s\%s\%s',base_dir,F1,F2,F3)); waitbar(1,wb,'Checking Excel Database'); guidata(hObject,handles); function manual_case_selection_Callback(hObject, eventdata, handles) %% --- Executes on button press in manual_case_selection. % hObject handle to manual_case_selection (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) sCase_dir = uigetdir(handles.base_dir, "Select all the DICOM data to load"); set(handles.output_dir,'String',sCase_dir); update_readme_Callback(handles.update_readme,eventdata,handles); warndlg('Please uptade the sCase.id accordingly with the chosen output directory') guidata(hObject,handles); function patient_num_Callback(hObject, eventdata, handles) %% Executes with 'Enter' in patient_num % hObject handle to patient_num (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of patient_num as text % str2double(get(hObject,'String')) returns contents of patient_num as a double % The output directory is stablish by the automatic Check of the DB or the % manual selection. Hence, here we only check whether the sCase number % matches with the selected output directory % From "patient_num" which corresponds to sCase number patient_num = get(handles.patient_num,'String'); % Get the sCase number F0=char(extractBefore(char(patient_num),1)) F0=char(extractBefore(char(patient_num),2)) % Extract the 'N' or 'P' F1=char(extractBetween(char(patient_num),2,2)) % Extract the first digit (1 to 9) F2=char(extractBetween(char(patient_num),3,3)) % Extract the second digit (0to9) % From "output_dir" which corresponds to output directory output_dir = get(handles.output_dir,'String'); % Get the ourput directory L = length(output_dir); % lenght of the string that contains the directory D0=char(extractBetween(char(output_dir),L-4,L-4)) % Extact the 'N' or 'P' D1=char(extractBetween(char(output_dir),L-2,L-2)) % Extract the first digit (1 to 9) D2=char(extractAfter(char(output_dir),L-1)) % Extract the second digit (0 to 9) if F0==D0 & F1==D1 & F2==D2 handles.enable_importation = 1 else handles.enable_importation = 0 warndlg('Importation cancelled. The sCase (patient number) must be consistant with the Output Directory') end guidata(hObject,handles); %% --- Executes on slider movement. function slider_2Dslice_Callback(hObject, eventdata, handles) % hObject handle to slider_2Dslice (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider handles.slice = int32(get(hObject,'Value')); set(handles.slice_num, 'String', handles.slice); handles.mydicom(:,:,handles.slice) = handles.dicomSet(handles.slice).data; axes(handles.axes_2Dviewer) % Set current axes imshow(handles.mydicom(:,:,handles.slice), [handles.CTmin handles.CTmax]) guidata(hObject,handles); function Import2database_Callback(hObject, eventdata, handles) %% --- Executes on button press in Import2database. % hObject handle to Import2database (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) sortPictures = 1; set(handles.info_text, 'String', 'Preparing DICOM images to be imported...'); drawnow % Warning message in info panel when any patient number is found if isempty(get(handles.patient_num,'String')) set(handles.info_text, 'ForegroundColor', 'red', 'FontWeight', 'bold', 'String', 'Please give a patient number') return else patient = get(handles.patient_num,'String'); end % W if isempty(get(handles.output_dir,'String')) outputdir = uigetdir(handles.home_path,"Select the Excel file with the database to be checked"); if outputdir set(handles.output_dir,'String', outputdir); end else outputdir = get(handles.output_dir,'String'); % outputdir = outputdir{1}; end if ~outputdir set(handles.info_text, 'ForegroundColor', 'red', 'FontWeight', 'bold',... 'String', 'Please select an output directoy'); return end set(handles.info_text, 'ForegroundColor', 'black', 'FontWeight', 'normal'); % Create name string newname = [patient, '-', handles.dicomSet(1).PatientID, '_',... handles.dicomSet(1).PatientName.FamilyName(1),... handles.dicomSet(1).PatientName.GivenName(1)]; if get(handles.radiobutton_sharp,'Value') newname = [newname, '-bone.']; elseif get(handles.radiobutton_smooth,'Value') newname = [newname, '-soft.']; else newname = [newname, '.']; end % Create output directory % outputdir = sprintf('%s%s%s%s%s%s%s%s%s%s%s',outputdir,'/', patient,'-',... % handles.dicomSet(1).PatientID,'/','CT-',patient,'-',... % handles.dicomSet(1).PatientID,handles.CTlabel_text.String,'/dicom/'); suggested_outputdir = sprintf('%s%s%s%s%s%s%s%s%s%s%s',outputdir,'/', patient,'-',... handles.dicomSet(1).PatientID,'/','CT-',patient,'-',... handles.dicomSet(1).PatientID,handles.CTlabel_text.String,'/dicom/'); if exist(suggested_outputdir)==7 outputdir = sprintf('%s%s%s%s%s%s%s%s%s%s%s',outputdir,'/', patient,'-',... handles.dicomSet(1).PatientID,'/','CT-',patient,'-',... handles.dicomSet(1).PatientID,'xxx','/dicom/'); else outputdir = suggested_outputdir; end mkdir(outputdir); % Sort pictures in correct order if sortPictures == 1 for h = 1:handles.N infoTemp = dicominfo(sprintf('%s%s', handles.dicomdir,... char(handles.dicomlist(h))),'UseDictionaryVR',true); SliceLoc(h,1) = h; % sprintf('%s%s', handles.dicomdir, char(handles.dicomlist(h))); SliceLoc(h,2) = infoTemp.SliceLocation; end SliceSorted = sortrows(SliceLoc,2); end guidata(hObject,handles); % Read all data % at the importation we set the dicomSet as definitively sorted handles.dicomSet=handles.dicomSetSorted; % with this sentence we could avoid the very previous sorting... for i = 1:handles.N str = sprintf('Copying dicom...\n%d / %d\n', i, handles.N); set(handles.info_text, 'String', str); drawnow % This replace the Given Name by the patient inicials and kernel info if get(handles.radiobutton_sharp,'Value') handles.dicomSet(i).PatientName.GivenName = ... [handles.dicomSet(i).PatientName.FamilyName(1),... handles.dicomSet(i).PatientName.GivenName(1), '_bone']; elseif get(handles.radiobutton_smooth,'Value') handles.dicomSet(i).PatientName.GivenName = ... [handles.dicomSet(i).PatientName.FamilyName(1),... handles.dicomSet(i).PatientName.GivenName(1), '_soft']; else handles.dicomSet(i).PatientName.GivenName = ... [handles.dicomSet(i).PatientName.FamilyName(1),... handles.dicomSet(i).PatientName.GivenName(1)]; end % This replace the Given Name by the IPP handles.dicomSet(i).PatientName.FamilyName = [patient, '_',... handles.dicomSet(1).PatientID]; % Importation dicomwrite(handles.dicomSet(i).data,[outputdir, newname,... sprintf('%04d',i), '.dcm'], handles.dicomSet(i)); end guidata(hObject,handles); % Create readme.txt file if it does not exist already localDir = string(get(handles.output_dir,'String')); localDir = sprintf('%s%s%s%s%s%s',localDir,'/', patient,'-', handles.dicomSet(1).PatientID,'/'); if ~exist(sprintf('%s%s',localDir,'readme.txt'), 'file') readme_file=fopen(sprintf('%s%s',localDir,'readme.txt'), 'wt'); fprintf(readme_file,sprintf('%s%s%s %s %s\r\n',patient,'-',... handles.dicomSet(1).PatientID,handles.initials_text.String,... handles.shoulder_side)); fprintf(readme_file,sprintf('\r\n%s',get(handles.text_readme,'String'))); fclose(readme_file); else readme_file=fopen(sprintf('%s%s',localDir,'readme.txt'), 'at'); % opening to append text fprintf(readme_file,sprintf('\r\n%s',get(handles.text_readme,'String'))); fclose(readme_file); end set(handles.info_text, 'String', 'Copied'); guidata(hObject,handles); function output_dir_Callback(hObject, eventdata, handles) %% % hObject handle to output_dir (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of output_dir as text % str2double(get(hObject,'String')) returns contents of output_dir as a double guidata(hObject,handles); function CTlabel_text_Callback(hObject, eventdata, handles) % hObject handle to CTlabel_text (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of CTlabel_text as text % str2double(get(hObject,'String')) returns contents of CTlabel_text as a double update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); function output_dir_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to output_dir (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'),... get(0,'defaultUicontrolBackgroundColor')); set(hObject,'BackgroundColor','white'); end guidata(hObject,handles); %% --- Executes during object creation, after setting all properties. function info_text_CreateFcn(hObject, eventdata, handles) % hObject handle to info_text (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called guidata(hObject,handles); %% --- Executes during object creation, after setting all properties. function DicomInfoBox_CreateFcn(hObject, eventdata, handles) % hObject handle to info_text (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called guidata(hObject,handles); %% --- Executes during object creation, after setting all properties. function slider_3Dviewer_CreateFcn(hObject, eventdata, handles) % hObject handle to slider_3Dviewer (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end guidata(hObject,handles); %% --- Executes on button press in DicomDetails. function DicomDetails_Callback(hObject, eventdata, handles) % hObject handle to DicomDetails (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) try load tempInfo.mat tempInfo handles.DicomInfoBox.String = ['Dicom Info: ',... 'Patient Sex: ', handles.dicomSet(1).PatientSex,... '. Birthday: ', handles.dicomSet(1).PatientBirthDate]; catch exception "Dicom info not available" end guidata(hObject,handles); % function checkbox_IPP_Callback(hObject, eventdata, handles) % %% --- Executes on button press in checkbox_IPP. % % hObject handle to checkbox_IPP (see GCBO) % % eventdata reserved - to be defined in a future version of MATLAB % % handles structure with handles and user data (see GUIDATA) % % % Hint: get(hObject,'Value') returns toggle state of checkbox_IPP % if get(hObject,'Value') %the IPP must be added to the readme line % try % handles.readme.IPP = string(handles.sCase); % ['IPP info to be added to the Readme line := ' handles.readme.IPP] % catch except % sprintf('%s%s','Error identifier: ',except.identifier) % ['IPP info failed to be added to the Readme line.'] % handles.readme.IPP = ''; % end % else % handles.readme.IPP = ''; % ['IPP info not present in Readme line'] % end % % update_infoFields(handles); % update_readme_Callback(handles.update_readme,eventdata,handles); % guidata(hObject,handles); function checkbox_CTdate_Callback(hObject, eventdata, handles) %% --- Executes on button press in checkbox_CTdate. % hObject handle to checkbox_CTdate (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of checkbox_CTdate if get(hObject,'Value') %the IPP must be added to the readme line try handles.readme.CTdate = string(handles.myinfo(1).AcquisitionDate); ['CTdate info to be added to the Readme line := ' handles.readme.CTdate] catch except sprintf('%s%s','Error identifier: ',except.identifier) ['CTdate info failed to be added to the Readme line.'] handles.readme.CTdate = ''; end else handles.readme.CTdate = ''; ['CTdate info not present in Readme line'] end % update_infoFields(handles); update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); function checkbox_resolution_Callback(hObject, eventdata, handles) %% --- Executes on button press in checkbox_resolution. % hObject handle to checkbox_resolution (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of checkbox_resolution if get(hObject,'Value') %the IPP must be added to the readme line try handles.readme.resolution = string(handles.myinfo(1).PixelSpacing(1)); ['Resolution info to be added to the Readme line := ' handles.readme.resolution] catch except sprintf('%s%s','Error identifier: ',except.identifier) ['Resolution info failed to be added to the Readme line.'] handles.readme.resolution = ''; end else handles.readme.resolution = ''; ['Resolution info not present in Readme line'] end % update_readme_line(handles) % update_infoFields(handles); update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); function radiobutton_shoulder_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_shoulder. % hObject handle to radiobutton_shoulder (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_shoulder handles.body = 'Shoulder'; set(handles.checkbox_contralateral,'Value',0); handles.contralateral=''; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_elbow_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_elbow. % hObject handle to radiobutton_elbow (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_elbow handles.body = 'Elbow'; set(handles.checkbox_contralateral,'Value',0); handles.contralateral=''; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_thorax_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_thorax. % hObject handle to radiobutton_thorax (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_thorax handles.body = 'Thorax'; set(handles.checkbox_contralateral,'Value',0); handles.contralateral=''; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function checkbox_contralateral_Callback(hObject, eventdata, handles) %% --- Executes on button press in checkbox_contralateral. % hObject handle to checkbox_contralateral (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of checkbox_contralateral if get(handles.checkbox_contralateral,'Value') handles.contralateral = ['Contralateral ']; else handles.contralateral = ''; end update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_otherBody_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_otherBody. % hObject handle to radiobutton_otherBody (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_otherBody set(handles.checkbox_contralateral,'Value',0); handles.contralateral=''; set(handles.edit_otherBody,'Enable','On'); handles.body = get(handles.edit_otherBody,'String'); update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function edit_otherBody_Callback(hObject, eventdata, handles) %% % hObject handle to edit_otherBody (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of edit_otherBody as text % str2double(get(hObject,'String')) returns contents of edit_otherBody as a double handles.body = get(handles.edit_otherBody,'String'); guidata(hObject,handles); function radiobutton_sharp_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_sharp. % hObject handle to radiobutton_sharp (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_sharp % % if get(hObject,'Value') % set(handles.radiobutton_none,'Value',0); % set(handles.radiobutton_smooth,'Value',0); % else % set(hObject,'Value',1); % end handles.kernel='Sharp'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_smooth_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_smooth. % hObject handle to radiobutton_smooth (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_smooth % % if get(hObject,'Value') % set(handles.radiobutton_none,'Value',0); % set(handles.radiobutton_sharp,'Value',0); % else % set(hObject,'Value',1); % end handles.kernel='Smooth'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_none_Callback(hObject, eventdata, handles) %% --- Executes on button press in radiobutton_none. % hObject handle to radiobutton_none (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_none handles.kernel='none'; warndlg(char("IMPORTANT: The operator must choose between 'Sharp (Bone)' or 'Smooth (Soft Tissue)'")); update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); function checkbox_with_phantoms_Callback(hObject, eventdata, handles) %% --- Executes on button press in checkbox_with_phantoms. % hObject handle to checkbox_with_phantoms (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of checkbox_with_phantoms handles.phantoms = 'with'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function radiobutton_without_phantoms_Callback(hObject, eventdata, handles) % --- Executes on button press in radiobutton_without_phantoms. % hObject handle to radiobutton_without_phantoms (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radiobutton_without_phantoms handles.phantoms = 'without'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); function edit_extra_Callback(hObject, eventdata, handles) %% % hObject handle to edit_extra (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'String') returns contents of edit_extra as text % str2double(get(hObject,'String')) returns contents of edit_extra as a double guidata(hObject,handles); function update_readme_Callback(hObject, eventdata, handles) %% --- Executes on button press in update_readme. % hObject handle to update_readme (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) if ~isfield(handles,'shoulder_side') handles.shoulder_side = '*Sh_Side*'; end if ~isfield(handles,'acquisition_stage') handles.acquisition_stage = '*Pre-o-Post_op*'; end % handles.patient_num always exists % handles.IPP_text % handles.CTdate_text % handles.Resolution_text % handles.CTlabel_text if ~isfield(handles,'body') handles.body = '*body*'; end if ~isfield(handles,'kernel') handles.kernel = '*kernel*'; end if ~isfield(handles,'phantoms') handles.phantoms = '*phantoms*'; end if ~isfield(handles,'contralateral') handles.contralateral = ''; end if ~isfield(handles,'N') handles.N = 0; end % choosing handles.CTlabel_text switch handles.acquisition_stage case 'Preop' if ~get(handles.checkbox_contralateral,'Value') switch handles.body case 'Shoulder' switch handles.phantoms case 'without' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-1'; case 'Smooth' handles.CTlabel_text.String = '-2'; otherwise handles.CTlabel_text.String = '-xxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end case 'with' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-3'; case 'Smooth' handles.CTlabel_text.String = '-4'; otherwise handles.CTlabel_text.String = '-xxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end otherwise handles.CTlabel_text.String = '-xxx'; end case 'Elbow' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-5'; case 'Smooth' handles.CTlabel_text.String = '-6'; otherwise handles.CTlabel_text.String = '-xxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end case 'Thorax' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-7'; case 'Smooth' handles.CTlabel_text.String = '-8'; otherwise handles.CTlabel_text.String = '-xxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end otherwise handles.CTlabel_text.String = '-x11'; end else switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-9'; case 'Smooth' handles.CTlabel_text.String = '-10'; otherwise handles.CTlabel_text.String = '-xxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end end case 'Postop' if ~get(handles.checkbox_contralateral,'Value') switch handles.body case 'Shoulder' switch handles.phantoms case 'without' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-p1'; case 'Smooth' handles.CTlabel_text.String = '-p2'; otherwise handles.CTlabel_text.String = '-pxxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end case 'with' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-p3'; case 'Smooth' handles.CTlabel_text.String = '-p4'; otherwise handles.CTlabel_text.String = '-pxxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end otherwise handles.CTlabel_text.String = '-pxxx'; end case 'Elbow' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-p5'; case 'Smooth' handles.CTlabel_text.String = '-p6'; otherwise handles.CTlabel_text.String = '-pxxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end case 'Thorax' switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-p7'; case 'Smooth' handles.CTlabel_text.String = '-p8'; otherwise handles.CTlabel_text.String = '-pxxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end otherwise handles.CTlabel_text.String = '-px11'; end else switch handles.kernel case 'Sharp' handles.CTlabel_text.String = '-p9'; case 'Smooth' handles.CTlabel_text.String = '-p10'; otherwise handles.CTlabel_text.String = '-pxxx'; warndlg('Operator must choose between Sharp or Smooth kernel'); end end otherwise handles.CTlabel_text.String = '-xxx'; end RL = sprintf('%s%s%s%s%s %s %s %s %s %s %s %s %s ','CT-',... handles.patient_num.String,'-',... handles.IPP_text.String,... handles.CTlabel_text.String,... handles.contralateral,... handles.body,... handles.kernel,... handles.phantoms,'phantoms',... handles.Resolution_text.String,... string(handles.N),'slices'),... get(handles.edit_extra,'String'); set(handles.text_readme,'String',RL); guidata(hObject,handles); function add_newSCase_excelDB_Callback(hObject, eventdata, handles) %% --- Executes on button press in add_newSCase_excelDB. % hObject handle to add_newSCase_excelDB (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Text info to the command window sprintf('%s:\n%s, %s, %s, %s,\n%s, %s, %s,',"The parameters to write in the Excel Database are",... "SCase_ID","SCase_idx","anonymity_IPP","anonymity_initials",... "anonymity_bithDate","patient_gender","shoulder_side") % The file SCaseImport.xlsx must be located inside the subfolder % xlsFromMatlab/ which is in the same directory of the main Database Excel ExcelImport_fullname = sprintf('%s%s',handles.ExcelDB_path,"xlsFromMatlab/SCaseImport.xlsx"); sprintf("%s%s", "Writting in file ", ExcelImport_fullname) % In order to avoid the repetition of any SCase we have to read and check % the SCaseImport.xlsx file: [~,txt,raw,dates]=xlsread(ExcelImport_fullname,'SCaseImport','','',@convertSpreadsheetExcelDates); % Looking for the "desired new SCase" in the existing list of SCases of % SCaseImport.xlsx % Removing void rows raw=raw(find(string(raw(:,1))~=''),:); found_SCase_Index = find(string(raw(:,1))==handles.patient_num.String); new_SCase_Index = length(string(raw(:,1)))+1; % If there is no coincidence we set the desired SCase as a new one and can % allow to write the new line of Import Parameters. Otherwise we will ask % to overwrite the existing line of parameters or doing nothing. if isempty(found_SCase_Index) SCase_Index = new_SCase_Index; else question = "The chosen SCase already exist. Do you want to overwrite it?"; title = "WARNING: SCase repeated"; btn1 = "Cancel"; btn2 = "Overwrite"; dfltbtn = "Cancel"; answer = questdlg(question,title,btn1,btn2,dfltbtn); % Handle response switch answer case "Overwrite" SCase_Index = found_SCase_Index; case "Cancel" disp("new SCase parameter line aborted") return; otherwise disp("new SCase parameter line aborted") return; end end % I don't know why this writting is taking a wrong ROW rowNumber=SCase_Index; new_SCase_ID = {handles.patient_num.String}; % Wrtitting SCase_ID SCaseCell=sprintf('%s%i','A',rowNumber); % % Security Check: avoiding doubling SCases % if isempty(find(string(handles.data.SCase_ID)==string(new_SCase_ID))) % "SCase_ID written" % xlswrite(ExcelImport_fullname,new_SCase_ID,'SCaseImport',SCaseCell) % else % "The selected SCase_ID is already present in the Database" % return % end % Writting SCase_idx IdxCell=sprintf('%s%i','B',rowNumber); xlswrite(ExcelImport_fullname,rowNumber,'SCaseImport',IdxCell) % Wrtitting anonymity_IPP IPP_number = str2num(handles.IPP_text.String); IPPCell=sprintf('%s%i','C',rowNumber); xlswrite(ExcelImport_fullname,IPP_number,'SCaseImport',IPPCell) % Wrtitting anonymity_initials Initials={extractBefore(extractAfter(handles.initials_text.String,'('),')')}; InitialsCell=sprintf('%s%i','D',rowNumber); xlswrite(ExcelImport_fullname,Initials,'SCaseImport',InitialsCell) % Wrtitting anonymity_birthDate BirthDate ={[handles.birthdate_text.String(1:4) ... '-' handles.birthdate_text.String(5:6) ... '-' handles.birthdate_text.String(7:8)]}; BirthDateCell=sprintf('%s%i','E',rowNumber); xlswrite(ExcelImport_fullname,BirthDate,'SCaseImport',BirthDateCell) % Wrtitting patient_gender GenderCell=sprintf('%s%i','F',rowNumber); Patient_Gender = handles.gender_text.String; xlswrite(ExcelImport_fullname,Patient_Gender,'SCaseImport',GenderCell) % Wrtitting shoulder_side ShSideCell=sprintf('%s%i','G',rowNumber); Shoulder_Side = handles.shoulder_side(1); xlswrite(ExcelImport_fullname,Shoulder_Side,'SCaseImport',ShSideCell) %launch 'dicominfoSCase.m' to update CTdicomInfo CSV and XLS currentFolder=pwd; cd([currentFolder '/..']) dicominfoSCase % Lauched without arguments a avoid current error (1'20") cd(currentFolder) msgbox("Operation completed!. Remember to add manually SCase_ID and SCase_idx") %% Internal functions function update_readme_line(handles) %% -- Updates readme line % Complete the line to be writen in the readme file as: % CT-P454-513554-1 sharp shoulder without phantoms 0.351562 mm 137 slices try handles.readme.IPP = handle.myinfo.PatientID; handles.readme.CTdate = handles.myinfo.AcquisitionDate; handles.readme.CT_label = handles.CTlabel_text; if handles.radiobutton_sharp handles.readme.kernel = 'Sharp (BONE)'; elseif handles.radiobutton_smooth handles.readme.kernel = 'Smooth (SOFT)'; else handles.readme.kernel = 'None'; end handles.readme.line = ['CT-' handles.readme.IPP '-' handles.readme.CT_label... ' ' handles.readme.kernel ' ' handles.readme.body ' '... handles.readme.phantoms ' ' handles.readme.resolution ' ' ... handles.readme.slices ' ' handles.readme.stage ' ' handles.readme.extra... ' '] catch except sprintf('%s%s','Error identifier: ',except.identifier) 'fields lacking' end guidata(hObject,handles); function sortedST = sortStructByField(ST,field,handles) try "internal fuction activated" STfields = fieldnames(ST); STcell = struct2cell(ST); sz = size(STcell); % Notice that this is a 3 dimensional array. % For MxN structure array with P fields, the size % of the converted cell array is PxMxN % Once it's a cell array, you can sort using sortrows: % Convert to a matrix STcell = reshape(STcell, sz(1), []); % Px(MxN) % Make each field a column STcell = STcell'; % (MxN)xP column=0 % Sort by field for k=1:length(STfields) if isequal(STfields(k),field) column = k; end end % fieldName=char(STfields(1)) % field=char(field) % fieldColum = find(fieldName(1:length(field))==field) if column == 0 ['Warning: field ' char(field) ' not found in dicom metadata'] sortedST = ST; return else fieldColumn = column; STcell = sortrows(STcell, fieldColumn); %And convert it back to a structure array: % Put back into original cell array format STcell = reshape(STcell', sz); % Convert to Struct STsorted = cell2struct(STcell, STfields, 1); sortedST = STsorted; end catch except except.identifier except.message ['exception arised during dicom sorting by field'] end % function update_infoFields(hObject, handles) % %% Updating CT labelling % % if and(get(handles.patient_num,'String') ~='P###',exist([handles.output_dir '/readme'])==2) % % set(handles.text_readme, "listo para leer") % % end % guidata(hObject,handles); %% Functions to be removed function slider_2Dcontrast_Callback(hObject, eventdata, handles) %% --- Executes on slider movement. % hObject handle to slider_2Dcontrast (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider if ~isfield(handles, 'mydicom'); return end handles.CTmax = (get(handles.slider_2Dbrightness,'Max') + get(handles.slider_2Dbrightness,'Min') - get(handles.slider_2Dbrightness,'Value')) + (get(hObject,'Max') - get(hObject,'Value'))/2; handles.CTmin = (get(handles.slider_2Dbrightness,'Max') + get(handles.slider_2Dbrightness,'Min') - get(handles.slider_2Dbrightness,'Value')) - (get(hObject,'Max') - get(hObject,'Value'))/2; if handles.CTmax > handles.bigmax handles.CTmax = handles.bigmax; end if handles.CTmin < handles.bigmin handles.CTmin = handles.bigmin; end imshow(handles.mydicom(:,:,handles.slice), [handles.CTmin handles.CTmax]); guidata(hObject,handles) function slider_2Dcontrast_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to slider_2Dcontrast (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end guidata(hObject,handles); function slider_2Dbrightness_Callback(hObject, eventdata, handles) %% --- Executes on slider movement. % hObject handle to slider_2Dbrightness (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: get(hObject,'Value') returns position of slider % get(hObject,'Min') and get(hObject,'Max') to determine range of slider if ~isfield(handles, 'mydicom'); return end handles.CTmax = (get(hObject,'Max') + get(hObject,'Min')... - get(hObject,'Value')) + (get(handles.slider_2Dcontrast,'Max')... - get(handles.slider_2Dcontrast,'Value'))/2; handles.CTmin = (get(hObject,'Max') + get(hObject,'Min')... - get(hObject,'Value')) - (get(handles.slider_2Dcontrast,'Max')... - get(handles.slider_2Dcontrast,'Value'))/2; if handles.CTmax > handles.bigmax handles.CTmax = handles.bigmax; end if handles.CTmin < handles.bigmin handles.CTmin = handles.bigmin; end imshow(handles.mydicom(:,:,handles.slice), [handles.CTmin handles.CTmax]); guidata(hObject,handles); function slider_2Dbrightness_CreateFcn(hObject, eventdata, handles) %% --- Executes during object creation, after setting all properties. % hObject handle to slider_2Dbrightness (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: slider controls usually have a light gray background. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end guidata(hObject,handles); diff --git a/dicominfoSCase.m b/dicominfoSCase.m index bb2bff7..d8c16b1 100755 --- a/dicominfoSCase.m +++ b/dicominfoSCase.m @@ -1,545 +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); + dataDir = uigetdir(pwd,"Please select the directory 'data or dataDev'"); 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(dataDir); % All SCases in data dir with valid dicom CT dir update = 0; elseif nargin == 1 update = 1; inputArg = varargin{1}; 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 => 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/openConfigFile.m b/openConfigFile.m index 6052c5d..756ceb7 100644 --- a/openConfigFile.m +++ b/openConfigFile.m @@ -1,47 +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); + dataDir = uigetdir(pwd,"Please select the directory 'data or dataDev'"); end end