Skip to content

Commit

Permalink
Merge branch 'develop' into ov/regression_reading_getUpdated
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleg Valiulin committed Dec 20, 2024
2 parents 2741805 + 54534e8 commit 04d23c5
Show file tree
Hide file tree
Showing 46 changed files with 1,485 additions and 626 deletions.
2 changes: 2 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@
"python": "${command:python.interpreterPath}",
"module": "pytest",
"args": [
"--verbose",
"--no-cov", // vscode debugger might not work otherwise
"tests/python/rest_api/"
],
"cwd": "${workspaceFolder}",
Expand Down
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- scriv-insert-here -->

<a id='changelog-2.24.0'></a>
## \[2.24.0\] - 2024-12-20

### Added

- \[CLI\] Added new commands: `project create`, `project delete`, `project ls`
(<https://github.com/cvat-ai/cvat/pull/8787>)

- \[SDK\] You can now use `client.projects.remove_by_ids` to remove multiple
projects
(<https://github.com/cvat-ai/cvat/pull/8787>)

- Support for boolean parameters in annotations actions
(<https://github.com/cvat-ai/cvat/pull/8798>)

### Changed

- Improved uniformity of validation frames distribution in honeypot tasks and
random honeypot rerolls
(<https://github.com/cvat-ai/cvat/pull/8776>)

- \[CLI\] Switched to a new subcommand hierarchy; now CLI subcommands
have the form `cvat-cli <resource> <action>`
(<https://github.com/cvat-ai/cvat/pull/8787>)

- \[CLI\] The output of the `task create`, `task create-from-backup` and
`project create` commands is now just the created resource ID,
making it machine-readable
(<https://github.com/cvat-ai/cvat/pull/8833>)

- /api/events can now be used to receive events from several sources
(<https://github.com/cvat-ai/cvat/pull/8799>)

### Deprecated

- \[CLI\] All existing CLI commands of the form `cvat-cli <action>`
are now deprecated. Use `cvat-cli task <action>` instead
(<https://github.com/cvat-ai/cvat/pull/8787>)

### Removed

- Automatic calculation of quality reports in tasks
(<https://github.com/cvat-ai/cvat/pull/8790>)

### Fixed

- Uploading a skeleton template in configurator does not work
(<https://github.com/cvat-ai/cvat/pull/8822>)

- Installation of YOLOv7 on GPU
(<https://github.com/cvat-ai/cvat/pull/8824>)

- \[Server API\] Significantly improved preformance of honeypot changes in tasks
(<https://github.com/cvat-ai/cvat/pull/8789>)
- \[Server API\] `PATCH tasks/id/validation_layout` responses now include correct
`disabled_frames` and handle simultaneous updates of
`disabled_frames` and honeypot frames correctly
(<https://github.com/cvat-ai/cvat/pull/8789>)

- Fixed handling of tracks keyframes from deleted frames on export
(<https://github.com/cvat-ai/cvat/pull/8834>)

- Exporting datasets could start significantly later than expected, both for 1
and several users in the same project/task/job (<https://github.com/cvat-ai/cvat/pull/8721>)
- Scheduled RQ jobs could not be restarted due to incorrect RQ job status
updating and handling (<https://github.com/cvat-ai/cvat/pull/8721>)

<a id='changelog-2.23.1'></a>
## \[2.23.1\] - 2024-12-09

Expand Down

This file was deleted.

20 changes: 0 additions & 20 deletions changelog.d/20241206_184906_roman_cli_hierarchy.md

This file was deleted.

This file was deleted.

4 changes: 0 additions & 4 deletions changelog.d/20241209_110126_sekachev.bs_support_boolean.md

This file was deleted.

This file was deleted.

This file was deleted.

6 changes: 0 additions & 6 deletions changelog.d/20241216_144316_roman_machine_readable_create.md

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion cvat-cli/requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
cvat-sdk~=2.23.2
cvat-sdk~=2.24.1
Pillow>=10.3.0
setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability
2 changes: 1 addition & 1 deletion cvat-cli/src/cvat_cli/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "2.23.2"
VERSION = "2.24.1"
2 changes: 1 addition & 1 deletion cvat-sdk/gen/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set -e

GENERATOR_VERSION="v6.0.1"

VERSION="2.23.2"
VERSION="2.24.1"
LIB_NAME="cvat_sdk"
LAYER1_LIB_NAME="${LIB_NAME}/api_client"
DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)"
Expand Down
2 changes: 1 addition & 1 deletion cvat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

from cvat.utils.version import get_version

VERSION = (2, 23, 2, "alpha", 0)
VERSION = (2, 24, 1, "alpha", 0)

__version__ = get_version(VERSION)
3 changes: 3 additions & 0 deletions cvat/apps/dataset_manager/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ def _unite_objects(obj0, obj1):
def _modify_unmatched_object(self, obj, end_frame):
pass


class TrackManager(ObjectManager):
def to_shapes(self, end_frame: int, *,
included_frames: Optional[Sequence[int]] = None,
Expand Down Expand Up @@ -929,6 +930,8 @@ def propagate(shape, end_frame, *, included_frames=None):
prev_shape = None
for shape in sorted(track["shapes"], key=lambda shape: shape["frame"]):
curr_frame = shape["frame"]
if included_frames is not None and curr_frame not in included_frames:
continue
if prev_shape and end_frame <= curr_frame:
# If we exceed the end_frame and there was a previous shape,
# we still need to interpolate up to the next keyframe,
Expand Down
9 changes: 0 additions & 9 deletions cvat/apps/dataset_manager/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,3 @@

class DatasetManagerConfig(AppConfig):
name = "cvat.apps.dataset_manager"

def ready(self) -> None:
from django.conf import settings

from . import default_settings

for key in dir(default_settings):
if key.isupper() and not hasattr(settings, key):
setattr(settings, key, getattr(default_settings, key))
2 changes: 1 addition & 1 deletion cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Attribute(NamedTuple):
value: Any

@classmethod
def add_prefetch_info(cls, queryset: QuerySet):
def add_prefetch_info(cls, queryset: QuerySet[Label]) -> QuerySet[Label]:
assert issubclass(queryset.model, Label)

return add_prefetch_fields(queryset, [
Expand Down
14 changes: 0 additions & 14 deletions cvat/apps/dataset_manager/default_settings.py

This file was deleted.

10 changes: 5 additions & 5 deletions cvat/apps/dataset_manager/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from cvat.apps.engine import models, serializers
from cvat.apps.engine.plugins import plugin_decorator
from cvat.apps.engine.log import DatasetLogManager
from cvat.apps.engine.utils import chunked_list
from cvat.apps.engine.utils import take_by
from cvat.apps.events.handlers import handle_annotations_change
from cvat.apps.profiler import silk_profile

Expand Down Expand Up @@ -84,7 +84,7 @@ def merge_table_rows(rows, keys_for_merge, field_id):

class JobAnnotation:
@classmethod
def add_prefetch_info(cls, queryset: QuerySet, prefetch_images: bool = True):
def add_prefetch_info(cls, queryset: QuerySet[models.Job], prefetch_images: bool = True) -> QuerySet[models.Job]:
assert issubclass(queryset.model, models.Job)

label_qs = add_prefetch_fields(models.Label.objects.all(), [
Expand Down Expand Up @@ -530,13 +530,13 @@ def _delete(self, data=None):
self.ir_data.shapes = data['shapes']
self.ir_data.tracks = data['tracks']

for labeledimage_ids_chunk in chunked_list(labeledimage_ids, chunk_size=1000):
for labeledimage_ids_chunk in take_by(labeledimage_ids, chunk_size=1000):
self._delete_job_labeledimages(labeledimage_ids_chunk)

for labeledshape_ids_chunk in chunked_list(labeledshape_ids, chunk_size=1000):
for labeledshape_ids_chunk in take_by(labeledshape_ids, chunk_size=1000):
self._delete_job_labeledshapes(labeledshape_ids_chunk)

for labeledtrack_ids_chunk in chunked_list(labeledtrack_ids, chunk_size=1000):
for labeledtrack_ids_chunk in take_by(labeledtrack_ids, chunk_size=1000):
self._delete_job_labeledtracks(labeledtrack_ids_chunk)

deleted_data = {
Expand Down
69 changes: 69 additions & 0 deletions cvat/apps/dataset_manager/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_paginated_collection, ForceLogin, generate_image_file, ApiTestBase
)


class _DbTestBase(ApiTestBase):
def setUp(self):
super().setUp()
Expand Down Expand Up @@ -87,6 +88,7 @@ def _create_task(self, data, image_data):

return task


class TaskExportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v2_task_id_annotations(task["id"], annotations)
Expand Down Expand Up @@ -520,6 +522,72 @@ def test_frames_outside_are_not_generated(self):
self.assertTrue(frame.frame in range(6, 10))
self.assertEqual(i + 1, 4)

def _delete_job_frames(self, job_id: int, deleted_frames: list[int]):
with ForceLogin(self.user, self.client):
response = self.client.patch(
f"/api/jobs/{job_id}/data/meta?org=",
data=dict(deleted_frames=deleted_frames),
format="json"
)
assert response.status_code == status.HTTP_200_OK, response.status_code

def test_track_keyframes_on_deleted_frames_do_not_affect_later_frames(self):
images = self._generate_task_images(4)
task = self._generate_task(images)
job = self._get_task_jobs(task["id"])[0]

annotations = {
"version": 0,
"tags": [],
"shapes": [],
"tracks": [
{
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": None,
"source": "manual",
"attributes": [],
"shapes": [
{
"frame": 0,
"points": [1, 2, 3, 4],
"type": "rectangle",
"occluded": False,
"outside": False,
"attributes": [],
},
{
"frame": 1,
"points": [5, 6, 7, 8],
"type": "rectangle",
"occluded": False,
"outside": True,
"attributes": [],
},
{
"frame": 2,
"points": [9, 10, 11, 12],
"type": "rectangle",
"occluded": False,
"outside": False,
"attributes": [],
},
]
},
]
}
self._put_api_v2_job_id_annotations(job["id"], annotations)
self._delete_job_frames(job["id"], [2])

task_ann = TaskAnnotation(task["id"])
task_ann.init_from_db()
task_data = TaskData(task_ann.ir_data, Task.objects.get(pk=task["id"]))
extractor = CvatTaskOrJobDataExtractor(task_data)
dm_dataset = Dataset.from_extractors(extractor)

assert len(dm_dataset.get("image_3").annotations) == 0


class FrameMatchingTest(_DbTestBase):
def _generate_task_images(self, paths): # pylint: disable=no-self-use
f = BytesIO()
Expand Down Expand Up @@ -612,6 +680,7 @@ def test_dataset_root(self):
root = find_dataset_root(dataset, task_data)
self.assertEqual(expected, root)


class TaskAnnotationsImportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v2_task_id_annotations(task["id"], annotations)
Expand Down
Loading

0 comments on commit 04d23c5

Please sign in to comment.