Skip to content

Commit

Permalink
fix(LAB-3447): correct calculation of vertices in kili format
Browse files Browse the repository at this point in the history
  • Loading branch information
Sihem Tchabi committed Feb 5, 2025
1 parent a66486a commit 3d72a33
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 16 deletions.
81 changes: 65 additions & 16 deletions src/kili/services/export/format/kili/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
from typing import Callable, Dict, List

from kili.services.export.exceptions import NotCompatibleInputType, NotCompatibleOptions
from kili.services.export.format.base import AbstractExporter
from kili.services.export.format.base import AbstractExporter, reverse_rotation_vertices
from kili.services.export.media.video import cut_video
from kili.services.types import Job


def _clean_json_response(asset: Dict):
if "latestLabel" in asset and "jsonResponse" in asset["latestLabel"]:
if "ROTATION_JOB" in asset["latestLabel"]["jsonResponse"]:
asset["latestLabel"]["jsonResponse"].pop("ROTATION_JOB")
if "labels" in asset:
for label in asset["labels"]:
if "jsonResponse" in label and "ROTATION_JOB" in label["jsonResponse"]:
label["jsonResponse"].pop("ROTATION_JOB")
return asset


class KiliExporter(AbstractExporter):
"""Common code for Kili exporters."""

Expand Down Expand Up @@ -96,10 +107,10 @@ def _cut_video_assets(self, assets: List[Dict]) -> List[Dict]:
def process_and_save(self, assets: List[Dict], output_filename: Path) -> None:
"""Extract formatted annotations from labels and save the json in the buckets."""
clean_assets = self.preprocess_assets(assets)

if self.normalized_coordinates is False:
clean_assets = [self.convert_to_pixel_coords(asset) for asset in clean_assets]

if self.project["inputType"] != "LLM_RLHF":
for i, asset in enumerate(clean_assets):
clean_assets[i] = self.convert_to_pixel_coords(asset)
clean_assets[i] = _clean_json_response(asset)
return self._save_assets_export(
clean_assets,
output_filename,
Expand Down Expand Up @@ -131,11 +142,21 @@ def _scale_label_vertices(self, label: Dict, asset: Dict) -> None:
else False
)

rotation_val = 0
if "ROTATION_JOB" in label["jsonResponse"]:
rotation_val = label["jsonResponse"]["ROTATION_JOB"]["rotation"]

normalized_vertices = True
if self.normalized_coordinates is False:
normalized_vertices = False

if self.project["inputType"] == "PDF":
self._scale_json_response_vertices(
label["jsonResponse"],
asset,
is_label_rotated,
rotation_val,
normalized_vertices,
_scale_normalized_vertices_pdf_annotation,
)

Expand All @@ -144,6 +165,8 @@ def _scale_label_vertices(self, label: Dict, asset: Dict) -> None:
label["jsonResponse"],
asset,
is_label_rotated,
rotation_val,
normalized_vertices,
_scale_normalized_vertices_image_video_annotation,
)

Expand All @@ -154,6 +177,8 @@ def _scale_label_vertices(self, label: Dict, asset: Dict) -> None:
frame_resp,
asset,
is_label_rotated,
rotation_val,
normalized_vertices,
_scale_normalized_vertices_image_video_annotation,
)

Expand All @@ -168,14 +193,16 @@ def _scale_json_response_vertices(
json_resp: Dict,
asset: Dict,
is_label_rotated: bool,
annotation_scaler: Callable[[Dict, Dict, bool], None],
rotation: int,
normalized_vertices: bool,
annotation_scaler: Callable[[Dict, Dict, bool, int, bool], None],
) -> None:
for job_name in json_resp:
if self._can_scale_vertices_for_job_name(job_name) and json_resp.get(job_name, {}).get(
"annotations"
):
for ann in json_resp[job_name]["annotations"]:
annotation_scaler(ann, asset, is_label_rotated)
annotation_scaler(ann, asset, is_label_rotated, rotation, normalized_vertices)

def _can_scale_vertices_for_job_name(self, job_name: str) -> bool:
return (
Expand Down Expand Up @@ -212,7 +239,11 @@ def _scale_all_vertices(object_, width: int, height: int):


def _scale_normalized_vertices_pdf_annotation(
annotation: Dict, asset: Dict, is_label_rotated: bool = False
annotation: Dict,
asset: Dict,
is_label_rotated: bool = False,
_rotation: int = 0,
_normalized_vertices: bool = False,
) -> None:
"""Scale normalized vertices of a PDF annotation.
Expand Down Expand Up @@ -265,31 +296,49 @@ def _scale_normalized_vertices_pdf_annotation(


def _scale_normalized_vertices_image_video_annotation(
annotation: Dict, asset: Dict, is_label_rotated: bool
annotation: Dict,
asset: Dict,
_is_label_rotated: bool,
rotation: int,
_normalized_vertices: bool,
) -> None:
"""Scale normalized vertices of an image/video object detection annotation."""
if "resolution" not in asset or asset["resolution"] is None:
if not _normalized_vertices and ("resolution" not in asset or asset["resolution"] is None):
raise NotCompatibleOptions(
"Image and video labels export with absolute coordinates require `resolution` in the"
" asset. Please use `kili.update_properties_in_assets(resolution_array=...)` to update"
" the resolution of your asset.`"
)

width = asset["resolution"]["width"] if not is_label_rotated else asset["resolution"]["height"]
height = asset["resolution"]["height"] if not is_label_rotated else asset["resolution"]["width"]
width = asset["resolution"]["width"] if "resolution" in asset else 0
height = asset["resolution"]["height"] if "resolution" in asset else 0

# bbox, segmentation, polygons
if "boundingPoly" in annotation:
if "boundingPoly" in annotation and _normalized_vertices:
annotation["boundingPoly"] = [
{
**norm_vertices_dict, # keep the original normalizedVertices
"vertices": _scale_all_vertices(
norm_vertices_dict["normalizedVertices"], width=width, height=height
"normalizedVertices": reverse_rotation_vertices(
norm_vertices_dict["normalizedVertices"], rotation
),
}
for norm_vertices_dict in annotation["boundingPoly"]
]
return

if "boundingPoly" in annotation and not _normalized_vertices:
annotation["boundingPoly"] = [
{
"normalizedVertices": reverse_rotation_vertices(
norm_vertices_dict["normalizedVertices"], rotation
),
"vertices": _scale_all_vertices(
reverse_rotation_vertices(norm_vertices_dict["normalizedVertices"], rotation),
width=width,
height=height,
),
}
for norm_vertices_dict in annotation["boundingPoly"]
]
# point jobs
if "point" in annotation:
annotation["pointPixels"] = _scale_all_vertices(
Expand Down
1 change: 1 addition & 0 deletions tests/unit/services/export/test_kili.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def test_preprocess_assets(mocker: pytest_mock.MockFixture):
def test_kili_exporter_convert_to_pixel_coords_pdf(mocker: pytest_mock.MockerFixture):
mocker.patch.object(KiliExporter, "__init__", return_value=None)
exporter = KiliExporter() # type: ignore # pylint: disable=no-value-for-parameter
exporter.normalized_coordinates = None
exporter.project = {
"inputType": "PDF",
"jsonInterface": {
Expand Down

0 comments on commit 3d72a33

Please sign in to comment.