cornucopia 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. cornucopia-0.4.0/PKG-INFO +93 -0
  2. {cornucopia-0.3.0 → cornucopia-0.4.0}/README.md +1 -1
  3. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/_version.py +3 -3
  4. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/base.py +22 -4
  5. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/baseutils.py +23 -0
  6. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/contrast.py +1 -1
  7. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/geometric.py +223 -69
  8. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/intensity.py +87 -18
  9. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/kspace.py +29 -17
  10. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/labels.py +28 -22
  11. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/noise.py +18 -12
  12. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/psf.py +7 -6
  13. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/qmri.py +74 -69
  14. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/random.py +60 -14
  15. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/synth.py +6 -2
  16. cornucopia-0.4.0/cornucopia/tests/test_backward_geometric.py +173 -0
  17. cornucopia-0.4.0/cornucopia/tests/test_backward_intensity.py +243 -0
  18. cornucopia-0.4.0/cornucopia/tests/test_backward_kspace.py +115 -0
  19. cornucopia-0.4.0/cornucopia/tests/test_backward_noise.py +169 -0
  20. cornucopia-0.4.0/cornucopia/tests/test_backward_psf.py +143 -0
  21. cornucopia-0.4.0/cornucopia/tests/test_backward_qmri.py +249 -0
  22. cornucopia-0.4.0/cornucopia/tests/test_backward_random.py +44 -0
  23. cornucopia-0.4.0/cornucopia/tests/test_backward_synth.py +72 -0
  24. cornucopia-0.4.0/cornucopia/tests/test_geometric.py +26 -0
  25. cornucopia-0.4.0/cornucopia/tests/test_intensity.py +9 -0
  26. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/b0.py +23 -23
  27. cornucopia-0.4.0/cornucopia/utils/compat.py +30 -0
  28. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/conv.py +14 -9
  29. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/kernels.py +124 -115
  30. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/py.py +19 -2
  31. cornucopia-0.4.0/cornucopia/utils/smart_inplace.py +163 -0
  32. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/warps.py +6 -3
  33. cornucopia-0.4.0/cornucopia.egg-info/PKG-INFO +93 -0
  34. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia.egg-info/SOURCES.txt +12 -0
  35. {cornucopia-0.3.0 → cornucopia-0.4.0}/setup.cfg +1 -1
  36. cornucopia-0.3.0/PKG-INFO +0 -87
  37. cornucopia-0.3.0/cornucopia.egg-info/PKG-INFO +0 -87
  38. {cornucopia-0.3.0 → cornucopia-0.4.0}/LICENSE +0 -0
  39. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/__init__.py +0 -0
  40. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/ctx.py +0 -0
  41. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/fov.py +0 -0
  42. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/io.py +0 -0
  43. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/special.py +0 -0
  44. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/__init__.py +0 -0
  45. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_contrast.py +0 -0
  46. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_fov.py +0 -0
  47. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_geometric.py +0 -0
  48. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_intensity.py +0 -0
  49. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_kspace.py +0 -0
  50. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_labels.py +0 -0
  51. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_noise.py +0 -0
  52. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_psf.py +0 -0
  53. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_qmri.py +0 -0
  54. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/tests/test_run_synth.py +0 -0
  55. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/__init__.py +0 -0
  56. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/bounds.py +0 -0
  57. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/gmm.py +0 -0
  58. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/indexing.py +0 -0
  59. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/io.py +0 -0
  60. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/jit.py +0 -0
  61. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/morpho.py +0 -0
  62. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/padding.py +0 -0
  63. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/patch.py +0 -0
  64. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia/utils/version.py +0 -0
  65. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia.egg-info/dependency_links.txt +0 -0
  66. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia.egg-info/requires.txt +0 -0
  67. {cornucopia-0.3.0 → cornucopia-0.4.0}/cornucopia.egg-info/top_level.txt +0 -0
  68. {cornucopia-0.3.0 → cornucopia-0.4.0}/pyproject.toml +0 -0
  69. {cornucopia-0.3.0 → cornucopia-0.4.0}/setup.py +0 -0
  70. {cornucopia-0.3.0 → cornucopia-0.4.0}/versioneer.py +0 -0
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: cornucopia
3
+ Version: 0.4.0
4
+ Summary: An abundance of augmentation layers
5
+ Author: Yael Balbastre
6
+ Author-email: yael.balbastre@gmail.com
7
+ License: MIT
8
+ Project-URL: Source Code, https://github.com/balbasty/cornucopia
9
+ Platform: OS Independent
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
16
+ Requires-Python: >=3.6
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: torch>=1.8
20
+ Requires-Dist: numpy
21
+ Requires-Dist: nibabel
22
+ Requires-Dist: torch-interpol>=0.2.4
23
+ Requires-Dist: torch-distmap
24
+ Dynamic: license-file
25
+
26
+ <picture align="center">
27
+ <source media="(prefers-color-scheme: dark)" srcset="docs/icons/cornucopia_lightorange.svg">
28
+ <source media="(prefers-color-scheme: light)" srcset="docs/icons/cornucopia_orange.svg">
29
+ <img alt="Cornucopia logo" src="https://github.com/balbasty/cornucopia/raw/main/docs/icons/cornucopia_orange.svg">
30
+ </picture>
31
+
32
+ The `cornucopia` package provides a generic framework for preprocessing,
33
+ augmentation, and domain randomization; along with an abundance of specific layers,
34
+ mostly targeted at (medical) imaging. `cornucopia` is written using a PyTorch
35
+ backend, and therefore runs **on the CPU or GPU**.
36
+
37
+ Cornucopia is *intended* to be used on the GPU for on-line augmentation.
38
+ A quick [benchmark](docs/examples/benchmark.ipynb) of affine and elastic augmentation
39
+ shows that while cornucopia is slower than [TorchIO](https://github.com/fepegar/torchio)
40
+ on the CPU (~ 3s vs 1s), it is greatly accelerated on the GPU (~ 50ms).
41
+
42
+ Since gradients are not expected to backpropagate through its layers, it can
43
+ theoretically be used within any dataloader pipeline,
44
+ independent of the downstream learning framework (pytorch, tensorflow, jax, ...).
45
+
46
+ ## Installation
47
+
48
+ ### Dependencies
49
+
50
+ - `pytorch >= 1.8`
51
+ - `numpy`
52
+ - `nibabel`
53
+ - `torch-interpol`
54
+ - `torch-distmap`
55
+
56
+ ### Conda
57
+
58
+ ```sh
59
+ conda install cornucopia -c balbasty -c pytorch -c conda-forge
60
+ ```
61
+
62
+ ### Pip (release)
63
+
64
+ ```sh
65
+ pip install cornucopia
66
+ ```
67
+
68
+ ### Pip (dev)
69
+
70
+ ```sh
71
+ pip install cornucopia@git+https://github.com/balbasty/cornucopia
72
+ ```
73
+
74
+ ## Documentation
75
+
76
+ Read the [documentation](https://cornucopia.readthedocs.io) and in particular:
77
+ - [installation](https://cornucopia.readthedocs.io/en/latest/install/)
78
+ - [get started](https://cornucopia.readthedocs.io/en/latest/start/)
79
+ - [examples](https://cornucopia.readthedocs.io/en/latest/examples/overview/)
80
+ - [API](https://cornucopia.readthedocs.io/en/latest/api/overview/)
81
+
82
+ ## Other augmentation packages
83
+
84
+ There are other great, and much more mature, augmentation packages
85
+ out-there (although few run on the GPU). Here's a non-exhaustive list:
86
+ - [MONAI](https://github.com/Project-MONAI/MONAI)
87
+ - [TorchIO](https://github.com/fepegar/torchio)
88
+ - [Albumentations](https://github.com/albumentations-team/albumentations) (2D only)
89
+ - [Volumentations](https://github.com/ZFTurbo/volumentations) (3D extension of Albumentations)
90
+
91
+ ## Contributions
92
+
93
+ If you find this project useful and wish to contribute, please reach out!
@@ -10,7 +10,7 @@ mostly targeted at (medical) imaging. `cornucopia` is written using a PyTorch
10
10
  backend, and therefore runs **on the CPU or GPU**.
11
11
 
12
12
  Cornucopia is *intended* to be used on the GPU for on-line augmentation.
13
- A quick [benchmark](examples/benchmark.ipynb) of affine and elastic augmentation
13
+ A quick [benchmark](docs/examples/benchmark.ipynb) of affine and elastic augmentation
14
14
  shows that while cornucopia is slower than [TorchIO](https://github.com/fepegar/torchio)
15
15
  on the CPU (~ 3s vs 1s), it is greatly accelerated on the GPU (~ 50ms).
16
16
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-04-19T14:23:50+0100",
11
+ "date": "2025-04-16T18:13:05+0100",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "37de94f181b9a97eebd21460f4df63ae4a0750f8",
15
- "version": "0.3.0"
14
+ "full-revisionid": "6f8ab58dfcfe8978c9aa9e8b05898dcf7d75bb5b",
15
+ "version": "0.4.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -447,6 +447,8 @@ class SequentialTransform(SpecialMixin, SharedMixin, Transform):
447
447
  def make_final(self, x, max_depth=float('inf')):
448
448
  if max_depth == 0:
449
449
  return self
450
+ if self.is_final:
451
+ return self
450
452
  # x = VirtualTensor.from_any(x, compute_stats=True)
451
453
  trf = []
452
454
  for t in self:
@@ -477,12 +479,23 @@ class SequentialTransform(SpecialMixin, SharedMixin, Transform):
477
479
  x = args[0]
478
480
  else:
479
481
  return None
480
- for trf in self.transforms:
482
+ for trf in self:
481
483
  with IncludeKeysTransform(trf, self.include), \
482
484
  ExcludeKeysTransform(trf, self.exclude):
483
485
  x = trf(x)
484
486
  return x
485
487
 
488
+ def xform(self, x):
489
+ # This should only be called when a Layer's `make_final` returns
490
+ # a `SequentialTransform`` (i.e., it is created implictly under
491
+ # the hood, not explicitly by the user).
492
+ # In such cases, `shared=False` and hopefully we can just fallback
493
+ # to `forward()`.
494
+ #
495
+ # FIXME
496
+ # what happens if there's weird stuff in returns/include/exclude?
497
+ return self(x)
498
+
486
499
  def __len__(self):
487
500
  return len(self.transforms)
488
501
 
@@ -556,9 +569,11 @@ class MaybeTransform(SpecialMixin, SharedMixin, Transform):
556
569
  img = (0.2 * gauss)(img)
557
570
  ```
558
571
  ```
559
- """
560
572
 
561
- def __init__(self, transform, prob=0.5, *, shared=False, **kwargs):
573
+ !!! changedin "![v0.4](https://img.shields.io/badge/v0.4-yellow) \
574
+ Default for `shared` changed from `False` to `True`"
575
+ """
576
+ def __init__(self, transform, prob=0.5, *, shared=True, **kwargs):
562
577
  """
563
578
 
564
579
  Parameters
@@ -617,9 +632,12 @@ class SwitchTransform(SpecialMixin, SharedMixin, Transform):
617
632
  ```python
618
633
  img = cc.switch({gauss: 0.5, chi: 0.5})(img)
619
634
  ```
635
+
636
+ !!! changedin "![v0.4](https://img.shields.io/badge/v0.4-yellow) \
637
+ Default for `shared` changed from `False` to `True`"
620
638
  """
621
639
 
622
- def __init__(self, transforms, prob=0, *, shared=False, **kwargs):
640
+ def __init__(self, transforms, prob=0, *, shared=True, **kwargs):
623
641
  """
624
642
 
625
643
  Parameters
@@ -117,6 +117,29 @@ def returns_find(flag, returned, returns):
117
117
  return None
118
118
 
119
119
 
120
+ def returns_update(value, flag, returned, returns):
121
+ """Find tensor corresponding to flag in returned structure"""
122
+ if returns is None:
123
+ if flag == 'output':
124
+ return value
125
+ else:
126
+ return None
127
+ if isinstance(returns, dict):
128
+ if flag in returns:
129
+ returned[flag] = value
130
+ return returned
131
+ elif isinstance(returns, (list, tuple)):
132
+ if flag in returns:
133
+ returned[returns.index(flag)] = value
134
+ return returned
135
+ else:
136
+ assert isinstance(returns, str)
137
+ if returns == flag:
138
+ return value
139
+ else:
140
+ return None
141
+
142
+
120
143
  def flatstruct(x):
121
144
  """Flatten a nested structure of tensors"""
122
145
 
@@ -184,7 +184,7 @@ class ContrastLookupTransform(NonFinalTransform):
184
184
 
185
185
  vmin, vmax = x.min(), x.max()
186
186
  edges = torch.linspace(vmin, vmax, self.nk+1)
187
- new_mu = torch.rand(self.nk) * (vmax - vmin) + vmin
187
+ new_mu = torch.rand(self.nk).to(x) * (vmax - vmin) + vmin
188
188
  return self.LookupFinalTransform(
189
189
  edges, new_mu, **self.get_prm()
190
190
  ).make_final(x, max_depth-1)