Bag of Features: A Powerful Technique for Image Recognition
Image recognition is a fundamental problem in computer vision with a wide range of applications, from organizing your personal photo collection to powering self-driving cars. However, getting computers to "see" and understand the contents of images is an extremely challenging task. One technique that has proven effective for simplifying the problem is known as the "bag of features" (BoF) or "bag of visual words" model.
In this blog post, we‘ll take a deep dive into the bag of features approach to image recognition. We‘ll start by explaining what BoF is and how it works at a high level. Then we‘ll go through each of the key steps in detail, from detecting and extracting local features, to clustering them into visual words, to representing images as histograms of visual word occurrences. Along the way, we‘ll look at code examples showing how to implement BoF in Python using popular open source libraries. Finally, we‘ll discuss some of the applications, limitations, and recent advances in BoF and feature representation for visual recognition.
What is Bag of Features?
At its core, the bag of features model is a way to represent images as collections of local features while ignoring the spatial relationships between those features. The name comes from an analogy to the bag-of-words representation commonly used in text processing, where documents are represented as unordered collections of word frequencies.
The key insight behind BoF is that images can be characterized by the local visual patterns they contain, regardless of exactly where those patterns occur. Just as the topics and themes of a document can be inferred from its word frequencies, the contents of an image can be inferred from the types and frequencies of visual patterns it contains.

The BoF approach generally involves three main steps:
- Detecting and extracting local features from images
- Clustering the extracted features into a "visual vocabulary"
- Representing images as histograms of visual word occurrences
By converting images into this common representation, many visual recognition tasks become straightforward. For example, to classify images into categories, we can simply train a classifier on the visual word histograms. To search for images similar to a query image, we can find the images whose histograms are closest to the query using a distance metric.
The power of BoF is that it transforms the difficult problem of understanding images in all their complexity into the more tractable problem of analyzing histograms. In the process, a lot of information is lost, but enough is retained to perform many visual recognition tasks surprisingly well.
Feature Detection and Extraction
The first step in the BoF pipeline is to detect and extract local visual features from images. A local feature is a visually interesting region in an image, such as a corner, edge, blob, or small patch with a distinctive appearance. The goal is to find local patterns that are invariant to changes in scale, rotation, illumination, and other imaging conditions, so that the same features can be detected in different views of the same object or scene.

Some of the most widely used local feature detectors and descriptors include:
- Scale-Invariant Feature Transform (SIFT)
- Speeded Up Robust Features (SURF)
- Oriented FAST and Rotated BRIEF (ORB)
- Binary Robust Invariant Scalable Keypoints (BRISK)
- Fast Retina Keypoint (FREAK)
These algorithms work by finding key points in an image and extracting a descriptor vector summarizing the appearance of a local patch around each key point. The descriptors encode the local shape and texture in a high-dimensional vector in a way that is invariant to various imaging transformations.
Here‘s an example of using OpenCV to detect and extract SIFT features from an image in Python:
import cv2
img = cv2.imread(‘image.jpg‘)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
keypoints, descriptors = sift.detectAndCompute(gray, None)
img_with_keypoints = cv2.drawKeypoints(img, keypoints, None)
cv2.imshow(‘SIFT features‘, img_with_keypoints)
cv2.waitKey(0)
cv2.destroyAllWindows()
This code loads an image, converts it to grayscale, initializes the SIFT feature detector, and detects keypoints and extracts descriptors from the image. It then displays the image with the keypoints overlaid as circles.
The result of feature extraction is a set of descriptor vectors, each describing the appearance of a local patch around a detected keypoint. These descriptors are typically 128-dimensional for SIFT or 64-dimensional for SURF, for example.
Clustering Features into Visual Words
The next step in the BoF approach is to cluster the extracted features into a manageable number of representative clusters called "visual words". The idea is to quantize the space of local feature descriptors, mapping each descriptor to its closest visual word. This reduces the representation of an image from a large set of high-dimensional descriptors to a smaller set of visual word labels.
To form the visual vocabulary, we apply a clustering algorithm such as k-means to the descriptors extracted from a set of training images. K-means partition the descriptor space into k clusters, where each cluster center becomes a visual word in the vocabulary. The number of clusters k is a hyperparameter that determines the size of the visual vocabulary, typically ranging from hundreds to tens of thousands of visual words.
Here‘s an example of using scikit-learn to build a visual vocabulary by clustering SIFT features with k-means:
import numpy as np
from sklearn.cluster import KMeans
k = 100 # size of visual vocabulary
descriptor_list = []
for img_path in training_imgs:
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
_, descriptors = sift.detectAndCompute(gray, None)
descriptor_list.extend(descriptors)
descriptor_array = np.array(descriptor_list)
kmeans = KMeans(n_clusters=k, n_init=10)
kmeans.fit(descriptor_array)
visual_words = kmeans.cluster_centers_
This code extracts SIFT descriptors from a set of training images, stacks them into a numpy array, and uses k-means to find k cluster centers in the descriptor space. The result is an array of k visual words representing the learned visual vocabulary.
Building the visual vocabulary is essentially a dimensionality reduction step that maps the raw features to a much smaller, more manageable set of cluster labels. It also makes the representation invariant to the details of individual descriptors, as only their closest visual word labels are retained.
Representing Images as Visual Word Histograms
The final step in the BoF pipeline is to represent each image as a histogram (bag) of visual words. To do this, we count how many of the feature descriptors extracted from the image fall into each cluster in the visual vocabulary. In other words, we map each descriptor to its closest visual word and accumulate the frequency counts into a histogram over the vocabulary.
Mathematically, let d be a descriptor vector extracted from an image, and let {v_1, …, v_k} be the set of visual words (cluster centers) in the vocabulary. The visual word histogram h for the image has k bins, where the i-th bin is the number of descriptors in the image that are closest to the i-th visual word v_i, in terms of Euclidean distance:
h[i] = sum(arg min_j ||d – v_j|| = i)
Here‘s an example of computing the visual word histogram for an image using the visual vocabulary from the previous step:
def compute_histogram(img_path, visual_words):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
_, descriptors = sift.detectAndCompute(gray, None)
histogram = np.zeros(len(visual_words))
for d in descriptors:
idx = np.argmin(np.linalg.norm(visual_words - d, axis=1))
histogram[idx] += 1
return histogram
This function takes an image path and the visual vocabulary array, extracts SIFT descriptors from the image, and counts how many descriptors are assigned to each visual word using nearest neighbor search. The result is a k-dimensional histogram counting the frequency of each visual word in the image.
The beauty of the BoF representation is that it transforms the variable-length set of descriptors into a fixed-length vector (the histogram) that can be easily compared across images using standard distance metrics. Images containing similar visual patterns will map to similar histograms, even if the features occur in different spatial arrangements.

Applications and Limitations
The bag of features model has been successfully applied to a variety of visual recognition tasks, including:
- Image classification: Training classifiers like SVMs on the visual word histograms to predict image categories
- Object detection: Sliding a window over an image and classifying each window as containing an object or not
- Content-based image retrieval: Finding images in a database whose histograms are most similar to a query image
- Scene recognition: Classifying images into semantic categories like beach, mountain, city, etc.
- Texture recognition: Identifying materials and surfaces based on their visual word distributions
Despite its many successes, the BoF approach has some significant limitations. One is that it discards all spatial information about the features, treating images as orderless collections. This means it can struggle to distinguish objects or scenes that are composed of similar parts arranged in different configurations.
Another limitation is that BoF is computationally expensive, especially for large-scale datasets. Extracting features, clustering them into visual words, and computing histograms for every image can be very time-consuming. Additionally, BoF needs a large, diverse training set to build a representative visual vocabulary.
Recent Advances
In recent years, deep learning models like convolutional neural networks (CNNs) have achieved state-of-the-art results on many visual recognition benchmarks, surpassing the performance of BoF-based methods. CNNs learn feature extractors and classifiers end-to-end from large datasets, optimizing the entire pipeline for the target task.
However, the underlying principles of feature extraction, quantization, and pooling that were pioneered in BoF are also found in modern CNNs. The main difference is that CNNs learn the feature extractors and vocabulary adaptively from data, rather than using hand-engineered features and unsupervised clustering.
Some recent extensions of BoF aim to incorporate spatial information while retaining the benefits of invariance and efficiency. For example, the spatial pyramid matching kernel partitions an image into increasingly fine grids and computes histograms in each grid cell. This allows for some spatial reasoning while keeping the representation compact.
Other advances include using sparse coding or Fisher vectors instead of k-means clustering to build the visual vocabulary. These techniques can produce more discriminative and informative visual words that better capture the underlying structure of the data.
Conclusion and Future Directions
In this post, we‘ve explored the bag of features model for image recognition in depth. We‘ve seen how BoF represents images as collections of local features, clusters those features into visual words, and encodes images as histograms over the visual vocabulary. This pipeline achieves a good balance of discriminative power, invariance, and computational efficiency.
While deep learning models have largely supplanted BoF for visual recognition, the core ideas of feature extraction, quantization, and pooling remain relevant. Potential avenues for future research include learning visual vocabularies adaptively from data, incorporating spatial reasoning into BoF, and scaling the techniques to handle ever-larger datasets.
As the field continues to advance, the goal is to build computer vision systems that can understand images as well as humans can, if not better. The bag of features model represents an important step towards that goal, providing a foundation for learning rich, invariant visual representations. With further innovations in feature learning, large-scale optimization, and model architectures, the promise of truly intelligent visual perception is within reach.