classdef ECGApp_final < matlab.apps.AppBase % Properties corresponding to UI components properties (Access = public) UIFigure matlab.ui.Figure LoadECGButton matlab.ui.control.Button PeakHeightLabel PeakDistLabel UseCustomAmpSwitch AmpField matlab.ui.control.NumericEditField AmpLabel ThresholdField matlab.ui.control.NumericEditField DistanceField matlab.ui.control.NumericEditField UseCustomThresholdSwitch % Switch for enabling custom threshold UseCustomThresholdLabel AmpThresholdButton matlab.ui.control.Button ECGAxes1 matlab.ui.control.UIAxes ECGAxes2 matlab.ui.control.UIAxes ECGAxes3 matlab.ui.control.UIAxes PCAAxes matlab.ui.control.UIAxes ProcessButton matlab.ui.control.Button MoveToReview MoveToError NoChangeButton UseCustomWidthLabel UseCustomWidthSwitch WidthField ResultButton matlab.ui.control.Button UITable RemoveButton AddButton ButtonGroup matlab.ui.container.ButtonGroup Option1RadioButton matlab.ui.control.RadioButton Option2RadioButton matlab.ui.control.RadioButton OutputLabel matlab.ui.control.Label ecgFigure end % Private properties (for internal data) properties (Access = private) ecgData % ECG data loaded pcaData % PCA result patientID % Current patient ID ecgFileList % List of ECG files currentFileIndex % Index of the currently loaded file cutoff %filter cutoff order %filter order threshold distance amplitude_threshold method path files spike_locs min_peaks percentage corrected_ECG axes1_new axes2_new axes3_new interpolation end % Callbacks for UI components methods (Access = private) % Button pushed function: NextButton function NextButtonPushed(app, event) cla(app.ECGAxes1) cla(app.ECGAxes2) cla(app.ECGAxes3) cla(app.PCAAxes) % Increment the current file index app.currentFileIndex = app.currentFileIndex + 1; app.patientID = erase(app.files(app.currentFileIndex),'.mat'); % If index exceeds the number of files, wrap around to the first file if app.currentFileIndex > length(app.ecgFileList) app.currentFileIndex = 1; % Go back to the first file end % Load the next ECG file loadNextECGFile(app); end function LoadECGButtonPushed(app, event) % Open a dialog for the user to select multiple ECG files cla(app.ECGAxes1) cla(app.ECGAxes2) cla(app.ECGAxes3) cla(app.PCAAxes) [files, path] = uigetfile('*.mat', 'Select ECG Files', 'MultiSelect', 'on'); app.path = path; app.files = files; % Ensure files were selected if iscell(files) || ischar(files) % Handles multiple or single file selection % Store the full file paths of the selected files if iscell(files) % If multiple files are selected app.ecgFileList = fullfile(path, files); else % If only a single file is selected app.ecgFileList = {fullfile(path, files)}; end % Initialize file index to 1 (first file) app.currentFileIndex = 1; if iscell(files) app.patientID = erase(files(app.currentFileIndex),'.mat'); else app.patientID = erase(files,'.mat'); end % Load the first file loadNextECGFile(app); else disp('No files selected.'); end end function clearAxes(app) % Clears the 12-lead ECG axes when a new file is loaded axesHandles = findall(app.ecgFigure, 'Type', 'axes'); for i = 1:length(axesHandles) cla(axesHandles(i)); % Clear content but keep axes end end function plot12LeadECG(app, signal) % signal: 12xN ECG matrix (rows=leads, columns=samples) lead_names = {'I','V1','II','V2','III','V3','aVR','V4', 'aVL','V5','aVF','V6'}; % Create a new figure for the 12-lead ECG % app.ecgFigure = figure('Name', ['12-Lead ECG - ' app.patientID{1}]); app.ecgFigure = figure('Name', ['12-Lead ECG']); % Set up a 6x2 grid layout for 12 subplots (12-lead ECG) for col = 1:2 for row = 1:6 % Correct indexing: Access the correct lead using the row index lead_idx = (row-1)*2 + col; % This will give values from 1 to 12 for the 12 leads % Create a subplot for each lead subplot(6, 2, (row-1)*2 + col); if col == 1 plot(signal(row, :)); else plot(signal(row+6, :)); end % Set the title, labels, and axis limits for each subplot title(['Lead ' num2str(lead_names{lead_idx})]); set(gca, 'XTickLabel', []) if row == 6 xlabel('Sample'); end end end end function plot12LeadECG2(app, signal, correctedLeads) % signal: 12xN ECG matrix (rows=leads, columns=samples) % correctedLeads: 12x1 logical array indicating which leads were corrected if strcmp(get(app.ecgFigure, 'Visible'),'on') figure(app.ecgFigure); end lead_names = {'I','V1','II','V2','III','V3','aVR','V4', 'aVL','V5','aVF','V6'}; % Set up a 6x2 grid layout for 12 subplots (12-lead ECG) for col = 1:2 for row = 1:6 % Correct indexing: Access the correct lead using the row index lead_idx = (row-1)*2 + col; % This will give values from 1 to 12 for the 12 leads % Determine actual lead number in the signal matrix if col == 1 actual_lead = row; % Leads 1-6 (I, II, III, aVR, aVL, aVF) else actual_lead = row + 6; % Leads 7-12 (V1-V6) end % Only replot if this lead was corrected if nargin < 3 || correctedLeads(actual_lead) % Create/select subplot for this lead subplot(6, 2, (row-1)*2 + col); hold on; plot(signal(actual_lead, :), 'Color', [0.9290 0.6940 0.1250]); % Set the title, labels, and axis limits for each subplot title(['Lead ' num2str(lead_names{lead_idx}) ' (Corrected)'], 'Color', [0.9290 0.6940 0.1250]); set(gca, 'XTickLabel', []) if row == 6 xlabel('Sample'); end % grid on; else % Just update the subplot if it exists but don't change the plot subplot(6, 2, (row-1)*2 + col); % Keep existing plot, just ensure title is standard if ~isempty(get(gca, 'Children')) title(['Lead ' num2str(lead_names{lead_idx})]); end end end end end function loadNextECGFile(app) multiplier = 1; % Get the file name of the next ECG recording filename = app.ecgFileList{app.currentFileIndex}; % Load ECG data from the file app.ecgData = load(filename); % Replace with your actual load function ECG12Lead_bwr = app.ecgData.ECG12Lead_bwr*multiplier; spiked_ECG = ECG12Lead_bwr'; kors_ecg = kors(spiked_ECG'); axes1_first = plot(app.ECGAxes1, kors_ecg(:,1)); hold(app.ECGAxes1, 'on'); axes2_first = plot(app.ECGAxes2, kors_ecg(:,2)); hold(app.ECGAxes2, 'on'); axes3_first = plot(app.ECGAxes3, kors_ecg(:,3)); hold(app.ECGAxes3, 'on'); % Check if there is an existing figure for the 12-lead ECG and close it if ishandle(app.ecgFigure) clearAxes(app); % Close the previous figure end plot12LeadECG(app, app.ecgData.ECG12Lead_bwr'); end function ProcessButtonPushed(app, event) % Default values defaultCutoff = 120; % Default cutoff frequency defaultOrder = 2; % Default filter order multiplier = 1; % % Check if custom parameters switch is 'On' % if strcmp(app.UseCustomParamsSwitch.Value, 'On') % % Get custom values from user input fields % cutoff = app.HighPassCutoffField.Value; % order = app.FilterOrderField.Value; % app.cutoff = app.HighPassCutoffField.Value; % app.order = app.FilterOrderField.Value; % disp(['Using custom cutoff: ', num2str(cutoff), ', order: ', num2str(order)]); % else % Use default values cutoff = defaultCutoff; order = defaultOrder; app.cutoff = defaultCutoff; app.order = defaultOrder; % disp(['Using default filter settings. cutoff: ', num2str(cutoff), ', order: ', num2str(order)]); % end % Apply high-pass filtering signal = app.ecgData.ECG12Lead_bwr*multiplier; Fs = app.ecgData.fs; [b, a] = butter(order, cutoff/(Fs/2), 'high'); filteredSignal = filtfilt(b, a, signal); filteredSignal = filteredSignal'; % Shannon's energy for i = 1:size(filteredSignal,1) power_signal(i, :) = filteredSignal(i, :).^2 .* log(filteredSignal(i, :).^2 + eps); end % Perform PCA [coeff, score, ~] = pca(power_signal'); app.pcaData = score(:, 1); % First principal component % Plot the first principal component plot(app.PCAAxes, app.pcaData); title(app.PCAAxes, 'First Principal Component'); xlabel(app.PCAAxes, 'Sample Index'); ylabel(app.PCAAxes, 'Amplitude'); app.method = 'Normal'; ApplyThreshold(app) end % Button pushed function: ApplyThresholdButton function ApplyThreshold(app, event) % Default values defaultThreshold = 4000; % Default cutoff frequency defaultDistance = 10; % Check if custom parameters switch is 'On' if strcmp(app.UseCustomThresholdSwitch.Value, 'On') % Get custom values from user input fields app.threshold = app.ThresholdField.Value; app.distance = app.DistanceField.Value; threshold = app.ThresholdField.Value; distance = app.DistanceField.Value; % disp(['Using custom peak height: ', num2str(threshold), 'Distance: ', num2str(distance)]); else % Use default values app.threshold = defaultThreshold; threshold = defaultThreshold; app.distance = defaultDistance; distance = defaultDistance; % disp(['Using default threshold: ', num2str(threshold), ' ,Distance: ', num2str(distance)]); end % Detect peaks using the threshold from the slider [peaks, locs] = findpeaks(app.pcaData, 'MinPeakHeight', threshold, 'MinPeakDistance',distance); app.spike_locs = locs; plotPCA(app) app.min_peaks = min(peaks); listPeaks(app) end function plotPCA(app) peaks = app.pcaData(app.spike_locs); cla(app.PCAAxes) plot(app.PCAAxes, app.pcaData); hold(app.PCAAxes, 'on'); plot(app.PCAAxes, app.spike_locs, peaks, 'ro'); for j = 1:length(app.spike_locs) text(app.PCAAxes, app.spike_locs(j)-100,1.05*app.pcaData(app.spike_locs(j)),num2str(app.spike_locs(j))) end hold(app.PCAAxes, 'off'); end function AmpThreshold(app, event) if min(app.pcaData) < 0 pcaData_2 = app.pcaData + abs(min(app.pcaData)); end locs = app.spike_locs; window_size = 5; for i = 1:length(locs) point = locs(i); strt_idx = max(1,point-window_size); end_idx = min(length(pcaData_2), point + window_size); pcaData_2(strt_idx:end_idx) = 0; end % pcaData_2(pcaData_2 > 5000) = 0; pcaData_2 = movmean(pcaData_2,20); default_amplitude_threshold = 30; % if strcmp(app.UseCustomAmpSwitch.Value, 'On') % % Get custom values from user input fields % amplitude_threshold = app.AmpField.Value; % app.amplitude_threshold = app.AmpField.Value; % disp(['Using custom Amplitude: ', num2str(amplitude_threshold)]); % else % Use default values amplitude_threshold = default_amplitude_threshold; app.amplitude_threshold = default_amplitude_threshold; % disp(['Using default setting. Amplitude: ', num2str(amplitude_threshold)]); % end % Find indices where the signal exceeds the threshold spike_indices = find(abs(pcaData_2) > amplitude_threshold); spike_indices = spike_indices'; diff_indices = diff(spike_indices); group_boundaries = find(diff_indices > 1); % Points where consecutive spikes stop % Add first and last groups group_start = [spike_indices(1), spike_indices(group_boundaries + 1)]; group_end = [spike_indices(group_boundaries), spike_indices(end)]; % Step 3: Classify based on the number of consecutive spikes (duration) small_spikes = []; noise = []; % Define a duration threshold (e.g., 3 consecutive spikes or fewer for small spikes) duration_threshold = 50; for i = 1:length(group_start) group_duration = group_end(i) - group_start(i) + 1; if group_duration <= duration_threshold && group_duration > 5 % Classify as small spikes small_spikes = [small_spikes; group_start(i), group_end(i)]; else % Classify as noise noise = [noise; group_start(i), group_end(i)]; end end % Highlight small spikes in green for i = 1:size(small_spikes, 1) [peaks,small_spike_indices(i)] = max(app.pcaData(small_spikes(i, 1):small_spikes(i, 2))); small_spike_indices(i) = small_spikes(i, 1) + small_spike_indices(i) - 1; end app.spike_locs = [app.spike_locs; small_spike_indices']; plotPCA(app) % app.min_peaks = min(peaks); listPeaks(app) app.method = 'Step 2'; end function listPeaks(app) % Display peaks in the UITable peakData = num2cell(app.spike_locs); % Convert peaks to a cell array for display app.UITable.Data = peakData; % Display the peaks in the UITable end % Callback for the Remove button function removeSelectedPeak(app, event) % Get the selected peak index from the UITable selectedRow = app.UITable.Selection; if ~isempty(selectedRow) % Remove the selected peak from the peaks array app.spike_locs(selectedRow(1)) = []; % Update the UITable to reflect the removed peak listPeaks(app); app.UITable.Selection = []; % Update the plot to reflect the removed peak plotPCA(app); else % Display a warning if no peak is selected uialert(app.UIFigure, 'Please select a peak to remove.', 'No Selection'); end app.method = 'manual'; end % Button pushed function: SaveToExcelButton function SaveToExcelButtonPushed(app, event) % Save the current threshold and alpha, beta values to Excel if iscell(app.patientID) patientID = app.patientID{1}; else patientID = app.patientID; end threshold = app.threshold; distance = app.distance; cutoff = app.cutoff; order = app.order; method = app.method; min_peaks = app.min_peaks; interpolation = app.interpolation; percentage = app.percentage; amplitude_threshold = app.amplitude_threshold; % Log data to Excel % filename = 'ECG_Parameters.xlsx'; % data = {patientID, cutoff, order, threshold, distance, method, min_peaks, interpolation, percentage, amplitude_threshold}; % writecell(data, filename, 'WriteMode', 'append'); % app.ecgFileList(app.currentFileIndex)=[]; end function getPointsFromPlot(app) % Create input dialog to enter x-coordinate prompt = {'Enter X-coordinate for the point:'}; dlgtitle = 'Input X-Coordinate'; dims = [1 35]; % Dialog dimensions (rows, columns) definput = {'0'}; % Default value for the input field answer = inputdlg(prompt, dlgtitle, dims, definput); % If the user didn't cancel the dialog, process the input if ~isempty(answer) % Convert the input string to a number xValue = str2double(answer{1}); if ~isnan(xValue) % Ensure the input is a valid number % Add the selected x-coordinate to app.spike_locs if isempty(app.spike_locs) app.spike_locs = xValue; % Initialize if empty else app.spike_locs = [app.spike_locs; xValue]; % Append the new x-coordinate listPeaks(app); plotPCA(app); end disp(['X-Coordinate Added: ', num2str(xValue)]); % Output to console for debugging else % Display a warning if the input was not a valid number warndlg('Invalid input! Please enter a numeric value.'); end else % If the dialog is canceled, display a message disp('Operation canceled by the user.'); end app.method = 'manual'; end % Button pushed function: ConfirmButton function [spike_starts, spike_ends] = ConfirmButtonPushed(app, event) if app.Option1RadioButton.Value app.OutputLabel.Text = 'By Envelope'; signal = app.pcaData; spike_locs = app.spike_locs; spiked_ECG = app.ecgData.ECG12Lead_bwr'; % Compute the envelope envelope_signal = envelope(signal); % Apply a smoothing filter window_length = 5; % Adjust the window length for desired smoothness smoothed_envelope = movmean(envelope_signal, window_length); % Set the percentage (30% default) default_per = 0.3; if strcmp(app.UseCustomWidthSwitch.Value, 'On') % Get custom values from user input fields percentage = app.WidthField.Value; app.percentage = app.WidthField.Value; % disp(['Using custom Percentage: ', num2str(percentage*100), '%']); else % Use default values percentage = default_per; app.percentage = default_per; % disp(['Using default percentage: ', num2str(percentage*100), '%']); end % Initialize arrays to store start and end indices spike_starts = zeros(size(spike_locs)); spike_ends = zeros(size(spike_locs)); % Loop through each spike location for i = 1:length(spike_locs) loc = spike_locs(i); % Define a window around the spike (adjust as necessary) window_size = 5; % Example: 5 samples before and after the spike window_start = max(1, loc - window_size); window_end = min(length(signal), loc + window_size); % Extract the envelope in the window envelope_window = smoothed_envelope(window_start:window_end); % Get the local maximum of the envelope in this window local_max = max(envelope_window); % Define the 30% threshold of the local maximum threshold = percentage * local_max; % Find the start and end points in the window where envelope crosses 50% start_idx = find(envelope_window >= threshold, 1, 'first'); end_idx = find(envelope_window >= threshold, 1, 'last'); % Convert the window indices to global indices spike_starts(i) = window_start + start_idx - 1; spike_ends(i) = window_start + end_idx - 1; end app.interpolation = 'Envelope'; elseif app.Option2RadioButton.Value app.OutputLabel.Text = 'By Sample'; signal = app.pcaData; spike_locs = app.spike_locs; spiked_ECG = app.ecgData.ECG12Lead_bwr'; default_per = 5; if strcmp(app.UseCustomWidthSwitch.Value, 'On') % Get custom values from user input fields percentage = app.WidthField.Value; app.percentage = app.WidthField.Value; % disp(['Using custom Sample: ', num2str(percentage)]); else % Use default values percentage = default_per; app.percentage = default_per; % disp(['Using default percentage: ', num2str(percentage), '%']); end % Initialize arrays to store start and end indices spike_starts = zeros(size(spike_locs)); spike_ends = zeros(size(spike_locs)); % Loop through each spike location for i = 1:length(spike_locs) loc = spike_locs(i); % Convert the window indices to global indices spike_starts(i) = max(1, loc - percentage); spike_ends(i) = min(length(signal), loc + percentage); end app.interpolation = 'Sample'; end end function ResultButtonPushed(app, event) if isprop(app,'axes1_new') && ~isempty(app.axes1_new) && isgraphics(app.axes1_new) delete(app.axes1_new); end if isprop(app,'axes2_new') && ~isempty(app.axes2_new) && isgraphics(app.axes2_new) delete(app.axes2_new); end if isprop(app,'axes3_new') && ~isempty(app.axes3_new) && isgraphics(app.axes3_new) delete(app.axes3_new); end [spike_starts, spike_ends] = ConfirmButtonPushed(app); % Initialize variables leadNames = {'I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6'}; artifactDetected = false(12, 1); % Track which leads have artifacts spiked_ECG = app.ecgData.ECG12Lead_bwr'; % First pass: Detect artifacts based on slope threshold for ch = 1:12 originalSignal = spiked_ECG(ch,:); hasArtifact = false; for i = 1:length(spike_ends) startIndex = spike_starts(i); endIndex = spike_ends(i); if startIndex < 1 || endIndex > length(originalSignal) continue; end segment = originalSignal(startIndex:endIndex); slope = max(abs(diff(segment))); if slope > 23 % slope threshold hasArtifact = true; break; end end artifactDetected(ch) = hasArtifact; end % Create dialog for user review dlg = uifigure('Name', 'Artifact Detection Review', 'Position', [100 100 600 500]); % Create table data leadsWithArtifact = leadNames(artifactDetected); leadsWithoutArtifact = leadNames(~artifactDetected); % Create UI components uilabel(dlg, 'Position', [20 450 560 30], 'Text', ... 'Review artifact detection. Check/uncheck leads to override algorithm decision:', ... 'FontWeight', 'bold', 'FontSize', 12); % Create checklist for all leads uilabel(dlg, 'Position', [20 420 300 20], 'Text', ... 'Select leads WITH pacing artifacts:', 'FontWeight', 'bold'); % Create checkboxes for each lead cbHandles = cell(12, 1); for i = 1:12 row = floor((i-1)/4); col = mod(i-1, 4); xPos = 30 + col * 140; yPos = 380 - row * 30; cbHandles{i} = uicheckbox(dlg, 'Position', [xPos yPos 120 22], ... 'Text', ['Lead ' leadNames{i}], ... 'Value', artifactDetected(i)); end % Summary labels summaryPanel = uipanel(dlg, 'Position', [20 150 560 110], 'Title', 'Summary'); artifactListLabel = uilabel(summaryPanel, 'Position', [10 60 520 20], ... 'Text', ['Leads WITH artifacts: ' strjoin(leadsWithArtifact, ', ')], ... 'FontColor', [0.8 0 0]); noArtifactListLabel = uilabel(summaryPanel, 'Position', [10 30 520 20], ... 'Text', ['Leads WITHOUT artifacts: ' strjoin(leadsWithoutArtifact, ', ')], ... 'FontColor', [0 0.6 0]); % Update summary when checkboxes change for i = 1:12 cbHandles{i}.ValueChangedFcn = @(src, event) updateSummary(); end function updateSummary() withArtifact = {}; withoutArtifact = {}; for j = 1:12 if cbHandles{j}.Value withArtifact{end+1} = leadNames{j}; else withoutArtifact{end+1} = leadNames{j}; end end if isempty(withArtifact) artifactListLabel.Text = 'Leads WITH artifacts: None'; else artifactListLabel.Text = ['Leads WITH artifacts: ' strjoin(withArtifact, ', ')]; end if isempty(withoutArtifact) noArtifactListLabel.Text = 'Leads WITHOUT artifacts: None'; else noArtifactListLabel.Text = ['Leads WITHOUT artifacts: ' strjoin(withoutArtifact, ', ')]; end end % Buttons confirmBtn = uibutton(dlg, 'Position', [350 50 100 40], ... 'Text', 'Apply Correction', ... 'ButtonPushedFcn', @(btn, event) applyCorrection()); cancelBtn = uibutton(dlg, 'Position', [470 50 100 40], ... 'Text', 'Cancel', ... 'ButtonPushedFcn', @(btn, event) close(dlg)); % Apply correction function function applyCorrection() % Get user selections userArtifactSelection = false(12, 1); for j = 1:12 userArtifactSelection(j) = cbHandles{j}.Value; end % Perform correction based on user selection corrected_ECG = spiked_ECG; for ch = 1:12 if userArtifactSelection(ch) % Only correct if user marked as having artifact originalSignal = spiked_ECG(ch,:); for i = 1:length(spike_ends) startIndex = spike_starts(i); endIndex = spike_ends(i); if startIndex < 1 || endIndex > length(originalSignal) continue; end % Get previous and next sample indices if startIndex == 1 prevSampleIndex = startIndex; else prevSampleIndex = startIndex - 1; end if endIndex == length(originalSignal) nextSampleIndex = endIndex; else nextSampleIndex = endIndex + 1; end % Values at the previous and next indices prevValue = originalSignal(prevSampleIndex); nextValue = originalSignal(nextSampleIndex); % Number of points to interpolate numPoints = endIndex - startIndex + 1; % Generate linearly interpolated values interpolatedValues = linspace(prevValue, nextValue, numPoints); % Replace with interpolated line originalSignal(startIndex:endIndex) = interpolatedValues; end corrected_ECG(ch,:) = originalSignal; end end % Store corrected ECG app.corrected_ECG = corrected_ECG; % Update plots kors_ecg_corrected = kors(corrected_ECG'); app.axes1_new = plot(app.ECGAxes1, kors_ecg_corrected(:,1), 'Color', [0.9290 0.6940 0.1250]); app.axes2_new = plot(app.ECGAxes2, kors_ecg_corrected(:,2), 'Color', [0.9290 0.6940 0.1250]); app.axes3_new = plot(app.ECGAxes3, kors_ecg_corrected(:,3), 'Color', [0.9290 0.6940 0.1250]); legend(app.ECGAxes1, 'Before', 'After') % Plot 12-lead ECG plot12LeadECG2(app, app.corrected_ECG, userArtifactSelection); % Close dialog close(dlg); end % Wait for dialog to close uiwait(dlg); end function MoveToErrorButtonPushed(app, event) hold(app.ECGAxes1, 'off'); hold(app.ECGAxes2, 'off'); hold(app.ECGAxes3, 'off'); file = app.ecgFileList(app.currentFileIndex); Error_folder = [app.path 'Error' '\']; if (exist(Error_folder) == 0) mkdir(Error_folder); end movefile(file{1},Error_folder) NextButtonPushed(app); end function MoveToReviewButtonPushed(app, event) app.ecgData.ECG12Lead_bwr_old = app.ecgData.ECG12Lead_bwr; app.ecgData.ECG12Lead_bwr = app.corrected_ECG; SaveToExcelButtonPushed(app); file = app.ecgFileList(app.currentFileIndex); ECG12Lead_bwr_old = app.ecgData.ECG12Lead_bwr_old; ECG12Lead_bwr = app.ecgData.ECG12Lead_bwr'; save(file{1},'ECG12Lead_bwr_old','ECG12Lead_bwr',"-append") Reviewed_folder = [app.path 'Reviewed' '\']; if (exist(Reviewed_folder) == 0) mkdir(Reviewed_folder); end movefile(file{1},Reviewed_folder) NextButtonPushed(app); end function NoButtonPushed(app, event) hold(app.ECGAxes1, 'off'); hold(app.ECGAxes2, 'off'); hold(app.ECGAxes3, 'off'); file = app.ecgFileList(app.currentFileIndex); NoChange_folder = [app.path 'No change' '\']; if (exist(NoChange_folder) == 0) mkdir (NoChange_folder); end movefile(file{1},NoChange_folder) NextButtonPushed(app); end end % App initialization and construction methods (Access = public) % Constructor function app = ECGApp_final % Create UIFigure and components createComponents(app); end % Create UI components function createComponents(app) % Create the main figure for the app app.UIFigure = uifigure('Position', [100, 100, 800, 600]); % Create the first UIAxes (for first ECG plot) app.ECGAxes1 = uiaxes(app.UIFigure, 'Position', [80, 500, 300, 200]); title(app.ECGAxes1, 'X/Corrected X'); % Create the second UIAxes (for the first principal component) app.ECGAxes2 = uiaxes(app.UIFigure, 'Position', [80, 275, 300, 200]); title(app.ECGAxes2, 'Y/Corrected Y'); % Create the third UIAxes (for the 12-lead ECG after peak replacement) app.ECGAxes3 = uiaxes(app.UIFigure, 'Position', [80, 40, 300, 200]); title(app.ECGAxes3, 'Z/Corrected Z'); app.PCAAxes = uiaxes(app.UIFigure, 'Position', [400, 275, 300, 400]); title(app.PCAAxes, 'First Principal Component'); app.RemoveButton = uibutton(app.UIFigure, 'push', 'Position', [600, 210, 60, 22], 'Text', 'Remove', 'FontWeight', 'bold', 'ButtonPushedFcn', @(~,~) removeSelectedPeak(app)); app.AddButton = uibutton(app.UIFigure, 'push', 'Position', [680, 210, 60, 22], 'Text', 'Add', 'FontWeight', 'bold', 'ButtonPushedFcn', @(~,~) getPointsFromPlot(app)); app.UITable = uitable(app.UIFigure, 'Position', [600, 50, 100, 150]); app.UITable.ColumnName = {'Peak Index'}; % app.ResAxes = uiaxes(app.UIFigure, 'Position', [400, 50, 300, 200]); app.LoadECGButton = uibutton(app.UIFigure, 'push', 'Position', [20, 570, 50, 22], 'Text', 'Load ECG', 'FontWeight', 'bold', 'ButtonPushedFcn', @(~,~) LoadECGButtonPushed(app)); app.UseCustomThresholdLabel = uilabel(app.UIFigure, 'Position', [20, 510, 50, 30], 'Text', 'Custom Threshold'); app.UseCustomThresholdSwitch = uiswitch(app.UIFigure, 'Position', [20, 500, 50, 22], 'Items', {'Off', 'On'}, 'Value', 'Off'); app.PeakHeightLabel = uilabel(app.UIFigure, 'Position', [50, 480, 50, 22], 'Text', 'Height'); app.ThresholdField = uieditfield(app.UIFigure, 'numeric', 'Position', [20, 460, 50, 22]); app.PeakDistLabel = uilabel(app.UIFigure, 'Position', [50, 440, 50, 22], 'Text', 'Distance'); app.DistanceField = uieditfield(app.UIFigure, 'numeric', 'Position', [20, 420, 50, 22]); app.ProcessButton = uibutton(app.UIFigure, 'push', 'Position', [20, 390, 50, 22], 'Text', 'PROCESS', 'FontWeight', 'bold', 'ButtonPushedFcn', @(~,~) ProcessButtonPushed(app)); % app.UseCustomThresholdLabel = uilabel(app.UIFigure, 'Position', [20, 350, 50, 30], 'Text', 'Small spike'); % app.UseCustomAmpSwitch = uiswitch(app.UIFigure, 'Position', [20, 330, 50, 22], 'Items', {'Off', 'On'}, 'Value', 'Off'); % app.AmpLabel = uilabel(app.UIFigure, 'Position', [20, 310, 70, 22], 'Text', 'Amplitude'); % app.AmpField = uieditfield(app.UIFigure, 'numeric', 'Position', [20, 290, 50, 22]); app.AmpThresholdButton = uibutton(app.UIFigure, 'push', 'Position', [20, 220, 50, 22], 'Text', 'Step 2', 'FontWeight', 'bold', 'ButtonPushedFcn', @(~,~) AmpThreshold(app)); app.UseCustomWidthLabel = uilabel(app.UIFigure, 'Position', [380, 210, 120, 30], 'Text', '%Envelope/Sample'); app.UseCustomWidthSwitch = uiswitch(app.UIFigure, 'Position', [400, 185, 50, 22], 'Items', {'Off', 'On'}, 'Value', 'Off'); app.WidthField = uieditfield(app.UIFigure, 'numeric', 'Position', [380, 160, 50, 22]); app.ButtonGroup = uibuttongroup(app.UIFigure, 'Position', [380, 50, 100, 60]); app.Option1RadioButton = uiradiobutton(app.ButtonGroup, 'Text', 'Envelope', 'Position', [10 25 100 50]); app.Option2RadioButton = uiradiobutton(app.ButtonGroup, 'Text', 'Sample', 'Position', [10 1 100 50]); app.OutputLabel = uilabel(app.UIFigure, 'Position', [400 140 150 30]); app.OutputLabel.Text = ''; app.ResultButton = uibutton(app.UIFigure, 'push', 'Position', [20, 130, 50, 22], 'Text', 'Plot Result', 'ButtonPushedFcn', @(~,~) ResultButtonPushed(app)); app.NoChangeButton = uibutton(app.UIFigure, 'push', 'Position', [20, 100, 50, 22], 'Text', 'No transformation', 'ButtonPushedFcn', @(~,~) NoButtonPushed(app)); app.MoveToError = uibutton(app.UIFigure, 'push', 'Position', [20, 60, 50, 25], 'Text', 'Error', 'ButtonPushedFcn', @(~,~) MoveToErrorButtonPushed(app)); app.MoveToReview = uibutton(app.UIFigure, 'push', 'Position', [20, 30, 50, 22], 'Text', 'Reviewed', 'ButtonPushedFcn', @(~,~) MoveToReviewButtonPushed(app)); end end end