-
Notifications
You must be signed in to change notification settings - Fork 26
Class Image line projection method #1150
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
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
af3b9f5
added 2D projection stub
6db02df
initial test file add
34cd127
Stashing initial project with test placeholder
ec00dde
Style Updates
53791db
Pytest fixtures
7057392
Cleanup
8822a6c
changed nufft call
cf157cb
added stub for image stack line project
garrettwrong 6d0fa47
Dimensional Test Fix
eccb182
Multidim FFT
b7de220
Integrated stack reshape to project
654cbbc
Fleshed out Image Project Single and Multidim Tests
dd41f0f
Fixed the grid issues yay
8473a21
Angle slow moving axis
b736730
Replaced FFT with rfft
c3ecb8f
Cleaned up other unit tests
0fab57f
Added Doc Test and Cleaned up Code
c98c93e
fixup sinogram tests and simpler multi test
garrettwrong 7740110
fix irfft and shift
garrettwrong d9d498d
added angles but need to change multidim
50be3d3
Added Changes from PR: parameterized angles, adjusted tests according…
a8adc05
Added extra comments + Integrated Changes from lineproject_dbg2 branch
adfa6e0
Changed angle fixture description + Id Name
7664e92
Docstring len cleanup
garrettwrong File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| import numpy as np | ||
| import pytest | ||
| from skimage import data | ||
| from skimage.transform import radon | ||
|
|
||
| from aspire.image import Image | ||
| from aspire.utils import grid_2d | ||
|
|
||
| # Relative tolerance comparing line projections to scikit | ||
| # The same tolerance will be used in all scikit comparisons | ||
| SK_TOL = 0.005 | ||
|
|
||
| IMG_SIZES = [ | ||
| 511, | ||
| 512, | ||
| ] | ||
|
|
||
| DTYPES = [ | ||
| np.float32, | ||
| np.float64, | ||
| ] | ||
|
|
||
| ANGLES = [ | ||
| 1, | ||
| 50, | ||
| pytest.param(90, marks=pytest.mark.expensive), | ||
| pytest.param(117, marks=pytest.mark.expensive), | ||
| pytest.param(180, marks=pytest.mark.expensive), | ||
| pytest.param(360, marks=pytest.mark.expensive), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.fixture(params=DTYPES, ids=lambda x: f"dtype={x}", scope="module") | ||
| def dtype(request): | ||
| """ | ||
| Dtypes for image. | ||
| """ | ||
| return request.param | ||
|
|
||
|
|
||
| @pytest.fixture(params=IMG_SIZES, ids=lambda x: f"px={x}", scope="module") | ||
| def img_size(request): | ||
| """ | ||
| Image size. | ||
| """ | ||
| return request.param | ||
|
|
||
|
|
||
| @pytest.fixture(params=ANGLES, ids=lambda x: f"n_angles={x}", scope="module") | ||
| def num_ang(request): | ||
| """ | ||
| Number of angles in radon transform. | ||
| """ | ||
| return request.param | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def masked_image(dtype, img_size): | ||
| """ | ||
| Creates a masked image fixture using camera data from Scikit-Image. | ||
| """ | ||
| g = grid_2d(img_size, normalized=True, shifted=True) | ||
| mask = g["r"] < 1 | ||
|
|
||
| image = data.camera().astype(dtype) | ||
| image = image[:img_size, :img_size] | ||
| return Image(image * mask) | ||
|
|
||
|
|
||
| # Image.project and compare results to skimage.radon | ||
| def test_image_project(masked_image, num_ang): | ||
| """ | ||
| Test Image.project on a single stack of images. Compares project method output with skimage project. | ||
| """ | ||
| ny = masked_image.resolution | ||
| angles = np.linspace(0, 360, num_ang, endpoint=False) | ||
| rads = angles / 180 * np.pi | ||
| s = masked_image.project(rads) | ||
| assert s.shape == (1, len(angles), ny) | ||
|
|
||
| # sci-kit image `radon` reference | ||
| # | ||
| # Note, Image.project's angles are wrt projection line (ie | ||
| # grid), while sk's radon are wrt the image. To correspond the | ||
| # rotations are inverted. This was the convention prefered by | ||
| # the original author of this method. | ||
| # | ||
| # Note, transpose sk output to match (angles, points) | ||
| reference_sinogram = radon(masked_image._data[0], theta=angles[::-1]).T | ||
| assert reference_sinogram.shape == (len(angles), ny), "Incorrect Shape" | ||
|
|
||
| # compare project method on ski-image reference | ||
| nrms = np.sqrt(np.mean((s[0] - reference_sinogram) ** 2, axis=-1)) / np.linalg.norm( | ||
| reference_sinogram, axis=-1 | ||
| ) | ||
|
|
||
| np.testing.assert_array_less(nrms, SK_TOL, "Error in image projections.") | ||
|
|
||
|
|
||
| def test_multidim(num_ang): | ||
| """ | ||
| Test Image.project on stacks of images. Extension of test_image_project but for multi-dimensional stacks. | ||
| """ | ||
|
|
||
| L = 512 # pixels | ||
| n = 3 | ||
| m = 2 | ||
|
|
||
| # Generate a mask | ||
| g = grid_2d(L, normalized=True, shifted=True) | ||
| mask = g["r"] < 1 | ||
|
|
||
| # Generate images | ||
| imgs = Image(np.random.random((m, n, L, L))) * mask | ||
|
|
||
| # Generate line project angles | ||
| angles = np.linspace(0, 360, num_ang, endpoint=False) | ||
| rads = angles / 180.0 * np.pi | ||
| s = imgs.project(rads) | ||
|
|
||
| # Compare | ||
| reference_sinograms = np.empty((m, n, num_ang, L)) | ||
| for i in range(m): | ||
| for j in range(n): | ||
| img = imgs[i, j] | ||
| # Compute the singleton case, and compare with stack. | ||
| single_sinogram = img.project(rads) | ||
|
|
||
| # These should be allclose up to determinism in the FFT and NUFFT. | ||
| np.testing.assert_allclose(s[i, j : j + 1], single_sinogram) | ||
|
|
||
| # Next individually compute sk's radon transform for each image. | ||
| reference_sinograms[i, j] = radon(img._data[0], theta=angles[::-1]).T | ||
|
|
||
| _nrms = np.sqrt(np.mean((s - reference_sinograms) ** 2, axis=-1)) / np.linalg.norm( | ||
| reference_sinograms, axis=-1 | ||
| ) | ||
| np.testing.assert_array_less(_nrms, SK_TOL, "Error in image projections.") | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.