634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 | Sinapsis Albumentations
9 |
10 |
11 |
12 | Templates for applying image transformations using Albumentations.
13 |
14 |
15 | 🐍 Installation •
16 | 🚀 Features •
17 | 📚 Usage example •
18 | 📙 Documentation •
19 | 🔍 License
20 |
21 |
22 | The **Sinapsis albumentations** module provides an extensive collection of templates powered by the [**Albumentations**](https://albumentations.ai/docs/) library. These templates allow users to apply a wide range of augmentations, from simple operations like flipping and resizing to more advanced transformations such as elastic distortions and geometric warping.
23 |
24 | 🐍 Installation
25 |
26 | Install using your package manager of choice. We encourage the use of uv
27 |
28 | Example with uv
:
29 |
30 | ```bash
31 | uv pip install sinapsis-albumentations --extra-index-url https://pypi.sinapsis.tech
32 | ```
33 | or with raw pip
:
34 | ```bash
35 | pip install sinapsis-albumentations --extra-index-url https://pypi.sinapsis.tech
36 | ```
37 |
38 | 🚀 Features
39 |
40 | Templates Supported
41 |
42 | > [!TIP]
43 | > Use CLI command ``` sinapsis info --all-template-names``` to show a list with all the available templates installed with the `sinapsis-albumentations` package.
44 |
45 |
46 | 🌍 General Attributes
47 |
48 | > [!NOTE]
49 | > All templates share the following attributes:
50 | > - **`apply_to_annotations` (bool, optional)**: Determines whether transformations should also be applied to annotations like bounding boxes, keypoints, and masks. Defaults to `False`.
51 | > - **`bbox_params` (dict[str, Any], optional)**: Configuration for transforming bounding boxes, following Albumentations' `BboxParams` format. Defaults to `None`.
52 | > - **`keypoints_params` (dict[str, Any], optional)**: Defines keypoint transformation settings using Albumentations' `KeypointParams`. Defaults to `None`.
53 | > - **`additional_targets` (dict[str, Any], optional)**: Specifies extra annotation types (e.g., segmentation masks) to be transformed alongside the image. Defaults to `{"mask": "mask"}`.
54 | >
55 | > Additional transformation-specific attributes can be dynamically assigned through the class initialization dictionary (`*_init` attributes). These attributes correspond directly to the arguments used in Albumentations.
56 |
57 | > [!TIP]
58 | > Use CLI command ```sinapsis info --example-template-config TEMPLATE_NAME``` to produce an example Agent config for the Template specified in ***TEMPLATE_NAME***.
59 | >
60 | > For example, for ***RotateWrapper*** use ```sinapsis info --example-template-config RotateWrapper``` to produce the following example config:
61 |
62 | ```yaml
63 | agent:
64 | name: my_test_agent
65 | templates:
66 | - template_name: InputTemplate
67 | class_name: InputTemplate
68 | - template_name: RotateWrapper
69 | class_name: RotateWrapper
70 | template_input: InputTemplate
71 | attributes:
72 | apply_to_annotations: false
73 | bbox_params: null
74 | keypoints_params: null
75 | additional_targets:
76 | mask: mask
77 | rotate_init:
78 | limit: [-45, 45]
79 | interpolation: 1
80 | border_mode: 4
81 | value: [0, 0, 0]
82 | mask_value: null
83 | rotate_method: "largest_box"
84 | crop_border: false
85 | fill_value: 0
86 | mask_fill_value: 0
87 | deterministic: true
88 | p: 1.0
89 | ```
90 |
91 | 📚 Usage example
92 |
93 | The following example demonstrates how to use **Sinapsis Albumentations** to apply multiple image augmentations. This setup loads a dataset of images, applies **horizontal flipping** and **elastic transformation**, and saves the results. Below is the full YAML configuration, followed by a breakdown of each component.
94 |
95 |
96 | Example configuration
97 |
98 |
99 |
100 |
101 | ```yaml
102 | agent:
103 | name: transforms_agent
104 | description: "Agent to apply an horizontal-flip and an elastic-transformation to a set of images loaded from a directory."
105 |
106 | templates:
107 | - template_name: InputTemplate
108 | class_name: InputTemplate
109 | attributes: {}
110 |
111 | - template_name: FolderImageDatasetCV2
112 | class_name: FolderImageDatasetCV2
113 | template_input: InputTemplate
114 | attributes:
115 | data_dir: my_dataset
116 |
117 | - template_name: HorizontalFlip
118 | class_name: HorizontalFlipWrapper
119 | template_input: FolderImageDatasetCV2
120 | attributes:
121 | horizontalflip_init:
122 | p: 1.0
123 |
124 | - template_name: ElasticTransform
125 | class_name: ElasticTransformWrapper
126 | template_input: HorizontalFlip
127 | attributes:
128 | elastictransform_init:
129 | mask_value: 150
130 | p: 1.0
131 | alpha: 100
132 | sigma: 50
133 |
134 | - template_name: ImageSaver
135 | class_name: ImageSaver
136 | template_input: ElasticTransform
137 | attributes:
138 | save_dir: results
139 | extension: jpg
140 | ```
141 |
142 |
143 |
144 | This configuration defines an **agent** and a sequence of **templates** to apply image transformations.
145 |
146 |
147 |
148 | > [!NOTE]
149 | > Attributes specified under the `*_init` keys (e.g., `elastictransform_init`, `horizontalflip_init`) correspond directly to the Albumentations transformation parameters. Ensure that values are assigned correctly according to the official [Albumentations documentation](https://albumentations.ai/docs/), as they affect the behavior and performance of each transformation.
150 |
151 | > [!IMPORTANT]
152 | > The FolderImageDataserCV2 and ImageSaver correspond to [sinapsis-data-readers](https://github.com/Sinapsis-AI/sinapsis-data-tools/tree/main/packages/sinapsis_data_readers) and [sinapsis-data-writers](https://github.com/Sinapsis-AI/sinapsis-data-tools/tree/main/packages/sinapsis_data_writers). If you want to use the example, please make sure you install the packages.
153 |
154 |
155 | To run the config, use the CLI:
156 | ```bash
157 | sinapsis run name_of_config.yml
158 | ```
159 |
160 |
161 |
162 |
163 | 📙 Documentation
164 |
165 | Documentation for this and other sinapsis packages is available on the [sinapsis website](https://docs.sinapsis.tech/docs)
166 |
167 | Tutorials for different projects within sinapsis are available at [sinapsis tutorials page](https://docs.sinapsis.tech/tutorials)
168 |
169 |
170 | 🔍 License
171 |
172 | This project is licensed under the AGPLv3 license, which encourages open collaboration and sharing. For more details, please refer to the [LICENSE](LICENSE) file.
173 |
174 | For commercial use, please refer to our [official Sinapsis website](https://sinapsis.tech) for information on obtaining a commercial license.
175 |
176 |
177 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "sinapsis-albumentations"
3 | version = "0.1.4"
4 | description = "Templates to perform image transformations using the albumentations library."
5 | authors = [
6 | { name = "SinapsisAI", email = "dev@sinapsis-ai.com" }
7 | ]
8 | readme = "README.md"
9 | license-files = ["LICENSE"]
10 | requires-python = ">=3.10"
11 | dependencies = [
12 | "albumentations>=2.0.5",
13 | "sinapsis>=0.1.1",
14 | ]
15 |
16 | [tool.uv.sources]
17 | [build-system]
18 | requires = ["setuptools"]
19 | build-backend = "setuptools.build_meta"
20 |
21 | [[tool.uv.index]]
22 | url = "https://pypi.sinapsis.tech/"
23 |
24 |
25 | [tool.ruff]
26 | lint.select = [
27 | "ARG",
28 | "ANN",
29 | "BLE",
30 | "C4",
31 | "E",
32 | "F",
33 | "FIX",
34 | "FLY",
35 | "I",
36 | "PERF",
37 | "PIE",
38 | "RUF",
39 | "RSE",
40 | "SIM",
41 | "SLOT",
42 | "T10",
43 | "T20",
44 | "TD",
45 | "TID",
46 | ]
47 | lint.ignore = ['ANN401']
48 | line-length = 120
49 | show-fixes = true
50 |
51 |
52 |
53 | [project.urls]
54 | Homepage = "https://sinapsis.tech"
55 | Documentation = "https://docs.sinapsis.tech/docs"
56 | Tutorials = "https://docs.sinapsis.tech/tutorials"
57 | Repository = "https://github.com/Sinapsis-AI/sinapsis-image-transforms.git"
58 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/.python-version:
--------------------------------------------------------------------------------
1 | 3.10.12
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sinapsis-AI/sinapsis-image-transforms/5076c9200000e8da2e8b7996d156d00b34215689/packages/sinapsis_albumentations/src/sinapsis_albumentations/__init__.py
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/configs/visualize_transforms_resize.yml:
--------------------------------------------------------------------------------
1 | agent:
2 | name: visualize_transforms_resize
3 | description: agent that performs longest max size, pad if needed transforms and saving the resulting image in a given folder
4 | templates:
5 | - template_name: InputTemplate
6 | class_name: InputTemplate
7 | template_input: null
8 | attributes: {}
9 |
10 |
11 | - template_name: LongestMaxSize
12 | class_name: LongestMaxSizeWrapper
13 | template_input: InputTemplate
14 | attributes:
15 | longestmaxsize_init:
16 | max_size: 800
17 | interpolation: 0
18 |
19 |
20 | - template_name: PadIfNeeded
21 | class_name: PadIfNeededWrapper
22 | template_input: LongestMaxSize
23 | attributes:
24 | padifneeded_init:
25 | min_height: 1024
26 | min_width: 800
27 | border_mode: 0
28 | value: [255, 255, 255]
29 | interpolation: 1
30 |
31 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/configs/visualize_transforms_rotate.yml:
--------------------------------------------------------------------------------
1 | agent:
2 | name: visualize_transforms_rotate
3 | description: agent that performs rotate transforms
4 |
5 | templates:
6 | - template_name: InputTemplate
7 | class_name: InputTemplate
8 | template_input: null
9 | attributes: { }
10 |
11 | - template_name: Rotate
12 | class_name: RotateWrapper
13 | template_input: InputTemplate
14 | attributes:
15 | rotate_init:
16 | limit: [-45, 45]
17 | interpolation: 1
18 | border_mode: 4
19 | value: [0, 0, 0]
20 | mask_value: null
21 | rotate_method: "largest_box"
22 | crop_border: false
23 | fill_value: 0
24 | mask_fill_value: 0
25 | deterministic: true
26 | p: 1.0
27 |
28 | - template_name: RandomRotate90
29 | class_name: RandomRotate90Wrapper
30 | template_input: Rotate
31 | attributes:
32 | randomrotate90_init:
33 | fill_value: 0
34 | mask_fill_value: 0
35 | deterministic: true
36 | interpolation: 1
37 | p: 1.0
38 |
39 | - template_name: SafeRotate
40 | class_name: SafeRotateWrapper
41 | template_input: RandomRotate90
42 | attributes:
43 | saferotate_init:
44 | limit: [-30, 30]
45 | interpolation: 1
46 | border_mode: 4
47 | value: [0, 0, 0]
48 | mask_value: null
49 | rotate_method: "largest_box"
50 | fill_value: 0
51 | mask_fill_value: 0
52 | deterministic: true
53 | p: 1.0
54 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/configs/visualize_transforms_with_anns_from_dir.yml:
--------------------------------------------------------------------------------
1 | agent:
2 | name: visualize_transforms_anns_dir
3 | description: agent to read from a given folder, performing horizontal flip, electric transform and rotation transforms, and saving the resulting image in a given folder
4 |
5 | templates:
6 | - template_name: InputTemplate
7 | class_name: InputTemplate
8 | template_input: null
9 | attributes: {}
10 |
11 | - template_name: HorizontalFlip
12 | class_name: HorizontalFlipWrapper
13 | template_input: InputTemplate
14 | attributes:
15 | horizontalflip_init:
16 | p: 1.0
17 |
18 | - template_name: ElasticTransform
19 | class_name: ElasticTransformWrapper
20 | template_input: HorizontalFlip
21 | attributes:
22 | elastictransform_init:
23 | mask_value: 150
24 | p: 1.0
25 | alpha: 100
26 | sigma: 50
27 |
28 | - template_name: Rotate
29 | class_name: RotateWrapper
30 | template_input: ElasticTransform
31 | attributes:
32 | rotate_init:
33 | limit: [-45, 45]
34 | interpolation: 1
35 | border_mode: 4
36 | value: [0, 0, 0]
37 | mask_value: null
38 | rotate_method: "largest_box"
39 | crop_border: false
40 | fill_value: 0
41 | mask_fill_value: 0
42 | deterministic: true
43 | p: 1.0
44 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/templates/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import importlib
3 | from typing import Callable
4 |
5 | from sinapsis.templates import _import_template_package
6 |
7 | _root_lib_path = "sinapsis_albumentations.templates"
8 |
9 | _template_lookup: dict = {}
10 |
11 | _ADDITIONAL_TEMPLATE_MODULES = [
12 | f"{_root_lib_path}.albumentations_transforms",
13 | ]
14 | for t_module in _ADDITIONAL_TEMPLATE_MODULES:
15 | _template_lookup |= _import_template_package(t_module)
16 |
17 |
18 | def __getattr__(name: str) -> Callable:
19 | if name in _template_lookup:
20 | module = importlib.import_module(_template_lookup[name])
21 | return getattr(module, name)
22 | raise AttributeError(f"template `{name}` not found in {_root_lib_path}")
23 |
24 |
25 | __all__ = list(_template_lookup.keys())
26 |
--------------------------------------------------------------------------------
/packages/sinapsis_albumentations/src/sinapsis_albumentations/templates/albumentations_transforms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from dataclasses import dataclass
3 | from typing import Any
4 |
5 | from albumentations import Compose as AlbCompose
6 | from albumentations.augmentations import transforms
7 | from albumentations.augmentations.blur import transforms as blur_transforms
8 | from albumentations.augmentations.crops import transforms as crops_transforms
9 | from albumentations.augmentations.geometric import resize as resize_transforms
10 | from albumentations.augmentations.geometric import rotate as rotate_transforms
11 | from albumentations.augmentations.geometric import transforms as geometric_transforms
12 | from albumentations.core.bbox_utils import BboxParams
13 | from albumentations.core.keypoints_utils import KeypointParams
14 | from pydantic import Field
15 | from sinapsis_core.data_containers.annotations import (
16 | BoundingBox,
17 | ImageAnnotations,
18 | KeyPoint,
19 | )
20 | from sinapsis_core.data_containers.data_packet import DataContainer, ImagePacket
21 | from sinapsis_core.template_base import Template
22 | from sinapsis_core.template_base.base_models import (
23 | OutputTypes,
24 | TemplateAttributes,
25 | TemplateAttributeType,
26 | UIPropertiesMetadata,
27 | )
28 | from sinapsis_core.template_base.dynamic_template import (
29 | BaseDynamicWrapperTemplate,
30 | WrapperEntryConfig,
31 | )
32 | from sinapsis_core.template_base.dynamic_template_factory import make_dynamic_template
33 | from sinapsis_core.utils.env_var_keys import SINAPSIS_BUILD_DOCS
34 | from sinapsis_core.utils.logging_utils import sinapsis_logger as logging
35 |
36 |
37 | @dataclass(frozen=True)
38 | class AlbumentationsComposeKeys:
39 | bboxes: str = "bboxes"
40 | keypoints: str = "keypoints"
41 | image: str = "image"
42 | class_labels: str = "class_labels"
43 | format: str = "format"
44 |
45 |
46 | def _make_additional_doc_str() -> str:
47 | return f"""
48 | Optionally, if apply_to_annotations is given, it applies the transforms to annotations.
49 | The following atts can be provided as dictionaries:
50 | bbox_params : {BboxParams.__doc__}
51 | keypoints_params: {KeypointParams.__doc__}
52 | """
53 |
54 |
55 | class AlbumentationAugmentationsTransforms(BaseDynamicWrapperTemplate):
56 | __doc__ = f"""
57 | Tempĺate to perform image transforms using the albumentations library
58 | The template takes as an input the DataContainer with images and for
59 | each of the ImagePacket applies the selected transform.
60 |
61 | If rewrite_image_packet is set to True, then the ith ImagePacket is
62 | replaced with the new image, otherwise the new image is appended to
63 | containers.images.
64 |
65 | {_make_additional_doc_str()}
66 |
67 | Usage example:
68 |
69 | agent:
70 | name: my_test_agent
71 | templates:
72 | - template_name: InputTemplate
73 | class_name: InputTemplate
74 | - template_name: RotateWrapper
75 | class_name: RotateWrapper #Note that since it is a dynamic template,
76 | template_input: InputTemplate ## the class name is the actual class imported
77 | attributes:
78 | apply_to_annotations: false
79 | bbox_params: null
80 | keypoints_params: null
81 | additional_targets:
82 | mask: mask
83 | rotate_init:
84 | limit: [-45, 45]
85 | interpolation: 1
86 | border_mode: 4
87 | value: [0, 0, 0]
88 | mask_value: null
89 | rotate_method: "largest_box"
90 | crop_border: false
91 | fill_value: 0
92 | mask_fill_value: 0
93 | deterministic: true
94 | p: 1.0
95 | """
96 |
97 | UIProperties = UIPropertiesMetadata(category="HuggingFace", output_type=OutputTypes.IMAGE)
98 | WrapperEntry = WrapperEntryConfig(wrapped_object=transforms)
99 |
100 | class AttributesBaseModel(TemplateAttributes):
101 | """Static attributes for the transformations.
102 | apply_to_annotations (bool): Flag to determine if transformations should also be applied to annotations
103 | bbox_params (dict[str, Any] | None : The values of the bboxes
104 | keypoints_params (dict[str, Any] | None: the values of the keypoints
105 | additional_targets (dict[str,Any]): Any other annotations that should be transformed.
106 | """
107 |
108 | apply_to_annotations: bool = False
109 | bbox_params: dict[str, Any] | None = None
110 | keypoints_params: dict[str, Any] | None = None
111 | additional_targets: dict[str, str] = Field(default={"mask": "mask"})
112 |
113 | def __init__(self, attributes: TemplateAttributeType) -> None:
114 | super().__init__(attributes)
115 | self.transform = AlbCompose(
116 | self.wrapped_callable,
117 | bbox_params=self.attributes.bbox_params,
118 | keypoint_params=self.attributes.keypoints_params,
119 | additional_targets=self.attributes.additional_targets,
120 | )
121 |
122 | def apply_transform_to_image(self, args: dict[str, Any]) -> dict[str, Any] | None:
123 | """Method that calls the apply method in the transforms class
124 | to transform a certain image.
125 | Args:
126 | args (dict[str, Any]): args to be passed to the Compose call method
127 | Returns:
128 | (dict[str, Any] | None): Optionally returns the transformed dictionary containing image, bboxes, etc.
129 | """
130 | try:
131 | transformed_img: dict[str, Any] = self.transform(**args)
132 |
133 | return transformed_img
134 | except ValueError as err:
135 | logging.error(f"Your transforms dictionary is not set correctly: {err}")
136 | return None
137 |
138 | def extract_anns_from_image_packet(
139 | self, image: ImagePacket
140 | ) -> tuple[list[list[float]], list[list[float]], list[str]]:
141 | """Prior to applying the transform,
142 | extract the annotations from the ImagePacket and returns as lists
143 | Args:
144 | image (ImagePacket): ImagePacket to extract the annotations from
145 | Returns:
146 | (list[list[float]]): bboxes as plain lists. the Length of the list corresponds
147 | to the number of annotations in the original ImagePacket
148 | (list[list[float]]): Keypoints as plain lists. the Length of the list corresponds
149 | to the number of annotations in the original ImagePacket
150 | list[str]: label classes as a list of strings. The length of the list
151 | corresponds to the number of annotations in the original ImagePacket
152 | """
153 | total_bboxes = []
154 | total_kpts = []
155 | total_labels = []
156 |
157 | if image.annotations is not None:
158 | for ann in image.annotations:
159 | bbox = ann.bbox
160 | kpts = ann.keypoints
161 | if (
162 | self.attributes.bbox_params
163 | and self.attributes.bbox_params.get(AlbumentationsComposeKeys.format) == "coco"
164 | ) and bbox:
165 | total_bboxes.append([bbox.x, bbox.y, bbox.w, bbox.h])
166 | elif (
167 | self.attributes.bbox_params
168 | and self.attributes.bbox_params.get(AlbumentationsComposeKeys.format) == "pascal_voc"
169 | ) and bbox:
170 | total_bboxes.append([bbox.x, bbox.y, bbox.x + bbox.w, bbox.y + bbox.h])
171 | if (
172 | self.attributes.keypoints_params
173 | and self.attributes.keypoints_params.get(AlbumentationsComposeKeys.format) == "xy"
174 | ) and kpts:
175 | total_kpts.append([kpts.x, kpts.y, kpts.score, kpts.label])
176 | total_labels.append(ann.label_str)
177 | return total_bboxes, total_kpts, total_labels
178 |
179 | @staticmethod
180 | def parse_anns(transformed_results: dict[str, Any], annotations: list[ImageAnnotations]) -> list[ImageAnnotations]:
181 | """Inserts the new annotations into the ImagePacket annotations field
182 | Args:
183 | transformed_results (dict[str, Any]): the dictionary returned by Compose
184 | with the new annotations.
185 | annotations (list[ImageAnnotations]): list of original anns to be overriden
186 | Returns:
187 | (list[ImageAnnotations]): New annotations corresponding to the transformed image
188 | """
189 | for ann in annotations:
190 | for bbox in transformed_results[AlbumentationsComposeKeys.bboxes]:
191 | ann.bbox = BoundingBox(*bbox)
192 | for kpt in transformed_results[AlbumentationsComposeKeys.keypoints]:
193 | ann.keypoint = KeyPoint(*kpt)
194 | for label in transformed_results[AlbumentationsComposeKeys.class_labels]:
195 | ann.label_str = label
196 | ann.label = label
197 | return annotations
198 |
199 | def execute(self, container: DataContainer) -> DataContainer:
200 | """
201 | This template receives a data container, applies the transforms for
202 | each of the images and return the modified / updated container
203 |
204 | Args:
205 | container (DataContainer): container with images to be transformed
206 | Returns:
207 | (DataContainer): Modified DataContainer
208 | """
209 | if not container.images:
210 | return container
211 |
212 | for image in container.images:
213 | dict_for_args = {AlbumentationsComposeKeys.image: image.content}
214 |
215 | if self.attributes.apply_to_annotations:
216 | bbox, kpts, labels = self.extract_anns_from_image_packet(image)
217 | dict_for_args.update(
218 | {
219 | AlbumentationsComposeKeys.bboxes: bbox,
220 | AlbumentationsComposeKeys.keypoints: kpts,
221 | AlbumentationsComposeKeys.class_labels: labels,
222 | }
223 | )
224 |
225 | transformed_results = self.apply_transform_to_image(dict_for_args)
226 |
227 | if transformed_results:
228 | image.content = transformed_results[AlbumentationsComposeKeys.image]
229 |
230 | if self.attributes.apply_to_annotations:
231 | image.annotations = self.parse_anns(transformed_results, [ImageAnnotations()])
232 | return container
233 |
234 |
235 | class AlbumentationAugmentationsTransformsBlur(AlbumentationAugmentationsTransforms):
236 | __doc__ = f"""
237 | Class for albumentations transforms, ``albumentations.transforms.blur.transforms`` module.
238 | The template takes as an input the DataContainer with images and for each of the
239 | ImagePacket applies the selected transform.
240 |
241 | If rewrite_image_packet is set to True, then the ith ImagePacket is
242 | replaced with the new image, otherwise the new image is appended to
243 | containers.images
244 |
245 | {_make_additional_doc_str()}
246 |
247 | """
248 | WrapperEntry = WrapperEntryConfig(wrapped_object=blur_transforms)
249 |
250 |
251 | class AlbumentationAugmentationsTransformsCrops(AlbumentationAugmentationsTransforms):
252 | __doc__ = f"""
253 | Class for albumentations transforms using ``albumentations.transforms.crops.transforms``
254 | module. The template takes as an input the DataContainer with images and for each of the
255 | ImagePacket applies the selected transform.
256 |
257 | If rewrite_image_packet is set to True, then the ith ImagePacket is
258 | replaced with the new image, otherwise the new image is appended to
259 | containers.images
260 |
261 | {_make_additional_doc_str()}
262 |
263 | """
264 |
265 | WrapperEntry = WrapperEntryConfig(wrapped_object=crops_transforms)
266 |
267 |
268 | class AlbumentationAugmentationsTransformsGeometric(AlbumentationAugmentationsTransforms):
269 | __doc__ = f"""
270 | Class for albumentations transforms, using ``albumentations.transforms.geometric.transforms``
271 | module. The template takes as an input the DataContainer with images and for each of the
272 | ImagePacket applies the selected transform.
273 |
274 | If rewrite_image_packet is set to True, then the ith ImagePacket is
275 | replaced with the new image, otherwise the new image is appended to
276 | containers.images
277 |
278 | {_make_additional_doc_str()}
279 |
280 | """
281 |
282 | WrapperEntry = WrapperEntryConfig(wrapped_object=geometric_transforms)
283 |
284 |
285 | class AlbumentationAugmentationsTransformsResize(AlbumentationAugmentationsTransforms):
286 | __doc__ = f"""
287 | Class for albumentations resize transforms, using ``albumentations.transforms.geometric.resize``.
288 | The template takes as an input the DataContainer with images and for each of the
289 | ImagePacket applies the selected resize transform.
290 |
291 | If rewrite_image_packet is set to True, then the ith ImagePacket is
292 | replaced with the new image, otherwise the new image is appended to
293 | containers.images
294 |
295 | {_make_additional_doc_str()}
296 | """
297 |
298 | WrapperEntry = WrapperEntryConfig(wrapped_object=resize_transforms)
299 |
300 |
301 | class AlbumentationAugmentationsTransformsRotate(AlbumentationAugmentationsTransforms):
302 | __doc__ = f"""
303 | Class for albumentations rotate transforms, using the ``albumentations.transforms.geometric.rotate``.
304 | The template takes as an input the DataContainer with images and for each of the
305 | ImagePacket applies the selected rotate transform.
306 |
307 | If rewrite_image_packet is set to True, then the ith ImagePacket is
308 | replaced with the new image, otherwise the new image is appended to
309 | containers.images
310 |
311 | {_make_additional_doc_str()}
312 | """
313 |
314 | WrapperEntry = WrapperEntryConfig(wrapped_object=rotate_transforms)
315 |
316 |
317 | def __getattr__(name: str) -> Template:
318 | """
319 | Only create a template if it's imported, this avoids creating all the base models for all templates
320 | and potential import errors due to not available packages.
321 | """
322 | if name in AlbumentationAugmentationsTransforms.WrapperEntry.module_att_names:
323 | return make_dynamic_template(name, AlbumentationAugmentationsTransforms)
324 | if name in AlbumentationAugmentationsTransformsBlur.WrapperEntry.module_att_names:
325 | return make_dynamic_template(name, AlbumentationAugmentationsTransformsBlur)
326 |
327 | if name in AlbumentationAugmentationsTransformsCrops.WrapperEntry.module_att_names:
328 | return make_dynamic_template(name, AlbumentationAugmentationsTransformsCrops)
329 |
330 | if name in AlbumentationAugmentationsTransformsGeometric.WrapperEntry.module_att_names:
331 | return make_dynamic_template(name, AlbumentationAugmentationsTransformsGeometric)
332 |
333 | if name in AlbumentationAugmentationsTransformsResize.WrapperEntry.module_att_names:
334 | return make_dynamic_template(name, AlbumentationAugmentationsTransformsResize)
335 |
336 | if name in AlbumentationAugmentationsTransformsRotate.WrapperEntry.module_att_names:
337 | return make_dynamic_template(name, AlbumentationAugmentationsTransformsRotate)
338 |
339 | raise AttributeError(f"template `{name}` not found in {__name__}")
340 |
341 |
342 | __all__ = (
343 | AlbumentationAugmentationsTransforms.WrapperEntry.module_att_names
344 | + AlbumentationAugmentationsTransformsBlur.WrapperEntry.module_att_names
345 | + AlbumentationAugmentationsTransformsCrops.WrapperEntry.module_att_names
346 | + AlbumentationAugmentationsTransformsGeometric.WrapperEntry.module_att_names
347 | + AlbumentationAugmentationsTransformsResize.WrapperEntry.module_att_names
348 | + AlbumentationAugmentationsTransformsRotate.WrapperEntry.module_att_names
349 | )
350 |
351 |
352 | if SINAPSIS_BUILD_DOCS:
353 | dynamic_templates = [__getattr__(template_name) for template_name in __all__]
354 |
355 | for template in dynamic_templates:
356 | globals()[template.__name__] = template
357 | del template
358 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "sinapsis-image-transforms"
3 | version = "0.2.3"
4 | description = "Package to handle image transformations"
5 | authors = [{ name = "SinapsisAI", email = "dev@sinapsis.tech" }]
6 | readme = "README.md"
7 | license-files = ["LICENSE"]
8 | requires-python = ">=3.10"
9 |
10 | dependencies = [
11 | "sinapsis>=0.1.1",
12 | ]
13 |
14 |
15 | [project.optional-dependencies]
16 | webapp-gradio = [
17 | "sinapsis[webapp]>=0.2.7",
18 | ]
19 | dev = [
20 | "ruff>=0.8.3",
21 | "pre-commit>=4.0.1",
22 | ]
23 | all = [
24 | "sinapsis-image-transforms[webapp-gradio]",
25 | "sinapsis-image-transforms[dev]",
26 | "sinapsis-albumentations",
27 | ]
28 |
29 | [tool.uv.workspace]
30 | members = ["packages/*"]
31 | exclude = ["packages/*.egg-info"]
32 |
33 | [tool.setuptools]
34 | packages = { find = { where = ["packages"] } }
35 |
36 | [[tool.uv.index]]
37 | url = "https://pypi.sinapsis.tech/"
38 |
39 | [tool.uv.sources]
40 | sinapsis-albumentations = { workspace = true }
41 |
42 | [tool.ruff]
43 | lint.select = [
44 | "ARG",
45 | "ANN",
46 | "BLE",
47 | "C4",
48 | "E",
49 | "F",
50 | "FIX",
51 | "FLY",
52 | "I",
53 | "PERF",
54 | "PIE",
55 | "RUF",
56 | "RSE",
57 | "SIM",
58 | "SLOT",
59 | "T10",
60 | "T20",
61 | "TD",
62 | "TID",
63 | ]
64 | lint.ignore = ['ANN401']
65 | line-length = 120
66 | show-fixes = true
67 |
68 |
69 | [project.urls]
70 | Homepage = "https://sinapsis.tech"
71 | Documentation = "https://docs.sinapsis.tech/docs"
72 | Tutorials = "https://docs.sinapsis.tech/tutorials"
73 | Repository = "https://github.com/Sinapsis-AI/sinapsis-image-transforms.git"
74 |
--------------------------------------------------------------------------------
/webapps/gradio_albumentations_transforms_visualizer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import gradio as gr
3 | from sinapsis.webapp.agent_gradio_helper import add_logo_and_title, css_header, init_image_inference
4 | from sinapsis_core.utils.env_var_keys import AGENT_CONFIG_PATH, GRADIO_SHARE_APP
5 |
6 | CONFIG_NAME = "visualize_transforms_with_anns_from_dir.yml"
7 | CONFIG_DIR = "packages/sinapsis_albumentations/src/sinapsis_albumentations/configs/"
8 | DEFAULT_CONFIG = CONFIG_DIR + CONFIG_NAME
9 | CONFIG_FILE = AGENT_CONFIG_PATH or DEFAULT_CONFIG
10 |
11 |
12 | def create_demo_interface() -> gr.Blocks:
13 | """Creates and returns the Gradio Blocks demo interface for the sinapsis albumentations webapp.
14 |
15 | Returns:
16 | gr.Blocks: The Gradio Blocks object containing the entire interface.
17 | """
18 | with gr.Blocks(css=css_header()) as demo:
19 | add_logo_and_title("Sinapsis Albumentations Demo")
20 | init_image_inference(
21 | CONFIG_FILE,
22 | "",
23 | False,
24 | )
25 | return demo
26 |
27 |
28 | if __name__ == "__main__":
29 | demo_interface = create_demo_interface()
30 | demo_interface.launch(share=GRADIO_SHARE_APP)
31 |
--------------------------------------------------------------------------------
/webapps/gradio_image_transform_visualizer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import gradio as gr
3 | from sinapsis.webapp.agent_gradio_helper import init_image_inference, css_header, add_logo_and_title
4 | from sinapsis_core.utils.env_var_keys import AGENT_CONFIG_PATH, GRADIO_SHARE_APP
5 |
6 | DEFAULT_CONFIG = "src/sinapsis_image_transforms/configs/visualize_transforms_with_anns_from_dir.yml"
7 | CONFIG_FILE = AGENT_CONFIG_PATH or DEFAULT_CONFIG
8 |
9 |
10 |
11 | def demo()-> gr.Blocks:
12 | with gr.Blocks(css=css_header()) as demo:
13 | add_logo_and_title("Sinapsis Albumentations Demo")
14 | init_image_inference(CONFIG_FILE, "", False, )
15 | return demo
16 |
17 |
18 | if __name__ == "__main__":
19 |
20 | live_interface = demo()
21 | live_interface.launch(share=GRADIO_SHARE_APP)
22 |
23 |
--------------------------------------------------------------------------------