Skip to content

Feature #640 hadamard decoding#840

Merged
MichaelStritt merged 11 commits intodevelopfrom
feature-#640_HadamardDecoding
Sep 20, 2021
Merged

Feature #640 hadamard decoding#840
MichaelStritt merged 11 commits intodevelopfrom
feature-#640_HadamardDecoding

Conversation

@BeatrizPadrela
Copy link
Contributor

Linked issue

Required: mention the issue number of the linked feature or bug here

How to test

Required: if not defined in the linked issue, add a simple test description here

Comments

Optional: add helpful comments for the reviewers here

@MichaelStritt MichaelStritt added the feature New feature, enhancement or request label Sep 15, 2021
@BeatrizPadrela BeatrizPadrela linked an issue Sep 15, 2021 that may be closed by this pull request
@BeatrizPadrela BeatrizPadrela added this to the Release 1.9.0 milestone Sep 15, 2021
@MichaelStritt MichaelStritt removed this from the Release 1.9.0 milestone Sep 16, 2021
@BeatrizPadrela
Copy link
Contributor Author

Ok I checked everything with Amnah's data and it creates these nice Volumes (with Hadamard4, so we only have 3 TIs) and actually two repetitions. 3TI * 8TEs * 2Rep = 48 volumes

However, at the end of the xASL_wrp_ResampleASL, there's this part where some averages are made, and of course it doesn't work for Hadamard. Should we skip it or just adapt it?

image

@BeatrizPadrela
Copy link
Contributor Author

Moreover, I added an output to the decoding function (ASL_im) because we need to save it for both Standard and MNI space (see lin 238)

In red we have the TimeEncoded part and in blue we have the normal data procedure.
the normal procedure includes saving a PWI4D=ASL_im, and a PWI which is a mean of the ASL_im volumes
To the TimeEncoded part I only aded the PWI4D=ASL_im. Should I also add the PWI? I think we will actually not use it afterwards for TimeEncoded data

WhatsApp Image 2021-09-16 at 16 27 01

@MichaelStritt
Copy link
Contributor

@BeatrizPadrela & @jan-petr

However, at the end of the xASL_wrp_ResampleASL, there's this part where some averages are made, and of course it doesn't work for Hadamard. Should we skip it or just adapt it?

I mean, I know how to fix these things, but you guys probably need to agree on how these things should be fixed first.

In red we have the TimeEncoded part and in blue we have the normal data procedure. The normal procedure includes saving a PWI4D=ASL_im, and a PWI which is a mean of the ASL_im volumes. To the TimeEncoded part I only added the PWI4D=ASL_im. Should I also add the PWI? I think we will actually not use it afterwards for TimeEncoded data.

Again, we probably need the expertise from @jan-petr on this.

Copy link
Member

@HenkMutsaerts HenkMutsaerts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Some questions, most comments are on redesigning your new function so it becomes readable and understandable, take a look at other existing functions as a template (e.g., xASL_system that I just wrote in #845).

numberTIs = xASL_str2num(jsonOut.SeriesDescription(startDetails+3:startDetails+4));
numberTEs = xASL_str2num(jsonOut.SeriesDescription(startDetails+10:startDetails+11));
fprintf('FME sequence, Hadamard-%d encoded images, %d TIs, %d TEs\n', jsonOut.TimeEncodedMatrixSize, numberTIs, numberTEs);
jsonOut.TimeEncodedEchoTimes = numberTEs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichaelStritt @jan-petr does it make sense to replace these variable names with BIDS field names, so these scripts are easier to debug?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just...

timEncodedInversionTimes = xASL_str2num(jsonOut.SeriesDescription(startDetails+3:startDetails+4));
timEncodedEchoTimes = xASL_str2num(jsonOut.SeriesDescription(startDetails+10:startDetails+11));
 jsonOut.TimeEncodedEchoTimes = timEncodedEchoTimes;

...?

Sure, we can, don't see a huge benefit, but it's easy to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

function [imASL_reorder] = xASL_quant_HadamardDecoding(Encoded_ASL, xDecodingFields, NumberEchoTimes)
%xASL_im_HadamardDecoding Hadamard-4 & Hadamard-8 Decoding
%
% FORMAT: [Decoded_ASL] = xASL_im_HadamardDecoding(HadamardType, sec)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BeatrizPadrela this doesn't fit with the function line above?
what does sec mean? It says here that it is an output from another function, but it would be nicer to have a longer variable name that is directly interpretable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BeatrizPadrela:

just change it to this:

function [imASL_reorder] = xASL_quant_HadamardDecoding(Encoded_ASL, xDecodingFields, NumberEchoTimes)
%xASL_quant_HadamardDecodingHadamard-4 & Hadamard-8 Decoding
%
% FORMAT:       [imASL_reorder] = xASL_quant_HadamardDecoding(Encoded_ASL, xDecodingFields, NumberEchoTimes)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


% Decoding Fields

TimeEncodedMatrixType = xDecodingFields.TimeEncodedMatrixType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this field doesn't exist? :)

Same for the other fields. You can check them, and give warnings if they are missing. This should of course not happen, but if it happens for whatever reason — e.g., there is something going wrong somewhere else in another function — then you are notified nicely, which can save you time in debugging later.

Also, an input field check is lacking, like:

if margin<3 || isempty('variable name')

Check other functions on how this should be done. Also, in the header you should note if an input is REQUIRED or OPTIONAL, and if OPTIONAL, what the default value is. See also in other functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid point, we never really know if those exist if the data is a bit different. We could optimize this in a second PR where we also support the data of Koen though, because there those things will probably "break".

I'd suggest just to add some...

if nargin ...
if isfield(...)
etc.

... for now and then we improve the logic within the Koen PR.

DecodingMatrix_input = [];
end

% #### For Walsh Decoding Matrix ####
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, ### is the same as %%% but for a non-Matlab language ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check xASL_system which I just created as a template, also for splitting the function in steps that are then easy to read, and that are listed in the function header.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but again, xASL_system was created in the meantime. Let's improve those in the next PR with Koens data, then we don't have to do that much cherry-picking and so on.

end

% #### For Hadamard Decoding Matrix ####
elseif strcmp(TimeEncodedMatrixType,'Hadamard')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if this field is not Hadamard or Walsh? Then there should be a warning right?
Also, you can use strcmpi to make your life easier. If you then accidentally typed hadamard instead of Hadamard it will still work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it 100% correctly like this:

switch lower(TimeEncodedMatrixType)
    case 'walsh'
        ...
    case 'hadamard'
        ...
    otherwise
        ...
end

if xASL_exist(x.dir.studyPar,'file')
studyPar = spm_jsonread(x.dir.studyPar);
if isfield(studyPar,'TimeEncodedMatrixSize') && ~isempty(studyPar,'TimeEncodedMatrixSize') || ... % Should be 4, 8 or 12
if isfield(studyPar,'TimeEncodedMatrixSize') && ~isempty(studyPar.TimeEncodedMatrixSize) || ... % Should be 4, 8 or 12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a warning or feedback here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not change this too much, because it was already modified/renamed etc. in another PR. Keep it like it is for now 👍

Copy link
Contributor Author

@BeatrizPadrela BeatrizPadrela Sep 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -159,7 +159,7 @@

if xASL_exist(x.dir.studyPar,'file')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a json file, exist works as well as xASL_exist

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not change this too much, because it was already modified/renamed etc. in another PR. Keep it like it is for now 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

%% TimeEncoded parsing

% Check if TimeEncoded is defined
if isfield(x,'TimeEncodedMatrixType') || ~isempty(x.TimeEncodedMatrixType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be || or && / :) Same below!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably &&, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes of course sorry, done

else

% Decoding of TimeEncoded data ===================
elseif x.modules.asl.bTimeEncoded
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct position for this function? So it occurs after motion correction, but before quantification? Should the function then be called quant or is it rather im?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe im is better 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -222,7 +227,18 @@ function xASL_wrp_ResampleASL(x)
elseif round(dim4/2)~=dim4/2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be the case for Hadamard or Walsh?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I guess @amahroo is checking for uneven numbers here 🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't make any changes in the resample function, no idea :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh true, no one changed the code here. Is this one of your "teaching" questions @HenkMutsaerts ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have multiples of 4 and 8, this should not be the case for Hadamard/Walsh

Comment on lines 33 to 43
if nargin<3 && isempty(Encoded_ASL)
warning('Encoded_ASL input is empty');
end

if nargin<3 && isempty(xDecodingFields)
warning('xDecodingFields input is empty');
end

if nargin<3 && isempty(NumberEchoTimes)
warning('NumberEchoTimes input is empty');
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BeatrizPadrela I think I'm distracting you too much 😆 try nargin<1, nargin<2, ... 😉

@MichaelStritt MichaelStritt force-pushed the feature-#640_HadamardDecoding branch from 7979b73 to 188554d Compare September 20, 2021 12:45
@MichaelStritt MichaelStritt merged commit 188554d into develop Sep 20, 2021
@MichaelStritt MichaelStritt deleted the feature-#640_HadamardDecoding branch September 20, 2021 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature, enhancement or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hadamard decoding: calculate the new PWI.nii

4 participants