diff --git a/skimage/data/lena_GRAY_U8_hog.npy b/skimage/data/lena_GRAY_U8_hog.npy new file mode 100644 index 00000000..71ebf61f Binary files /dev/null and b/skimage/data/lena_GRAY_U8_hog.npy differ diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index afb2b4e9..760102e7 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -5,7 +5,8 @@ from . import _hoghistogram def hog(image, orientations=9, pixels_per_cell=(8, 8), - cells_per_block=(3, 3), visualise=False, normalise=False): + cells_per_block=(3, 3), visualise=False, normalise=False, + feature_vector=True): """Extract Histogram of Oriented Gradients (HOG) for a given image. Compute a Histogram of Oriented Gradients (HOG) by @@ -31,6 +32,9 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), normalise : bool, optional Apply power law compression to normalise the image before processing. + feature_vector : bool, optional + Return the data as a feature vector by calling .ravel() on the result + just before returning. Returns ------- @@ -127,13 +131,11 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), orientations_arr = np.arange(orientations) dx_arr = radius * np.cos(orientations_arr / orientations * np.pi) dy_arr = radius * np.sin(orientations_arr / orientations * np.pi) - cr2 = cy + cy - cc2 = cx + cx hog_image = np.zeros((sy, sx), dtype=float) for x in range(n_cellsx): for y in range(n_cellsy): for o, dx, dy in zip(orientations_arr, dx_arr, dy_arr): - centre = tuple([y * cr2 // 2, x * cc2 // 2]) + centre = tuple([y * cy + cy // 2, x * cx + cx // 2]) rr, cc = draw.line(int(centre[0] - dx), int(centre[1] + dy), int(centre[0] + dx), @@ -171,8 +173,11 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), overlapping grid of blocks covering the detection window into a combined feature vector for use in the window classifier. """ + + if feature_vector: + normalised_blocks = normalised_blocks.ravel() if visualise: - return normalised_blocks.ravel(), hog_image + return normalised_blocks, hog_image else: - return normalised_blocks.ravel() + return normalised_blocks diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 3f8899dd..cd34caae 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -10,7 +10,9 @@ cdef float cell_hog(double[:, ::1] magnitude, float orientation_start, float orientation_end, int cell_columns, int cell_rows, int column_index, int row_index, - int size_columns, int size_rows) nogil: + int size_columns, int size_rows, + int range_rows_start, int range_rows_stop, + int range_columns_start, int range_columns_stop) nogil: """Calculation of the cell's HOG value Parameters @@ -35,22 +37,23 @@ cdef float cell_hog(double[:, ::1] magnitude, Number of columns. size_rows : int Number of rows. + range_rows_start : int + Start row of cell. + range_rows_stop : int + Stop row of cell. + range_columns_start : int + Start column of cell. + range_columns_stop : int + Stop column of cell Returns ------- total : float The total HOG value. """ - cdef int cell_column, cell_row, cell_row_index, cell_column_index, \ - range_columns_start, range_columns_stop, range_rows_start, \ - range_rows_stop - - range_rows_stop = cell_rows/2 - range_rows_start = -range_rows_stop - range_columns_stop = cell_columns/2 - range_columns_start = -range_columns_stop - + cdef int cell_column, cell_row, cell_row_index, cell_column_index cdef float total = 0. + for cell_row in range(range_rows_start, range_rows_stop): cell_row_index = row_index + cell_row if (cell_row_index < 0 or cell_row_index >= size_rows): @@ -67,7 +70,7 @@ cdef float cell_hog(double[:, ::1] magnitude, total += magnitude[cell_row_index, cell_column_index] - return total + return total / (cell_rows * cell_columns) def hog_histograms(double[:, ::1] gradient_columns, double[:, ::1] gradient_rows, @@ -106,7 +109,9 @@ def hog_histograms(double[:, ::1] gradient_columns, gradient_rows) cdef double[:, ::1] orientation = \ np.arctan2(gradient_rows, gradient_columns) * (180 / np.pi) % 180 - cdef int i, x, y, o, yi, xi, cc, cr, x0, y0 + cdef int i, x, y, o, yi, xi, cc, cr, x0, y0, \ + range_rows_start, range_rows_stop, \ + range_columns_start, range_columns_stop cdef float orientation_start, orientation_end, \ number_of_orientations_per_180 @@ -115,6 +120,10 @@ def hog_histograms(double[:, ::1] gradient_columns, cc = cell_rows * number_of_cells_rows cr = cell_columns * number_of_cells_columns number_of_orientations_per_180 = 180. / number_of_orientations + range_rows_stop = cell_rows/2 + range_rows_start = -range_rows_stop + range_columns_stop = cell_columns/2 + range_columns_start = -range_columns_stop with nogil: # compute orientations integral images @@ -134,7 +143,8 @@ def hog_histograms(double[:, ::1] gradient_columns, while x < cr: orientation_histogram[yi, xi, i] = cell_hog(magnitude, orientation, orientation_start, orientation_end, - cell_columns, cell_rows, x, y, size_columns, size_rows) + cell_columns, cell_rows, x, y, size_columns, size_rows, + range_rows_start, range_rows_stop, range_columns_start, range_columns_stop) xi += 1 x += cell_columns diff --git a/skimage/feature/tests/test_hog.py b/skimage/feature/tests/test_hog.py index 6112d726..3afc0d89 100644 --- a/skimage/feature/tests/test_hog.py +++ b/skimage/feature/tests/test_hog.py @@ -1,5 +1,7 @@ +import os import numpy as np from scipy import ndimage as ndi +import skimage as si from skimage import data from skimage import feature from skimage import img_as_float @@ -9,7 +11,7 @@ from numpy.testing import (assert_raises, ) -def test_histogram_of_oriented_gradients(): +def test_histogram_of_oriented_gradients_output_size(): img = img_as_float(data.astronaut()[:256, :].mean(axis=2)) fd = feature.hog(img, orientations=9, pixels_per_cell=(8, 8), @@ -18,6 +20,17 @@ def test_histogram_of_oriented_gradients(): assert len(fd) == 9 * (256 // 8) * (512 // 8) +def test_histogram_of_oriented_gradients_output_correctness(): + img = np.load(os.path.join(si.data_dir, 'lena_GRAY_U8.npy')) + correct_output = np.load(os.path.join(si.data_dir, 'lena_GRAY_U8_hog.npy')) + + output = feature.hog(img, orientations=9, pixels_per_cell=(8, 8), + cells_per_block=(3, 3), feature_vector=True, + normalise=False, visualise=False) + + assert_almost_equal(output, correct_output) + + def test_hog_image_size_cell_size_mismatch(): image = data.camera()[:150, :200] fd = feature.hog(image, orientations=9, pixels_per_cell=(8, 8),