Question

I am trying to make hand gesture recognition (similar to face recognition) using Principal Component Analysis(PCA) in python. I have a Test image and I want to get its nearest match from a set of Training images.

Here is my code:

import os, sys
import numpy as np
import PIL.Image as Image


def read_images(path, sz=None):
    c = 0
    X,y = [], []
    for dirname, dirnames, filenames in os.walk(path):
        for subdirname in dirnames:
            subject_path = os.path.join(dirname, subdirname)
            for filename in os.listdir(subject_path):
                try:
                    im = Image.open(os.path.join(subject_path, filename))
                    im = im.convert("L")
                    # resize to given size (if given)
                    if (sz is not None):
                        im = im.resize(sz, Image.ANTIALIAS)
                    X.append(np.asarray(im, dtype=np.uint8))
                    y.append(c)
                except IOError:
                    print "I/O error({0}): {1}".format(errno, strerror)
                except:
                    print "Unexpected error:", sys.exc_info()[0]
                    raise
            c = c+1
    return [X,y]


def asRowMatrix(X):
    if len(X) == 0:
        return np.array([])
    mat = np.empty((0, X[0].size), dtype=X[0].dtype)
    for row in X:
        mat = np.vstack((mat, np.asarray(row).reshape(1,-1)))
    return mat


def asColumnMatrix(X):
    if len(X) == 0:
        return np.array([])
    mat = np.empty((X[0].size, 0), dtype=X[0].dtype)
    for col in X:
        mat = np.hstack((mat, np.asarray(col).reshape(-1,1)))
    return mat


def pca(X, y, num_components=0):
    [n,d] = X.shape
    if (num_components <= 0) or (num_components>n):
        num_components = n
    mu = X.mean(axis=0)
    X = X - mu
    if n>d:
        C = np.dot(X.T,X)
        [eigenvalues,eigenvectors] = np.linalg.eigh(C)
    else:
        C = np.dot(X,X.T)
        [eigenvalues,eigenvectors] = np.linalg.eigh(C)
        eigenvectors = np.dot(X.T,eigenvectors)
        for i in xrange(n):
            eigenvectors[:,i] = eigenvectors[:,i]/np.linalg.norm(eigenvectors[:,i])
    # or simply perform an economy size decomposition
    # eigenvectors, eigenvalues, variance = np.linalg.svd(X.T, full_matrices=False)
    # sort eigenvectors descending by their eigenvalue
    idx = np.argsort(-eigenvalues)
    eigenvalues = eigenvalues[idx]
    eigenvectors = eigenvectors[:,idx]
    # select only num_components
    eigenvalues = eigenvalues[0:num_components].copy()
    eigenvectors = eigenvectors[:,0:num_components].copy()
    return [eigenvalues, eigenvectors, mu, X]


#Get eigenvalues, eigenvectors, mean and shifted images (Training)
[a, b] = read_images('C:\\Users\\Karim\\Desktop\\Training & Test images\\AT&T\\att_faces', (90,90))
[evalues, evectors, mean_image, shifted_images] = pca(asRowMatrix(a), b)


#Input(Test) image
input_image = Image.open('C:\\Users\\Karim\\Desktop\\Training & Test images\\AT&T\\Test\\4.pgm').convert('L').resize((90, 90))
input_image = np.asarray(input_image).flatten()


#Normalizing input image
shifted_in = input_image - mean_image


#Finding weights
w = evectors.T * shifted_images 
w = np.asarray(w)
w_in = evectors.T * shifted_in
w_in = np.asarray(w_in)


#Euclidean distance
df = np.asarray(w - w_in)                # the difference between the images
dst = np.sqrt(np.sum(df**2, axis=1))     # their euclidean distances

Now I have an array of distances dst containing the euclidean distance between the Test image and each image in the set of Training image.

How to get the image with the nearest (minimum) distance and its path (or subdirectory name)? Not the value of the minimum distance nor its index in the array dst

Was it helpful?

Solution

dst.argmin() will tell you the index of the element in dst which is smallest.

So the closest image would be

idx = dst.argmin()
closest = a[idx]

since a is a list of arrays representing training faces.

To display the closest image, you could use:

img = Image.fromarray(closest, 'L')
img.show()

To find the file path of the closest image, I would alter read_images to return a list of all the file paths, so it could be indexed just like the list of images.

def read_images(path, sz=None):
    X, y = [], []
    for dirname, dirnames, filenames in os.walk(path):
        for filename in filenames:
            subject_path = os.path.join(dirname, filename)
            try:
                im = Image.open(subject_path)
            except IOError as err:
                print "I/O error: {e}: {f}".format(e=err, f=subject_path)
            except:
                print "Unexpected error:", sys.exc_info()[0]
                raise
            else:
                im = im.convert("L")
                # resize to given size (if given)
                if (sz is not None):
                    im = im.resize(sz, Image.ANTIALIAS)
                X.append(np.asarray(im, dtype=np.uint8))
                y.append(subject_path)
    return [X, y]

Below, call it like this:

images, paths = read_images(TRAINING_DIR, (90, 90))

You can then obtain the full path to the closest image with

idx = dst.argmin()
filename = paths[idx]

If you want just the path to the subdirectory, use

os.path.dirname(filename)

And for the name of the subdirectory, use

os.path.basename(os.path.dirname(filename))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top