From “What?” to “What and Where?”
In the previous module, you learned image classification: feed an image in, get a class label out. “This is a cat.” “This is a defective part.”
Classification is powerful, but it has a fundamental limitation: it can only look at one thing at a time, and it only tells you what is in the image, not where it is.
Object detection solves this. A detection model does both simultaneously:
- Finds every instance of every object class in the image
- Draws a bounding box around each one (a rectangle defined by its corners)
- Outputs a confidence score (how sure it is this is really an object)
- Labels each box with its class name
A detection model looking at a street scene might output something like:
Car , 97% confidence, (x1=45, y1=120, x2=340, y2=280)
Person , 92% confidence, (x1=380, y1=95, x2=430, y2=310)
Car , 88% confidence, (x1=510, y1=130, x2=720, y2=275)
Traffic Light, 76% confidence, (x1=290, y1=10, x2=310, y2=80)
This is the foundation for traffic monitoring, people counting, shelf-gap detection, and countless other real applications.
How YOLO Works (Without the Math)
The most popular family of object detection models is YOLO (You Only Look Once). Understanding how it works helps you use it well.
Here’s the core idea:
-
Divide the image into a grid. YOLO splits the input image into, say, a 20×20 grid of cells.
-
Each cell makes predictions. Every cell asks: “Does the centre of any object fall within my cell?” If yes, that cell predicts a bounding box for that object, a confidence score, and class probabilities.
-
Everything happens in one pass. This is the “You Only Look Once” insight. Older approaches (like R-CNN) would first propose ~2000 candidate regions, then run a classifier on each. YOLO predicts everything simultaneously in a single pass through the network, making it 10-100× faster.
-
Post-processing: Non-Maximum Suppression (NMS). Multiple grid cells may each think they’ve found the same car. NMS removes duplicates: it computes the overlap between boxes (called IoU, Intersection over Union), and discards any box that overlaps too much with a higher-confidence box for the same class.
IoU: Measuring How Well Boxes Overlap
IoU (Intersection over Union) is the standard metric for measuring how well a predicted box matches the ground truth:
IoU = (Area of overlap) / (Area of union)
If IoU = 1.0, the boxes are identical. If IoU = 0.0, they don’t overlap at all. A typical detection is considered “correct” if IoU ≥ 0.5 with the ground truth box.
You’ll see IoU everywhere in detection: in NMS, in evaluation metrics, and in training loss functions.
Running YOLOv8 in Python
The easiest way to run detection today is with the ultralytics library, which packages YOLOv8 with a clean API:
pip install ultralytics
Now load a model and run it on an image:
from ultralytics import YOLO
# yolov8n = nano (smallest, fastest)
# yolov8s = small, yolov8m = medium, yolov8l = large, yolov8x = xlarge
model = YOLO('yolov8n.pt') # downloads automatically on first run
# Run detection: accepts a file path, URL, or numpy array
results = model('street.jpg')
YOLO downloads pretrained weights on first use. The nano model is ~6MB and processes images in milliseconds; the xlarge model is much more accurate but slower.
Understanding the Results
The results object contains everything the model found:
for result in results:
boxes = result.boxes # all detected bounding boxes
print(f"Found {len(boxes)} objects")
for box in boxes:
# Bounding box coordinates in pixel space
x1, y1, x2, y2 = box.xyxy[0].tolist()
# Confidence score (0.0 to 1.0)
confidence = box.conf[0].item()
# Class ID (integer) and class name (string)
class_id = int(box.cls[0].item())
class_name = model.names[class_id]
print(f"{class_name}: {confidence:.1%} at ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f})")
box.xyxy gives the top-left and bottom-right corners as (x1, y1, x2, y2). These are pixel coordinates in the original image. model.names is a dictionary mapping class IDs to class names: the pretrained model knows 80 classes (people, cars, animals, furniture, and more) from the COCO dataset.
Drawing Results on the Image
Visualising detections is essential for debugging and demonstrations:
import cv2
# Load the original image
img = cv2.imread('street.jpg')
for result in results:
for box in result.boxes:
x1, y1, x2, y2 = [int(v) for v in box.xyxy[0].tolist()]
conf = box.conf[0].item()
label = model.names[int(box.cls[0])]
Now draw a rectangle and label for each detection:
# Draw the bounding box: green rectangle, 2px thick
cv2.rectangle(img, (x1, y1), (x2, y2), color=(0, 255, 0), thickness=2)
# Add text label above the box
text = f"{label} {conf:.0%}"
cv2.putText(img, text, org=(x1, y1 - 10),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.6, color=(0, 255, 0), thickness=2)
# Save the annotated image
cv2.imwrite('detected.jpg', img)
print("Saved to detected.jpg")
The cv2.putText arguments look intimidating but are straightforward: position (x1, y1-10 puts text just above the box), font, scale (size), colour, and thickness.
Fine-Tuning on Your Own Objects
The pretrained model knows 80 common objects. If you need to detect something not in that list, a specific product defect, a company logo, a medical device, you need to fine-tune on custom labelled data.
Organising your data is the main effort. YOLO expects a specific format. Tools like Roboflow let you label images with bounding boxes and export in YOLO format.
Once you have labelled data, training is just a few lines:
# Load the pretrained model as a starting point
model = YOLO('yolov8n.pt')
# Fine-tune on your custom dataset
results = model.train(
data='dataset.yaml', # path to your dataset config file
epochs=50, # number of training cycles
imgsz=640, # input image size
batch=16, # images processed per step
)
The dataset.yaml file tells YOLO where your images and labels are and what classes you have. Roboflow generates this file automatically when you export.
After training, run model.val() to see your mAP (mean Average Precision), the standard metric for detection quality.
Pretrained vs. Custom: When to Do Each
| Situation | Recommendation |
|---|---|
| Objects are in COCO’s 80 classes | Use pretrained model directly |
| Domain-specific objects, 500+ labelled images | Fine-tune pretrained model |
| Very few labelled images (<100) | Use cloud API instead |
| Real-time on mobile/edge | Use yolov8n (nano) or yolov8s (small) |
| Maximum accuracy matters most | Use yolov8x (xlarge) |
Exercise: Run the pretrained YOLOv8n model on 3-5 photos from your own environment (your desk, a street, a room). Print all detected objects with their confidence scores. Are there any false detections (wrong objects)? Any missed objects that should have been detected? Try the same photos with yolov8m and compare, does accuracy improve? By how much does speed decrease?