Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add task_size parameter to CVAT backend #1457

Merged
merged 4 commits into from
Dec 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions docs/source/integrations/cvat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ In addition, the following CVAT-specific parameters from
:class:`CVATBackendConfig <fiftyone.utils.cvat.CVATBackendConfig>` can also be
provided:

- **task_size** (*None*): an optional maximum number of images to upload per
task. Videos are always uploaded one per task
- **segment_size** (*None*): the maximum number of images to upload per job.
Not applicable to videos
- **image_quality** (*75*): an int in `[0, 100]` determining the image
Expand Down Expand Up @@ -1685,6 +1687,59 @@ will be assigned using a round-robin strategy.
results.cleanup()
dataset.delete_annotation_run(anno_key)

.. _cvat-large-runs:

Large annotation runs
---------------------

The CVAT API imposes a limit on the size of all requests. By default, all
images are uploaded to a single CVAT task, which can result in errors when
uploading annotation runs for large sample collections.

.. note::

The CVAT maintainers are working on
`an update <https://github.com/openvinotoolkit/cvat/pull/3692>`_
to resolve this issue natively. In the meantime, the following workflow is
our recommended approach to circumvent this issue.

You can use the `task_size` parameter to break image annotation runs into
multiple CVAT tasks, each with a specified maximum number of images. Note that
we recommend providing a `project_name` whenever you use the `task_size`
pararmeter so that the created tasks will be grouped together.

The `task_size` parameter can also be used in conjunction with the
`segment_size` parameter to configure both the number of images per task as
well as the number of images per job within each task.

.. code:: python
:linenos:

import fiftyone as fo
import fiftyone.zoo as foz

dataset = foz.load_zoo_dataset("quickstart", max_samples=20).clone()

anno_key = "batch_upload"

results = dataset.annotate(
anno_key,
label_field="ground_truth",
task_size=6, # 6 images per task
segment_size=2, # 2 images per job
project_name="batch_example",
launch_editor=True,
)

# Annotate in CVAT...

dataset.load_annotations(anno_key, cleanup=True)

.. note::

The `task_size` parameter only applies to image datasets, since videos are
always uploaded one per task.

.. _cvat-scalar-labels:

Scalar labels
Expand Down
20 changes: 15 additions & 5 deletions fiftyone/utils/cvat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2498,6 +2498,8 @@ class CVATBackendConfig(foua.AnnotationBackendConfig):
password (None): the CVAT password
headers (None): an optional dict of headers to add to all CVAT API
requests
task_size (None): an optional maximum number of images to upload per
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI- I moved task_size up here so that it appears near other relevant parameters.

I think this is more user-friendly than strictly adding new kwargs to the end, which makes it harder to grok the available options. This is also why I always recommend using key=value syntax when calling any function with kwargs, so that order can be changed later in cases like this.

task. Videos are always uploaded one per task
segment_size (None): maximum number of images per job. Not applicable
to videos
image_quality (75): an int in `[0, 100]` determining the image quality
Expand Down Expand Up @@ -2536,6 +2538,7 @@ def __init__(
username=None,
password=None,
headers=None,
task_size=None,
segment_size=None,
image_quality=75,
use_cache=True,
Expand All @@ -2551,6 +2554,7 @@ def __init__(
):
super().__init__(name, label_schema, media_field=media_field, **kwargs)
self.url = url
self.task_size = task_size
self.segment_size = segment_size
self.image_quality = image_quality
self.use_cache = use_cache
Expand Down Expand Up @@ -3489,6 +3493,7 @@ def upload_samples(self, samples, backend):
config = backend.config
label_schema = config.label_schema
occluded_attr = config.occluded_attr
task_size = config.task_size
project_name, project_id = self._parse_project_details(
config.project_name, config.project_id
)
Expand All @@ -3507,7 +3512,7 @@ def upload_samples(self, samples, backend):
labels_task_map = {}

num_samples = len(samples)
batch_size = self._get_batch_size(samples)
batch_size = self._get_batch_size(samples, task_size)

(
cvat_schema,
Expand Down Expand Up @@ -3988,7 +3993,9 @@ def _ensure_one_field_per_type(self, label_schema, verbose=True):
label_field,
)

def _get_batch_size(self, samples):
def _get_batch_size(self, samples, task_size):
samples.compute_metadata()

if samples.media_type == fom.VIDEO:
# The current implementation (both upload and download) requires
# frame IDs for all frames that might get labels
Expand All @@ -3997,10 +4004,13 @@ def _get_batch_size(self, samples):
# CVAT only allows for one video per task
return 1

samples.compute_metadata()
num_samples = len(samples)

if task_size is None:
# Put all image samples in one task
return num_samples

# Put all image samples in one task
return len(samples)
return min(task_size, num_samples)

def _create_task_upload_data(
self,
Expand Down