-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: Add RandNonCentralChiNoise transform #8618
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
base: dev
Are you sure you want to change the base?
feat: Add RandNonCentralChiNoise transform #8618
Conversation
Adds RandNonCentralChiNoise and RandNonCentralChiNoised, which generalize Rician noise to k degrees of freedom. Standard brain MRI typically uses 32 (or more) quadrature coils, so accurate noise simulation requires this modification, especially in the low SNR limit Includes array, dictionary, and test files.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR adds a new transform RandNonCentralChiNoise (array variant) and a dictionary wrapper RandNonCentralChiNoised, exports them via monai/transforms/init.py, updates CHANGELOG.md to list the new public entities, adds .gitignore entry monai-dev/, and includes unit tests for both the array and dictionary transforms (tests/transforms/test_rand_noncentralchi_noise.py and tests/transforms/test_rand_noncentralchi_noised.py). The array transform implements channel-wise and global modes, DoF validation, relative scaling and dtype handling; the dictionary class wraps and propagates RNG state and provides D/Dict aliases. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Key areas to inspect:
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
for more information, see https://pre-commit.ci
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.
Greptile Overview
Greptile Summary
This PR adds RandNonCentralChiNoise and RandNonCentralChiNoised transforms to generalize Rician noise simulation for modern MRI systems with multiple quadrature coils.
Key Changes:
- Implemented
RandNonCentralChiNoiseinarray.pywith configurable degrees of freedom (k), defaulting to 64 for typical 32-channel head coils - Added dictionary-based wrapper
RandNonCentralChiNoisedfollowing MONAI patterns - Comprehensive test coverage for both k=64 and k=2 (Rician) cases with numerical validation
- Updated exports in
__init__.pyand CHANGELOG - Mathematical implementation: computes sqrt(sum(k Gaussian noise arrays²)) where first array includes the signal
Implementation Quality:
- Follows existing
RandRicianNoisepatterns closely for consistency - Supports both NumPy and PyTorch backends
- Proper validation for degrees_of_freedom parameter (must be int ≥ 1)
- Channel-wise and relative noise options maintained
- Tests verify correctness against manual calculations
Confidence Score: 5/5
- Safe to merge - well-tested feature addition with proper validation and comprehensive test coverage
- Clean implementation following established patterns, comprehensive tests covering edge cases (k=2 Rician case and k=64), proper input validation, and no breaking changes to existing code
- No files require special attention
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| monai/transforms/intensity/array.py | 4/5 | Added RandNonCentralChiNoise transform generalizing Rician noise to k degrees of freedom with proper validation and both NumPy/Torch support |
| monai/transforms/intensity/dictionary.py | 5/5 | Added RandNonCentralChiNoised dictionary wrapper with proper random state management and key iteration |
| tests/transforms/test_rand_noncentralchi_noise.py | 5/5 | Comprehensive tests for array transform covering zero/non-zero mean, k=64 and k=2 (Rician) cases with numerical validation |
| tests/transforms/test_rand_noncentralchi_noised.py | 5/5 | Comprehensive tests for dictionary transform covering multiple keys, k=64 and k=2 cases with numerical validation |
Sequence Diagram
sequenceDiagram
participant User
participant RandNonCentralChiNoise
participant RandomizableTransform
participant _add_noise
participant NumPy/Torch
User->>RandNonCentralChiNoise: __call__(img, randomize=True)
RandNonCentralChiNoise->>RandNonCentralChiNoise: convert_to_tensor(img, dtype)
RandNonCentralChiNoise->>RandomizableTransform: randomize(None)
RandomizableTransform-->>RandNonCentralChiNoise: set _do_transform based on prob
alt not _do_transform
RandNonCentralChiNoise-->>User: return original img
else _do_transform
alt channel_wise
loop for each channel
RandNonCentralChiNoise->>_add_noise: _add_noise(channel, mean, std, k)
_add_noise->>NumPy/Torch: sample k Gaussian noise arrays
_add_noise->>_add_noise: add img to first noise array
_add_noise->>NumPy/Torch: compute sqrt(sum(noises²))
_add_noise-->>RandNonCentralChiNoise: noised channel
end
else not channel_wise
RandNonCentralChiNoise->>_add_noise: _add_noise(img, mean, std, k)
_add_noise->>NumPy/Torch: sample k Gaussian noise arrays (shape: k×img.shape)
_add_noise->>_add_noise: add img to first noise array [0]
_add_noise->>NumPy/Torch: compute sqrt(sum(noises²)) along axis 0
_add_noise-->>RandNonCentralChiNoise: noised img
end
RandNonCentralChiNoise-->>User: return noised img
end
5 files reviewed, 2 comments
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
monai/transforms/intensity/dictionary.py (1)
70-174: Missing public exports for alias variants.The aliases
RandNonCentralChiNoiseDandRandNonCentralChiNoiseDict(defined at line 2032) must be added to__all__to match the pattern of other transforms.Add these entries after line 73:
"RandGaussianNoised", "RandRicianNoised", "RandNonCentralChiNoised", + "RandNonCentralChiNoiseD", + "RandNonCentralChiNoiseDict", "ShiftIntensityd",
🧹 Nitpick comments (2)
monai/transforms/intensity/array.py (1)
176-176: Missing space after comment hash.Line 176 comment should be
# 64 default because...not#64 default because...Apply this diff:
- degrees_of_freedom: int = 64, #64 default because typical modern brain MRI is 32 quadrature coils + degrees_of_freedom: int = 64, # 64 default because typical modern brain MRI is 32 quadrature coilstests/transforms/test_rand_noncentralchi_noise.py (1)
33-33: Missing space after comment hash.Line 33 should be
# 64 is common...not#64 is common...Apply this diff:
- degrees_of_freedom = 64 #64 is common due to 32 channel head coil + degrees_of_freedom = 64 # 64 is common due to 32 channel head coil
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (7)
.gitignore(1 hunks)CHANGELOG.md(1 hunks)monai/transforms/__init__.py(2 hunks)monai/transforms/intensity/array.py(2 hunks)monai/transforms/intensity/dictionary.py(4 hunks)tests/transforms/test_rand_noncentralchi_noise.py(1 hunks)tests/transforms/test_rand_noncentralchi_noised.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.md
⚙️ CodeRabbit configuration file
Remember that documentation must be updated with the latest information.
Files:
CHANGELOG.md
**/*.py
⚙️ CodeRabbit configuration file
Review the Python code for quality and correctness. Ensure variable names adhere to PEP8 style guides, are sensible and informative in regards to their function, though permitting simple names for loop and comprehension variables. Ensure routine names are meaningful in regards to their function and use verbs, adjectives, and nouns in a semantically appropriate way. Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings. Examine code for logical error or inconsistencies, and suggest what may be changed to addressed these. Suggest any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness. Ensure new or modified definitions will be covered by existing or new unit tests.
Files:
tests/transforms/test_rand_noncentralchi_noise.pymonai/transforms/intensity/array.pymonai/transforms/intensity/dictionary.pytests/transforms/test_rand_noncentralchi_noised.pymonai/transforms/__init__.py
🪛 Ruff (0.14.2)
monai/transforms/intensity/array.py
169-169: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
187-187: Avoid specifying long messages outside the exception class
(TRY003)
237-237: Avoid specifying long messages outside the exception class
(TRY003)
239-239: Avoid specifying long messages outside the exception class
(TRY003)
242-242: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: packaging
- GitHub Check: build-docs
- GitHub Check: flake8-py3 (pytype)
- GitHub Check: quick-py3 (macOS-latest)
- GitHub Check: flake8-py3 (mypy)
- GitHub Check: flake8-py3 (codeformat)
- GitHub Check: quick-py3 (ubuntu-latest)
- GitHub Check: quick-py3 (windows-latest)
- GitHub Check: min-dep-pytorch (2.8.0)
- GitHub Check: min-dep-pytorch (2.7.1)
- GitHub Check: min-dep-pytorch (2.6.0)
- GitHub Check: min-dep-py3 (3.12)
- GitHub Check: min-dep-pytorch (2.5.1)
- GitHub Check: min-dep-py3 (3.11)
- GitHub Check: min-dep-py3 (3.9)
- GitHub Check: min-dep-py3 (3.10)
- GitHub Check: min-dep-os (ubuntu-latest)
- GitHub Check: min-dep-os (macOS-latest)
- GitHub Check: min-dep-os (windows-latest)
🔇 Additional comments (7)
monai/transforms/intensity/array.py (2)
194-212: LGTM!The
_add_noiseimplementation correctly follows the non-central chi distribution: generates k Gaussian noise components, adds the signal to the first component, then computes the magnitude. Handles both torch and numpy backends.
214-244: LGTM!The
__call__method correctly handles both channel-wise and global noise application. Type validation for mean/std parameters prevents runtime errors. Implementation is consistent withRandRicianNoise.tests/transforms/test_rand_noncentralchi_noise.py (1)
29-79: LGTM!Tests comprehensively verify correctness by replaying the random generation process and comparing against analytically computed expected results. Coverage includes both k=64 (typical MRI) and k=2 (Rician noise special case), with both zero and non-zero mean scenarios.
tests/transforms/test_rand_noncentralchi_noised.py (2)
35-35: Verify dtype choice.Tests use
dtype=np.float64while the array-based tests use default dtype (float32). Is higher precision intentionally required for dictionary variant testing, or should this match the array tests?
31-73: LGTM!Tests verify dictionary-based transform correctness by replaying random generation per key and comparing against expected results. Coverage includes multiple keys, both k=64 and k=2, and various mean/std combinations.
monai/transforms/intensity/dictionary.py (2)
241-313: LGTM - Implementation follows correct pattern.The implementation correctly follows the
RandRicianNoisedpattern with independent noise per key (line 312), which is appropriate since non-central chi noise generalizes Rician noise. Docstring is comprehensive, type hints are correct, and random state propagation is properly implemented.
2032-2032: Aliases correctly defined.Follows MONAI naming convention pattern.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: karllandheer <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: karllandheer <[email protected]>
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
monai/transforms/intensity/array.py (1)
171-192: Remove redundant assignment.Line 183 assigns
self.prob = prob, butRandomizableTransform.__init__(self, prob)on line 182 already sets this attribute.def __init__( self, prob: float = 0.1, mean: Sequence[float] | float = 0.0, std: Sequence[float] | float = 1.0, degrees_of_freedom: int = 64, # 64 default because typical modern brain MRI is 32 quadrature coils channel_wise: bool = False, relative: bool = False, sample_std: bool = True, dtype: DtypeLike = np.float32, ) -> None: RandomizableTransform.__init__(self, prob) - self.prob = prob self.mean = mean self.std = std if not isinstance(degrees_of_freedom, int) or degrees_of_freedom < 1: raise ValueError("degrees_of_freedom must be an integer >= 1.") self.degrees_of_freedom = degrees_of_freedom self.channel_wise = channel_wise self.relative = relative self.sample_std = sample_std self.dtype = dtypetests/transforms/test_rand_noncentralchi_noise.py (1)
29-79: Consider testing additional modes.Current tests validate core correctness well. For more comprehensive coverage, consider adding tests for
channel_wise=Trueandrelative=Truemodes, which have distinct code paths in the implementation.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (2)
monai/transforms/intensity/array.py(2 hunks)tests/transforms/test_rand_noncentralchi_noise.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
⚙️ CodeRabbit configuration file
Review the Python code for quality and correctness. Ensure variable names adhere to PEP8 style guides, are sensible and informative in regards to their function, though permitting simple names for loop and comprehension variables. Ensure routine names are meaningful in regards to their function and use verbs, adjectives, and nouns in a semantically appropriate way. Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings. Examine code for logical error or inconsistencies, and suggest what may be changed to addressed these. Suggest any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness. Ensure new or modified definitions will be covered by existing or new unit tests.
Files:
tests/transforms/test_rand_noncentralchi_noise.pymonai/transforms/intensity/array.py
🪛 Ruff (0.14.2)
monai/transforms/intensity/array.py
169-169: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
187-187: Avoid specifying long messages outside the exception class
(TRY003)
237-237: Avoid specifying long messages outside the exception class
(TRY003)
239-239: Avoid specifying long messages outside the exception class
(TRY003)
242-242: Avoid specifying long messages outside the exception class
(TRY003)
🔇 Additional comments (5)
monai/transforms/intensity/array.py (3)
42-44: LGTM - proper module export.The new transform is correctly added to
__all__with appropriate alphabetical positioning.
194-212: LGTM - correct non-central chi noise implementation.The method properly implements non-central chi distribution by adding the image to the first of k Gaussian noise channels, then computing the root sum of squares. Handles both Torch and NumPy paths efficiently.
214-244: LGTM - robust implementation with proper validation.The
__call__method correctly handles both channel-wise and global noise application modes, with appropriate type checking and relative scaling support. The pattern is consistent with similar transforms in the codebase.tests/transforms/test_rand_noncentralchi_noise.py (2)
29-52: LGTM - thorough correctness validation.Test properly validates the transform by manually replicating the noise generation with controlled seeding. Checks dtype preservation and numerical correctness with appropriate tolerance.
54-79: LGTM - validates Rician noise special case.Explicitly testing degrees_of_freedom=2 is valuable since Rician noise is a well-known special case of non-central chi distribution mentioned in the transform documentation.
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.
Greptile Overview
Greptile Summary
Adds RandNonCentralChiNoise and RandNonCentralChiNoised transforms to generalize Rician noise (k=2) to arbitrary degrees of freedom (k), enabling accurate noise simulation for modern MRI with 32+ quadrature coils.
Key changes:
- Implements array transform
RandNonCentralChiNoisewith proper input validation (degrees_of_freedommust be int >= 1) - Adds dictionary wrapper
RandNonCentralChiNoisedfollowing MONAI's established patterns - Supports both PyTorch and NumPy backends with proper device handling
- Includes comprehensive tests verifying mathematical correctness for k=2 (Rician case) and k=64
- Properly exports new transforms in
__init__.pyand updates CHANGELOG - Follows existing codebase patterns from
RandRicianNoisefor consistency
The implementation correctly models the non-central chi distribution by generating k Gaussian noise arrays, adding the signal to the first array, and computing the magnitude as sqrt(sum(squares)). The code structure, error handling, and testing approach align well with MONAI conventions.
Confidence Score: 4/5
- This PR is safe to merge with minor considerations
- The implementation follows established MONAI patterns closely (mirroring
RandRicianNoise), includes proper input validation, comprehensive tests with mathematical verification, and supports both major backends. Score of 4 rather than 5 due to limited test coverage of edge cases (channel_wise, relative, sample_std=False modes not explicitly tested) and the addition of a personal dev directory to .gitignore. - No files require special attention - all implementations follow established patterns and include proper validation
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| monai/transforms/intensity/array.py | 4/5 | Added RandNonCentralChiNoise transform that generalizes Rician noise to k degrees of freedom, with proper validation and support for both PyTorch and NumPy backends |
| monai/transforms/intensity/dictionary.py | 5/5 | Added dictionary wrapper RandNonCentralChiNoised following existing MONAI patterns for dictionary transforms |
| tests/transforms/test_rand_noncentralchi_noise.py | 4/5 | Comprehensive tests for array transform with zero/non-zero mean cases and k=2 (Rician) validation |
| tests/transforms/test_rand_noncentralchi_noised.py | 4/5 | Dictionary transform tests covering multiple keys with proper randomization state verification |
| .gitignore | 5/5 | Added monai-dev/ directory to gitignore (developer's local directory) |
Sequence Diagram
sequenceDiagram
participant User
participant RandNonCentralChiNoised
participant RandNonCentralChiNoise
participant RandomState
User->>RandNonCentralChiNoised: __call__(data_dict)
RandNonCentralChiNoised->>RandomState: randomize() - check prob
alt transform should apply
loop for each key in data_dict
RandNonCentralChiNoised->>RandNonCentralChiNoise: __call__(img, randomize=True)
RandNonCentralChiNoise->>RandomState: randomize() - check prob
RandNonCentralChiNoise->>RandNonCentralChiNoise: _add_noise(img, mean, std, k)
RandNonCentralChiNoise->>RandomState: uniform(0, std) if sample_std
RandNonCentralChiNoise->>RandomState: normal(mean, std, size=(k, ...))
Note over RandNonCentralChiNoise: Generate k Gaussian noise arrays
Note over RandNonCentralChiNoise: Add signal to first array: noises[0] += img
Note over RandNonCentralChiNoise: Compute sqrt(sum(noises^2))
RandNonCentralChiNoise-->>RandNonCentralChiNoised: noised_img
end
RandNonCentralChiNoised-->>User: noised_data_dict
else skip transform
RandNonCentralChiNoised-->>User: original_data_dict
end
5 files reviewed, no comments
Adds RandNonCentralChiNoise and RandNonCentralChiNoised, which generalize Rician noise to k degrees of freedom. Standard brain MRI typically uses 32 (or more) quadrature coils, so accurate noise simulation requires this modification, especially in the low SNR limit.
Includes array, dictionary, and test files.
Fixes # .
Description
A few sentences describing the changes proposed in this pull request.
Types of changes
./runtests.sh -f -u --net --coverage../runtests.sh --quick --unittests --disttests.make htmlcommand in thedocs/folder.