Intuition Behind Histogram of Oriented Gradients (HOG)

Histogram of Oriented Gradients (HOG) is a feature descriptor used in computer vision and image processing for the purpose of object detection. The intuition behind HOG is that the local object appearance and shape within an image can be characterized by the distribution of intensity gradients or edge directions.

Gradients and Edge Detection:

Orientation Binning

Local Contrast Normalization:

Feature Vector:

import matplotlib.pyplot as plt
import cv2

# Load the black and white image
image_path = '5.png'  # Replace with your image path
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# Resize the image to 32x16
resized_image = cv2.resize(image, (50, 48))

# Display the resized image
plt.imshow(resized_image, cmap='gray')
plt.title('Resized Image (32x16)')
plt.axis('off')  # Hide axis
plt.show()

# Print the pixel values of the resized image
print("Pixel values of the resized image:")
for i in range(resized_image.shape[0]):
    for j in range(resized_image.shape[1]):
        print(resized_image[i, j], end=" ")
    print()

Pixel values of the resized image:
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 248 237 239 250 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 94 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 4 4 4 7 7 7 7 7 7 7 7 7 3 2 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81 124 124 124 236 246 246 246 246 246 246 246 246 100 62 16 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17 87 152 174 200 215 215 215 252 255 255 255 255 255 255 255 255 207 194 52 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 128 224 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 69 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 42 76 212 244 255 255 255 255 255 255 255 255 255 255 239 212 207 172 171 171 46 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 115 143 255 242 237 226 220 220 220 204 202 187 185 185 168 139 132 94 93 93 25 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 54 252 255 255 207 191 149 128 128 128 69 60 4 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 11 21 21 2 0 0 48 220 220 212 171 158 124 106 106 106 57 50 4 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 26 67 123 124 12 0 0 13 63 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 38 65 105 150 149 15 0 0 11 50 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 97 191 217 255 255 247 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 56 153 247 250 248 151 139 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 47 65 162 255 255 247 136 124 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 169 234 245 255 231 187 26 14 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 184 255 255 255 244 222 120 111 79 59 38 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 184 255 255 255 255 251 195 189 134 100 64 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 176 216 255 255 253 232 230 209 197 183 161 80 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 92 128 193 255 255 255 255 255 255 255 255 228 129 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 33 45 68 91 125 173 173 175 247 255 255 245 210 176 41 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 38 94 94 97 193 218 238 242 255 227 116 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 57 119 191 204 254 255 255 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 43 103 175 192 254 252 239 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 128 155 254 242 191 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 64 137 162 254 242 191 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 1 3 3 4 2 2 1 0 0 0 1 3 62 122 192 205 254 242 189 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 27 123 131 183 95 62 30 0 0 3 57 67 229 250 252 253 255 230 130 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 28 128 136 191 99 64 31 0 0 3 59 70 236 255 255 255 255 230 128 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 75 139 238 239 246 234 229 225 221 221 221 229 230 252 255 255 243 200 163 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 141 217 255 255 255 255 255 255 255 255 255 255 255 255 221 177 155 74 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 174 255 255 255 255 255 255 255 255 255 255 255 255 255 200 128 101 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 126 200 255 255 255 204 184 202 219 204 179 116 111 111 88 56 44 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 87 156 255 255 255 163 128 160 191 165 120 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 37 64 106 106 106 67 53 66 79 68 50 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

image.png

For a black and white image it's moderately easy to predict the digit

Screenshot 2024-07-05 131426.png

But its difficult to predict an animal just based on the pixel values (in this case R+G+B/3) because the surrounding ,posture and other factors might also affect this

image.png

image.png

Thus the two rabbits may be predicted as different species

For such classifications we use HOC

Why HOG Works ?

Now let's take an image

# Load the black and white image
image_path = 'dog.jpeg'
image = cv2.imread(image_path)
plt.imshow(image)
plt.axis('off')  # Hide axis
plt.show()

image.png

import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage.feature import hog

Step 1: Image Preprocessing

The first step is to preprocess the image to a consistent size and convert it to grayscale. Grayscale conversion reduces the complexity by converting the image to a single channel.


image = cv2.imread('dog.jpeg')
# Convert to grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Display the grayscale image
plt.figure(figsize=(10, 10))
plt.subplot(221)
plt.title('Grayscale Image')
plt.imshow(gray_image, cmap='gray')
<matplotlib.image.AxesImage at 0x7b07975f85b0>

image.png

Step 2: Calculate Gradient and Orientation

Gradients represent the change in intensity of the image. They are computed using derivatives in both the x and y directions (Gx and Gy). The magnitude and orientation (angle) of the gradient are then calculated as follows:

Magnitude= (Gx^2 + Gy^2)^1/2

Orientation=arctan (Gy/Gx)

image.png

image.png

image.png

# Calculate the gradients
gx = cv2.Sobel(gray_image, cv2.CV_64F, 1, 0, ksize=3)
gy = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)

# Calculate gradient magnitude and direction
magnitude, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

# Display the gradient magnitude
plt.subplot(222)
plt.title('Gradient Magnitude')
plt.imshow(magnitude, cmap='gray')

# Display the gradient direction
plt.subplot(223)
plt.title('Gradient Direction')
plt.imshow(angle, cmap='gray')
<matplotlib.image.AxesImage at 0x7b079792d060>

image.png

Step 3: Build Histogram

Intuition:

The image is divided into small spatial regions (cells), and for each cell, a histogram of gradient orientations is created. The idea is to capture the distribution of edge directions within each cell.

Mathematical Explanation:

Each pixel within a cell votes for an orientation bin based on its gradient magnitude and orientation. The histogram bins typically cover 0 to 180 or 0 to 360 degrees, depending on the implementation.

# Calculate HOG features and HOG image
hog_features, hog_image = hog(gray_image, orientations=9, pixels_per_cell=(8, 8),
                              cells_per_block=(2, 2), block_norm='L2-Hys',
                              visualize=True, transform_sqrt=True)

# Display the HOG image
plt.subplot(224)
plt.title('HOG Image')
plt.imshow(hog_image, cmap='gray')

plt.tight_layout()
plt.show()

image.png

image.png

This is what from scratch implementation of Building the histogram would look like

# Calculate gradients
grad_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)

# Calculate magnitude and orientation
magnitude = cv2.magnitude(grad_x, grad_y)
orientation = cv2.phase(grad_x, grad_y, angleInDegrees=True)

plt.subplot(1, 2, 1)
plt.imshow(magnitude, cmap='gray')
plt.title('Gradient Magnitude')

plt.subplot(1, 2, 2)
plt.imshow(orientation, cmap='gray')
plt.title('Gradient Orientation')
plt.show()

# Parameters for HOG
cell_size = 8
num_bins = 9
angle_unit = 180 / num_bins

def calculate_histogram(magnitude, orientation, cell_size, num_bins):
    height, width = magnitude.shape
    histogram = np.zeros((height // cell_size, width // cell_size, num_bins))
    for i in range(0, height // cell_size):
        for j in range(0, width // cell_size):
            cell_magnitude = magnitude[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size]
            cell_orientation = orientation[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size]
            cell_histogram = np.zeros(num_bins)
            for p in range(cell_size):
                for q in range(cell_size):
                    bin_idx = int(cell_orientation[p, q] // angle_unit)
                    # Ensure bin_idx is within the valid range
                    bin_idx = min(bin_idx, num_bins - 1)
                    cell_histogram[bin_idx] += cell_magnitude[p, q]
            histogram[i, j, :] = cell_histogram
    return histogram

histogram = calculate_histogram(magnitude, orientation, cell_size, num_bins)

# Display histogram for a specific cell
cell_hist = histogram[0, 0, :]
plt.bar(range(num_bins), cell_hist)
plt.title('Histogram of Gradients for First Cell')
plt.xlabel('Orientation Bins')
plt.ylabel('Magnitude')
plt.show()

# Checking the histogram values for debugging
print(f"Histogram of first cell: {cell_hist}")
print(f"Sum of histogram values (should be > 0 if non-empty): {np.sum(cell_hist)}")

image.png image.png

Applications

HOG descriptors are widely used in various computer vision tasks, including: