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
|