New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added eigh version of localpca to svd version #1297
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Please rewrite the tests to make sure that they run both methods (and maybe that they give the same answer?).
dipy/denoise/localpca.py
Outdated
if out_dtype is None: | ||
out_dtype = arr.dtype | ||
|
||
# We retain float64 precision, iff the input is in this precision: | ||
# We retain float64 precision, if the input is in this precision: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was intentional! To denote that we do this "if and only if".
dipy/denoise/localpca.py
Outdated
@@ -97,6 +105,8 @@ def localpca(arr, sigma, patch_radius=2, tau_factor=2.3, out_dtype=None): | |||
for j in range(patch_radius, arr.shape[1] - patch_radius): | |||
for i in range(patch_radius, arr.shape[0] - patch_radius): | |||
# Shorthand for indexing variables: | |||
if mask[i, j, k] == False: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
@@ -133,4 +152,5 @@ def localpca(arr, sigma, patch_radius=2, tau_factor=2.3, out_dtype=None): | |||
|
|||
denoised_arr = thetax / theta | |||
denoised_arr.clip(min=0, out=denoised_arr) | |||
denoised_arr[~mask] = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put back the original values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, this is an application of the mask. If we put back the original values we will have noise in the background which is undesired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. Might be worth being explicit about this in the docstring. Some users might be surprised to see non-brain parts of the image (e.g., skull) disappear.
Please also @ssheybani include more information in the description and show how eigh compares with svd in some slices (not in tests - just in the description for reference and helping the reviewers understand). We may have discussed some things in person but that doesn't mean that the others here know what we have discussed. |
Fix typos in method documentation for the dti.py reconstruction file.
Add missing label to reciprocal space equation in b_and_q.rst file in the doc/theory folder. Although according to http://www.sphinx-doc.org/en/stable/ext/math.html cross-referencing equations via their label will not work across different documents. The equation at issue is referenced from doc/faq.rst.
1 similar comment
Codecov Report
@@ Coverage Diff @@
## master #1297 +/- ##
=========================================
- Coverage 87.13% 87.03% -0.1%
=========================================
Files 228 228
Lines 28800 28834 +34
Branches 3093 3099 +6
=========================================
+ Hits 25094 25095 +1
- Misses 3003 3035 +32
- Partials 703 704 +1
Continue to review full report at Codecov.
|
…FormulainDoc DOC: Add missing label to reciprocal space eq.
dipy/denoise/localpca.py
Outdated
# If mask is not specified, use the whole volume | ||
mask = np.ones_like(arr, dtype=bool)[..., 0] | ||
|
||
if pca_method == 'svd': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add the imports at the top of the file.
dipy/denoise/tests/test_lpca.py
Outdated
@@ -223,13 +228,13 @@ def test_phantom(): | |||
rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc - DWI)) / \ | |||
np.sum(np.abs(DWI_clean_wrc)) | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test with the mask.
dipy/denoise/localpca.py
Outdated
# order: | ||
W = Vt[::-1].T | ||
|
||
if pca_method == 'svd': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to check the string each time. Add a boolean variable.
DOC: Fix typos in dti.py reconstruction file doc.
1 similar comment
1 similar comment
Fix inversion in the dti mode doc
Where does this stand? Looks like we are still missing a test with the mask, but that this is otherwise ready to go? |
1 similar comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small additional comments about mask documentation and test.
dipy/denoise/tests/test_lpca.py
Outdated
# Try this with a sigma volume, instead of a scalar | ||
sigma_vol = sigma * np.ones(DWI.shape[:-1]) | ||
DWI_den = localpca(DWI, sigma_vol, patch_radius=3) | ||
mask = np.ones_like(DWI, dtype=bool)[..., 0] | ||
DWI_den = localpca(DWI, sigma_vol, mask, patch_radius=3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now, this doesn't really test that the mask does anything. Maybe make a mask with some ones and some zeros and check that the part that has ones in it gets denoised, while the part that has zeros returns as all zeros.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that. But assert_(rmse_den < rmse_noisy) gave me an error. I think that's most likely because setting the out-of-mask areas to zero has caused the denoised image to have more error than the original noisy one. I checked the DWI_clean matrix and it looks like that for a good number of slices, the background is not actually black (0) but white (1)! Do you have any suggestions on what is the preferred way to deal with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds to me like the mask is doing something funky! I am not sure where that arises. Could you implement this and push it here? I might be able to fetch your branch and play around with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just added this for instance:
mask[0, ...] = False
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get this failure only for the second comparison:
assert_(rmse_den_wrc < rmse_noisy_wrc)
not for the first one:
assert_(rmse_den < rmse_noisy)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just corrected it . What I've done is when it comes to calculating rmse values, I applied the mask on the original data as well, to avoid the problem with the background. Thus it didn't raise any error.
dipy/denoise/localpca.py
Outdated
r"""Local PCA-based denoising of diffusion datasets. | ||
|
||
Parameters | ||
---------- | ||
arr : 4D array | ||
Array of data to be denoised. The dimensions are (X, Y, Z, N), where N | ||
are the diffusion gradient directions. | ||
mask : 3D boolean array | ||
A mask with voxels that are true inside the brain and false outside of | ||
it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still missing an explanation of what the mask does here: it denoises within the True part and returns zeros outside of those voxels, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add that to the docstring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ping @ssheybani there are some final requests here. It would be great to merge this asap. Thanks. |
1 similar comment
Yup. I think that was it. This is ready to merge from my point of view. @Garyfallidis : feel free to merge if all your comments have been addressed. |
Great! Thank you @ssheybani :) |
Added eigh version of localpca to svd version
Denoising with LocalPCA:
The bottleneck of LocalPCA is decomposition of the data into the eigenvalues/singular values and the corresponding vectors (basis). For this purpose, Eigenvalue decomposition was used but was previously replaced with Singular Values Decomposition, for more accuracy. However, SVD is many times slower than EVD. In this pull request, the code is changed to include both of them, as an optional parameter, "pca_method".
Also for more speed up, the function accepts the brain mask as an optional parameter and the denoising will be only applied to the voxels inside the mask.