Skip to content

Commit 1c5a549

Browse files
committed
bugfix, rawdata reader display steady state wrong because of assumed nshot nseg too early
1 parent 5259878 commit 1c5a549

6 files changed

Lines changed: 227 additions & 27 deletions

File tree

File renamed without changes.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
function [myMriAcquisition_node, reconFoV] = correctMetadataInteractive(myMriAcquisition_node, reconFoV)
2+
%CORRECTMETADATAINTERACTIVE
3+
% Opens the metadata editing window for user correction.
4+
% Standalone version — only displays the parameter table.
5+
%
6+
% Authors:
7+
% Dominik Helbing
8+
% adapted and simplified by Mauro Leidi, 2024
9+
%
10+
% -------------------------------------------------------------------------
11+
12+
%% --- Input check -----------------------------------------------------
13+
if ~isa(myMriAcquisition_node, 'bmMriAcquisitionParam')
14+
error("First argument must be an object of class bmMriAcquisitionParam.");
15+
end
16+
17+
%% --- Parameter setup -------------------------------------------------
18+
paramNames = {'N', 'nLine', 'nShot', 'nSeg', 'nCh', 'nEcho', ...
19+
'nShotOff', 'FoV (acq)', 'FoV (recon)'};
20+
21+
automatedValues = [myMriAcquisition_node.N, ...
22+
myMriAcquisition_node.nLine, ...
23+
myMriAcquisition_node.nShot, ...
24+
myMriAcquisition_node.nSeg, ...
25+
myMriAcquisition_node.nCh, ...
26+
myMriAcquisition_node.nEcho, ...
27+
myMriAcquisition_node.nShot_off, ...
28+
mode(myMriAcquisition_node.FoV), ...
29+
mode(reconFoV)];
30+
31+
%% --- Create main UI figure ------------------------------------------
32+
fig = uifigure('Name', 'Manual Parameter Adjustment', ...
33+
'Position', [100 100 650 285]);
34+
movegui(fig, 'center');
35+
36+
g = uigridlayout(fig, ...
37+
'RowHeight', {'fit','fit','1x'}, ...
38+
'ColumnWidth', {'1x','fit'});
39+
40+
g2 = uigridlayout(g, [5,1], ...
41+
'RowHeight', {'fit', 'fit', 'fit', 30, 30});
42+
g2.Layout.Row = 2;
43+
g2.Layout.Column = 2;
44+
45+
% Title label
46+
t = uilabel(g, ...
47+
'Text', 'Adjust Acquisition Parameters', ...
48+
'FontSize', 14, ...
49+
'FontWeight', 'bold', ...
50+
'HorizontalAlignment', 'center');
51+
t.Layout.Row = 1;
52+
t.Layout.Column = [1,2];
53+
54+
%% --- Table -----------------------------------------------------------
55+
s = uistyle('BackgroundColor', [0.9290 0.6940 0.1250]); % orange highlight
56+
data = [paramNames', num2cell(automatedValues)', num2cell(automatedValues)'];
57+
58+
uit = uitable(g, ...
59+
'Data', data, ...
60+
'ColumnEditable', [false false true], ...
61+
'ColumnName', {'Acquisition Parameters', 'Extracted Value', 'User Value'}, ...
62+
'ColumnWidth', '1x');
63+
uit.Layout.Row = 2;
64+
uit.Layout.Column = 1;
65+
addStyle(uit, s, "column", 3);
66+
67+
%% --- Controls (dropdowns, checkbox, buttons) -------------------------
68+
% Add dropdown for different cases, maybe table changes depending on case
69+
dd = uidropdown(g2, 'Items', {'Non-Cartesian', 'Cartesian', 'Case 3'}, ...
70+
"ValueChangedFcn",@(src,event) updateTable(src, uit));
71+
dropDown = dd.Value;
72+
73+
% Add dropdown for navigation
74+
dd_self = uidropdown(g2, 'Items', {'SI Navigation', ...
75+
'Pilot Tone Navigation', ...
76+
'Other'});
77+
78+
% If self nav is not used atm
79+
if ~myMriAcquisition_node.selfNav_flag
80+
dd_self.Value = 'Other';
81+
end
82+
83+
% Add a checkbox for roosk_flag
84+
cbx_rooks = uicheckbox(g2, 'Text', 'Remove oversampling', 'Value', ...
85+
myMriAcquisition_node.roosk_flag);
86+
87+
% Add a button to confirm changes
88+
uibutton(g2, 'Text', 'Confirm', ...
89+
'ButtonPushedFcn', @(btn,event) confirmCallback(fig));
90+
91+
% Add a button to cancel
92+
uibutton(g2, 'Text', 'Cancel', 'BackgroundColor', '#d0d0d0', ...
93+
'ButtonPushedFcn', @(btn,event) closeFigure(fig));
94+
95+
% Handle figure close request to resume execution
96+
fig.CloseRequestFcn = @(src, event) closeFigure(fig);
97+
98+
99+
%% --- Wait for user interaction --------------------------------------
100+
uiwait(fig);
101+
102+
%% --- Retrieve values after closing ----------------------------------
103+
% Only update the values if figure was closed using "Confirm"
104+
if ishandle(fig)
105+
% Table values
106+
automatedValues = cell2mat(uit.Data(:,3))';
107+
108+
% Checkbox values
109+
selfNav = strcmp(dd_self.Value, 'SI Navigation');
110+
myMriAcquisition_node.selfNav_flag = selfNav;
111+
myMriAcquisition_node.roosk_flag = cbx_rooks.Value;
112+
113+
% Dropdown value
114+
dropDown = dd.Value;
115+
116+
% Close the figure
117+
close(fig);
118+
end
119+
120+
%% Return values
121+
% Update values in myMriAcquisition_node
122+
if strcmp(dropDown, 'Non-Cartesian')
123+
% Table values
124+
myMriAcquisition_node.N = automatedValues(1);
125+
myMriAcquisition_node.nLine = automatedValues(2);
126+
myMriAcquisition_node.nShot = automatedValues(3);
127+
myMriAcquisition_node.nSeg = automatedValues(4);
128+
myMriAcquisition_node.nCh = automatedValues(5);
129+
myMriAcquisition_node.nEcho = automatedValues(6);
130+
myMriAcquisition_node.nShot_off = automatedValues(7);
131+
newFoV = ones(size(myMriAcquisition_node.FoV)).*automatedValues(8);
132+
myMriAcquisition_node.FoV = newFoV;
133+
reconFoV = ones(size(myMriAcquisition_node.FoV)).*automatedValues(9);
134+
end
135+
136+
if strcmp(dropDown, 'Cartesian')
137+
error("Case not implemented yet");
138+
end
139+
140+
if strcmp(dropDown, 'Case 3')
141+
error("Case not implemented yet");
142+
end
143+
% End of function
144+
145+
%% --- Nested callback functions --------------------------------------
146+
function confirmCallback(fig)
147+
uiresume(fig);
148+
end
149+
150+
function updateTable(src, uit)
151+
% Callback function to handle a dropdown change NOT IN USE YET
152+
% Change the background color of the columns depending on the drop
153+
% down. This is only as an example on how to use it.
154+
cols = uistyle('BackgroundColor', [0.9290 0.6940 0.1250]);
155+
removeStyle(uit);
156+
switch src.Value
157+
case 'Non-Cartesian'
158+
col = 3;
159+
case 'Cartesian'
160+
col = 2;
161+
case 'Case 3'
162+
col = 1;
163+
otherwise
164+
col = 1;
165+
end
166+
addStyle(uit, cols, "column", col);
167+
end
168+
169+
170+
function closeFigure(fig)
171+
% Callback function to handle figure close event
172+
173+
% Resume execution and close the figure
174+
uiresume(fig);
175+
delete(fig);
176+
end
177+
178+
end
File renamed without changes.
File renamed without changes.

src/rawDataReader/siemens/dhSiemensReadMetaData.m

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -77,26 +77,51 @@
7777
end
7878
end
7979

80+
81+
myMriAcquisition_node = bmMriAcquisitionParam([]);
82+
myMriAcquisition_node.N = N;
83+
myMriAcquisition_node.nLine = nLine;
84+
myMriAcquisition_node.nShot = nShot;
85+
myMriAcquisition_node.nSeg = nSeg;
86+
myMriAcquisition_node.nCh = 1;
87+
myMriAcquisition_node.nEcho = nEcho;
88+
myMriAcquisition_node.FoV = FoV;
89+
myMriAcquisition_node.timestamp = timestamp;
90+
reconFoV = FoV;
91+
% Find the shot where the standard deviation falls below the threshold
92+
myMriAcquisition_node.nShot_off = 0;
93+
94+
%% Set flags in myMriAcquisition_node (Maybe other way to handle them?)
95+
myMriAcquisition_node.selfNav_flag = true;
96+
myMriAcquisition_node.roosk_flag = false;
97+
98+
99+
%% HERE STEP ONE: LET THE USER CORRECT THE AUTOMATICALLY EXTRACTED METADATA
100+
if ~autoFlag || isequal(reconFoV, [-1, -1, -1])
101+
[myMriAcquisition_node, reconFoV] = correctMetadataInteractive(myMriAcquisition_node, reconFoV);
102+
end
103+
104+
80105
% unsorted() returns the unsorted data as an array [N, nCh, nLine]
81-
y_raw = myTwix.image.unsorted();
106+
y_raw = myTwix.image.unsorted(); % this is [N, nCh, nLine]
82107

83108
if nEcho == 1
84109
% Change structure to [nCh, N, nLine] and seperate nLine into nSeg and
85110
% nShot
86-
y_raw = permute(y_raw, [2, 1, 3]);
111+
y_raw = permute(y_raw, [2, 1, 3]); % [nCh, N, nLine]
87112
% Get nCh
88113
y_raw_size = size(y_raw);
89114
y_raw_size = y_raw_size(:)';
90-
nCh = y_raw_size(1, 1);
91-
y_raw = reshape(y_raw, [nCh, N, nSeg, nShot]);
115+
myMriAcquisition_node.nCh = y_raw_size(1, 1);
116+
y_raw = reshape(y_raw, [myMriAcquisition_node.nCh, myMriAcquisition_node.N, myMriAcquisition_node.nSeg, myMriAcquisition_node.nShot]); % [nCh, N, nSeg, nShot]
92117
else
93118
error('bmTwix_data : nEcho > 1, case not implemented, yet ');
94119
end
95120

96121

97122
% Reduce the array to a 3D array, only containing the values for the first
98123
% segment
99-
mySI = squeeze(y_raw(:, :, 1, :));
124+
mySI = squeeze(y_raw(:, :, 1, :)); % [nCh, N, nShot]
100125

101126
% Calculate the inverse discret Fourier transform
102127
mySI = bmIDF(mySI, 1, [], 2);
@@ -127,27 +152,10 @@
127152
running_std = movstd(s_mean, window_size, 'Endpoints', 'discard');
128153

129154
% Define a threshold for the std to consider steady state
130-
threshold = prctile(running_std, 15); % 10th percentile of std
131-
%% Extract acquisition parameters
132-
% Node for trajectory
133-
myMriAcquisition_node = bmMriAcquisitionParam([]);
134-
myMriAcquisition_node.N = N;
135-
myMriAcquisition_node.nLine = nLine;
136-
myMriAcquisition_node.nShot = nShot;
137-
myMriAcquisition_node.nSeg = nSeg;
138-
myMriAcquisition_node.nCh = nCh;
139-
myMriAcquisition_node.nEcho = nEcho;
140-
myMriAcquisition_node.FoV = FoV;
141-
myMriAcquisition_node.timestamp = timestamp;
155+
threshold = prctile(running_std, 15); % 15th percentile of std
156+
142157
% Find the shot where the standard deviation falls below the threshold
143158
myMriAcquisition_node.nShot_off = find(running_std < threshold, 1);
144-
145-
%% Set flags in myMriAcquisition_node (Maybe other way to handle them?)
146-
myMriAcquisition_node.selfNav_flag = true;
147-
myMriAcquisition_node.roosk_flag = false;
148-
149-
150-
reconFoV = FoV;
151159

152160
if ~autoFlag || isequal(reconFoV, [-1, -1, -1])
153161
[myMriAcquisition_node, reconFoV] = checkMetadataInteractive(mySI, s_mean, ...
@@ -164,4 +172,3 @@
164172
end
165173

166174
end
167-

src/rawDataReader/siemens/mleSiemensReader.m

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,23 @@
1818
% getRedouts: extract the readouts, returns a complex array
1919
% containing the data
2020
function rawdata = readRawData(obj, flagSS, flagExcludeSI)
21-
% Handle default values for optional arguments: if no argument
22-
% is passed there is no data filtering.
21+
%READRAWDATA Read raw data with optional filtering.
22+
%
23+
% rawdata = readRawData(obj)
24+
% rawdata = readRawData(obj, flagSS)
25+
% rawdata = readRawData(obj, flagSS, flagExcludeSI)
26+
%
27+
% INPUTS:
28+
% obj - Reader object containing acquisition parameters
29+
% and the raw file handle.
30+
%
31+
% flagSS - (logical, optional) If true, the initial
32+
% non–steady-state readouts are filtered out.
33+
% Default = false.
34+
%
35+
% flagExcludeSI - (logical, optional) If true, the SI projection
36+
% readouts (self-navigation lines) are excluded
37+
% from the output. Default = false.
2338
if nargin < 2
2439
flagSS = false; % If true the non steady state readouts are filtered out
2540
end

0 commit comments

Comments
 (0)