Skip to content

Comments

Improve Affine transform documentation and add compute_w_affine tests#8727

Open
engmohamedsalah wants to merge 1 commit intoProject-MONAI:devfrom
engmohamedsalah:improve-affine-docs-7092
Open

Improve Affine transform documentation and add compute_w_affine tests#8727
engmohamedsalah wants to merge 1 commit intoProject-MONAI:devfrom
engmohamedsalah:improve-affine-docs-7092

Conversation

@engmohamedsalah
Copy link
Contributor

@engmohamedsalah engmohamedsalah commented Jan 31, 2026

Summary

This PR improves the documentation for the Affine transform and adds unit tests for the compute_w_affine method.

Fixes #7092

Changes

Documentation improvements (monai/transforms/spatial/array.py)

  • Added Note section to Affine class documenting the center-origin coordinate system assumption
  • Clarified normalized parameter documentation with user-friendly explanation
  • Added comprehensive docstring to compute_w_affine classmethod (previously undocumented)

Unit tests (tests/transforms/test_affine.py)

  • Added TestComputeWAffine test class with focused tests:
    • 2D/3D identity matrix with same input/output size
    • Different input/output sizes with expected translation offsets
    • Output shape validation
    • Torch tensor input compatibility

Verification

  • All existing Affine tests pass (no regressions)
  • All new compute_w_affine tests pass
  • Documentation matches actual code logic

Type of change

  • Documentation improvement
  • Test coverage improvement
  • Breaking change

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a public classmethod compute_w_affine(spatial_rank, mat, img_size, sp_size) to Affine in monai/transforms/spatial/array.py. The method converts mat to an (r+1)x(r+1) affine, composes translations to shift from image center to origin, applies mat, then shifts to output center, and returns the resulting center-based affine. Updated Affine docstring and __init__ normalized parameter docs to describe center-origin handling. Added unit tests for compute_w_affine (2D/3D identity, different sizes, output shape, torch input); the test class appears duplicated in the diff.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR addresses issue #7092 by documenting coordinate system assumptions and adding tests for compute_w_affine, but does not resolve the core bug (incorrect output affine after resampling). Implement functional fix to make Affine return correct target affine as described in #7092, not just documentation/tests. Current changes only address explicitness, not the underlying problem.
Out of Scope Changes check ⚠️ Warning Test file contains duplicate TestComputeWAffine class definition (noted in summary), which is an unintended change and inconsistent with stated PR scope. Remove the duplicate TestComputeWAffine class definition in tests/transforms/test_affine.py to ensure only one instance exists.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes main changes: documentation improvements and test additions for Affine transform's compute_w_affine method.
Description check ✅ Passed Description covers documentation improvements, test additions, and verification. Follows template structure with changes listed and type of change indicated.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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/spatial/array.py (1)

2333-2358: ⚠️ Potential issue | 🟡 Minor

Add focused unit test for compute_w_affine and preserve input type.

The method lacks direct test coverage. Add a focused test (e.g., 2D case with known center shifts). Additionally, align with MONAI patterns by preserving the input matrix type:

Type preservation refactor
-        mat = shift_1 @ convert_data_type(mat, np.ndarray)[0] @ shift_2
-        return mat
+        mat_np = shift_1 @ convert_data_type(mat, np.ndarray)[0] @ shift_2
+        return convert_to_dst_type(mat_np, mat)[0]

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/transforms/test_affine.py (2)

220-228: Partial assertion + zip without strict=.

Two issues in test_different_sizes:

  1. result[:2, 2] only verifies the translation column; the upper-left 2×2 sub-matrix (should be np.eye(2) for an identity mat) is never asserted. A full matrix comparison would be more robust.
  2. zip(img_size, sp_size) silently truncates if lengths differ — add strict=True.
♻️ Proposed fix
     mat = np.eye(3)
     img_size = (4, 4)
     sp_size = (8, 8)
     result = Affine.compute_w_affine(2, mat, img_size, sp_size)
     # Translation should account for the shift: (4-1)/2 - (8-1)/2 = 1.5 - 3.5 = -2.0
-    expected_translation = np.array([(d1 - 1) / 2 - (d2 - 1) / 2 for d1, d2 in zip(img_size, sp_size)])
-    assert_allclose(result[:2, 2], expected_translation, atol=1e-6)
+    expected_translation = np.array([(d1 - 1) / 2 - (d2 - 1) / 2 for d1, d2 in zip(img_size, sp_size, strict=True)])
+    expected = np.eye(3)
+    expected[:2, 2] = expected_translation
+    assert_allclose(result, expected, atol=1e-6)

As per coding guidelines, ensure logic errors and edge-case inconsistencies are addressed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_affine.py` around lines 220 - 228, The
test_different_sizes currently only asserts the translation column and uses
zip(img_size, sp_size) without strict checking; update the test to construct the
full expected 3x3 affine matrix (upper-left 2x2 should be np.eye(2) for identity
mat and bottom row [0,0,1]) with the translation computed via zip(img_size,
sp_size, strict=True), then assert_allclose(result, expected_matrix, atol=1e-6)
against Affine.compute_w_affine's returned result to verify both rotation/scale
and translation.

202-241: No test covers a non-identity mat.

Every test passes np.eye(n) as mat, so the centering composition shift_1 @ R @ shift_2 is never exercised for a rotation or scale. The most important behaviour of compute_w_affine (that a non-trivial transform is correctly sandwiched between center-shifts) is untested.

A minimal addition — e.g. a 90° rotation in 2D with known expected output — would close this gap.

As per coding guidelines, "ensure new or modified definitions will be covered by existing or new unit tests."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_affine.py` around lines 202 - 241, Add a new unit test
in tests/transforms/test_affine.py that passes a non-identity transform to
Affine.compute_w_affine to exercise the centering composition; for example
create a 2D 90° rotation matrix (use numpy or torch), call
Affine.compute_w_affine(2, rot, img_size, sp_size) with equal img_size and
sp_size (so center shifts cancel), and assert the result equals the pure
rotation (use assert_allclose with atol). Name the test e.g. test_rotation_90_2d
and reuse the existing patterns (np.eye tests) to keep shape and tolerance
checks consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/transforms/test_affine.py`:
- Around line 220-228: The test_different_sizes currently only asserts the
translation column and uses zip(img_size, sp_size) without strict checking;
update the test to construct the full expected 3x3 affine matrix (upper-left 2x2
should be np.eye(2) for identity mat and bottom row [0,0,1]) with the
translation computed via zip(img_size, sp_size, strict=True), then
assert_allclose(result, expected_matrix, atol=1e-6) against
Affine.compute_w_affine's returned result to verify both rotation/scale and
translation.
- Around line 202-241: Add a new unit test in tests/transforms/test_affine.py
that passes a non-identity transform to Affine.compute_w_affine to exercise the
centering composition; for example create a 2D 90° rotation matrix (use numpy or
torch), call Affine.compute_w_affine(2, rot, img_size, sp_size) with equal
img_size and sp_size (so center shifts cancel), and assert the result equals the
pure rotation (use assert_allclose with atol). Name the test e.g.
test_rotation_90_2d and reuse the existing patterns (np.eye tests) to keep shape
and tolerance checks consistent.

ℹ️ Review info

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

📥 Commits

Reviewing files that changed from the base of the PR and between e98a3cd and f08fa24.

📒 Files selected for processing (1)
  • tests/transforms/test_affine.py

@engmohamedsalah engmohamedsalah force-pushed the improve-affine-docs-7092 branch from f08fa24 to 7bbcf4e Compare February 23, 2026 11:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/transforms/test_affine.py (1)

220-228: test_different_sizes only validates the translation column — consider asserting the full matrix.

With mat=np.eye(3), the linear part stays identity and the expected full result is a pure translation matrix. Asserting result[:2, 2] alone leaves the rotation/scale block untested.

♻️ Proposed refactor
     def test_different_sizes(self):
         """When img_size != sp_size, result should include net translation."""
         mat = np.eye(3)
         img_size = (4, 4)
         sp_size = (8, 8)
         result = Affine.compute_w_affine(2, mat, img_size, sp_size)
-        # Translation should account for the shift: (4-1)/2 - (8-1)/2 = 1.5 - 3.5 = -2.0
-        expected_translation = np.array([(d1 - 1) / 2 - (d2 - 1) / 2 for d1, d2 in zip(img_size, sp_size)])
-        assert_allclose(result[:2, 2], expected_translation, atol=1e-6)
+        # (4-1)/2 - (8-1)/2 = 1.5 - 3.5 = -2.0 along each axis
+        expected = np.array([[1.0, 0.0, -2.0], [0.0, 1.0, -2.0], [0.0, 0.0, 1.0]])
+        assert_allclose(result, expected, atol=1e-6)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_affine.py` around lines 220 - 228, Update the
test_different_sizes to assert the entire affine matrix returned by
Affine.compute_w_affine (called with mat, img_size, sp_size) rather than only
the translation column: construct the expected full 3x3 translation matrix
(identity in the upper-left 2x2, expected_translation in result[:2,2], and
[0,0,1] as the last row) and replace the single-column assert with an assertion
that result equals this expected matrix (use the same assert_allclose with
atol=1e-6).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/transforms/test_affine.py`:
- Line 227: Update the zip call in the expected_translation assignment to make
intent explicit by adding strict=False; change the comprehension using
zip(img_size, sp_size) in tests/transforms/test_affine.py (the
expected_translation expression) to use zip(img_size, sp_size, strict=False) so
Ruff B905 is satisfied and the equal-length assumption is explicit.

---

Nitpick comments:
In `@tests/transforms/test_affine.py`:
- Around line 220-228: Update the test_different_sizes to assert the entire
affine matrix returned by Affine.compute_w_affine (called with mat, img_size,
sp_size) rather than only the translation column: construct the expected full
3x3 translation matrix (identity in the upper-left 2x2, expected_translation in
result[:2,2], and [0,0,1] as the last row) and replace the single-column assert
with an assertion that result equals this expected matrix (use the same
assert_allclose with atol=1e-6).

ℹ️ Review info

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

📥 Commits

Reviewing files that changed from the base of the PR and between f08fa24 and 7bbcf4e.

📒 Files selected for processing (1)
  • tests/transforms/test_affine.py

@engmohamedsalah engmohamedsalah force-pushed the improve-affine-docs-7092 branch from 7bbcf4e to 70e1978 Compare February 23, 2026 11:59
@engmohamedsalah engmohamedsalah changed the title Improve Affine transform documentation Improve Affine transform documentation and add compute_w_affine tests Feb 23, 2026
- Add Note section documenting center-origin coordinate system assumption
- Clarify normalized parameter documentation with user-friendly explanation
- Add comprehensive docstring to compute_w_affine method
- Add focused unit tests for compute_w_affine (2D/3D identity, different
  sizes, output shape, torch input compatibility)

Fixes Project-MONAI#7092

Signed-off-by: Mohamed Salah <eng.mohamed.tawab@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/transforms/test_affine.py (1)

227-227: zip() missing explicit strict=.

Ruff B905 flags this; same issue previously raised.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_affine.py` at line 227, The list comprehension
assigning expected_translation uses zip(img_size, sp_size) without the strict
flag; update that expression in tests/transforms/test_affine.py (the
expected_translation assignment) to call zip(img_size, sp_size, strict=True) so
the test enforces equal-length iterables (keeping the rest of the list
comprehension intact).
🧹 Nitpick comments (1)
monai/transforms/spatial/array.py (1)

2334-2358: Returns: should specify type and shape; method signature missing type annotations.

The docstring addition is welcome, but the Returns: entry doesn't state the concrete type or the resulting matrix shape, both of which are easily derivable from the body:

-        Returns:
-            The adjusted affine matrix that can be applied to image coordinates.
+        Returns:
+            np.ndarray: The adjusted (r+1) x (r+1) affine matrix expressed in
+            corner-based image coordinates.

Also, the method signature (line 2334) still lacks type annotations. Given the rest of Affine is fully annotated, adding them here would be consistent:

-    def compute_w_affine(cls, spatial_rank, mat, img_size, sp_size):
+    def compute_w_affine(
+        cls,
+        spatial_rank: int,
+        mat: NdarrayOrTensor,
+        img_size: Sequence[int],
+        sp_size: Sequence[int],
+    ) -> np.ndarray:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/transforms/spatial/array.py` around lines 2334 - 2358, Add precise type
annotations to compute_w_affine and a concrete Returns entry in the docstring:
annotate the signature as compute_w_affine(cls, spatial_rank: int, mat: Any,
img_size: Sequence[int], sp_size: Sequence[int]) -> numpy.ndarray (or
np.ndarray) and update the Returns section to say it returns a numpy.ndarray of
shape (r+1, r+1) (where r = spatial_rank) representing the homogeneous affine
matrix; keep references to the helper functions used (to_affine_nd,
create_translate, convert_data_type) so reviewers can locate the code that
guarantees the output shape and type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/transforms/test_affine.py`:
- Line 227: The list comprehension assigning expected_translation uses
zip(img_size, sp_size) without the strict flag; update that expression in
tests/transforms/test_affine.py (the expected_translation assignment) to call
zip(img_size, sp_size, strict=True) so the test enforces equal-length iterables
(keeping the rest of the list comprehension intact).

---

Nitpick comments:
In `@monai/transforms/spatial/array.py`:
- Around line 2334-2358: Add precise type annotations to compute_w_affine and a
concrete Returns entry in the docstring: annotate the signature as
compute_w_affine(cls, spatial_rank: int, mat: Any, img_size: Sequence[int],
sp_size: Sequence[int]) -> numpy.ndarray (or np.ndarray) and update the Returns
section to say it returns a numpy.ndarray of shape (r+1, r+1) (where r =
spatial_rank) representing the homogeneous affine matrix; keep references to the
helper functions used (to_affine_nd, create_translate, convert_data_type) so
reviewers can locate the code that guarantees the output shape and type.

ℹ️ Review info

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7bbcf4e and 70e1978.

📒 Files selected for processing (2)
  • monai/transforms/spatial/array.py
  • tests/transforms/test_affine.py

@engmohamedsalah engmohamedsalah force-pushed the improve-affine-docs-7092 branch from 70e1978 to 9c468d8 Compare February 23, 2026 12:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
monai/transforms/spatial/array.py (1)

2333-2358: ⚠️ Potential issue | 🟡 Minor

Add type annotations and types in docstring.

The method signature has no type hints, and the Google-style docstring omits types in Args/Returns. Per coding guidelines, both should be present.

✏️ Proposed fix
-    def compute_w_affine(cls, spatial_rank, mat, img_size, sp_size):
+    def compute_w_affine(
+        cls,
+        spatial_rank: int | float,
+        mat: NdarrayOrTensor,
+        img_size: Sequence[int],
+        sp_size: Sequence[int],
+    ) -> np.ndarray:
         """
         ...
         Args:
-            spatial_rank: number of spatial dimensions (e.g., 2 for 2D, 3 for 3D).
-            mat: the base affine transformation matrix to be adjusted.
-            img_size: spatial dimensions of the input image.
-            sp_size: spatial dimensions of the output (transformed) image.
+            spatial_rank (int | float): number of spatial dimensions (e.g., 2 for 2D, 3 for 3D).
+            mat (NdarrayOrTensor): the base affine transformation matrix to be adjusted.
+            img_size (Sequence[int]): spatial dimensions of the input image.
+            sp_size (Sequence[int]): spatial dimensions of the output (transformed) image.

         Returns:
-            The adjusted affine matrix that can be applied to image coordinates.
+            np.ndarray: The adjusted affine matrix that can be applied to image coordinates.
         """

As per coding guidelines, "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."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/transforms/spatial/array.py` around lines 2333 - 2358, Add Python type
hints to the compute_w_affine signature and include types for each parameter and
the return value in the Google-style docstring: annotate spatial_rank as int,
mat as ArrayLike/np.ndarray (or Union of supported affine types), img_size and
sp_size as Sequence[int] (or Tuple[int, ...]), and the return as np.ndarray;
update the Args and Returns sections to include those types and short
descriptions, and mention any exceptions raised (e.g., on invalid ranks) if
applicable — edit the compute_w_affine method and its docstring accordingly.
🧹 Nitpick comments (1)
tests/transforms/test_affine.py (1)

202-241: TestComputeWAffine — LGTM on the basics; two improvements worth considering.

The assertions are mathematically correct and the strict=False fix is already applied. Two gaps:

  1. test_different_sizes checks only the translation column. For mat=eye, the top-left 2×2 block is trivially identity, but the test doesn't assert it. A full-matrix comparison removes any hidden corner cases.

  2. No non-identity mat test. The center-adjustment path is only meaningful when mat isn't identity. A rotation or scale matrix as input would actually exercise shift_1 @ mat @ shift_2.

✏️ Suggested additions
     def test_different_sizes(self):
         ...
-        assert_allclose(result[:2, 2], expected_translation, atol=1e-6)
+        expected = np.eye(3)
+        expected[:2, 2] = expected_translation
+        assert_allclose(result, expected, atol=1e-6)

+    def test_non_identity_mat_2d(self):
+        """Center-adjustment with a 90-degree rotation mat."""
+        angle = np.pi / 2
+        mat = np.array([[np.cos(angle), -np.sin(angle), 0],
+                        [np.sin(angle),  np.cos(angle), 0],
+                        [0,              0,             1]], dtype=float)
+        img_size = (4, 4)
+        sp_size = (4, 4)
+        result = Affine.compute_w_affine(2, mat, img_size, sp_size)
+        # Same size + pure rotation: translation columns should be zero after center composition
+        assert_allclose(result[:2, :2], mat[:2, :2], atol=1e-6)
+        assert_allclose(result[:2, 2], np.zeros(2), atol=1e-6)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_affine.py` around lines 202 - 241, Update
TestComputeWAffine::test_different_sizes to assert the entire affine matrix (not
just the translation column) when mat = np.eye(3); compute the expected full
matrix by constructing the center shifts and verifying result equals shift_1 @
mat @ shift_2 (or equivalent full-matrix expression) so the top-left block and
translation are tested together. Also add a new test (e.g.,
test_non_identity_mat) that passes a non-identity transform (scale or rotation
matrix) into Affine.compute_w_affine and asserts the result equals the composed
shift_1 @ mat @ shift_2 matrix to exercise the center-adjustment path and ensure
compute_w_affine handles non-identity mat correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@monai/transforms/spatial/array.py`:
- Around line 2333-2358: Add Python type hints to the compute_w_affine signature
and include types for each parameter and the return value in the Google-style
docstring: annotate spatial_rank as int, mat as ArrayLike/np.ndarray (or Union
of supported affine types), img_size and sp_size as Sequence[int] (or Tuple[int,
...]), and the return as np.ndarray; update the Args and Returns sections to
include those types and short descriptions, and mention any exceptions raised
(e.g., on invalid ranks) if applicable — edit the compute_w_affine method and
its docstring accordingly.

---

Nitpick comments:
In `@tests/transforms/test_affine.py`:
- Around line 202-241: Update TestComputeWAffine::test_different_sizes to assert
the entire affine matrix (not just the translation column) when mat = np.eye(3);
compute the expected full matrix by constructing the center shifts and verifying
result equals shift_1 @ mat @ shift_2 (or equivalent full-matrix expression) so
the top-left block and translation are tested together. Also add a new test
(e.g., test_non_identity_mat) that passes a non-identity transform (scale or
rotation matrix) into Affine.compute_w_affine and asserts the result equals the
composed shift_1 @ mat @ shift_2 matrix to exercise the center-adjustment path
and ensure compute_w_affine handles non-identity mat correctly.

ℹ️ Review info

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

📥 Commits

Reviewing files that changed from the base of the PR and between 70e1978 and 9c468d8.

📒 Files selected for processing (2)
  • monai/transforms/spatial/array.py
  • tests/transforms/test_affine.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Affine Transform does not return target affine

1 participant