fix(compose): preserve keypoint label mappings with instance binding#261
Conversation
Reviewer's GuideThis PR fixes how Compose handles keypoint label mappings when instance binding is enabled by remapping internal label-field names back to public names in both runtime transformations and serialization, and adds tests to validate correct behavior. Sequence diagram for label_mapping flow with keypoint instance bindingsequenceDiagram
actor User
participant Compose
participant KeypointParams as KP
participant Transform as T
User->>Compose: create_with_instance_binding(label_fields, label_mapping)
Compose->>KP: initialize(label_fields, label_mapping)
Compose->>Compose: _apply_keypoints_instance_binding(targets)
Compose->>Compose: build _kp_label_map(internal_field -> user_field)
Compose->>Compose: user_to_internal = reverse(_kp_label_map)
Compose->>KP: KP.label_mapping = _remap_label_mapping_fields(KP.label_mapping, user_to_internal)
Compose->>KP: KP.label_fields = internal_fields + _KP_INSTANCE_ID
Compose->>KP: KP.remove_invisible = False
Compose->>KP: KP.check_each_transform = False
loop for each image
User->>Compose: __call__(image, keypoints, labels)
Compose->>T: apply(image, keypoints, labels, label_mapping)
T-->>Compose: transformed_image, transformed_keypoints, labels
end
User->>Compose: to_dict()
Compose->>Compose: _get_init_params()
Compose->>Compose: _remap_label_mapping_fields(KP.label_mapping, _kp_label_map)
Compose-->>User: serialized_config_with_user_label_fields
Class diagram for Compose keypoint label_mapping handling with instance bindingclassDiagram
class Compose {
- _kp_label_map: dict[str, str]
+ _apply_keypoints_instance_binding(targets: frozenset[str]) void
+ _set_processors_for_transforms(transforms: TransformsSeqType) void
+ _clean_params_dict(kp_params: KeypointParams, params_dict: dict[str, Any], label_map: dict[str, str]) dict[str, Any]
+ _get_init_params() dict[str, Any]
+ _remap_label_mapping_fields(label_mapping: dict[str, dict[str, dict[Any, Any]]], field_map: dict[str, str]) dict[str, dict[str, dict[Any, Any]]]
}
class KeypointParams {
+ label_fields: list[str]
+ label_mapping: dict[str, dict[str, dict[Any, Any]]]
+ remove_invisible: bool
+ check_each_transform: bool
+ angle_in_degrees: bool
}
Compose --> KeypointParams : configures
class InstanceBindingInternalFields {
+ _ibl_kp_name: str
+ _KP_INSTANCE_ID: str
+ _INSTANCE_ID_FERRY_KEYS: set[str]
}
Compose ..> InstanceBindingInternalFields : uses
%% Highlight key internal data flow
Compose : _apply_keypoints_instance_binding()
Compose : - build _kp_label_map
Compose : - remap kp_params.label_mapping to internal fields
Compose : _clean_params_dict()
Compose : - remap label_fields and label_mapping back to user fields
Compose : _get_init_params()
Compose : - remap kp.label_mapping from internal to user fields for serialization
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Pull request overview
This PR fixes instance binding behavior in Compose so that keypoint label_mapping continues to work when keypoint label fields are temporarily rewritten to internal (hidden) field names for instance binding.
Changes:
- Remap
KeypointParams.label_mappingfield keys from user label field names (e.g."name") to internal instance-binding field names (e.g."_ibl_kp_name") when instance binding is enabled for keypoints. - Ensure
to_dict_private()and_get_init_params()convert internal keypoint label mapping field names back to the public/user field names. - Add/extend tests to verify correct label swapping behavior and serialization output when instance binding is active.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
albumentations/core/composition.py |
Adds label-mapping field-key remapping for bound keypoint label fields and restores public names during serialization/init-param export. |
tests/test_instance_binding.py |
Adds a regression test for keypoint label swapping with instance binding and checks that serialization omits internal label-mapping keys. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33424c6859
ℹ️ 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".
| internal_fields = [f"_ibl_kp_{f}" for f in user_fields] | ||
| self._kp_label_map = dict(zip(internal_fields, user_fields, strict=True)) | ||
| user_to_internal = {user_name: internal_name for internal_name, user_name in self._kp_label_map.items()} | ||
| kp_params.label_mapping = self._remap_label_mapping_fields(kp_params.label_mapping, user_to_internal) |
There was a problem hiding this comment.
Keep keypoint label swaps within each instance
When instance_binding is enabled, remapping the public label mapping to _ibl_kp_* makes the existing keypoint swap logic run on the flattened keypoint array. That swap uses global np.where matches over all rows and swaps entire rows, including _kp_instance_id, so inputs where different instances do not each contain both sides of a mapping (for example one instance has only left_eye and another only right_eye) will either swap keypoints between instances during repack or hit a shape mismatch. The mapping needs to be applied per _KP_INSTANCE_ID group, or otherwise avoid global row swaps for bound instances.
Useful? React with 👍 / 👎.
Summary by Sourcery
Preserve public keypoint label field names and mappings when instance binding is enabled in Compose, ensuring serialization reflects user-facing labels.
Bug Fixes:
Enhancements:
Tests: