Computer Vision Dataset Annotation with Pre-labeling and Quality Control
Poor annotation is the main reason for low accuracy in Computer Vision models. One client lost 12% mAP due to an IoU difference of 0.15 between annotators. This cost $810–1.2k and two weeks of retraining. We have been annotating CV data for more than 5 years and completed 50+ projects, from 100 to 100,000 images. Our clients save on average 60–75% of time compared to annotating from scratch, which translates to tens of about $9–13 in savings per project.
Problems We Solve
Consistency between annotators is a key factor. IoU of 0.65 between two annotators on small objects is not uncommon. If two bounding boxes of the same object differ by 20%, the model gets contradictory signals. Another problem is edge cases: partial occlusion, low resolution, unusual angles. We develop class ontologies and detailed instructions to minimize variance. For quality control, we use Inter-rater reliability — a standard in medical and industrial annotation.
Missing a defect due to poor annotation can lead to $1.8k–2.6k losses in retraining. Therefore we implement multi-level QA.
Comparison of Annotation Tools
| Tool | Key Feature | When to Choose |
|---|---|---|
| CVAT | Self-hosted, REST API, team collaboration | Large projects with comprehensive QA pipeline |
| Label Studio | Multimodal annotation, conditional logic | Complex ontologies, mixed data |
| Roboflow | Versioning, built-in augmentation | Quick start, prototyping |
CVAT handles large datasets 3 times faster than Label Studio due to its built-in task queue. Label Studio wins in flexibility for annotating heterogeneous entities.
How We Do It
We start with a thorough analysis of the task, formalizing the class ontology and estimating complexity. Then we set up the tool — either CVAT or Label Studio — load images, and if needed deploy a GPU server for automatic pre-labeling. A pilot annotation of 50–100 images calibrates the instructions. After that, annotators work in parallel with continuous QA. Finally, we export in the required format and provide a quality report.
Case Study: Automotive Defect Detection
A client needed to detect scratches and dents on car bodies. Initial annotations had high intra-class variation due to unclear instructions. We redefined the ontology, created a guideline with visual examples, and implemented inter-annotator agreement checks. The mean IoU improved from 0.65 to 0.85, and model mAP increased by 15%. The project of 5,000 images was completed in 4 weeks with only 2% requiring rework.
Quality Control: Inter-Annotator Agreement
We use inter-annotator agreement with a target mean IoU > 0.80. If deviation occurs, we return for re-annotation. A senior specialist checks every tenth object. For large projects, we implement continuous QA.
import numpy as np
from itertools import combinations
def calculate_iou(box1: list, box2: list) -> float:
inter_x1 = max(box1[0], box2[0])
inter_y1 = max(box1[1], box2[1])
inter_x2 = min(box1[2], box2[2])
inter_y2 = min(box1[3], box2[3])
if inter_x2 < inter_x1 or inter_y2 < inter_y1:
return 0.0
inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1)
area1 = (box1[2]-box1[0]) * (box1[3]-box1[1])
area2 = (box2[2]-box2[0]) * (box2[3]-box2[1])
return inter_area / (area1 + area2 - inter_area)
def inter_annotator_iou(annotations_by_annotator: dict) -> dict:
annotators = list(annotations_by_annotator.keys())
results = {}
for a1, a2 in combinations(annotators, 2):
boxes1 = annotations_by_annotator[a1]
boxes2 = annotations_by_annotator[a2]
ious = []
for b1 in boxes1:
best_iou = max(
(calculate_iou(b1, b2) for b2 in boxes2),
default=0.0
)
if best_iou > 0.1:
ious.append(best_iou)
results[f'{a1}_vs_{a2}'] = {
'mean_iou': np.mean(ious) if ious else 0.0,
'n_matched': len(ious)
}
return results
Auto-labeling for Faster Annotation
Pre-labeling by a model plus manual correction is the standard for large volumes. Time savings of 60–75% with the right model. One client saved 3 months of work for two specialists by implementing our auto-labeling pipeline, equivalent to $1.6k–2.3k.
from ultralytics import YOLO
import json
def auto_label_batch(
image_paths: list[str],
model_path: str = 'yolov8l-world.pt',
conf_threshold: float = 0.5,
output_format: str = 'yolo'
) -> dict:
model = YOLO(model_path)
results = {}
for img_path in image_paths:
preds = model.predict(
img_path,
conf=conf_threshold,
verbose=False
)[0]
confident_boxes = []
needs_review_boxes = []
for box in preds.boxes:
conf = float(box.conf)
entry = {
'bbox': box.xyxy[0].tolist(),
'class_id': int(box.cls),
'confidence': conf
}
if conf > 0.7:
confident_boxes.append(entry)
else:
needs_review_boxes.append(entry)
results[img_path] = {
'auto_labeled': confident_boxes,
'needs_review': needs_review_boxes,
'review_required': len(needs_review_boxes) > 0
}
return results
Export Formats
| Format | Use Case | Tool |
|---|---|---|
| YOLO TXT | YOLOv5/v8/v11 training | Ultralytics |
| COCO JSON | Detectron2, MMDetection | torchvision |
| Pascal VOC XML | TensorFlow Object Detection API | TF OD API |
| LabelMe JSON | Segmentation, polygons | LabelMe |
| CVAT XML | CVAT import/export | cvat-sdk |
Timelines and Volumes
| Annotation Type | Speed (objects/hour) | Relative Cost |
|---|---|---|
| Bounding box | 200–400 | 1x |
| Polygon | 40–80 | 4–6x |
| Semantic segmentation | 2–5 images | 15–20x |
| Keypoints | 50–100 persons | 3x |
| Dataset Volume | Timeline with QA |
|---|---|
| 1,000 images, bounding box | 1–2 weeks |
| 5,000 images, bounding box | 3–4 weeks |
| 2,000 images, polygon | 3–5 weeks |
What's Included in Our Work
- Analysis of the task and development of class ontology
- Tool selection and project setup
- Pilot annotation for calibration
- Main annotation with parallel QA
- Export in required formats + quality report
- Support during model training
Our team has 5+ years of experience and 50+ annotation projects for Computer Vision. We guarantee annotation consistency and adherence to deadlines. Contact us to discuss your project — we will prepare a dataset with quality guarantee.







