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 90,000 ₽ 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 thousands of rubles 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 200,000 ₽ 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 180,000 ₽.
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.







