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 20-Feb-2019 10:59:13 %% 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 % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % From here this script has been edited by Jorge Solana Mu?oz, working at % the Laboratory of Biomechanical Orthopedics at EPFL (4.4.2019) % This function opens the grafical interfaces using the FIG file with the % same name of the current one 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 2019/04/04 % Setting default values for some global variables % Default directory = the current directory handles.script_dir = pwd; % Radiobuttons default values 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'); % Information extra added to the output README file handles.extra_info='.'; % Updates global variables guidata(hObject,handles); % Function definition needed for the interface. Don't edit it. 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. % Those functions define graphical parameters %% --- Executes during object creation, after setting all properties. function patient_num_CreateFcn(hObject, eventdata, handles) % 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 % Updates global variables guidata(hObject,handles); %% --- Executes during object creation, after setting all properties. function edit_otherBody_CreateFcn(hObject, eventdata, handles) % 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 % Updates global variables guidata(hObject,handles); %% --- Executes during object creation, after setting all properties. function edit_extra_CreateFcn(hObject, eventdata, handles) % 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...'); % Updates global variables guidata(hObject,handles); %% ====================================================================== %% ********************************************************************** %% "CALLBACK Functions" execute on hObject's action events %% ********** Start Pending Directory / potentialy new SCase ************** %% --- Executes on button press in Select_Pending_Directory. function Select_Pending_Directory_Callback(hObject, eventdata, handles) % hObject handle to Select_Pending_Directory (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 pending folder containing a % group of CTs. It counts the files per subfolder to clasify them %% Choose the main folder for the desired case % This open a navigation window allowing the user to choose a new directory if isfield(handles,'home_dir') % After the first use of this button, the software remembers the home % directory, saving time to the user newPath = uigetdir(handles.home_dir,'Chose the folder containing the all the CT sets'); else % If the home directory saved is wrong or is the first CT load the % current working directory is temporaly used as home directory newPath = uigetdir(pwd,'Chose the folder containing all the CT sets'); end % The new directory is saved as "patient directory" (handles.patien_dir) current_dir = pwd; cd([newPath '/..']); handles.patient_dir = pwd; cd(current_dir); % Finding "data" directory from "patient directory" and defining "home" and % "data" directories % All those paths will be needed further in this script handles.home_dir = extractBefore(handles.patient_dir,'data'); data_dir_name = extractAfter(handles.patient_dir,handles.home_dir); data_dir_name = extractBefore(data_dir_name,'\'); handles.data_dir = sprintf('%s%s',handles.home_dir,data_dir_name); handles.home_path = handles.home_dir; %% Set the patient group from directory % Since the patient group is coded in the directory path, the folowing % lines extract the patient group information from it and define the % correspondign global variable (handles.patient_group) patient_group = newPath; patient_group = extractAfter(patient_group,'\data'); patient_group = extractAfter(patient_group,'\'); patient_group = extractBefore(patient_group,'\'); handles.patient_group = patient_group; % Call to another function which ask the user whether the 3D visualization % should be activated or not act3Dview_Callback(handles.act3Dview, eventdata, handles); % Updates the field "patient_num" with the "patient group" information patient_num = get(handles.patient_num,'String'); set(handles.patient_num,'String',[handles.patient_group patient_num(2:end)]); %% Select a limit of minimun dicom files to discard scout scans & other % small scans as elbows % every CT with <30 DICOM files will be considered a scout scan scoutScanLimit = 30; % every CT with >29 and <90 DICOM files will be considered a potential % elbow scan smallScanLimit = 90; %% Check the chosen path and extract data from every folder and file inside dirList = dir(newPath); % Select only the folders from the current path and create a list 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 % A "waitbar" is prompted showing the progres to the user wb = waitbar(0,'Checking folders size'); for k=1:folderN % *files* will contain a struct with data of files in the desired % folder % updating the "waitbar" waitbar(k/folderN,wb,sprintf('%s%s%s%i%s%i',"Checking directory ",... string(folderList(k).name),": ",k,"/",folderN)); % Creates a list of files present in the folder files=dir([folderList(k).folder '/' folderList(k).name]); filesDicom = 0; % Initialitation of dicom files counter for n=1:length(files) % if there is a file and this file is a DICOM file, counts 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 don't count it files(n).isdicom = 0; end end % Each folder has its own counter of dicom files folderList(k).dicomN = filesDicom; % Adds a prefix with the number of files per CT, helping to prioritize % 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 %% This part could be removed since with the count of DICOM files per % folder should be enough information for the user % 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, the removable part%% end % This shows the path to the database used, in an info-box set(handles.text_data_dir,'String',... sprintf('%s%s','Database directory ',string(handles.data_dir))); % Updates global variables guidata(hObject,handles); %% ************** Select CT scan ************** %% --- Executes on button press in Select_CT_Scan. function Select_CT_Scan_Callback(hObject, eventdata, handles) % hObject handle to Select_CT_Scan (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 % Also remove the fields 'myinfo' and 'mydicom' 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 % A navigator windows askes the user to select the DICOM files to be loaded % Using last patient directory if isfield(handles,'patient_dir') [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',handles.patient_dir,'MultiSelect', 'on'); % When no "patient directory" is present, this uses the "database" path elseif isfield(handles,'data_dir') [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',handles.data_dir,'MultiSelect', 'on'); % Finding patient_dir current_dir = pwd; cd([handles.dicomdir '/../..']); handles.patient_dir = pwd; cd(current_dir); % If neither the "database" path is recorded, this uses the "home % directory" elseif isfield(handles,'home_dir') [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',handles.home_dir,'MultiSelect', 'on'); % Finding patient_dir current_dir = pwd; cd([handles.dicomdir '/../..']); handles.patient_dir = pwd; cd(current_dir); % Finding "data" directory from patient directory data_dir_name = extractAfter(handles.patient_dir,handleshome_dir); data_dir_name = extractBefore(data_dir_name,'\'); handles.data_dir = sprintf('%s%s',handles.home_dir,data_dir_name); handles.home_path = handles.home_dir; else % When even the "home directory" is missing, this uses the current % working directory [handles.dicomlist, handles.dicomdir] = uigetfile('*.*',... 'Select the images to load',pwd,'MultiSelect', 'on'); % Finding patient_dir current_dir = pwd; cd([handles.dicomdir '/../..']); handles.patient_dir = pwd; cd(current_dir); % Finding "data" directory from patient directory handles.home_dir = extractBefore(handles.patient_dir,'data'); data_dir_name = extractAfter(handles.patient_dir,handles.home_dir); data_dir_name = extractBefore(data_dir_name,'\'); handles.data_dir = sprintf('%s%s',handles.home_dir,data_dir_name); handles.home_path = handles.home_dir; end % If the "patient group" has not been obtained yet, this gets it from the % chosen path if ~isfield(handles,'patient_group') %% Set the patient group from directory patient_group = handles.dicomdir; patient_group = extractAfter(patient_group,'\data'); patient_group = extractAfter(patient_group,'\'); patient_group = extractBefore(patient_group,'\'); handles.patient_group = patient_group; % Call to another function which ask the user whether the 3D visualization % should be activated or not act3Dview_Callback(handles.act3Dview, eventdata, handles); patient_num = get(handles.patient_num,'String'); set(handles.patient_num,'String',[handles.patient_group patient_num(2:end)]); end % When only one DICOM files is selected this selects all DICOMs present % This is a quick way to select the whole CT scan if length(string(handles.dicomlist))==1 files=dir(handles.dicomdir); filesDicom = 0; % Initialitation of dicom files counter handles.dicomlist=[]; 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]) handles.dicomlist = [handles.dicomlist {files(n).name}]; end end end % Showing the dicom name in the interface set(handles.text_dicom_name,'String',... sprintf('%s%s',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 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) % 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,{'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') 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 % removes previous object "mydicom" if existed if isfield(handles,'mydicom') handles=rmfield(handles,'mydicom'); end % load *handles.mydicom* with the current slice image handles.mydicom(:,:,handles.slice) = handles.dicomSet(handles.slice).data; % Setting some visualization parammeters 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 % finaly shows the current CT slice image imshow(handles.dicomSet(handles.slice).data, [handles.CTmin handles.CTmax]); %% ISOSURFACE section ==== 3D ======================================== % Call to another function which ask the user whether the 3D visualization % should be activated or not act3Dview_Callback(handles.act3Dview, eventdata, handles); axes(handles.axes_2Dviewer); % Set current axes % Updates global variables guidata(hObject,handles); %% Reset the DB chech result info box set(handles.text_coincidences_info,'String',"Check DB for coincidences..."); set(handles.info_text, 'String', 'CT loaded'); set(handles.info_add_SCase, 'String', ''); set(handles.text_data_dir,'String',... sprintf('%s%s','Database directory ',string(handles.data_dir))); 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)); 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) % New way of reading the Excel Database: try DBpath = sprintf('%s%s',handles.data_dir,'\Excel\xlsFromMatlab\'); excelDB= "SCaseImport.xlsx"; catch [excelDB, DBpath, ~] = uigetfile('*.xlsx','Select the Excel Database File',pwd); end wb = waitbar(0,'Checking Excel DataBase'); "Openning Excel Database" 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,'SCaseImport','','',@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'); 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 string(sCase_list(end,:))=="" | ismissing(string(sCase_list(end,:)))% Last position empty... sCase_list = sCase_list(1:end-1,:); s0=s0+1; waitbar(s0/s1,wb,'Checking Excel Database'); end handles.DB_length=length(sCase_list); sCase_N=[]; sCase_P=[]; sCase_list_sorted=string(sCase_list); % This have to be cleaned for n=1:s1-s0 if sCase_list_sorted(n,:)~="" & ~ismissing(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(1:handles.DB_length)); handles.data.shoulder_side=cell2mat(handles.data.shoulder_side(1:handles.DB_length)); % *** 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.data_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.data_dir,F1,'/',F2,'/',F3));%sprintf('%s%s\%s\%s',data_dir,F1,F2,F3)); waitbar(1,wb,'Checking Excel Database'); guidata(hObject,handles); %% --- Executes on button press in Manual_SCase. function Manual_SCase_Callback(hObject, eventdata, handles) % hObject handle to Manual_SCase (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.data_dir, "Select the output directory",handles.data_dir); 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); %% Executes with 'Enter' in patient_num function patient_num_Callback(hObject, eventdata, handles) % 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]) % update slice name info set(handles.text_dicom_name,'String',... sprintf('%s%s%s','DICOM File Full-name: ',string(handles.dicomdir),... string(handles.dicomlist(str2num(handles.slice_num.String))))); guidata(hObject,handles); %% --- Executes on button press in Anonymise_Import. function Anonymise_Import_Callback(hObject, eventdata, handles) % hObject handle to Anonymise_Import (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 no patient's 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 % Warning message in info panel when no output dir is found if isempty(get(handles.output_dir,'String')) outputdir = uigetdir(handles.home_path,"Select the output directory where the CT must be imported"); if outputdir set(handles.output_dir,'String', outputdir); end else outputdir = get(handles.output_dir,'String'); 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 % Checking the existance of a folder with similar SCase_ID and different % IPP if exist(outputdir)==7 f=dir(outputdir); if length(f)>2 % When a subfolder is present this length is >=3 for n=3:length(f) SC{n-2}=extractBefore(f(n).name,'-'); IPP{n-2}=extractAfter(f(n).name,'-'); end SC=string(SC); IPP=string(IPP); SC_dx = find(SC==string(patient)); if ~isempty(SC_dx) if IPP(SC_dx(1))~=string(handles.dicomSet(1).PatientID) errordlg(sprintf("%s %s %s %s", "SCase =",string(patient),... "present with a IPP = ", IPP(SC_dx)),... 'Current SCase_ID already in the directories database'); set(handles.info_text, 'String', 'Preparing DICOM images to be imported...'); drawnow return end end end end 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 % Creating the output directory 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; 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', 'CT Imported'); 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); %% --- Executes on button press in checkbox_CTdate. function checkbox_CTdate_Callback(hObject, eventdata, handles) % 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_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); %% --- Executes on button press in checkbox_resolution. function checkbox_resolution_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in radiobutton_shoulder. function radiobutton_shoulder_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in radiobutton_elbow. function radiobutton_elbow_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in radiobutton_thorax. function radiobutton_thorax_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in checkbox_contralateral. function checkbox_contralateral_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in radiobutton_otherBody. function radiobutton_otherBody_Callback(hObject, eventdata, handles) % 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); %% --- Executes on button press in radiobutton_sharp. function radiobutton_sharp_Callback(hObject, eventdata, handles) % 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 handles.kernel='Sharp'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); %% --- Executes on button press in radiobutton_smooth. function radiobutton_smooth_Callback(hObject, eventdata, handles) % 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 handles.kernel='Smooth'; update_readme_Callback(handles.update_readme, eventdata, handles); guidata(hObject,handles); %% --- Executes on button press in radiobutton_none. function radiobutton_none_Callback(hObject, eventdata, handles) % 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 handles.extra_info=handles.edit_extra.String; update_readme_Callback(handles.update_readme,eventdata,handles); guidata(hObject,handles); %% --- Executes on button press in update_readme. function update_readme_Callback(hObject, eventdata, handles) % 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 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 from radiobutton option chosen by user 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',... handles.extra_info); set(handles.text_readme,'String',RL); guidata(hObject,handles); %% --- Executes on button press in add_SCase_toDB. function add_SCase_toDB_Callback(hObject, eventdata, handles) % hObject handle to add_SCase_toDB (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % This function writes a new line inside the file "SCaseImport.xlsx" (F1) % and launches the script "dicominfoSCase.m" (F2) which read one CT from % SCase present in the Database to fill the corresponding DICOM parameters % in the main Excel File "ShoulderDataBase.xlsx" %% F1 - writting the new line inside the file "SCaseImport.xlsx" % Collecting new row parameters and translation to Excel compatible format try new_SCase_ID = {handles.patient_num.String}; IPP_number = str2num(handles.IPP_text.String); Initials={extractBefore(extractAfter(handles.initials_text.String,'('),')')}; BirthDate = {[handles.birthdate_text.String(1:4) ... '-' handles.birthdate_text.String(5:6) ... '-' handles.birthdate_text.String(7:8)]}; Patient_Gender = handles.gender_text.String; Shoulder_Side = handles.shoulder_side(1); CT_date = {[handles.CTdate_text.String(1:4) ... '-' handles.CTdate_text.String(5:6) ... '-' handles.CTdate_text.String(7:8)]}; %handles.dicomSet(1).AcquisitionDate; % Question dialog box to allow the user to validate the data or abort quest = sprintf('%s:\n%s, %s, %s, %s,\n%s, %s, %s, %s.\n %s',... "Please confirm if you want to add the followin line to the Excel Database",... string(new_SCase_ID),'SCase_idx*',string(IPP_number),... string(Initials),string(BirthDate),string(Patient_Gender),... string(Shoulder_Side),string(CT_date),'* to be determined while updating'); btn1 = 'Confirm'; btn2 = 'Abort'; defbtn = 'Abort'; catch error % A comun error arrives when the function is launched before selecting % every option in the script graphical interface warndlg("Some parameters are maybe missing. Action Aborted") error return end % The file SCaseImport.xlsx must be located inside the subfolder % xlsFromMatlab/ which is in the same directory of the main Database Excel try % If the Excel directory has been correctly stored before, we can % find the excel file without problems and continue if isfield(handles,'ExcelDB_path') ExcelImport_fullname = sprintf('%s%s',handles.ExcelDB_path,"SCaseImport.xlsx"); sprintf("%s%s", "Writting in file ", ExcelImport_fullname) title = sprintf('%s %s','Adding new SCase row to Excel file:',ExcelImport_fullname); % A dialog box opens to let the operator validate the data answer = questdlg(quest,title,btn1,btn2,defbtn); % Else, if at least the "data" or "dataDev" directory has been stored, % we can still find the excel file and continue elseif isfield(handles,'data_dir') title = sprintf('%s %s','Adding new SCase row to Excel file:','FILE NOT FOUND'); % A dialog box opens to let the operator validate the data answer = questdlg(quest,title,btn1,btn2,defbtn); [excelDB, DBpath, ~] = uigetfile('*.xlsx','Select the Excel Database File',handles.data_dir); handles.ExcelDB_path = DBpath; ExcelImport_fullname = sprintf('%s%s',handles.ExcelDB_path,"SCaseImport.xlsx"); sprintf("%s%s", "Writting in file ", ExcelImport_fullname) else title = sprintf('%s %s','Adding new SCase row to Excel file:','FILE NOT FOUND'); quest = sprintf('%s \n%s','WARNING abort action recommended. Any DICOM info loaded.',quest); answer = questdlg(quest,title,btn1,btn2,defbtn); [excelDB, DBpath, ~] = uigetfile('*.xlsx','Select the Excel Database File',pwd); handles.ExcelDB_path = DBpath; ExcelImport_fullname = sprintf('%s%s',handles.ExcelDB_path,"SCaseImport.xlsx"); sprintf("%s%s", "Writting in file ", ExcelImport_fullname) end catch error errordlg("database directory not found"); end switch answer case 'Abort' warningdlg("Action aborted"); case 'Confirm' % Text info to the command window info=sprintf('%s:\n%s, %s, %s, %s,\n%s, %s, %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",'and',"CT_date") set(handles.info_add_SCase,'String',info) % 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))~=''),:); % If there is no coincidence we set the desired SCase as a new one and can % allow to write the new line of Importation Parameters. Otherwise we will ask % to overwrite the existing line of parameters or doing nothing. found_SCase_Index = find(string(raw(:,1))==handles.patient_num.String); new_SCase_Index = handles.DB_length +2; %length(string(raw(:,1)))+1; 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 rowNumber=SCase_Index; % Writting SCase_ID "Writting SCase_ID" SCaseCell = sprintf('%s%i','A',rowNumber); xlswrite(ExcelImport_fullname,new_SCase_ID,'SCaseImport',SCaseCell) % Writting SCase_idx "Writting SCase_idx" IdxCell = sprintf('%s%i','B',rowNumber); xlswrite(ExcelImport_fullname,rowNumber,'SCaseImport',IdxCell) % Writting anonymity_IPP IPPCell=sprintf('%s%i','C',rowNumber); xlswrite(ExcelImport_fullname,IPP_number,'SCaseImport',IPPCell) % Writting anonymity_initials InitialsCell=sprintf('%s%i','D',rowNumber); xlswrite(ExcelImport_fullname,Initials,'SCaseImport',InitialsCell) % Writting anonymity_birthDate BirthDateCell=sprintf('%s%i','E',rowNumber); xlswrite(ExcelImport_fullname,BirthDate,'SCaseImport',BirthDateCell) % Writting patient_gender GenderCell = sprintf('%s%i','F',rowNumber); xlswrite(ExcelImport_fullname,Patient_Gender,'SCaseImport',GenderCell) % Writting shoulder_side ShSideCell = sprintf('%s%i','G',rowNumber); xlswrite(ExcelImport_fullname,Shoulder_Side,'SCaseImport',ShSideCell) % Writting CT_date CTdateCell = sprintf('%s%i','H',rowNumber); xlswrite(ExcelImport_fullname,CT_date,'SCaseImport',CTdateCell) %% launch 'dicominfoSCase.m' to update CTdicomInfo CSV and XLS cd([handles.script_dir '/..']) dicominfoSCase % Lauched without arguments to avoid current error (1'20") cd(handles.script_dir) msgbox("Operation completed!") otherwise warndlg("Action aborted"); end %%*********************************************** %% 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 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 %% Functions to be removed or reused 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);