ECMM426-Template/Question 2.ipynb

13 KiB

None <html> <head> </head>

Question 2 (20 marks)

In [1]:
import cv2
import numpy as np
In [2]:
n_clusters = 100

Read images

In [3]:
im_book = cv2.imread('data/books.jpg', cv2.IMREAD_GRAYSCALE)
im_mount = cv2.imread('data/mount_rushmore_1.jpg', cv2.IMREAD_GRAYSCALE)
im_notre = cv2.imread('data/notre_dame_1.jpg', cv2.IMREAD_GRAYSCALE)

Generate Clusters

In [4]:
def get_features(image):
    image = image[:, :, np.newaxis]

    # Initialize a SIFT detector
    sift = cv2.SIFT_create()

    # Detect keypoints and compute descriptors
    keypoints, descriptors = sift.detectAndCompute(image, None)

    return keypoints, descriptors
In [5]:
from sklearn.cluster import KMeans

def get_clusters(keypoints, descriptors, n_clusters=100):

    # Perform k-means clustering
    kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
    kmeans.fit(descriptors)

    # Assign descriptors to clusters
    clusters = kmeans.predict(descriptors)

    # Convert keypoints to locations (x, y coordinates)
    locations = np.array([kp.pt for kp in keypoints], dtype=np.int64)
    
    return clusters, locations
In [6]:
kpts_book, des_book = get_features(im_book)
kpts_mount, des_mount = get_features(im_mount)
kpts_notre, des_notre = get_features(im_notre)
In [7]:
clusters_book, locations_book = get_clusters(kpts_book, des_book, n_clusters=n_clusters)
clusters_mount, locations_mount = get_clusters(kpts_mount, des_mount, n_clusters=n_clusters)
clusters_notre, locations_notre = get_clusters(kpts_notre, des_notre, n_clusters=n_clusters)

Method 1 (Two FOR loops)

In [8]:
def m1_generate_bovw_spatial_histogram(im, locations, clusters, division):
    """
    Create bag of visual words representation of an image based on the division information.
    
    Parameters:
    im (numpy.ndarray): Image array of data type uint8.
    locations (numpy.ndarray): Array of shape (N, 2) with Cartesian coordinates (x, y).
    clusters (numpy.ndarray): Array of shape (N,) with quantised cluster id.
    division (list): List of integers of length 2 indicating division along Y and X axes.
    
    Returns:
    numpy.ndarray: 1-dimensional array representing the BoVW spatial histogram.
    """

    # Determine the size of each division
    div_height = im.shape[0] // division[0]
    div_width = im.shape[1] // division[1]
    
    # Initialize the histogram
    num_clusters = np.unique(clusters).size
    histogram = np.zeros((division[0] * division[1] * num_clusters,), dtype=np.int64)

    # Two FOR loops
    for div_y in range(division[0]):
        for div_x in range(division[1]):
            # Define the bounds of the current division
            y_start = div_y * div_height
            y_end = (div_y + 1) * div_height
            x_start = div_x * div_width
            x_end = (div_x + 1) * div_width

            # Find features within the current division
            div_mask = (locations[:, 1] >= y_start) & (locations[:, 1] < y_end) & \
                       (locations[:, 0] >= x_start) & (locations[:, 0] < x_end)
            div_locations = locations[div_mask]
            div_clusters = clusters[div_mask]

            # Calculate the histogram for the current division
            for i in range(num_clusters):
                cluster_mask = (div_clusters == i)
                histogram[div_y * division[1] * num_clusters + div_x * num_clusters + i] = np.sum(cluster_mask)
    
    return histogram

Method 2 (One FOR loop)

In [9]:
def m2_generate_bovw_spatial_histogram(im, locations, clusters, division):

    img_shape = np.shape(im)

    height = img_shape[0]
    width = img_shape[1]

    ## Possible Mistakes: Some students swapped x and y
    div_x = division[1]
    div_y = division[0]

    x_size = width / div_x
    y_size = height / div_y

    num_divisions = division[0] * division[1]

    num_clusters = np.max(clusters) + 1

    histogram = np.zeros(num_clusters * num_divisions)

    # One FOR loop
    for i in range(len(locations)):
        point = locations[i]
        cluster = clusters[i]

        x_div = np.ceil((point[0] + 1) / x_size).astype(np.int64) - 1
        y_div = np.ceil((point[1] + 1) / y_size).astype(np.int64) - 1

        # Possible Mistakes: Some students miscalculated the boundary condition
        # x_div = np.ceil(point[0] / x_size).astype(np.int64) - 1
        # y_div = np.ceil(point[1] / y_size).astype(np.int64) - 1

        # Calculate the array position
        div = x_div + (y_div * div_x)
        array_pos = (div * num_clusters) + cluster
        
        # Update the histogram
        histogram[array_pos] = histogram[array_pos] + 1

    return histogram.astype(int)

Put students' implementations here

In [10]:
# Be careful, some students used a different function name (e.g. bowv rather than bovw)
def generate_bovw_spatial_histogram(im, locations, clusters, division):
    # Determine the number of clusters
    num_clusters = np.unique(clusters).size

    # Initialize histogram
    spatial_histogram = np.zeros(num_clusters * np.prod(division), dtype=np.int64)

    div_size_y = im.shape[0] // division[0]
    div_size_x = im.shape[1] // division[1]

    for div_y in range(division[0]):
        for div_x in range(division[1]):
            start_y = div_y * div_size_y
            end_y = (div_y + 1) * div_size_y if div_y < division[0] - 1 else im.shape[0]
            start_x = div_x * div_size_x
            end_x = (div_x + 1) * div_size_x if div_x < division[1] - 1 else im.shape[1]
            for loc, cluster_id in zip(locations, clusters):
                x, y = loc
                if start_y <= y < end_y and start_x <= x < end_x:
                    index = (div_y * division[1] + div_x) * num_clusters + cluster_id
                    spatial_histogram[index] += 1
    return spatial_histogram

Test (Should output ALL PASS)

Restart and Run ALL for each submission

In [11]:
histograms = []
for division in [ [1, 1], [2, 2], [2, 3] ]:
    print('Testing division:', division)

    m1_histogram_book = m1_generate_bovw_spatial_histogram(im_book, locations_book, clusters_book, division)
    m1_histogram_mount = m1_generate_bovw_spatial_histogram(im_mount, locations_mount, clusters_mount, division)
    m1_histogram_notre = m1_generate_bovw_spatial_histogram(im_notre, locations_notre, clusters_notre, division)

    m2_histogram_book = m2_generate_bovw_spatial_histogram(im_book, locations_book, clusters_book, division)
    m2_histogram_mount = m2_generate_bovw_spatial_histogram(im_mount, locations_mount, clusters_mount, division)
    m2_histogram_notre = m2_generate_bovw_spatial_histogram(im_notre, locations_notre, clusters_notre, division)

    # Students' implementations
    histogram_book = generate_bovw_spatial_histogram(im_book, locations_book, clusters_book, division)
    histogram_mount = generate_bovw_spatial_histogram(im_mount, locations_mount, clusters_mount, division)
    histogram_notre = generate_bovw_spatial_histogram(im_notre, locations_notre, clusters_notre, division)
    
    assert np.allclose(m1_histogram_book, m2_histogram_book)
    assert np.allclose(m1_histogram_book, histogram_book)
    print("PASS: Book")

    assert np.allclose(m1_histogram_mount, m2_histogram_mount)
    assert np.allclose(m1_histogram_mount, histogram_mount)
    print("PASS: Mount")

    assert np.allclose(m1_histogram_notre, m2_histogram_notre)
    assert np.allclose(m1_histogram_notre, histogram_notre)
    print("PASS: Notre")

    histograms.append( [m1_histogram_book, m1_histogram_mount, m1_histogram_notre] )
print("ALL PASS")
Testing division: [1, 1]
PASS: Book
PASS: Mount
PASS: Notre
Testing division: [2, 2]
PASS: Book
PASS: Mount
PASS: Notre
Testing division: [2, 3]
PASS: Book
PASS: Mount
PASS: Notre
ALL PASS

Save Output

In [12]:
np.save('data/question_3_histogram.npy', histograms)
D:\Anaconda3\envs\what\lib\site-packages\numpy\lib\npyio.py:521: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  arr = np.asanyarray(arr)
In [ ]:

</html>