| # CVAT API Usage Guide | |
| Complete guide for working with CVAT tasks, jobs, and annotations. | |
| ## Table of Contents | |
| - [Installation](#installation) | |
| - [Initialization](#initialization) | |
| - [Common Workflows](#common-workflows) | |
| - [Download and Update Job Annotations](#download-and-update-job-annotations) | |
| - [Download and Update Task Annotations](#download-and-update-task-annotations) | |
| - [Update Job Metadata](#update-job-metadata) | |
| - [Update Task Metadata](#update-task-metadata) | |
| - [Complete Examples](#complete-examples) | |
| ## Installation | |
| ```bash | |
| pip install -r requirements.txt | |
| ``` | |
| ## Initialization | |
| ```python | |
| from cvat_api import CvatApiClient | |
| from pathlib import Path | |
| # Initialize client | |
| client = CvatApiClient( | |
| cvat_host="https://app.cvat.ai", | |
| cvat_username="your_username", | |
| cvat_password="your_password", | |
| cvat_organization="your_organization" | |
| ) | |
| ``` | |
| ## Common Workflows | |
| ### Download and Update Job Annotations | |
| Complete workflow for downloading annotations, modifying them, and uploading back: | |
| ```python | |
| # 1. Download annotations | |
| job_id = 2355063 | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| # 2. Modify annotations | |
| # Add a new shape | |
| from shared.schema.cvat import CvatApiShape | |
| new_shape = CvatApiShape( | |
| id=0, # Will be assigned by server | |
| type="rectangle", | |
| frame=0, | |
| label_id=1, | |
| points=[100.0, 100.0, 200.0, 200.0], | |
| occluded=False, | |
| z_order=0, | |
| source="manual", | |
| attributes=[] | |
| ) | |
| annotations.shapes.append(new_shape) | |
| # Or modify existing shapes | |
| for shape in annotations.shapes: | |
| if shape.label_id == 5: | |
| shape.occluded = True # Mark as occluded | |
| # 3. Upload modified annotations | |
| updated_annotations = client.annotations.put_job_annotations( | |
| job_id=job_id, | |
| annotations=annotations | |
| ) | |
| print(f"✅ Updated {len(updated_annotations.shapes)} shapes") | |
| ``` | |
| ### Download and Update Task Annotations | |
| Same workflow but at task level: | |
| ```python | |
| # 1. Download task annotations | |
| task_id = 1322143 | |
| annotations = client.annotations.get_task_annotations(task_id) | |
| # 2. Modify annotations (same as above) | |
| # ... make your changes ... | |
| # 3. Upload modified annotations | |
| updated_annotations = client.annotations.put_task_annotations( | |
| task_id=task_id, | |
| annotations=annotations | |
| ) | |
| ``` | |
| ### Update Job Metadata | |
| Update job status, assignee, stage, etc.: | |
| ```python | |
| # 1. Get current job details | |
| job_id = 2355063 | |
| job_details = client.jobs.get_job_details(job_id) | |
| # 2. Modify job metadata | |
| job_details.state = "completed" # Change state | |
| job_details.stage = "acceptance" # Change stage | |
| # 3. Update job | |
| updated_job = client.jobs.update_job( | |
| job_id=job_id, | |
| job_data=job_details | |
| ) | |
| print(f"✅ Job {job_id} updated: {updated_job.state} / {updated_job.stage}") | |
| ``` | |
| ### Update Task Metadata | |
| Update task name, assignee, labels, etc.: | |
| ```python | |
| # 1. Get current task details | |
| task_id = 1322143 | |
| task_details = client.tasks.get_task_details(task_id) | |
| # 2. Modify task metadata | |
| task_details.name = "Updated Task Name" | |
| # Note: Use model_dump(exclude_unset=True) in update method to only send changed fields | |
| # 3. Update task | |
| updated_task = client.tasks.update_task( | |
| task_id=task_id, | |
| task_data=task_details | |
| ) | |
| print(f"✅ Task {task_id} updated: {updated_task.name}") | |
| ``` | |
| ## Complete Examples | |
| ### Example 1: Batch Process Job Annotations | |
| Process annotations for all jobs in a task: | |
| ```python | |
| from cvat_api import CvatApiClient | |
| # Initialize | |
| client = CvatApiClient(...) | |
| # Get all jobs in task | |
| task_id = 1322143 | |
| job_ids = client.tasks.get_task_job_ids(task_id) | |
| # Process each job | |
| for job_id in job_ids: | |
| # Download annotations | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| # Apply your modifications | |
| for shape in annotations.shapes: | |
| if shape.label_id == 5: # Example: filter by label | |
| # Modify shape properties | |
| shape.occluded = True | |
| # Upload modified annotations | |
| client.annotations.put_job_annotations(job_id, annotations) | |
| print(f"✅ Processed job {job_id}") | |
| ``` | |
| ### Example 2: Download All Images and Annotations | |
| Download complete dataset from a task: | |
| ```python | |
| from pathlib import Path | |
| task_id = 1322143 | |
| output_dir = Path("./output") | |
| # 1. Download all images | |
| images = client.downloads.download_task_images( | |
| task_id=task_id, | |
| output_dir=output_dir / "images" | |
| ) | |
| print(f"Downloaded {len(images)} images") | |
| # 2. Download annotations | |
| annotations = client.annotations.get_task_annotations(task_id) | |
| # 3. Save annotations to file | |
| import json | |
| output_file = output_dir / "annotations.json" | |
| output_file.write_text(annotations.model_dump_json(indent=2)) | |
| print(f"Saved annotations to {output_file}") | |
| ``` | |
| ### Example 3: Quality Control Workflow | |
| Implement a QC workflow that marks reviewed jobs: | |
| ```python | |
| from shared.schema.cvat import CvatApiJobsListRequest | |
| # List all jobs in "annotation" stage | |
| request = CvatApiJobsListRequest( | |
| task_id=1322143, | |
| stage="annotation", | |
| state="completed" | |
| ) | |
| response = client.jobs.list_jobs(request) | |
| for job in response.results: | |
| # Download and review annotations | |
| annotations = client.annotations.get_job_annotations(job.id) | |
| # Run your QC checks | |
| issues_found = run_quality_checks(annotations) | |
| # Update job based on QC results | |
| job_details = client.jobs.get_job_details(job.id) | |
| if not issues_found: | |
| job_details.stage = "acceptance" | |
| job_details.state = "completed" | |
| else: | |
| job_details.state = "rejected" | |
| client.jobs.update_job(job.id, job_details) | |
| print(f"✅ QC processed job {job.id}") | |
| ``` | |
| ### Example 4: Export Annotations with Metadata | |
| Export complete annotation dataset with metadata: | |
| ```python | |
| # Get project details | |
| project_id = 239220 | |
| project = client.projects.get_project_details(project_id) | |
| labels = client.projects.get_project_labels(project_id) | |
| # Get all tasks | |
| task_ids = client.projects.get_project_tasks(project_id) | |
| # Build export structure | |
| export_data = { | |
| "project": project.model_dump(), | |
| "labels": [label.model_dump() for label in labels], | |
| "tasks": [] | |
| } | |
| # Export each task | |
| for task_id in task_ids: | |
| task_details = client.tasks.get_task_details(task_id) | |
| annotations = client.annotations.get_task_annotations(task_id) | |
| export_data["tasks"].append({ | |
| "task": task_details.model_dump(), | |
| "annotations": annotations.model_dump() | |
| }) | |
| # Save export | |
| import json | |
| Path("export.json").write_text(json.dumps(export_data, indent=2)) | |
| print(f"✅ Exported {len(task_ids)} tasks") | |
| ``` | |
| ## Tips and Best Practices | |
| ### 1. Use Partial Updates | |
| When updating jobs or tasks, use only the fields you want to change: | |
| ```python | |
| # Good - only update specific fields | |
| job_details = client.jobs.get_job_details(job_id) | |
| job_details.state = "completed" | |
| client.jobs.update_job(job_id, job_details) | |
| ``` | |
| ### 2. Handle Errors | |
| Always handle potential errors: | |
| ```python | |
| try: | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| except requests.HTTPError as e: | |
| print(f"Failed to get annotations: {e}") | |
| # Handle error appropriately | |
| ``` | |
| ### 3. Batch Operations | |
| For multiple jobs/tasks, use batch operations: | |
| ```python | |
| job_ids = client.tasks.get_task_job_ids(task_id) | |
| for job_id in job_ids: | |
| try: | |
| # Process each job | |
| process_job(client, job_id) | |
| except Exception as e: | |
| print(f"Error processing job {job_id}: {e}") | |
| continue # Continue with next job | |
| ``` | |
| ### 4. Validate Before Upload | |
| Validate annotations before uploading: | |
| ```python | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| # Validate | |
| for shape in annotations.shapes: | |
| assert shape.label_id in valid_label_ids | |
| assert len(shape.points) >= 4 # For rectangles | |
| # Upload after validation | |
| client.annotations.put_job_annotations(job_id, annotations) | |
| ``` | |
| ## Error Handling | |
| The client includes automatic retry logic for transient errors (500/502/503/504): | |
| ```python | |
| # This will automatically retry up to 10 times for server errors | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| ``` | |
| For custom error handling: | |
| ```python | |
| import requests | |
| from requests.exceptions import Timeout, ConnectionError | |
| try: | |
| annotations = client.annotations.get_job_annotations(job_id) | |
| except Timeout: | |
| print("Request timed out") | |
| except ConnectionError: | |
| print("Connection failed") | |
| except requests.HTTPError as e: | |
| if e.response.status_code == 404: | |
| print("Job not found") | |
| elif e.response.status_code == 403: | |
| print("Access denied") | |
| else: | |
| print(f"HTTP error: {e}") | |
| ``` | |
| ## See Also | |
| - [API Documentation](./client.py) - Complete API reference | |
| - [Test Suite](../tests/test_cvat_api/) - Usage examples in tests | |
| - [CVAT API Docs](https://app.cvat.ai/api/docs/) - Official CVAT API documentation | |