Skip to content

dwi2mask: Fix for low image intensities#1083

Merged
Lestropie merged 1 commit intomasterfrom
dwi2mask_lowvalue_fix
Aug 8, 2017
Merged

dwi2mask: Fix for low image intensities#1083
Lestropie merged 1 commit intomasterfrom
dwi2mask_lowvalue_fix

Conversation

@Lestropie
Copy link
Copy Markdown
Member

@Lestropie Lestropie commented Aug 8, 2017

When an input DWI contains exceptionally low values (i.e. even in the b=0 volumes there's no voxels greater than 1.0), dwi2mask will provide an empty mask, which can cause errors at later steps (e.g. providing an empty processing mask to eddy in dwipreproc). Somehow, changing the syntax by which the sum of image intensities within a shell is calculated resolves the issue.

Thanks to Hamed Zivariadab for reporting the issue & providing data.

It's literally this change that fixes the issue... 🤷‍♂️

When an input DWI contains exceptionally low values (i.e. even in the b=0 volumes there's no voxels greater than 1.0), dwi2mask will provide an empty mask, which can cause errors at later steps (e.g. providing an empty processing mask to eddy in dwipreproc). Somehow, changing the syntax by which the sum of image intensities within a shell is calculated resolves the issue.
@Lestropie Lestropie self-assigned this Aug 8, 2017
@Lestropie Lestropie added the bug label Aug 8, 2017
@thijsdhollander
Copy link
Copy Markdown
Contributor

thijsdhollander commented Aug 8, 2017

Ok, I was first a bit at a loss how that could actually make a difference. Numerically, nothing really changed. There's also only really 2 (simple) ways I can think of to compute means, both being better in one or a different scenario: you could either sum everything and divide by the count, or you could first divide everything by the count and then sum all the outcomes. The latter takes more operations but is better protected against overflow, the former is better protected against underflow or low precision or something. But those things haven't changed between both implementations...

So it must be about what precision is used somehow for the summing operation. But unless the compiler does something clever (or dumb) in one of those scenarios that sets it apart from the other one, they're just both loops that each time add a number to an existing total. I'm not into what compilers these days can detect to e.g. see we're after a sum of a lot of numbers here, and then do something else than adding each number in that exact order sequentially to an accumulating total...

The only thing I suspect can really drive this difference is this line having 0.0 rather than 0 or the fact that in the first implementation at this line there also weren't 0.0's rather than 0's combined with the fact that it was a single line (or the combination of both of those reasons). Some of those combinations (and 0 appearing in places rather than 0.0), I suspect, can influence the precision used for the actual addition operation in the original implementation... so it becomes integer arithmetic rather than floating point (if it's mixed types, then it always / most cases becomes the lower precision, right?). This will then indeed become most prevalent in practice if the whole intensity range of the image is comprised of low magnitudes.

So all that to say that: I think there may be a possibility to fix it without doing away with the one liner of the original version. But it doesn't really matter in practice: if the proposed new implementation fixes it just as well, then we're good! So I'll approve the pull request already any way.

Copy link
Copy Markdown
Contributor

@thijsdhollander thijsdhollander left a comment

Choose a reason for hiding this comment

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

If it does the job, then I'm good with it! As I mentioned, I suppose something originally led to a mix of types, and integer arithmetic then being performed at line 92.

@jdtournier
Copy link
Copy Markdown
Member

OK, the explanation is much simpler: the ternary comparison operator has a return type, and that type is determined by the first return option. In other words, in cond ? A : B, the return type is determined by A, and if B can't be cast to the same type, you get a compile-time error. Since in this case, A is a regular int (0), the return type of the whole expression is an int, casting the image intensity to an int in the process... So the simplest fix here would have been:

mean += (input.value() < value_type(0)) ? value_type(0) : input.value();

Happy with the current fix as it stands though, the mean should always be considered to be floating-point even for integer inputs, and using double (default_type) here ensures the highest precision with no loss of performance that I can foresee...

@Lestropie
Copy link
Copy Markdown
Member Author

Ah of course. Was thinking that somehow something was operating as an integer because of being an integer image type with a tiny intensity multiplier, but that never made sense to me.

Prefer proposed change since it results in one call to input.value() rather than two. :-P

@Lestropie Lestropie merged commit ca446aa into master Aug 8, 2017
@Lestropie Lestropie deleted the dwi2mask_lowvalue_fix branch August 8, 2017 23:20
@Lestropie Lestropie mentioned this pull request Aug 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants