mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-09 21:35:20 +08:00
Merge pull request #244 from pcampr/master
BUG: Fix histogram of gradients.
This commit is contained in:
@@ -108,3 +108,6 @@
|
||||
Adaptive thresholding
|
||||
Implementation of Matlab's `regionprops`
|
||||
Estimation of geometric transformation parameters
|
||||
|
||||
- Pavel Campr
|
||||
Fixes and tests for Histograms of Oriented Gradients.
|
||||
|
||||
+12
-7
@@ -59,7 +59,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8),
|
||||
shadowing and illumination variations.
|
||||
"""
|
||||
|
||||
if image.ndim > 3:
|
||||
if image.ndim > 2:
|
||||
raise ValueError("Currently only supports grey-level images")
|
||||
|
||||
if normalise:
|
||||
@@ -75,6 +75,11 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8),
|
||||
e.g. bar like structures in bicycles and limbs in humans.
|
||||
"""
|
||||
|
||||
if image.dtype.kind == 'u':
|
||||
# convert uint image to float
|
||||
# to avoid problems with subtracting unsigned numbers in np.diff()
|
||||
image = image.astype('float')
|
||||
|
||||
gx = np.zeros(image.shape)
|
||||
gy = np.zeros(image.shape)
|
||||
gx[:, :-1] = np.diff(image, n=1, axis=1)
|
||||
@@ -96,7 +101,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8),
|
||||
"""
|
||||
|
||||
magnitude = sqrt(gx**2 + gy**2)
|
||||
orientation = arctan2(gy, (gx + 1e-15)) * (180 / pi) + 90
|
||||
orientation = arctan2(gy, gx) * (180 / pi) % 180
|
||||
|
||||
sy, sx = image.shape
|
||||
cx, cy = pixels_per_cell
|
||||
@@ -113,11 +118,11 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8),
|
||||
# isolate orientations in this range
|
||||
|
||||
temp_ori = np.where(orientation < 180 / orientations * (i + 1),
|
||||
orientation, 0)
|
||||
orientation, -1)
|
||||
temp_ori = np.where(orientation >= 180 / orientations * i,
|
||||
temp_ori, 0)
|
||||
temp_ori, -1)
|
||||
# select magnitudes for those orientations
|
||||
cond2 = temp_ori > 0
|
||||
cond2 = temp_ori > -1
|
||||
temp_mag = np.where(cond2, magnitude, 0)
|
||||
|
||||
temp_filt = uniform_filter(temp_mag, size=(cy, cx))
|
||||
@@ -137,8 +142,8 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8),
|
||||
centre = tuple([y * cy + cy // 2, x * cx + cx // 2])
|
||||
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)
|
||||
rr, cc = draw.bresenham(centre[0] - dy, centre[1] - dx,
|
||||
centre[0] + dy, centre[1] + dx)
|
||||
hog_image[rr, cc] += orientation_histogram[y, x, o]
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from skimage import data
|
||||
from skimage import feature
|
||||
from skimage import img_as_float
|
||||
from skimage.draw import draw
|
||||
from numpy.testing import *
|
||||
|
||||
def test_histogram_of_oriented_gradients():
|
||||
img = img_as_float(data.lena()[:256, :].mean(axis=2))
|
||||
@@ -16,6 +20,115 @@ def test_hog_image_size_cell_size_mismatch():
|
||||
cells_per_block=(1, 1))
|
||||
assert len(fd) == 9 * (150 // 8) * (200 // 8)
|
||||
|
||||
def test_hog_color_image_unsupported_error():
|
||||
image = np.zeros((20, 20, 3))
|
||||
assert_raises(ValueError, feature.hog, image)
|
||||
|
||||
def test_hog_basic_orientations_and_data_types():
|
||||
# scenario:
|
||||
# 1) create image (with float values) where upper half is filled by zeros, bottom half by 100
|
||||
# 2) create unsigned integer version of this image
|
||||
# 3) calculate feature.hog() for both images, both with 'normalise' option enabled and disabled
|
||||
# 4) verify that all results are equal where expected
|
||||
# 5) verify that computed feature vector is as expected
|
||||
# 6) repeat the scenario for 90, 180 and 270 degrees rotated images
|
||||
|
||||
# size of testing image
|
||||
width = height = 35
|
||||
|
||||
image0 = np.zeros((height, width), dtype='float')
|
||||
image0[height / 2:] = 100
|
||||
|
||||
for rot in range(4):
|
||||
# rotate by 0, 90, 180 and 270 degrees
|
||||
image_float = np.rot90(image0, rot)
|
||||
|
||||
# create uint8 image from image_float
|
||||
image_uint8 = image_float.astype('uint8')
|
||||
|
||||
(hog_float, hog_img_float) = feature.hog(image_float, orientations=4, pixels_per_cell=(8, 8),
|
||||
cells_per_block=(1, 1), visualise=True, normalise=False)
|
||||
(hog_uint8, hog_img_uint8) = feature.hog(image_uint8, orientations=4, pixels_per_cell=(8, 8),
|
||||
cells_per_block=(1, 1), visualise=True, normalise=False)
|
||||
(hog_float_norm, hog_img_float_norm) = feature.hog(image_float, orientations=4, pixels_per_cell=(8, 8),
|
||||
cells_per_block=(1, 1), visualise=True, normalise=True)
|
||||
(hog_uint8_norm, hog_img_uint8_norm) = feature.hog(image_uint8, orientations=4, pixels_per_cell=(8, 8),
|
||||
cells_per_block=(1, 1), visualise=True, normalise=True)
|
||||
|
||||
# set to True to enable manual debugging with graphical output,
|
||||
# must be False for automatic testing
|
||||
if False:
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure()
|
||||
plt.subplot(2, 3, 1); plt.imshow(image_float); plt.colorbar(); plt.title('image')
|
||||
plt.subplot(2, 3, 2); plt.imshow(hog_img_float); plt.colorbar(); plt.title('HOG result visualisation (float img)')
|
||||
plt.subplot(2, 3, 5); plt.imshow(hog_img_uint8); plt.colorbar(); plt.title('HOG result visualisation (uint8 img)')
|
||||
plt.subplot(2, 3, 3); plt.imshow(hog_img_float_norm); plt.colorbar(); plt.title('HOG result (normalise) visualisation (float img)')
|
||||
plt.subplot(2, 3, 6); plt.imshow(hog_img_uint8_norm); plt.colorbar(); plt.title('HOG result (normalise) visualisation (uint8 img)')
|
||||
plt.show()
|
||||
|
||||
# results (features and visualisation) for float and uint8 images must be almost equal
|
||||
assert_almost_equal(hog_float, hog_uint8)
|
||||
assert_almost_equal(hog_img_float, hog_img_uint8)
|
||||
|
||||
# resulting features should be almost equal when 'normalise' is enabled or disabled (for current simple testing image)
|
||||
assert_almost_equal(hog_float, hog_float_norm, decimal=4)
|
||||
assert_almost_equal(hog_float, hog_uint8_norm, decimal=4)
|
||||
|
||||
# reshape resulting feature vector to matrix with 4 columns (each corresponding to one of 4 directions),
|
||||
# only one direction should contain nonzero values (this is manually determined for testing image)
|
||||
actual = np.max(hog_float.reshape(-1, 4), axis=0)
|
||||
|
||||
if rot in [0, 2]:
|
||||
# image is rotated by 0 and 180 degrees
|
||||
desired = [0, 0, 1, 0]
|
||||
elif rot in [1, 3]:
|
||||
# image is rotated by 90 and 270 degrees
|
||||
desired = [1, 0, 0, 0]
|
||||
else:
|
||||
raise Exception('Result is not determined for this rotation.')
|
||||
|
||||
assert_almost_equal(actual, desired, decimal=2)
|
||||
|
||||
def test_hog_orientations_circle():
|
||||
# scenario:
|
||||
# 1) create image with blurred circle in the middle
|
||||
# 2) calculate feature.hog()
|
||||
# 3) verify that the resulting feature vector contains uniformly distributed values for all orientations,
|
||||
# i.e. no orientation is lost or emphasized
|
||||
# 4) repeat the scenario for other 'orientations' option
|
||||
|
||||
# size of testing image
|
||||
width = height = 100
|
||||
|
||||
image = np.zeros((height, width))
|
||||
rr, cc = draw.circle(height/2, width/2, width/3)
|
||||
image[rr, cc] = 100
|
||||
image = ndimage.gaussian_filter(image, 2)
|
||||
|
||||
for orientations in range(2, 15):
|
||||
(hog, hog_img) = feature.hog(image, orientations=orientations, pixels_per_cell=(8, 8),
|
||||
cells_per_block=(1, 1), visualise=True, normalise=False)
|
||||
|
||||
# set to True to enable manual debugging with graphical output,
|
||||
# must be False for automatic testing
|
||||
if False:
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure()
|
||||
plt.subplot(1, 2, 1); plt.imshow(image); plt.colorbar(); plt.title('image_float')
|
||||
plt.subplot(1, 2, 2); plt.imshow(hog_img); plt.colorbar(); plt.title('HOG result visualisation, orientations=%d' % (orientations))
|
||||
plt.show()
|
||||
|
||||
# reshape resulting feature vector to matrix with N columns (each column corresponds to one direction),
|
||||
hog_matrix = hog.reshape(-1, orientations)
|
||||
|
||||
# compute mean values in the resulting feature vector for each direction,
|
||||
# these values should be almost equal to the global mean value (since the image contains a circle),
|
||||
# i.e. all directions have same contribution to the result
|
||||
actual = np.mean(hog_matrix, axis=0)
|
||||
desired = np.mean(hog_matrix)
|
||||
assert_almost_equal(actual, desired, decimal=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy.testing import run_module_suite
|
||||
run_module_suite()
|
||||
|
||||
Reference in New Issue
Block a user