KevsRobots Learning Platform
33% Percent Complete
By Kevin McAleer, 7 Minutes
In this lesson, youβll learn how to prepare a custom object detection dataset from scratch. Youβll discover best practices for collecting images, how to annotate them with bounding boxes, and how to organize your dataset for training. By the end of this lesson, youβll have a production-ready dataset for your custom objects.
By the end of this lesson, you will be able to:
Your model is only as good as your dataset. A well-prepared dataset with diverse examples will create a robust model that works in real-world conditions. A poor dataset leads to models that fail when conditions change.
Real-world impact:
Quality over quantity wins every time!
Here are real-world examples to guide your dataset size:
| Use Case | Objects to Detect | Images Needed | Why |
|---|---|---|---|
| Smart doorbell | person, package, pet | 200-500 each | Varied people, lighting conditions |
| Warehouse robot | box, pallet, forklift | 300-600 each | Different angles, distances, stacking |
| Plant monitor | healthy_leaf, diseased_leaf, pest | 400-800 each | Many disease variations |
| Tool organizer | hammer, screwdriver, wrench | 150-300 each | Different brands, conditions |
Rule of thumb: Start with 200-300 images per class. Add more if validation accuracy is low (<80%).
Diversity is key:
For a coffee mug detector:
# Good dataset composition:
- 100 images: mug on desk from various angles
- 75 images: mug with hand holding it
- 50 images: mug partially hidden behind laptop
- 50 images: different lighting (morning, evening, artificial)
- 25 images: multiple mugs in frame
Your smartphone is perfect for dataset collection:
Weβll use LabelImg - a simple, powerful tool for drawing bounding boxes:
# Install LabelImg
pip install labelImg
If installation fails, try the from-source method:
# Clone the repository
git clone https://github.com/HumanSignal/labelImg
cd labelImg
# Install dependencies
pip install PyQt5
# Run from source
python labelImg.py
Letβs walk through annotating a coffee mug:
labelImg
LabelImg creates an XML file next to your image:
<annotation>
<folder>images</folder>
<filename>mug_001.jpg</filename>
<size>
<width>640</width>
<height>480</height>
</size>
<object>
<name>mug</name>
<bndbox>
<xmin>120</xmin>
<ymin>80</ymin>
<xmax>340</xmax>
<ymax>420</ymax>
</bndbox>
</object>
</annotation>
Your first annotation is complete!
Good: Box tightly fits the object with minimal padding
ββββββββ
β MUG β
ββββββββ
Bad: Too much empty space
ββββββββββββ
β β
β MUG β
β β
ββββββββββββ
If an object is partially hidden:
When annotating multiple objects:
Organize your annotated dataset like this:
custom_dataset/
βββ train/
β βββ images/
β β βββ img_001.jpg
β β βββ img_002.jpg
β β βββ ...
β βββ annotations/
β βββ img_001.xml
β βββ img_002.xml
β βββ ...
βββ test/
β βββ images/
β β βββ img_201.jpg
β β βββ ...
β βββ annotations/
β βββ img_201.xml
β βββ ...
βββ labels.txt
Create labels.txt:
mug
laptop
keyboard
mouse
Common splits:
Python script to split dataset:
import os
import shutil
import random
def split_dataset(source_dir, train_ratio=0.8):
"""Split images and annotations into train/test sets"""
# Get all image files
images = [f for f in os.listdir(source_dir) if f.endswith('.jpg')]
random.shuffle(images)
# Calculate split point
split_idx = int(len(images) * train_ratio)
train_images = images[:split_idx]
test_images = images[split_idx:]
# Create directories
for split in ['train', 'test']:
os.makedirs(f'{split}/images', exist_ok=True)
os.makedirs(f'{split}/annotations', exist_ok=True)
# Copy files
for img in train_images:
shutil.copy(f'{source_dir}/{img}', f'train/images/{img}')
xml = img.replace('.jpg', '.xml')
shutil.copy(f'{source_dir}/{xml}', f'train/annotations/{xml}')
for img in test_images:
shutil.copy(f'{source_dir}/{img}', f'test/images/{img}')
xml = img.replace('.jpg', '.xml')
shutil.copy(f'{source_dir}/{xml}', f'test/annotations/{xml}')
print(f"β Split complete!")
print(f" Training images: {len(train_images)}")
print(f" Testing images: {len(test_images)}")
# Use it:
split_dataset('my_images')
Challenge: Annotate a complex scene with 3+ different objects. Give each object type a different label.
Problem: βModuleNotFoundError: No module named βPyQt5ββ
Solution: Install PyQt5: pip install PyQt5
Why: LabelImg requires PyQt5 for its graphical interface
Problem: Annotations saved in wrong format (YOLO instead of PascalVOC) Solution: In LabelImg menu, click βViewβ β ensure βPascalVOCβ is selected Why: Different frameworks use different annotation formats. We need PascalVOC (XML) for PyTorch
Problem: Canβt see bounding boxes clearly Solution: Use βCtrl+Plusβ to zoom in, click and drag box corners to adjust Why: Precision matters - boxes should tightly fit objects for best model accuracy
Problem: LabelImg crashes when opening images Solution: Ensure images are valid JPG/PNG files, try converting with an image tool Why: Corrupted or unusual image formats can cause crashes
Problem: Lost all my annotations! Solution: Always enable βAuto Saveβ mode and check XML files are being created alongside images Why: Manual saving can be forgotten, especially when annotating hundreds of images
Before moving to training, verify:
Youβve learned:
You now have a professional-quality dataset ready for training!
In the next lesson, youβll use your annotated dataset to train a custom PyTorch object detection model using transfer learning.
You can use the arrows β β on your keyboard to navigate between lessons.
Comments