From 5db89d365ede66c8dc01272bba282bde4ecac3f9 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Mon, 26 Sep 2011 00:57:55 -0700 Subject: [PATCH] ENH: Use new draw module to construct HOG image. --- scikits/image/feature/hog.py | 126 +++++++++++------------- scikits/image/feature/tests/test_hog.py | 6 +- 2 files changed, 64 insertions(+), 68 deletions(-) diff --git a/scikits/image/feature/hog.py b/scikits/image/feature/hog.py index 8826ddba..8b71f8a3 100644 --- a/scikits/image/feature/hog.py +++ b/scikits/image/feature/hog.py @@ -1,51 +1,42 @@ -""" -:author: Brian Holt, 2011 -:license: modified BSD -""" - import numpy as np from scipy import sqrt, pi, arctan2, cos, sin + +# XXX Replace with integral after merge from ..transform import sat_sum -def hog(image, n_orientations=9, pixels_per_cell=(8, 8), +def hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3), visualise=False, normalise=False): - """ Extract Histogram of Oriented Gradients (HOG) for a given image. + """Extract Histogram of Oriented Gradients (HOG) for a given image. Compute a Histogram of Oriented Gradients (HOG) by 1) (optional) global image normalisation 2) computing the gradient image in x and y 3) computing gradient histograms - 3) normalise across blocks - 4) flatten into a feature vector + 3) normalising across blocks + 4) flattening into a feature vector Parameters ---------- - image: ndarray, 2D - 2D image (greyscale) - - n_orientations : int - number of orientation bins - - pixels_per_cell : 2 tuple (int,int) - pixels per cell, size in pixels of a cell - + image : (M, N) ndarray + Input image (greyscale). + orientations : int + Number of orientation bins. + pixels_per_cell : 2 tuple (int, int) + Size (in pixels) of a cell. cells_per_block : 2 tuple (int,int) - cells per block, number of cells in each block - + Number of cells in each block. visualise : bool, optional - return an image of the HOG - + Also return an image of the HOG. normalise : bool, optional - apply power law compression to normalise the image before - processing + Apply power law compression to normalise the image before + processing. Returns ------- newarr : ndarray HOG for the image as a 1D (flattened) array. - - hog_image : PIL Image (if visualise=True) - A visualisation of the HOG image + hog_image : ndarray (if visualise=True) + A visualisation of the HOG image. References ---------- @@ -54,8 +45,8 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), * Dalal, N and Triggs, B, Histograms of Oriented Gradients for Human Detection, IEEE Computer Society Conference on Computer Vision and Pattern Recognition 2005 San Diego, CA, USA - """ + """ image = np.atleast_2d(image) """ @@ -68,9 +59,9 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), shadowing and illumination variations. """ - if image.ndim == 3: - # replace RGB with locally dominant colour channel - pass # TODO + if image.ndim > 3: + raise ValueError("Currently only supports grey-level images") + if normalise: image = sqrt(image) @@ -91,30 +82,31 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), """ The third stage aims to produce an encoding that is sensitive to - local image content while remaining resistant to small changes in pose - or appearance. The adopted method pools gradient orientation information - locally in the same way as the SIFT [Lowe 2004] feature. The image window - is divided into small spatial regions, called "cells". For each cell we - accumulate a local 1-D histogram of gradient or edge orientations over - all the pixels in the cell. This combined cell-level 1-D histogram - forms the basic "orientation histogram" representation. Each orientation - histogram divides the gradient angle range into a fixed number of - predetermined bins. The gradient magnitudes of the pixels in the cell - are used to vote into the orientation histogram. + local image content while remaining resistant to small changes in + pose or appearance. The adopted method pools gradient orientation + information locally in the same way as the SIFT [Lowe 2004] + feature. The image window is divided into small spatial regions, + called "cells". For each cell we accumulate a local 1-D histogram + of gradient or edge orientations over all the pixels in the + cell. This combined cell-level 1-D histogram forms the basic + "orientation histogram" representation. Each orientation histogram + divides the gradient angle range into a fixed number of + predetermined bins. The gradient magnitudes of the pixels in the + cell are used to vote into the orientation histogram. """ magnitude = sqrt(gx ** 2 + gy ** 2) orientation = arctan2(gy, (gx + 1e-15)) * (180 / pi) + 90 - # compute n_orientations integral images + # compute orientations integral images integral_images = [] - for i in range(0, n_orientations): + for i in range(orientations): #create new integral image for this orientation # isolate orientations in this range - temp_ori = np.where(orientation < 180 / n_orientations * (i + 1), + temp_ori = np.where(orientation < 180 / orientations * (i + 1), orientation, 0) - temp_ori = np.where(orientation >= 180 / n_orientations * i, + temp_ori = np.where(orientation >= 180 / orientations * i, temp_ori, 0) # select magnitudes for those orientations cond2 = temp_ori > 0 @@ -122,7 +114,7 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), #compute integral image integral = np.cumsum(np.cumsum(temp_mag, axis=0, dtype=float), - axis=1, dtype=float) + axis=1, dtype=float) integral_images.append(integral) sx, sy = image.shape @@ -133,19 +125,16 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), n_cellsy = int(np.floor(sy // cy)) # number of cells in y # now for each cell, compute the histogram - orientation_histogram = np.zeros((n_cellsx, n_cellsy, n_orientations)) + orientation_histogram = np.zeros((n_cellsx, n_cellsy, orientations)) radius = min(cx, cy) // 2 - 1 hog_image = None if visualise: - import Image - import ImageDraw - hog_image = Image.new("F", (sy, sx)) - draw = ImageDraw.Draw(hog_image) + hog_image = np.zeros((sy, sx), dtype=float) - for x in range(0, n_cellsx): - for y in range(0, n_cellsy): - for o in range(0, n_orientations): + for x in range(n_cellsx): + for y in range(n_cellsy): + for o in range(orientations): # compute the histogram from integral image orientation_histogram[x, y, o] = sat_sum(integral_images[o], y * cy, @@ -153,13 +142,18 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), (y + 1) * cy - 1, (x + 1) * cx - 1) - if visualise: + if visualise: + from scikits.image import draw + + for x in range(n_cellsx): + for y in range(n_cellsy): + for o in range(orientations): centre = tuple([y * cy + cy // 2, x * cx + cx // 2]) - dx = radius * cos(float(o) / n_orientations * np.pi) - dy = radius * sin(float(o) / n_orientations * np.pi) - draw.line([(centre[0] - dx, centre[1] - dy), - (centre[0] + dx, centre[1] + dy)], - fill=orientation_histogram[x, y, o]) + dx = radius * cos(float(o) / orientations * np.pi) + dy = radius * sin(float(o) / orientations * np.pi) + rr, cc = draw.bresenham(centre[0] - dx, centre[1] - dy, + centre[0] + dx, centre[1] + dy) + hog_image[rr, cc] += orientation_histogram[x, y, o] """ The fourth stage computes normalisation, which takes local groups of @@ -179,18 +173,18 @@ def hog(image, n_orientations=9, pixels_per_cell=(8, 8), n_blocksx = (n_cellsx - bx) + 1 n_blocksy = (n_cellsy - by) + 1 normalised_blocks = np.zeros((n_blocksx, n_blocksy, - bx, by, n_orientations)) + bx, by, orientations)) - for x in range(0, n_blocksx): - for y in range(0, n_blocksy): + for x in range(n_blocksx): + for y in range(n_blocksy): block = orientation_histogram[x:x + bx, y:y + by, :] eps = 1e-5 normalised_blocks[x, y, :] = block / sqrt(block.sum() ** 2 + eps) """ - The final step collects the HOG descriptors from all blocks of a dense - overlapping grid of blocks covering the detection window into a combined - feature vector for use in the window classifier + The final step collects the HOG descriptors from all blocks of a dense + overlapping grid of blocks covering the detection window into a combined + feature vector for use in the window classifier. """ if visualise: diff --git a/scikits/image/feature/tests/test_hog.py b/scikits/image/feature/tests/test_hog.py index b37e0e98..6f375cea 100644 --- a/scikits/image/feature/tests/test_hog.py +++ b/scikits/image/feature/tests/test_hog.py @@ -4,10 +4,12 @@ import scipy from scikits.image.feature import hog def test_histogram_of_oriented_gradients(): - img = scipy.lena().astype(np.int8) + # Replace with scikits.image.data.lena() after merge + img = scipy.misc.lena().astype(np.int8) - fd = hog(img, n_orientations=9, pixels_per_cell=(8, 8), + fd = hog(img, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(1, 1)) + assert len(fd) == 9 * (512//8) ** 2 if __name__ == '__main__':