diff --git a/CHANGELOG.md b/CHANGELOG.md index 5139a9e61291197b54740cc9c5046999f98e8f55..4cd6ab0ad81b821df9b934698b53d4ca780eb1a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## \[2.5.0] - Unreleased ### Added -- New option ``semi-auto`` is available as annotations source () - \[API\] Support for Ground Truth job creation and removal () - \[API\] Task quality estimation endpoints () @@ -24,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - TDB ### Fixed -- TDB +- Running serverless models for EXIF-rotated images () ### Security - TDB diff --git a/cvat/apps/engine/media_extractors.py b/cvat/apps/engine/media_extractors.py index e6186d88dd0a0c768e28d88d9f960120fa50f956..447e1d73a1e38b5f258a7f40f7f34965a0888da5 100644 --- a/cvat/apps/engine/media_extractors.py +++ b/cvat/apps/engine/media_extractors.py @@ -31,7 +31,6 @@ from utils.dataset_manifest import VideoManifestManager, ImageManifestManager ORIENTATION_EXIF_TAG = 274 - class ORIENTATION(IntEnum): NORMAL_HORIZONTAL=1 MIRROR_HORIZONTAL=2 @@ -42,7 +41,6 @@ class ORIENTATION(IntEnum): MIRROR_HORIZONTAL_90_ROTATED=7 NORMAL_270_ROTATED=8 - def get_mime(name): for type_name, type_def in MEDIA_TYPES.items(): if type_def['has_mime_type'](name): @@ -648,22 +646,37 @@ class IChunkWriter(ABC): pass class ZipChunkWriter(IChunkWriter): + IMAGE_EXT = 'jpeg' + POINT_CLOUD_EXT = 'pcd' + + def _write_pcd_file(self, image): + image_buf = open(image, "rb") if isinstance(image, str) else image + try: + properties = ValidateDimension.get_pcd_properties(image_buf) + w, h = int(properties["WIDTH"]), int(properties["HEIGHT"]) + image_buf.seek(0, 0) + return io.BytesIO(image_buf.read()), self.POINT_CLOUD_EXT, w, h + finally: + if isinstance(image, str): + image_buf.close() + def save_as_chunk(self, images, chunk_path): with zipfile.ZipFile(chunk_path, 'x') as zip_chunk: for idx, (image, path, _) in enumerate(images): - arcname = '{:06d}{}'.format(idx, os.path.splitext(path)[1]) - if isinstance(image, io.BytesIO): - zip_chunk.writestr(arcname, image.getvalue()) + ext = os.path.splitext(path)[1] + output = io.BytesIO() + if self._dimension == DimensionType.DIM_2D: + pil_image = rotate_within_exif(Image.open(image)) + pil_image.save(output, format=pil_image.format if pil_image.format else ext or self.IMAGE_EXT, quality=100, subsampling=0) else: - zip_chunk.write(filename=image, arcname=arcname) + output, ext = self._write_pcd_file(image)[0:2] + arcname = '{:06d}.{}'.format(idx, ext) + zip_chunk.writestr(arcname, output.getvalue()) # return empty list because ZipChunkWriter write files as is # and does not decode it to know img size. return [] -class ZipCompressedChunkWriter(IChunkWriter): - IMAGE_EXT = 'jpeg' - POINT_CLOUD_EXT = 'pcd' - +class ZipCompressedChunkWriter(ZipChunkWriter): def save_as_chunk( self, images, chunk_path, *, compress_frames: bool = True, zip_compress_level: int = 0 ): @@ -680,12 +693,7 @@ class ZipCompressedChunkWriter(IChunkWriter): extension = self.IMAGE_EXT else: - image_buf = open(image, "rb") if isinstance(image, str) else image - properties = ValidateDimension.get_pcd_properties(image_buf) - w, h = int(properties["WIDTH"]), int(properties["HEIGHT"]) - extension = self.POINT_CLOUD_EXT - image_buf.seek(0, 0) - image_buf = io.BytesIO(image_buf.read()) + image_buf, extension, w, h = self._write_pcd_file(image) image_sizes.append((w, h)) arcname = '{:06d}.{}'.format(idx, extension) zip_chunk.writestr(arcname, image_buf.getvalue()) diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index 74ac1e115ccac21986fc39ad31d4da6ad15216b4..46c767f0c28da4edbae279f5f21adb9512ee0d81 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -897,7 +897,7 @@ def _create_thread( if validate_dimension.dimension == models.DimensionType.DIM_3D: kwargs["dimension"] = validate_dimension.dimension compressed_chunk_writer = compressed_chunk_writer_class(db_data.image_quality, **kwargs) - original_chunk_writer = original_chunk_writer_class(original_quality) + original_chunk_writer = original_chunk_writer_class(original_quality, **kwargs) # calculate chunk size if it isn't specified if db_data.chunk_size is None: diff --git a/cvat/apps/lambda_manager/static/lambda_manager/decoder.onnx b/cvat/apps/lambda_manager/static/lambda_manager/decoder.onnx index 336a7ba506ca4ac8a067236cb36bb3eadc3cf126..72c5a1e8a1cb8894447a2e1cc2e571a67a26f994 100644 Binary files a/cvat/apps/lambda_manager/static/lambda_manager/decoder.onnx and b/cvat/apps/lambda_manager/static/lambda_manager/decoder.onnx differ