Skip to content

fix(transforms3d): remap CubicSymmetry labels - #433

Merged
ternaus merged 3 commits into
mainfrom
codex/cubic-symmetry-semantic-mapping
Aug 16, 2026
Merged

fix(transforms3d): remap CubicSymmetry labels#433
ternaus merged 3 commits into
mainfrom
codex/cubic-symmetry-semantic-mapping

Conversation

@ternaus

@ternaus ternaus commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #432.

CubicSymmetry emits the CubicSymmetry mapping event only for realized rotoreflections (indices 24..47).
Pure rotations (0..23) preserve semantic labels.

For Transform3D transforms that emit a mapping event:

  • mask3d and every mask3d alias undergo geometry followed by a simultaneous class-ID remap.
  • KeypointParams.label_mapping changes label-field values in the same rows that already contain the transformed XYZ
    coordinates. It never swaps complete keypoint rows, preserving a manual annotation and instance alignment.

The keypoint behavior is centralized in Transform3D, so Flip3D and CubicSymmetry share the invariant. The
maintainer correctness-contract documentation records the target, row-order, alias, and instance-binding rules.

Tests and validation:

  • uv run pytest -q tests/transforms3d/test_transforms.py — 443 passed
  • uv run pytest -q tests/test_semantic_mask_label_mapping.py — 39 passed
  • uv run pytest -q tests/test_instance_binding.py — 125 passed
  • uv run python tools/quality_gate.py fast — passed, including mypy, Pyrefly, 1,276 contract tests, and pre-commit

Public Compose benchmark, forced rotoreflection, (32, 64, 64, 1) volume (median microseconds/call; original PR →
this commit):

  • unmapped labels: 2 keypoints 78.72 → 78.06; 1,024 keypoints 175.09 → 177.42
  • mapped labels: 2 keypoints 85.85 → 85.36; 1,024 keypoints 196.35 → 196.77

@sourcery-ai

sourcery-ai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

CubicSymmetry now only emits a semantic label-mapping event for rotoreflection operations (indices 24–47), leaving pure rotations (indices 0–23) label-preserving, and a new regression test verifies that semantic mask labels are remapped only when a rotoreflection is realized.

Sequence diagram for CubicSymmetry semantic label-mapping event gating

sequenceDiagram
    participant Pipeline
    participant CubicSymmetry
    participant SemanticMappingObserver

    Pipeline->>CubicSymmetry: sample_parameters(data)
    CubicSymmetry-->>Pipeline: { index, volume_shape }

    Pipeline->>CubicSymmetry: _get_label_transform_name(index)
    CubicSymmetry-->>Pipeline: "CubicSymmetry" or None

    alt [index >= 24]
        Pipeline->>SemanticMappingObserver: CubicSymmetry
        SemanticMappingObserver-->>Pipeline: semantic_labels_remapped
    else [index < 24]
        Pipeline-->>SemanticMappingObserver: no_event
        SemanticMappingObserver-->>Pipeline: labels_preserved
    end
Loading

File-Level Changes

Change Details Files
Limit CubicSymmetry semantic label remapping to rotoreflection operations and expose this via a dedicated label-transform-name hook.
  • Add a _get_label_transform_name method to CubicSymmetry that returns the semantic mapping event name only when the sampled index is a rotoreflection (index >= 24)
  • Keep rotation operations (indices 0–23) from triggering semantic mask label remapping by returning None from _get_label_transform_name
albumentations/augmentations/transforms3d/transforms.py
Add a regression test that forces specific CubicSymmetry operations and asserts geometry plus conditional label remapping behavior.
  • Use monkeypatch to override CubicSymmetry.sample_parameters and force a fixed index for the operation under test
  • Compose a transform pipeline with a CubicSymmetry step and explicit semantic_mask_label_mappings for labels 2 and 3
  • Compute the expected mask3d by applying CubicSymmetry.apply_to_mask3d with the forced index, then conditionally swapping labels 2 and 3 only when a rotoreflection is used
  • Verify that the pipeline output mask3d matches the expected mask under both rotation and rotoreflection scenarios via np.testing.assert_array_equal
tests/test_semantic_mask_label_mapping.py

Assessment against linked issues

Issue Objective Addressed Explanation
#432 Ensure CubicSymmetry emits the semantic mapping event for orientation-reversing rotoreflections (indices 24..47) so semantic_mask_label_mappings are applied to mask3d and aliases.
#432 Ensure CubicSymmetry does not emit a semantic mapping event for pure rotations (indices 0..23), preserving labels.
#432 Add deterministic regression tests that cover one rotation and one rotoreflection, verifying mask3d geometry and expected label remapping behavior.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Consider replacing the magic index thresholds (0..47 and >=24) with named constants or an enum that documents which indices are rotations vs rotoreflections to make the mapping logic more self-explanatory.
  • In _get_label_transform_name, you may want to guard against out-of-range indices (e.g., >47) or unexpected types more explicitly so that future changes to sample_parameters don’t silently skip semantic mappings.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider replacing the magic index thresholds (0..47 and >=24) with named constants or an enum that documents which indices are rotations vs rotoreflections to make the mapping logic more self-explanatory.
- In `_get_label_transform_name`, you may want to guard against out-of-range indices (e.g., >47) or unexpected types more explicitly so that future changes to `sample_parameters` don’t silently skip semantic mappings.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0887265160

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

meanings and must not alter semantic labels.
"""
index = params.get("index")
return "CubicSymmetry" if isinstance(index, int) and index >= 24 else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve CubicSymmetry keypoint row order

When KeypointParams.label_mapping contains a CubicSymmetry mapping and an index in 24..47 is sampled, this hook is also consumed by DualTransform._apply_label_mapping_to_keypoints, whose default implementation swaps entire keypoint rows. That is inconsistent with the established 3D reflection behavior in Flip3D, which preserves transformed coordinate-row order and remaps only the encoded label column; consequently a CubicSymmetry rotoreflection can unexpectedly reorder a user's keypoints. Override the keypoint mapping as Flip3D does, or decouple this semantic-mask event from keypoint mapping.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Antigravity Review

Findings

1. CRITICAL CORRECTNESS BUG: Keypoint Spatial Coordinate Corruption during Label Mapping

  • File: albumentations/augmentations/transforms3d/transforms.py
  • Line: 1922 (class CubicSymmetry)
  • Severity: High
  • Description: CubicSymmetry supports 3D keypoints, and when an orientation-reversing rotoreflection (indices 24–47) is applied, keypoint label mapping is triggered if configured. However, CubicSymmetry does not override _apply_label_mapping_to_keypoints. As a result, it falls back to the default implementation in DualTransform._apply_label_mapping_to_keypoints (defined in albumentations/core/transforms_interface.py). The default implementation calls _swap_keypoint_rows_by_labels, which swaps entire coordinate rows in the keypoints array. Because the physical 3D coordinates have already been correctly transformed, swapping coordinate rows based on label matching corrupts the spatial locations of the keypoints.
  • Remediation: Override _apply_label_mapping_to_keypoints in CubicSymmetry to rename/remap the label column values in-place without changing the coordinate row indices, identical to the implementation in Flip3D:
    def _apply_label_mapping_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:
        processor = self.get_processor("keypoints")
        transform_name = self._get_label_transform_name(**params)
        if (
            not isinstance(processor, KeypointsProcessor)
            or not processor.params.label_fields
            or keypoints.size == 0
            or transform_name is None
        ):
            return keypoints
    
        field_mappings = processor.encoded_label_mappings.get(transform_name)
        if not field_mappings:
            return keypoints
    
        result = keypoints.copy()
        for label_offset, label_field in enumerate(processor.params.label_fields):
            mapping = field_mappings.get(label_field)
            column_index = NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS + label_offset
            if not mapping or column_index >= keypoints.shape[1]:
                continue
            source_values = keypoints[:, column_index]
            for source_label, target_label in mapping.items():
                result[source_values == source_label, column_index] = target_label
        return result

2. MISSING TESTS: Lack of Keypoint Label Mapping Verification

  • File: tests/test_semantic_mask_label_mapping.py
  • Severity: Medium
  • Description: While the pull request introduces test_semantic_mask_label_mapping_follows_realized_cubic_symmetry_operation to verify mask3d semantic label mapping, there are no tests verifying keypoint label mapping for CubicSymmetry.
  • Remediation: Add a parameterized test similar to test_flip3d_odd_reflections_remap_keypoint_labels_without_reordering_rows to verify that orientation-reversing rotoreflections under CubicSymmetry correctly remap keypoint labels in-place without corrupting/swapping coordinate rows.

3. MISSING DOCUMENTATION: Missing Label Mapping Details in Class Docstring

  • File: albumentations/augmentations/transforms3d/transforms.py
  • Line: 1922
  • Severity: Low
  • Description: The docstring for CubicSymmetry lacks documentation regarding the newly introduced label mapping behavior. Users will not know that rotoreflections (indices 24–47) emit the CubicSymmetry label-mapping event, whereas pure rotations (indices 0–23) do not.
  • Remediation: Append a note to the docstring of CubicSymmetry describing this behavior for parity with Flip3D's docstring.

4. ROBUSTNESS: Strict Type Verification for index Parameter

  • File: albumentations/augmentations/transforms3d/transforms.py
  • Line: 2011
  • Severity: Low
  • Description: In _get_label_transform_name, isinstance(index, int) is used. If index is passed as a numpy integer (e.g., np.int64) during manual execution or custom parameter pipelines, this check can return False.
  • Remediation: Robustly check for integer types using isinstance(index, (int, np.integer)) or cast safely after verification.

Residual Risk

  • Test Execution: Because we did not run the full testing suite via pytest, we cannot guarantee there are no indirect regressions.
  • Code Standards & Linting: Code quality and style checks (such as Ruff and Mypy) were not run on the patched files to verify strict type correctness and linter compatibility.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Antigravity Review

There are no actionable findings. The pull request is demonstrably correct, safe, backwards-compatible, and conforms to all repository design principles, correctness contracts, and testing conventions.

Summary of Strengths

  1. Design & Correctness:
    • Restricting CubicSymmetry semantic-label remapping only to rotoreflections (index $\ge 24$) while keeping pure rotations (index $0 \dots 23$) label-preserving is correct. Pure rotations preserve volume chirality/handedness, so semantic labels must remain unchanged.
    • Elevating _apply_label_mapping_to_keypoints to Transform3D is an excellent design choice. It prevents code duplication between Flip3D and CubicSymmetry, and ensures that 3D transforms correctly remap label columns in-place within keypoint coordinate rows rather than swapping entire coordinate rows (which is the behavior for 2D keypoint regression).
  2. Quality of Implementation:
    • The simultaneous label remappings (e.g., 2: 3, 3: 2) are correctly implemented using source array copies, preventing double-overwriting.
    • Checked types and boundaries are handled safely.
  3. Thorough Coverage:
    • Robust test coverage was added to verify both semantic mask mapping and keypoint label mapping under both parity-preserving (rotation) and parity-changing (rotoreflection) operations.
    • docs/maintaining/correctness-contracts.md was updated with clear documentation on the 3D Orientation Label Mappings contract.

Material Residual Risks

  • Operational & Test Execution Verification: Because of the strict instruction prohibiting the execution of shell tools and test runners (pytest, quality_gate.py), we could not run the test suite or verify the static type checker (mypy) results on the modified/added code.
  • Performance Verification: The performance improvements (e.g., cubic symmetry composite routes runtime reduction) reported in the PR description could not be independently verified via benchmarks.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Antigravity Review

No actionable correctness bugs, security issues, behavioral regressions, maintainability risks, or missing tests/documentation were identified in this pull request. The implementation is highly robust, correct, elegant, and completely adheres to the project's established conventions, styling, and correctness contracts.

Summary of Strengths & Verifications

  • Design Cleanliness: Consolidating _apply_label_mapping_to_keypoints within the Transform3D base class is an excellent design choice. It removes repetitive logic from subclasses and ensures consistent row-aligned label mapping across all 3D geometric augmentations.
  • Simultaneous Label Swaps: The keypoint label-mapping loop evaluates conditions against a copy of the original (unmodified) keypoint array while writing to the result array. This perfectly handles swap label dependencies (e.g. 2: 3, 3: 2) simultaneously and correctly.
  • Mathematical Integrity: Limit checking the CubicSymmetry label-mapping event to rotoreflections (index $\ge 24$) ensures that class identities are preserved during pure rotations (indices $0..23$), which is mathematically correct.
  • Test Coverage: Added tests in both tests/test_semantic_mask_label_mapping.py and tests/transforms3d/test_transforms.py are thorough and correctly parameterized. They comprehensively cover index $0$ and index $24$ cases, asserting coordinate transformations and label mappings with high precision.

Material Residual Test or Operational Risk

  1. Custom Subclass Alignment Risk
    Custom third-party or future internal 3D transforms subclassing Transform3D will now automatically inherit the row-aligned keypoint label mapping behavior instead of the row-swapping behavior used in 2D dual transforms. While this is explicitly documented in the updated docs/maintaining/correctness-contracts.md, it represents an operational paradigm change for 3D augmentations that developers must be aware of when authoring custom transforms.

  2. Symmetry Group Index Ordering Dependency
    The mapping check index >= 24 assumes that rotoreflections are strictly grouped at indices $24$ to $47$. While this currently aligns perfectly with the underlying functional module (f3d.transform_cube), any future re-ordering or optimization of the 48 cubic symmetry group elements in the functional layer will break this assumption. This risk is heavily mitigated by the added unit tests.

@ternaus
ternaus merged commit 9e300ca into main Aug 16, 2026
46 checks passed
@ternaus
ternaus deleted the codex/cubic-symmetry-semantic-mapping branch August 16, 2026 11:38
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.

fix(CubicSymmetry): emit semantic mapping for rotoreflections

1 participant