|  | 
|  | 1 | +function res_table = measure_LCD_single_dose(signal_present_img, signal_absent_img, observers, ground_truth, offset, n_reader, n_inserts, insert_r, load_coords_filename) | 
|  | 2 | +    % Calculate low contrast detectability using signal-present and signal-absent images. | 
|  | 3 | +    % | 
|  | 4 | +    % :param signal_present_img: The signal-present image | 
|  | 5 | +    % :param signal_absent_img: The signal-absent image (signal-free) | 
|  | 6 | +    % :param observers: List of observer objects or strings of the name of observers | 
|  | 7 | +    % :param ground_truth: Image or filename of the image with no noise of MITA LCD phantom | 
|  | 8 | +    % :param offset: An optional offset subtracted from each image (default 0) | 
|  | 9 | +    % :param n_reader: Number of readers (default is 10) | 
|  | 10 | +    % :param n_inserts: Number of inserts to be selected manually | 
|  | 11 | +    % :param insert_r: The radius of the inserts (same for all inserts) | 
|  | 12 | +    % :param load_coords_filename: Optional filename to load coordinates from (if it exists) | 
|  | 13 | +    % | 
|  | 14 | +    % :return res_table: Table ready for saving or plotting | 
|  | 15 | + | 
|  | 16 | +    %% Define observers | 
|  | 17 | +    if ~exist('observers', 'var') | 
|  | 18 | +        observers = {LG_CHO_2D()};  % Default observer | 
|  | 19 | +    end | 
|  | 20 | +    pct_split = 0.5;  | 
|  | 21 | +    seed_split = randi(10000, n_reader, 1); | 
|  | 22 | + | 
|  | 23 | +    %% Load or select coordinates | 
|  | 24 | +    if exist(load_coords_filename, 'file') | 
|  | 25 | +        % Load previously saved coordinates | 
|  | 26 | +        coords_data = load(load_coords_filename); | 
|  | 27 | +        x_coords = coords_data.x_coords; | 
|  | 28 | +        y_coords = coords_data.y_coords; | 
|  | 29 | +        disp('Loaded coordinates from file.'); | 
|  | 30 | +    else | 
|  | 31 | +        % Manually select the coordinates for each insert using ginput | 
|  | 32 | +        centerSliceIndex = round(size(signal_present_img, 3) / 2); | 
|  | 33 | +        figure; imagesc(signal_present_img(:, :, centerSliceIndex)); colormap gray; axis off; axis tight; axis equal;  | 
|  | 34 | +        title('Select centers of the inserts'); | 
|  | 35 | +        disp(['Please select ', num2str(n_inserts), ' insert locations']); | 
|  | 36 | +        [x_coords, y_coords] = ginput(n_inserts);  % Get n_insert points manually | 
|  | 37 | +         | 
|  | 38 | +        % Automatically save the coordinates to a file | 
|  | 39 | +        coords_data.x_coords = x_coords; | 
|  | 40 | +        coords_data.y_coords = y_coords; | 
|  | 41 | +        save(load_coords_filename, '-struct', 'coords_data'); | 
|  | 42 | +    end | 
|  | 43 | + | 
|  | 44 | +    % Use the same radius for all inserts, defined by the input parameter `insert_r` | 
|  | 45 | +    insert_rs = ones(1, n_inserts) * insert_r;  % Set the same radius for all inserts | 
|  | 46 | + | 
|  | 47 | +    %% Subtract offset from signal-present and signal-absent images | 
|  | 48 | +    sp_raw_array = signal_present_img - offset; | 
|  | 49 | +    sa_raw_array = signal_absent_img - offset; | 
|  | 50 | + | 
|  | 51 | +    % Initialize arrays for storing results | 
|  | 52 | +    observer = []; | 
|  | 53 | +    dose_level = []; | 
|  | 54 | +    snr = []; | 
|  | 55 | +    auc = []; | 
|  | 56 | +    reader = []; | 
|  | 57 | +    insert_HU = []; | 
|  | 58 | +    insert_diameter_pix = []; | 
|  | 59 | + | 
|  | 60 | +    %% Iterate through inserts and observers | 
|  | 61 | +    for i = 1:length(observers) | 
|  | 62 | +        model_observer = observers{i}; | 
|  | 63 | + | 
|  | 64 | +        for insert_idx = 1:n_inserts | 
|  | 65 | +            % Get the manually selected insert coordinates and radius | 
|  | 66 | +            x_center = round(x_coords(insert_idx)); | 
|  | 67 | +            y_center = round(y_coords(insert_idx)); | 
|  | 68 | + | 
|  | 69 | +            % Update observer properties (if necessary) | 
|  | 70 | +            if isa(model_observer, "LG_CHO_2D") | 
|  | 71 | +                model_observer.channel_width = 2 / 3 * insert_r;  % Update channel width for LG_CHO observer | 
|  | 72 | +            end | 
|  | 73 | + | 
|  | 74 | +            %% Extract regions of interest (ROIs) using the manually selected center and radius | 
|  | 75 | +            sp_imgs = get_ROI_from_manual_selection(sp_raw_array, x_center, y_center, insert_r); | 
|  | 76 | +            sa_imgs = get_ROI_from_manual_selection(sa_raw_array, x_center, y_center, insert_r); | 
|  | 77 | + | 
|  | 78 | +            % Remove DC component (mean intensity) from images | 
|  | 79 | +            for i_sp = 1:size(sp_imgs, 3) | 
|  | 80 | +                sp_imgs(:, :, i_sp) = sp_imgs(:, :, i_sp) - mean(sp_imgs(:, :, i_sp), 'all'); | 
|  | 81 | +            end | 
|  | 82 | +            for i_sa = 1:size(sa_imgs, 3) | 
|  | 83 | +                sa_imgs(:, :, i_sa) = sa_imgs(:, :, i_sa) - mean(sa_imgs(:, :, i_sa), 'all'); | 
|  | 84 | +            end | 
|  | 85 | + | 
|  | 86 | +            %% Perform observer study | 
|  | 87 | +            for n = 1:n_reader | 
|  | 88 | +                % Split the data into training and testing sets | 
|  | 89 | +                [sa_train, sa_test, sp_train, sp_test] = train_test_split(sa_imgs, sp_imgs, pct_split, seed_split(n)); | 
|  | 90 | + | 
|  | 91 | +                % Perform the observer study | 
|  | 92 | +                res = model_observer.perform_study(sa_train, sp_train, sa_test, sp_test); | 
|  | 93 | + | 
|  | 94 | +                % Store the results | 
|  | 95 | +                observer = [observer; string(model_observer.type)]; | 
|  | 96 | +                insert_HU = [insert_HU; mode(ground_truth(y_center, x_center))];  % Use the ground truth at the selected point | 
|  | 97 | +                insert_diameter_pix = [insert_diameter_pix; 2 * insert_r]; | 
|  | 98 | +                dose_level = [dose_level; 1];  % Since you have only one dose level, set it to 1 | 
|  | 99 | +                snr = [snr; res.snr]; | 
|  | 100 | +                auc = [auc; res.auc]; | 
|  | 101 | +                reader = [reader; n]; | 
|  | 102 | +            end | 
|  | 103 | +        end | 
|  | 104 | +    end | 
|  | 105 | + | 
|  | 106 | +    % Return results as a table | 
|  | 107 | +    res_table = table(observer, insert_HU, dose_level, snr, auc, reader, insert_diameter_pix); | 
|  | 108 | +end | 
0 commit comments