from __future__ import print_function from builtins import zip from builtins import range from past.builtins import xrange import matplotlib import numpy as np from scipy.ndimage import uniform_filter def extract_features(imgs, feature_fns, verbose=False): """ Given pixel data for images and several feature functions that can operate on single images, apply all feature functions to all images, concatenating the feature vectors for each image and storing the features for all images in a single matrix. Inputs: - imgs: N x H X W X C array of pixel data for N images. - feature_fns: List of k feature functions. The ith feature function should take as input an H x W x D array and return a (one-dimensional) array of length F_i. - verbose: Boolean; if true, print progress. Returns: An array of shape (N, F_1 + ... + F_k) where each column is the concatenation of all features for a single image. """ num_images = imgs.shape[0] if num_images == 0: return np.array([]) # Use the first image to determine feature dimensions feature_dims = [] first_image_features = [] for feature_fn in feature_fns: feats = feature_fn(imgs[0].squeeze()) assert len(feats.shape) == 1, 'Feature functions must be one-dimensional' feature_dims.append(feats.size) first_image_features.append(feats) # Now that we know the dimensions of the features, we can allocate a single # big array to store all features as columns. total_feature_dim = sum(feature_dims) imgs_features = np.zeros((num_images, total_feature_dim)) imgs_features[0] = np.hstack(first_image_features).T # Extract features for the rest of the images. for i in range(1, num_images): idx = 0 for feature_fn, feature_dim in zip(feature_fns, feature_dims): next_idx = idx + feature_dim imgs_features[i, idx:next_idx] = feature_fn(imgs[i].squeeze()) idx = next_idx if verbose and i % 1000 == 999: print('Done extracting features for %d / %d images' % (i+1, num_images)) return imgs_features def rgb2gray(rgb): """Convert RGB image to grayscale Parameters: rgb : RGB image Returns: gray : grayscale image """ return np.dot(rgb[...,:3], [0.299, 0.587, 0.144]) def hog_feature(im): """Compute Histogram of Gradient (HOG) feature for an image Modified from skimage.feature.hog http://pydoc.net/Python/scikits-image/0.4.2/skimage.feature.hog Reference: Histograms of Oriented Gradients for Human Detection Navneet Dalal and Bill Triggs, CVPR 2005 Parameters: im : an input grayscale or rgb image Returns: feat: Histogram of Gradient (HOG) feature """ # convert rgb to grayscale if needed if im.ndim == 3: image = rgb2gray(im) else: image = np.at_least_2d(im) sx, sy = image.shape # image size orientations = 9 # number of gradient bins cx, cy = (8, 8) # pixels per cell gx = np.zeros(image.shape) gy = np.zeros(image.shape) gx[:, :-1] = np.diff(image, n=1, axis=1) # compute gradient on x-direction gy[:-1, :] = np.diff(image, n=1, axis=0) # compute gradient on y-direction grad_mag = np.sqrt(gx ** 2 + gy ** 2) # gradient magnitude grad_ori = np.arctan2(gy, (gx + 1e-15)) * (180 / np.pi) + 90 # gradient orientation n_cellsx = int(np.floor(sx / cx)) # number of cells in x n_cellsy = int(np.floor(sy / cy)) # number of cells in y # compute orientations integral images orientation_histogram = np.zeros((n_cellsx, n_cellsy, orientations)) for i in range(orientations): # create new integral image for this orientation # isolate orientations in this range temp_ori = np.where(grad_ori < 180 / orientations * (i + 1), grad_ori, 0) temp_ori = np.where(grad_ori >= 180 / orientations * i, temp_ori, 0) # select magnitudes for those orientations cond2 = temp_ori > 0 temp_mag = np.where(cond2, grad_mag, 0) orientation_histogram[:,:,i] = uniform_filter(temp_mag, size=(cx, cy))[round(cx/2)::cx, round(cy/2)::cy].T return orientation_histogram.ravel() def color_histogram_hsv(im, nbin=10, xmin=0, xmax=255, normalized=True): """ Compute color histogram for an image using hue. Inputs: - im: H x W x C array of pixel data for an RGB image. - nbin: Number of histogram bins. (default: 10) - xmin: Minimum pixel value (default: 0) - xmax: Maximum pixel value (default: 255) - normalized: Whether to normalize the histogram (default: True) Returns: 1D vector of length nbin giving the color histogram over the hue of the input image. """ ndim = im.ndim bins = np.linspace(xmin, xmax, nbin+1) hsv = matplotlib.colors.rgb_to_hsv(im/xmax) * xmax imhist, bin_edges = np.histogram(hsv[:,:,0], bins=bins, density=normalized) imhist = imhist * np.diff(bin_edges) # return histogram return imhist