No description has been provided for this image

CSCI 3240U --- Computer Vision I¶

Lab 7 --- Interest Points and Local Descriptors¶

Faisal Z. Qureshi
Faculty of Science, Ontario Tech University
Oshawa ON Canada
http://vclab.science.ontariotechu.ca

Fall 2026

Copyright information¶

© Faisal Qureshi

License¶

Creative Commons Licence
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Outline¶

  • Image derivatives
  • Image gradients
  • Gradient magnitudes and angles
  • Quantization
  • Histogram computation

Tasks¶

1¶

Complete the various steps needed to compute the rotation invariant descriptor. You can use the following image to test your code

DATA_FOLDER = '.'
img = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-2.jpg'), 0)
img = cv.resize(img, (256, 256))

plt.figure(figsize=(5,5))
plt.imshow(img, cmap='gray');
plt.plot([74],[175],'r.')

2¶

Combine this code in the following method

descriptor, I_patch, grad_patch, grad_patch_weighted, grad_color_patch = my_local_descriptor(img, r, c)

This method takes in an image img and row r and column c location. The method returns a 128-dimensional descriptor. It should also return $4$ $16\times16$ patches around (r,c) locations. I_patch is image intensity patch, grad_patch is gradient magnitudes, grad_patch_weighted is weighted gradient magnitudes, and grad_color_patch returns the colorized gradient magnitudes.

3¶

Use this method to compute descriptors as follows:

DATA_FOLDER = '.'
img1 = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-2.jpg'), 0)
img1 = cv.resize(img1, (256, 256))

img2 = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-1.jpg'), 0)
img2 = cv.resize(img2, (256, 256))

desc1, p1, g1, gw1, gc1 = my_local_descriptor(img1, 175, 74)
desc2, p2, g2, gw2, gc2 = my_local_descriptor(img2, 197, 106) 
desc3, p3, g3, gw3, gc3 = my_local_descriptor(img2, 100, 54)

4¶

Next compute the distances between the three descriptors as follows:

X = np.vstack([desc1, desc2, desc3])

from scipy.spatial import distance_matrix
distance_matrix(X, X)

5¶

Comment whether or not desc1 is closer to desc2 than to desc3.

Starter code¶

In [1]:
import cv2 as cv
import numpy as np
import scipy as sp
from scipy import signal
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import os
In [2]:
DATA_FOLDER = '.'
img = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-2.jpg'), 0)
img = cv.resize(img, (256, 256))

plt.figure(figsize=(5,5))
plt.imshow(img, cmap='gray')
plt.plot([74],[175],'r.');
No description has been provided for this image

Image derivatives¶

In [3]:
 
In [5]:
plt.figure(figsize=(15,15))
plt.subplot(221)
plt.title('Ix')
plt.imshow(Ix, cmap='gray')
plt.subplot(222)
plt.title('Iy')
plt.imshow(Iy, cmap='gray');
plt.subplot(223)
plt.title('Grad')
plt.imshow(Igrad, cmap='gray')
plt.subplot(224)
plt.title('Angle visualization')
plt.imshow(Igrad_color);
No description has been provided for this image

16x16 window patch¶

Extract a $16x16$ window around location $(x,y)$. Notice how images are stored in numpy. Unlike the 2D $xy$ coordinate system that we are used to, here $x$ corresponds to columns and increase from left to right. $y$ corresponds to rows and increase from top to bottom.

In [6]:
 
In [9]:
plt.figure(figsize=(5,5))
plt.imshow(grad_patch, cmap='gray');
No description has been provided for this image

Weighted gradient magnitudes¶

Get gradient magnitude and multiply it by a $16 \times 16$ Gaussian window of $\sigma=2.5$

In [10]:
 
In [12]:
plt.figure(figsize=(15,5))
plt.subplot(131)
plt.title('Gradient magnitude')
plt.imshow(grad_patch, cmap='gray')
plt.subplot(132)
plt.title('Gaussian kernel')
plt.imshow(g, cmap='gray')
plt.subplot(133)
plt.title('Gradient magnitude weighted')
plt.imshow(grad_patch_weighted, cmap='gray');
No description has been provided for this image

Finding dominant angle¶

Quantize angles to 36 bins (from 0 to 360 degrees). Find the dominant angle (the angle that is most repeated). Subtract dominant angle from every angle to achieve rotation invariant.

In [13]:
 

Construct feature descriptor¶

Divide $16 \times 16$ into $16$ blocks. Each block is $4 \times 4$. Within each block, quantize angles into 8 bins (from 0 to 360 degrees). Computed gradient magnitude weighted, normalized, angle histograms. Each block thus returns an $8$-dimensional vector. Concatenate $16$ $8$-dimensional vectors to construct a $128$-dimensional vector.

In [15]:
 

Putting it all together¶

Now put the code together in one convenient function that computes the 128 dimensional descriptor at a particular location of a given grayscale image.

In [17]:
 
In [24]:
def my_local_descriptor(img, r, c):

    descriptor = None 
    I_patch = None 
    grad_patch = None 
    grad_patch_weighted = None 
    grad_color_patch = None
    
    return descriptor, I_patch, grad_patch, grad_patch_weighted, grad_color_patch

Check for rotation invariance¶

In [19]:
DATA_FOLDER = '.'
img1 = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-2.jpg'), 0)
img1 = cv.resize(img1, (256, 256))

img2 = cv.imread(os.path.join(DATA_FOLDER, 'local-features-construction-1.jpg'), 0)
img2 = cv.resize(img2, (256, 256))

plt.figure(figsize=(10,5))
plt.subplot(121)
plt.imshow(img1, cmap='gray');
plt.plot([74],[175],'r.')
plt.subplot(122)
plt.imshow(img2, cmap='gray');
plt.plot([106],[196],'r.');
plt.plot([54],[100],'b.');
No description has been provided for this image
In [20]:
desc1, p1, g1, gw1, gc1 = my_local_descriptor(img1, 175, 74)
desc2, p2, g2, gw2, gc2 = my_local_descriptor(img2, 197, 106) 
desc3, p3, g3, gw3, gc3 = my_local_descriptor(img2, 100, 54)
dominant angle = 130.0 degrees
dominant angle = 150.0 degrees
dominant angle = 260.0 degrees
In [21]:
plt.figure(figsize=(15, 15))
plt.subplot(331)
plt.imshow(p1, cmap='gray')
plt.subplot(332)
plt.imshow(p2, cmap='gray')
plt.subplot(333)
plt.imshow(p3, cmap='gray')
plt.subplot(334)
plt.imshow(gc1, cmap='gray')
plt.subplot(335)
plt.imshow(gc2, cmap='gray')
plt.subplot(336)
plt.imshow(gc3, cmap='gray')
plt.subplot(337)
plt.imshow(gw1, cmap='gray')
plt.subplot(338)
plt.imshow(gw2, cmap='gray')
plt.subplot(339)
plt.imshow(gw3, cmap='gray');
No description has been provided for this image

Lets compute distance between the three descriptors, and confirm that descriptor 1,2 are closer to each other than descriptors 1,3 and 2,3.

In [25]:
 

Submission¶

Include code and results on the images found in the above folder in a single jupyter notebook. Submit via canvas.

No description has been provided for this image
In [ ]: