From f09907961a910e4c8ccba1899ea3dd9b2e66a4c0 Mon Sep 17 00:00:00 2001 From: Tim Sheerman-Chase Date: Wed, 19 Jun 2013 09:39:22 +0100 Subject: [PATCH 01/25] Initial attempt at optimising HOG features with cython --- skimage/feature/_hog.py | 20 ++------- skimage/feature/_hoghistogram.pyx | 70 +++++++++++++++++++++++++++++++ skimage/feature/setup.py | 3 ++ 3 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 skimage/feature/_hoghistogram.pyx diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 11f7099d..b9d62926 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -2,7 +2,7 @@ import numpy as np from scipy import sqrt, pi, arctan2, cos, sin from scipy.ndimage import uniform_filter from .._shared.utils import assert_nD - +import _hoghistogram def hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3), visualise=False, normalise=False): @@ -114,24 +114,10 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), 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_cellsy, n_cellsx, orientations)) - subsample = np.index_exp[cy // 2:cy * n_cellsy:cy, - cx // 2:cx * n_cellsx:cx] - for i in range(orientations): - # create new integral image for this orientation - # isolate orientations in this range - temp_ori = np.where(orientation < 180.0 / orientations * (i + 1), - orientation, -1) - temp_ori = np.where(orientation >= 180.0 / orientations * i, - temp_ori, -1) - # select magnitudes for those orientations - cond2 = temp_ori > -1 - temp_mag = np.where(cond2, magnitude, 0) - - temp_filt = uniform_filter(temp_mag, size=(cy, cx)) - orientation_histogram[:, :, i] = temp_filt[subsample] + _hoghistogram.HogHistograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, visualise, orientations, + orientation_histogram) # now for each cell, compute the histogram hog_image = None diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx new file mode 100644 index 00000000..96112730 --- /dev/null +++ b/skimage/feature/_hoghistogram.pyx @@ -0,0 +1,70 @@ +# cython: profile=True +# cython: cdivision=True +# cython: boundscheck=False +# cython: wraparound=False + +import cmath, math +cimport numpy as np +import numpy as np +from scipy import pi, arctan2, cos, sin + +cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, + np.ndarray[np.float64_t, ndim=2] orientation, + float ori1, float ori2, + int cx, int cy, int xi, int yi, int sx, int sy): + cdef int cx1, cy1 + + cdef float total = 0. + for cy1 in range(-cy/2, cy/2): + for cx1 in range(-cx/2, cx/2): + if yi + cy1 < 0: continue + if yi + cy1 >= sy: continue + if xi + cx1 < 0: continue + if xi + cx1 >= sx: continue + if orientation[yi + cy1, xi + cx1] >= ori1: continue + if orientation[yi + cy1, xi + cx1] < ori2: continue + + total += magnitude[yi + cy1, xi + cx1] + + return total + +def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, \ + np.ndarray[np.float64_t, ndim=2] gy, + int cx, int cy, #Pixels per cell + int sx, int sy, #Image size + int n_cellsx, int n_cellsy, + int visualise, int orientations, + np.ndarray[np.float64_t, ndim=3] orientation_histogram): + + cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.sqrt(gx**2 + gy**2) + cdef np.ndarray[np.float64_t, ndim=2] orientation = arctan2(gy, gx) * (180 / pi) % 180 + cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 + cdef float ori1, ori2 + + # compute orientations integral images + + for i in range(orientations): + # isolate orientations in this range + + ori1 = 180. / orientations * (i + 1) + ori2 = 180. / orientations * i + + y = cy / 2 + cy2 = cy * n_cellsy + x = cx / 2 + cx2 = cx * n_cellsx + yi = 0 + xi = 0 + + while y < cy2: + xi = 0 + x = cx / 2 + + while x < cx2: + orientation_histogram[yi, xi, i] = CellHog(magnitude, orientation, ori1, ori2, cx, cy, x, y, sx, sy) + xi += 1 + x += cx + + yi += 1 + y += cy + diff --git a/skimage/feature/setup.py b/skimage/feature/setup.py index d7be3dcf..62bd950b 100644 --- a/skimage/feature/setup.py +++ b/skimage/feature/setup.py @@ -18,6 +18,7 @@ def configuration(parent_package='', top_path=None): cython(['brief_cy.pyx'], working_path=base_path) cython(['_texture.pyx'], working_path=base_path) cython(['_hessian_det_appx.pyx'], working_path=base_path) + cython(['_hoghistogram.pyx'], working_path=base_path) config.add_extension('corner_cy', sources=['corner_cy.c'], include_dirs=[get_numpy_include_dirs()]) @@ -31,6 +32,8 @@ def configuration(parent_package='', top_path=None): include_dirs=[get_numpy_include_dirs(), '../_shared']) config.add_extension('_hessian_det_appx', sources=['_hessian_det_appx.c'], include_dirs=[get_numpy_include_dirs()]) + config.add_extension('_hoghistogram', sources=['_hoghistogram.c'], + include_dirs=[get_numpy_include_dirs(), '../_shared']) return config From d55135ddbb64dba7b1d1c948bed18719a77430ea Mon Sep 17 00:00:00 2001 From: Tim Sheerman-Chase Date: Wed, 19 Jun 2013 09:45:26 +0100 Subject: [PATCH 02/25] Fix comments and tab whitespace --- skimage/feature/_hog.py | 1 + skimage/feature/_hoghistogram.pyx | 90 +++++++++++++++---------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index b9d62926..64ef3bdf 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -114,6 +114,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), 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_cellsy, n_cellsx, orientations)) _hoghistogram.HogHistograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, visualise, orientations, diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 96112730..df38f945 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -9,62 +9,62 @@ import numpy as np from scipy import pi, arctan2, cos, sin cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, - np.ndarray[np.float64_t, ndim=2] orientation, - float ori1, float ori2, - int cx, int cy, int xi, int yi, int sx, int sy): - cdef int cx1, cy1 + np.ndarray[np.float64_t, ndim=2] orientation, + float ori1, float ori2, + int cx, int cy, int xi, int yi, int sx, int sy): + cdef int cx1, cy1 - cdef float total = 0. - for cy1 in range(-cy/2, cy/2): - for cx1 in range(-cx/2, cx/2): - if yi + cy1 < 0: continue - if yi + cy1 >= sy: continue - if xi + cx1 < 0: continue - if xi + cx1 >= sx: continue - if orientation[yi + cy1, xi + cx1] >= ori1: continue - if orientation[yi + cy1, xi + cx1] < ori2: continue + cdef float total = 0. + for cy1 in range(-cy/2, cy/2): + for cx1 in range(-cx/2, cx/2): + if yi + cy1 < 0: continue + if yi + cy1 >= sy: continue + if xi + cx1 < 0: continue + if xi + cx1 >= sx: continue + if orientation[yi + cy1, xi + cx1] >= ori1: continue + if orientation[yi + cy1, xi + cx1] < ori2: continue - total += magnitude[yi + cy1, xi + cx1] + total += magnitude[yi + cy1, xi + cx1] - return total + return total def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, \ - np.ndarray[np.float64_t, ndim=2] gy, - int cx, int cy, #Pixels per cell - int sx, int sy, #Image size - int n_cellsx, int n_cellsy, - int visualise, int orientations, - np.ndarray[np.float64_t, ndim=3] orientation_histogram): + np.ndarray[np.float64_t, ndim=2] gy, + int cx, int cy, #Pixels per cell + int sx, int sy, #Image size + int n_cellsx, int n_cellsy, + int visualise, int orientations, + np.ndarray[np.float64_t, ndim=3] orientation_histogram): - cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.sqrt(gx**2 + gy**2) - cdef np.ndarray[np.float64_t, ndim=2] orientation = arctan2(gy, gx) * (180 / pi) % 180 - cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 - cdef float ori1, ori2 + cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.sqrt(gx**2 + gy**2) + cdef np.ndarray[np.float64_t, ndim=2] orientation = arctan2(gy, gx) * (180 / pi) % 180 + cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 + cdef float ori1, ori2 - # compute orientations integral images + # compute orientations integral images - for i in range(orientations): - # isolate orientations in this range + for i in range(orientations): + # isolate orientations in this range - ori1 = 180. / orientations * (i + 1) - ori2 = 180. / orientations * i + ori1 = 180. / orientations * (i + 1) + ori2 = 180. / orientations * i - y = cy / 2 - cy2 = cy * n_cellsy - x = cx / 2 - cx2 = cx * n_cellsx - yi = 0 - xi = 0 + y = cy / 2 + cy2 = cy * n_cellsy + x = cx / 2 + cx2 = cx * n_cellsx + yi = 0 + xi = 0 - while y < cy2: - xi = 0 - x = cx / 2 + while y < cy2: + xi = 0 + x = cx / 2 - while x < cx2: - orientation_histogram[yi, xi, i] = CellHog(magnitude, orientation, ori1, ori2, cx, cy, x, y, sx, sy) - xi += 1 - x += cx + while x < cx2: + orientation_histogram[yi, xi, i] = CellHog(magnitude, orientation, ori1, ori2, cx, cy, x, y, sx, sy) + xi += 1 + x += cx - yi += 1 - y += cy + yi += 1 + y += cy From 2e56e685d8eda467c8e5bcf619acde5eb9ff730b Mon Sep 17 00:00:00 2001 From: Tim Sheerman-Chase Date: Wed, 10 Jul 2013 19:06:52 +0100 Subject: [PATCH 03/25] change import syntax for python 3 --- skimage/feature/_hog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 64ef3bdf..e9bfdeb6 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -2,7 +2,7 @@ import numpy as np from scipy import sqrt, pi, arctan2, cos, sin from scipy.ndimage import uniform_filter from .._shared.utils import assert_nD -import _hoghistogram +from . import _hoghistogram def hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3), visualise=False, normalise=False): From 5aad171d132d958c757506fd10159694d2091899 Mon Sep 17 00:00:00 2001 From: Tim Sheerman-Chase Date: Wed, 10 Jul 2013 19:58:11 +0100 Subject: [PATCH 04/25] Add _hoghistogram.pyx to bento.info --- bento.info | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bento.info b/bento.info index cba403d9..feb1116b 100644 --- a/bento.info +++ b/bento.info @@ -98,6 +98,9 @@ Library: Extension: skimage.feature.corner_cy Sources: skimage/feature/corner_cy.pyx + Extension: skimage.feature._hoghistogram + Sources: + skimage/feature/_hoghistogram.pyx Extension: skimage.feature._texture Sources: skimage/feature/_texture.pyx From c756d2d4927369352c3f50f3a23bb3f8137f24e6 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Mon, 15 Jun 2015 11:49:21 +0200 Subject: [PATCH 05/25] Partially fulfilled code review. --- skimage/feature/_hog.py | 4 +- skimage/feature/_hoghistogram.pyx | 85 ++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index e9bfdeb6..7fafc10b 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -117,8 +117,8 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), # compute orientations integral images orientation_histogram = np.zeros((n_cellsy, n_cellsx, orientations)) - _hoghistogram.HogHistograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, visualise, orientations, - orientation_histogram) + _hoghistogram.HogHistograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, + visualise, orientations, orientation_histogram) # now for each cell, compute the histogram hog_image = None diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index df38f945..42df311f 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -1,43 +1,99 @@ -# cython: profile=True # cython: cdivision=True # cython: boundscheck=False # cython: wraparound=False -import cmath, math -cimport numpy as np import numpy as np -from scipy import pi, arctan2, cos, sin +cimport numpy as np +# cnp.float64_t[:, :] magnitude cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, np.ndarray[np.float64_t, ndim=2] orientation, float ori1, float ori2, int cx, int cy, int xi, int yi, int sx, int sy): + """CellHog + + Parameters + ---------- + magnitude : ndarray + Coordinate to be clipped. + orientation : ndarray + The lower bound. + ori1 : float + The higher bound. + ori2 : float + The higher bound. + cx : int + The higher bound. + cy : int + The higher bound. + xi : int + The higher bound. + yi : int + The higher bound. + sx : int + The higher bound. + sy : int + The higher bound. + + Returns + ------- + total : float + The total HOG value. + """ cdef int cx1, cy1 cdef float total = 0. for cy1 in range(-cy/2, cy/2): for cx1 in range(-cx/2, cx/2): - if yi + cy1 < 0: continue - if yi + cy1 >= sy: continue - if xi + cx1 < 0: continue - if xi + cx1 >= sx: continue - if orientation[yi + cy1, xi + cx1] >= ori1: continue - if orientation[yi + cy1, xi + cx1] < ori2: continue + if (yi + cy1 < 0 + or yi + cy1 >= sy + or xi + cx1 < 0 + or xi + cx1 >= sx + or orientation[yi + cy1, xi + cx1] >= ori1 + or orientation[yi + cy1, xi + cx1] < ori2): continue total += magnitude[yi + cy1, xi + cx1] return total -def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, \ +def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, np.ndarray[np.float64_t, ndim=2] gy, int cx, int cy, #Pixels per cell int sx, int sy, #Image size int n_cellsx, int n_cellsy, int visualise, int orientations, np.ndarray[np.float64_t, ndim=3] orientation_histogram): + """HogHistograms - cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.sqrt(gx**2 + gy**2) - cdef np.ndarray[np.float64_t, ndim=2] orientation = arctan2(gy, gx) * (180 / pi) % 180 + Parameters + ---------- + gx : ndarray + Coordinate to be clipped. + gy : ndarray + The lower bound. + cx : int + The higher bound. + cy : int + The higher bound. + sx : int + The higher bound. + sy : int + The higher bound. + n_cellsx : int + The higher bound. + n_cellsy : int + The higher bound. + visualise : int + The higher bound. + orientations : int + The higher bound. + orientation_histogram : ndarray + The histogram to fill. + """ + + cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.hypot(gx, gy) + cdef np.ndarray[np.float64_t, ndim=2] orientation = ( + np.arctan2(gy, gx) * (180 / np.pi) % 180) cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 cdef float ori1, ori2 @@ -61,7 +117,8 @@ def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, \ x = cx / 2 while x < cx2: - orientation_histogram[yi, xi, i] = CellHog(magnitude, orientation, ori1, ori2, cx, cy, x, y, sx, sy) + orientation_histogram[yi, xi, i] = CellHog(magnitude, + orientation, ori1, ori2, cx, cy, x, y, sx, sy) xi += 1 x += cx From 3719ff216cb585a854d14d11394d815e103a6172 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Mon, 15 Jun 2015 13:14:34 +0200 Subject: [PATCH 06/25] Updated docstrings. --- skimage/feature/_hog.py | 13 ++++----- skimage/feature/_hoghistogram.pyx | 46 +++++++++++++++---------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 7fafc10b..51d0d060 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -1,5 +1,4 @@ import numpy as np -from scipy import sqrt, pi, arctan2, cos, sin from scipy.ndimage import uniform_filter from .._shared.utils import assert_nD from . import _hoghistogram @@ -63,7 +62,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), assert_nD(image, 2) if normalise: - image = sqrt(image) + image = np.sqrt(image) """ The second stage computes first order image gradients. These capture @@ -104,8 +103,8 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), cell are used to vote into the orientation histogram. """ - magnitude = sqrt(gx ** 2 + gy ** 2) - orientation = arctan2(gy, gx) * (180 / pi) % 180 + magnitude = np.hypot(gx, gy) + orientation = np.arctan2(gy, gx) * (180 / np.pi) % 180 sy, sx = image.shape cx, cy = pixels_per_cell @@ -132,8 +131,8 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), 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) / orientations * np.pi) - dy = radius * sin(float(o) / orientations * np.pi) + dx = radius * np.cos(float(o) / orientations * np.pi) + dy = radius * np.sin(float(o) / orientations * np.pi) rr, cc = draw.line(int(centre[0] - dx), int(centre[1] + dy), int(centre[0] + dx), @@ -164,7 +163,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), for y in range(n_blocksy): block = orientation_histogram[y:y + by, x:x + bx, :] eps = 1e-5 - normalised_blocks[y, x, :] = block / sqrt(block.sum() ** 2 + eps) + normalised_blocks[y, x, :] = block / np.sqrt(block.sum() ** 2 + eps) """ The final step collects the HOG descriptors from all blocks of a dense diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 42df311f..77c63f4b 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -15,25 +15,25 @@ cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, Parameters ---------- magnitude : ndarray - Coordinate to be clipped. + The gradient magnitudes of the pixels. orientation : ndarray - The lower bound. + Lookup table for orientations. ori1 : float - The higher bound. + Orientation range start. ori2 : float - The higher bound. + Orientation range end. cx : int - The higher bound. + Pixels per cell (x). cy : int - The higher bound. + Pixels per cell (y). xi : int - The higher bound. + Block index (x). yi : int - The higher bound. + Block index (y). sx : int - The higher bound. + Image size (x). sy : int - The higher bound. + Image size (y). Returns ------- @@ -58,35 +58,35 @@ cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, np.ndarray[np.float64_t, ndim=2] gy, - int cx, int cy, #Pixels per cell - int sx, int sy, #Image size + int cx, int cy, + int sx, int sy, int n_cellsx, int n_cellsy, int visualise, int orientations, np.ndarray[np.float64_t, ndim=3] orientation_histogram): - """HogHistograms + """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters ---------- gx : ndarray - Coordinate to be clipped. + First order image gradients (x). gy : ndarray - The lower bound. + First order image gradients (y). cx : int - The higher bound. + Pixels per cell (x). cy : int - The higher bound. + Pixels per cell (y). sx : int - The higher bound. + Image size (x). sy : int - The higher bound. + Image size (y). n_cellsx : int - The higher bound. + Number of cells (x). n_cellsy : int - The higher bound. + Number of cells (y). visualise : int - The higher bound. + Also return an image of the HOG. orientations : int - The higher bound. + Number of orientation bins. orientation_histogram : ndarray The histogram to fill. """ From aedf63f1f7abe9cd164d994e6c58cc4282ce740a Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Mon, 15 Jun 2015 15:25:30 +0200 Subject: [PATCH 07/25] Switched to cnp where applicable. --- skimage/feature/_hoghistogram.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 77c63f4b..5e181058 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -3,11 +3,11 @@ # cython: wraparound=False import numpy as np -cimport numpy as np +cimport numpy as cnp # cnp.float64_t[:, :] magnitude -cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, - np.ndarray[np.float64_t, ndim=2] orientation, +cdef float CellHog(cnp.ndarray[cnp.float64_t, ndim=2] magnitude, + cnp.ndarray[cnp.float64_t, ndim=2] orientation, float ori1, float ori2, int cx, int cy, int xi, int yi, int sx, int sy): """CellHog @@ -56,13 +56,13 @@ cdef float CellHog(np.ndarray[np.float64_t, ndim=2] magnitude, return total -def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, - np.ndarray[np.float64_t, ndim=2] gy, +def HogHistograms(cnp.ndarray[cnp.float64_t, ndim=2] gx, + cnp.ndarray[cnp.float64_t, ndim=2] gy, int cx, int cy, int sx, int sy, int n_cellsx, int n_cellsy, int visualise, int orientations, - np.ndarray[np.float64_t, ndim=3] orientation_histogram): + cnp.ndarray[cnp.float64_t, ndim=3] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters @@ -91,8 +91,8 @@ def HogHistograms(np.ndarray[np.float64_t, ndim=2] gx, The histogram to fill. """ - cdef np.ndarray[np.float64_t, ndim=2] magnitude = np.hypot(gx, gy) - cdef np.ndarray[np.float64_t, ndim=2] orientation = ( + cdef cnp.ndarray[cnp.float64_t, ndim=2] magnitude = np.hypot(gx, gy) + cdef cnp.ndarray[cnp.float64_t, ndim=2] orientation = ( np.arctan2(gy, gx) * (180 / np.pi) % 180) cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 cdef float ori1, ori2 From ecdf71d14d7d5bd92c7bc113cb693efe5894c9b2 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Mon, 15 Jun 2015 15:41:18 +0200 Subject: [PATCH 08/25] Switched to memory view syntax. --- skimage/feature/_hoghistogram.pyx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 5e181058..d6d9732d 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -5,9 +5,8 @@ import numpy as np cimport numpy as cnp -# cnp.float64_t[:, :] magnitude -cdef float CellHog(cnp.ndarray[cnp.float64_t, ndim=2] magnitude, - cnp.ndarray[cnp.float64_t, ndim=2] orientation, +cdef float CellHog(cnp.float64_t[:, :] magnitude, + cnp.float64_t[:, :] orientation, float ori1, float ori2, int cx, int cy, int xi, int yi, int sx, int sy): """CellHog @@ -56,13 +55,13 @@ cdef float CellHog(cnp.ndarray[cnp.float64_t, ndim=2] magnitude, return total -def HogHistograms(cnp.ndarray[cnp.float64_t, ndim=2] gx, - cnp.ndarray[cnp.float64_t, ndim=2] gy, +def HogHistograms(cnp.float64_t[:, :] gx, + cnp.float64_t[:, :] gy, int cx, int cy, int sx, int sy, int n_cellsx, int n_cellsy, int visualise, int orientations, - cnp.ndarray[cnp.float64_t, ndim=3] orientation_histogram): + cnp.float64_t[:, :, :] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters @@ -91,9 +90,8 @@ def HogHistograms(cnp.ndarray[cnp.float64_t, ndim=2] gx, The histogram to fill. """ - cdef cnp.ndarray[cnp.float64_t, ndim=2] magnitude = np.hypot(gx, gy) - cdef cnp.ndarray[cnp.float64_t, ndim=2] orientation = ( - np.arctan2(gy, gx) * (180 / np.pi) % 180) + cdef cnp.float64_t[:, :] magnitude = np.hypot(gx, gy) + cdef cnp.float64_t[:, :] orientation = np.arctan2(gy, gx) * (180 / np.pi) % 180 cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 cdef float ori1, ori2 From 502377e9cec9788657369c1dd4eb37edd6cc33d3 Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Mon, 15 Jun 2015 22:49:20 +0200 Subject: [PATCH 09/25] Update _hoghistogram.pyx Made the function names PEP8 compatible and added another level of indentation. I also tried renaming some things where I considered columns useful, but for example cells per row isn't entirely accurate. Any suggestions? --- skimage/feature/_hoghistogram.pyx | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index d6d9732d..4d3e2dfe 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -5,11 +5,13 @@ import numpy as np cimport numpy as cnp -cdef float CellHog(cnp.float64_t[:, :] magnitude, - cnp.float64_t[:, :] orientation, - float ori1, float ori2, - int cx, int cy, int xi, int yi, int sx, int sy): - """CellHog +cdef float cell_hog(cnp.float64_t[:, :] magnitude, + cnp.float64_t[:, :] orientation, + float ori1, float ori2, + int cx, int cy, + int xi, int yi, + int sx, int sy): + """Calculation of the cell's HOG value Parameters ---------- @@ -26,13 +28,13 @@ cdef float CellHog(cnp.float64_t[:, :] magnitude, cy : int Pixels per cell (y). xi : int - Block index (x). + Block column index. yi : int - Block index (y). + Block row index. sx : int - Image size (x). + Number of columns. sy : int - Image size (y). + Number of rows. Returns ------- @@ -55,13 +57,13 @@ cdef float CellHog(cnp.float64_t[:, :] magnitude, return total -def HogHistograms(cnp.float64_t[:, :] gx, - cnp.float64_t[:, :] gy, - int cx, int cy, - int sx, int sy, - int n_cellsx, int n_cellsy, - int visualise, int orientations, - cnp.float64_t[:, :, :] orientation_histogram): +def hog_histograms(cnp.float64_t[:, :] gx, + cnp.float64_t[:, :] gy, + int cx, int cy, + int sx, int sy, + int n_cellsx, int n_cellsy, + int visualise, int orientations, + cnp.float64_t[:, :, :] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters @@ -75,9 +77,9 @@ def HogHistograms(cnp.float64_t[:, :] gx, cy : int Pixels per cell (y). sx : int - Image size (x). + Number of columns. sy : int - Image size (y). + Number of rows. n_cellsx : int Number of cells (x). n_cellsy : int From c37fc9ec200b767c1dfffb95a9f465239b00ab3d Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Mon, 15 Jun 2015 22:51:30 +0200 Subject: [PATCH 10/25] Update _hog.py Made hog_histograms snake case --- skimage/feature/_hog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 51d0d060..40175066 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -116,7 +116,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), # compute orientations integral images orientation_histogram = np.zeros((n_cellsy, n_cellsx, orientations)) - _hoghistogram.HogHistograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, + _hoghistogram.hog_histograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, visualise, orientations, orientation_histogram) # now for each cell, compute the histogram From 8ab6e63e75852c36772c6f4793277b30528ac3c7 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Tue, 16 Jun 2015 09:36:02 +0200 Subject: [PATCH 11/25] Removed unused import, applied pep8 formatting and corrected a method call that should have been refactored. --- skimage/feature/_hog.py | 6 +++--- skimage/feature/_hoghistogram.pyx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 40175066..98ffb32d 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -1,8 +1,8 @@ import numpy as np -from scipy.ndimage import uniform_filter from .._shared.utils import assert_nD from . import _hoghistogram + 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. @@ -116,8 +116,8 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), # compute orientations integral images orientation_histogram = np.zeros((n_cellsy, n_cellsx, orientations)) - _hoghistogram.hog_histograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, - visualise, orientations, orientation_histogram) + _hoghistogram.hog_histograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, + visualise, orientations, orientation_histogram) # now for each cell, compute the histogram hog_image = None diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 4d3e2dfe..76ad2add 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -117,7 +117,7 @@ def hog_histograms(cnp.float64_t[:, :] gx, x = cx / 2 while x < cx2: - orientation_histogram[yi, xi, i] = CellHog(magnitude, + orientation_histogram[yi, xi, i] = cell_hog(magnitude, orientation, ori1, ori2, cx, cy, x, y, sx, sy) xi += 1 x += cx From 78cf5a0f9a2af8c5cbf41711a92c85a77cbab582 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Tue, 16 Jun 2015 09:40:04 +0200 Subject: [PATCH 12/25] Removed unused code. --- skimage/feature/_hog.py | 5 +---- skimage/feature/_hoghistogram.pyx | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 98ffb32d..877117bb 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -103,9 +103,6 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), cell are used to vote into the orientation histogram. """ - magnitude = np.hypot(gx, gy) - orientation = np.arctan2(gy, gx) * (180 / np.pi) % 180 - sy, sx = image.shape cx, cy = pixels_per_cell bx, by = cells_per_block @@ -117,7 +114,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), orientation_histogram = np.zeros((n_cellsy, n_cellsx, orientations)) _hoghistogram.hog_histograms(gx, gy, cx, cy, sx, sy, n_cellsx, n_cellsy, - visualise, orientations, orientation_histogram) + orientations, orientation_histogram) # now for each cell, compute the histogram hog_image = None diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 76ad2add..cec180f7 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -62,7 +62,7 @@ def hog_histograms(cnp.float64_t[:, :] gx, int cx, int cy, int sx, int sy, int n_cellsx, int n_cellsy, - int visualise, int orientations, + int orientations, cnp.float64_t[:, :, :] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. @@ -84,8 +84,6 @@ def hog_histograms(cnp.float64_t[:, :] gx, Number of cells (x). n_cellsy : int Number of cells (y). - visualise : int - Also return an image of the HOG. orientations : int Number of orientation bins. orientation_histogram : ndarray From 605eac2e8cf2e320125a0a50296e52b672a671eb Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Tue, 16 Jun 2015 09:54:54 +0200 Subject: [PATCH 13/25] Renamed to use row/column naming convention. --- skimage/feature/_hoghistogram.pyx | 102 +++++++++++++++--------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index cec180f7..02a6c92e 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -7,10 +7,10 @@ cimport numpy as cnp cdef float cell_hog(cnp.float64_t[:, :] magnitude, cnp.float64_t[:, :] orientation, - float ori1, float ori2, - int cx, int cy, - int xi, int yi, - int sx, int sy): + float orientation_start, float orientation_end, + int cell_columns, int cell_rows, + int column_index, int row_index, + int size_columns, int size_rows): """Calculation of the cell's HOG value Parameters @@ -19,21 +19,21 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, The gradient magnitudes of the pixels. orientation : ndarray Lookup table for orientations. - ori1 : float + orientation_start : float Orientation range start. - ori2 : float + orientation_end : float Orientation range end. - cx : int + cell_columns : int Pixels per cell (x). - cy : int + cell_rows : int Pixels per cell (y). - xi : int + column_index : int Block column index. - yi : int + row_index : int Block row index. - sx : int + size_columns : int Number of columns. - sy : int + size_rows : int Number of rows. Returns @@ -41,85 +41,85 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, total : float The total HOG value. """ - cdef int cx1, cy1 + cdef int cell_column, cell_row cdef float total = 0. - for cy1 in range(-cy/2, cy/2): - for cx1 in range(-cx/2, cx/2): - if (yi + cy1 < 0 - or yi + cy1 >= sy - or xi + cx1 < 0 - or xi + cx1 >= sx - or orientation[yi + cy1, xi + cx1] >= ori1 - or orientation[yi + cy1, xi + cx1] < ori2): continue + for cell_row in range(-cell_rows/2, cell_rows/2): + for cell_column in range(-cell_columns/2, cell_columns/2): + if (row_index + cell_row < 0 + or row_index + cell_row >= size_rows + or column_index + cell_column < 0 + or column_index + cell_column >= size_columns + or orientation[row_index + cell_row, column_index + cell_column] >= orientation_start + or orientation[row_index + cell_row, column_index + cell_column] < orientation_end): continue - total += magnitude[yi + cy1, xi + cx1] + total += magnitude[row_index + cell_row, column_index + cell_column] return total -def hog_histograms(cnp.float64_t[:, :] gx, - cnp.float64_t[:, :] gy, - int cx, int cy, - int sx, int sy, - int n_cellsx, int n_cellsy, - int orientations, +def hog_histograms(cnp.float64_t[:, :] gradient_columns, + cnp.float64_t[:, :] gradient_rows, + int cell_columns, int cell_rows, + int size_columns, int size_rows, + int number_of_cells_columns, int number_of_cells_rows, + int number_of_orientations, cnp.float64_t[:, :, :] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters ---------- - gx : ndarray + gradient_columns : ndarray First order image gradients (x). - gy : ndarray + gradient_rows : ndarray First order image gradients (y). - cx : int + cell_columns : int Pixels per cell (x). - cy : int + cell_rows : int Pixels per cell (y). - sx : int + size_columns : int Number of columns. - sy : int + size_rows : int Number of rows. - n_cellsx : int + number_of_cells_columns : int Number of cells (x). - n_cellsy : int + number_of_cells_rows : int Number of cells (y). - orientations : int + number_of_orientations : int Number of orientation bins. orientation_histogram : ndarray The histogram to fill. """ - cdef cnp.float64_t[:, :] magnitude = np.hypot(gx, gy) - cdef cnp.float64_t[:, :] orientation = np.arctan2(gy, gx) * (180 / np.pi) % 180 + cdef cnp.float64_t[:, :] magnitude = np.hypot(gradient_columns, gradient_rows) + cdef cnp.float64_t[:, :] orientation = np.arctan2(gradient_rows, gradient_columns) * (180 / np.pi) % 180 cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 - cdef float ori1, ori2 + cdef float orientation_start, orientation_end # compute orientations integral images - for i in range(orientations): + for i in range(number_of_orientations): # isolate orientations in this range - ori1 = 180. / orientations * (i + 1) - ori2 = 180. / orientations * i + orientation_start = 180. / number_of_orientations * (i + 1) + orientation_end = 180. / number_of_orientations * i - y = cy / 2 - cy2 = cy * n_cellsy - x = cx / 2 - cx2 = cx * n_cellsx + y = cell_rows / 2 + cy2 = cell_rows * number_of_cells_rows + x = cell_columns / 2 + cx2 = cell_columns * number_of_cells_columns yi = 0 xi = 0 while y < cy2: xi = 0 - x = cx / 2 + x = cell_columns / 2 while x < cx2: orientation_histogram[yi, xi, i] = cell_hog(magnitude, - orientation, ori1, ori2, cx, cy, x, y, sx, sy) + orientation, orientation_start, orientation_end, cell_columns, cell_rows, x, y, size_columns, size_rows) xi += 1 - x += cx + x += cell_columns yi += 1 - y += cy + y += cell_rows From 03b7d638a7ac9ba71844be9f008ba1656e192db5 Mon Sep 17 00:00:00 2001 From: Korijn van Golen Date: Tue, 16 Jun 2015 09:57:49 +0200 Subject: [PATCH 14/25] Wrapped long lines according to pep8. --- skimage/feature/_hoghistogram.pyx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 02a6c92e..1851b907 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -50,8 +50,10 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, or row_index + cell_row >= size_rows or column_index + cell_column < 0 or column_index + cell_column >= size_columns - or orientation[row_index + cell_row, column_index + cell_column] >= orientation_start - or orientation[row_index + cell_row, column_index + cell_column] < orientation_end): continue + or orientation[row_index + cell_row, column_index + cell_column] + >= orientation_start + or orientation[row_index + cell_row, column_index + cell_column] + < orientation_end): continue total += magnitude[row_index + cell_row, column_index + cell_column] @@ -90,8 +92,10 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, The histogram to fill. """ - cdef cnp.float64_t[:, :] magnitude = np.hypot(gradient_columns, gradient_rows) - cdef cnp.float64_t[:, :] orientation = np.arctan2(gradient_rows, gradient_columns) * (180 / np.pi) % 180 + cdef cnp.float64_t[:, :] magnitude = np.hypot(gradient_columns, + gradient_rows) + cdef cnp.float64_t[:, :] orientation = \ + np.arctan2(gradient_rows, gradient_columns) * (180 / np.pi) % 180 cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 cdef float orientation_start, orientation_end @@ -116,7 +120,8 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, while x < cx2: orientation_histogram[yi, xi, i] = cell_hog(magnitude, - orientation, orientation_start, orientation_end, cell_columns, cell_rows, x, y, size_columns, size_rows) + orientation, orientation_start, orientation_end, + cell_columns, cell_rows, x, y, size_columns, size_rows) xi += 1 x += cell_columns From 1b9a15a4d4f47bf0b5a6f7b2ea64e447515f73ec Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 18:38:59 +0200 Subject: [PATCH 15/25] Made the indents line up with the ( --- skimage/feature/_hoghistogram.pyx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 1851b907..fde149dd 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -6,11 +6,11 @@ import numpy as np cimport numpy as cnp cdef float cell_hog(cnp.float64_t[:, :] magnitude, - cnp.float64_t[:, :] orientation, - float orientation_start, float orientation_end, - int cell_columns, int cell_rows, - int column_index, int row_index, - int size_columns, int size_rows): + cnp.float64_t[:, :] orientation, + float orientation_start, float orientation_end, + int cell_columns, int cell_rows, + int column_index, int row_index, + int size_columns, int size_rows): """Calculation of the cell's HOG value Parameters @@ -60,12 +60,12 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, return total def hog_histograms(cnp.float64_t[:, :] gradient_columns, - cnp.float64_t[:, :] gradient_rows, - int cell_columns, int cell_rows, - int size_columns, int size_rows, - int number_of_cells_columns, int number_of_cells_rows, - int number_of_orientations, - cnp.float64_t[:, :, :] orientation_histogram): + cnp.float64_t[:, :] gradient_rows, + int cell_columns, int cell_rows, + int size_columns, int size_rows, + int number_of_cells_columns, int number_of_cells_rows, + int number_of_orientations, + cnp.float64_t[:, :, :] orientation_histogram): """Extract Histogram of Oriented Gradients (HOG) for a given image. Parameters From 9c141eadf8759bc503c3c22f6b2e354d3f28a2f7 Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 18:45:29 +0200 Subject: [PATCH 16/25] Extracted row_index and cell_row from the inner loop Defined constants that don't have to be recomputed several times --- skimage/feature/_hoghistogram.pyx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index fde149dd..5f9fa2c1 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -45,17 +45,19 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, cdef float total = 0. for cell_row in range(-cell_rows/2, cell_rows/2): + cell_row_index = row_index + cell_row + if (cell_row_index < 0 or cell_row_index >= size_rows): + continue + for cell_column in range(-cell_columns/2, cell_columns/2): - if (row_index + cell_row < 0 - or row_index + cell_row >= size_rows - or column_index + cell_column < 0 - or column_index + cell_column >= size_columns - or orientation[row_index + cell_row, column_index + cell_column] + cell_column_index = column_index + cell_column + if (cell_column_index < 0 or cell_column_index >= size_columns + or orientation[cell_row_index, cell_column_index] >= orientation_start - or orientation[row_index + cell_row, column_index + cell_column] + or orientation[cell_row_index, cell_column_index] < orientation_end): continue - total += magnitude[row_index + cell_row, column_index + cell_column] + total += magnitude[cell_row_index, cell_column_index] return total From f633262f031eafe60f558ec4a1542a51be3db30e Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 18:50:34 +0200 Subject: [PATCH 17/25] Moved more variables out of the loops and modified the docstring --- skimage/feature/_hoghistogram.pyx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 5f9fa2c1..afdeb7e4 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -91,7 +91,7 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, number_of_orientations : int Number of orientation bins. orientation_histogram : ndarray - The histogram to fill. + The histogram array which is modified in place. """ cdef cnp.float64_t[:, :] magnitude = np.hypot(gradient_columns, @@ -101,24 +101,25 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 cdef float orientation_start, orientation_end - # compute orientations integral images + x0 = cell_columns / 2 + y0 = cell_rows / 2 + cy2 = cell_rows * number_of_cells_rows + cx2 = cell_columns * number_of_cells_columns + # compute orientations integral images for i in range(number_of_orientations): # isolate orientations in this range - orientation_start = 180. / number_of_orientations * (i + 1) orientation_end = 180. / number_of_orientations * i - y = cell_rows / 2 - cy2 = cell_rows * number_of_cells_rows - x = cell_columns / 2 - cx2 = cell_columns * number_of_cells_columns + x = x0 + y = y0 yi = 0 xi = 0 while y < cy2: xi = 0 - x = cell_columns / 2 + x = x0 while x < cx2: orientation_histogram[yi, xi, i] = cell_hog(magnitude, From ded652c2abffaefaec01e8ea154a47736240fe40 Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 18:55:32 +0200 Subject: [PATCH 18/25] Moved more computations out of loops and added division from future --- skimage/feature/_hog.py | 10 +++++++--- skimage/feature/_hoghistogram.pyx | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 877117bb..a66e5e4e 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -1,3 +1,4 @@ +from __future__ import division import numpy as np from .._shared.utils import assert_nD from . import _hoghistogram @@ -123,13 +124,16 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), from .. import draw radius = min(cx, cy) // 2 - 1 + orientations_arr = np.array(orientations) + dx_arr = radius * np.cos(orientations_arr / orientations_arr * np.pi) + dy_arr = radius * np.sin(orientations_arr / orientations_arr * np.pi) + hog_image = np.zeros((sy, sx), dtype=float) for x in range(n_cellsx): for y in range(n_cellsy): - for o in range(orientations): + for o, dx, dy in zip(orientations_arr, dx_arr, dy_arr): centre = tuple([y * cy + cy // 2, x * cx + cx // 2]) - dx = radius * np.cos(float(o) / orientations * np.pi) - dy = radius * np.sin(float(o) / orientations * np.pi) + rr, cc = draw.line(int(centre[0] - dx), int(centre[1] + dy), int(centre[0] + dx), diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index afdeb7e4..6366e070 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -106,11 +106,13 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, cy2 = cell_rows * number_of_cells_rows cx2 = cell_columns * number_of_cells_columns + number_of_orientations_per_180 = 180. / number_of_orientations + # compute orientations integral images for i in range(number_of_orientations): # isolate orientations in this range - orientation_start = 180. / number_of_orientations * (i + 1) - orientation_end = 180. / number_of_orientations * i + orientation_start = number_of_orientations_per_180 * (i + 1) + orientation_end = number_of_orientations_per_180 * i x = x0 y = y0 From bea9886a12e329ba5eef139e6a8caeafa1b42f7e Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 22:01:44 +0200 Subject: [PATCH 19/25] I noticed that cy+cy and cx+cx was repeated in the inner-most loop as well --- skimage/feature/_hog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index a66e5e4e..743597b0 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -128,11 +128,13 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), dx_arr = radius * np.cos(orientations_arr / orientations_arr * np.pi) dy_arr = radius * np.sin(orientations_arr / orientations_arr * np.pi) + cc = cy + cy + cr = 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 * cy + cy // 2, x * cx + cx // 2]) + centre = tuple([y * cc // 2, x * cr // 2]) rr, cc = draw.line(int(centre[0] - dx), int(centre[1] + dy), From 3d85503472ad3e27071b9766458184d242bc1ff2 Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 22:03:21 +0200 Subject: [PATCH 20/25] Renamed x to rows and y to columns, though I might have still missed it in some spots. Indented the long if statements and put continue on a new line Precomputed the range start and stop values Defined additional variables --- skimage/feature/_hoghistogram.pyx | 52 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 6366e070..bd304c64 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -24,9 +24,9 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, orientation_end : float Orientation range end. cell_columns : int - Pixels per cell (x). + Pixels per cell (rows). cell_rows : int - Pixels per cell (y). + Pixels per cell (columns). column_index : int Block column index. row_index : int @@ -41,21 +41,29 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, total : float The total HOG value. """ - cdef int cell_column, cell_row + 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 float total = 0. - for cell_row in range(-cell_rows/2, cell_rows/2): + 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): continue - for cell_column in range(-cell_columns/2, cell_columns/2): + for cell_column in range(range_columns_start, range_columns_stop): cell_column_index = column_index + cell_column if (cell_column_index < 0 or cell_column_index >= size_columns - or orientation[cell_row_index, cell_column_index] + or orientation[cell_row_index, cell_column_index] >= orientation_start - or orientation[cell_row_index, cell_column_index] - < orientation_end): continue + or orientation[cell_row_index, cell_column_index] + < orientation_end): + continue total += magnitude[cell_row_index, cell_column_index] @@ -73,21 +81,21 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, Parameters ---------- gradient_columns : ndarray - First order image gradients (x). + First order image gradients (rows). gradient_rows : ndarray - First order image gradients (y). + First order image gradients (columns). cell_columns : int - Pixels per cell (x). + Pixels per cell (rows). cell_rows : int - Pixels per cell (y). + Pixels per cell (columns). size_columns : int Number of columns. size_rows : int Number of rows. number_of_cells_columns : int - Number of cells (x). + Number of cells (rows). number_of_cells_rows : int - Number of cells (y). + Number of cells (columns). number_of_orientations : int Number of orientation bins. orientation_histogram : ndarray @@ -98,14 +106,14 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, gradient_rows) cdef cnp.float64_t[:, :] orientation = \ np.arctan2(gradient_rows, gradient_columns) * (180 / np.pi) % 180 - cdef int i, x, y, o, yi, xi, cy1, cy2, cx1, cx2 - cdef float orientation_start, orientation_end + cdef int i, x, y, o, yi, xi, cc, cr, x0, y0 + cdef float orientation_start, orientation_end, \ + number_of_orientations_per_180 x0 = cell_columns / 2 y0 = cell_rows / 2 - cy2 = cell_rows * number_of_cells_rows - cx2 = cell_columns * number_of_cells_columns - + cc = cell_rows * number_of_cells_rows + cr = cell_columns * number_of_cells_columns number_of_orientations_per_180 = 180. / number_of_orientations # compute orientations integral images @@ -113,17 +121,16 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, # isolate orientations in this range orientation_start = number_of_orientations_per_180 * (i + 1) orientation_end = number_of_orientations_per_180 * i - x = x0 y = y0 yi = 0 xi = 0 - while y < cy2: + while y < cc: xi = 0 x = x0 - while x < cx2: + 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) @@ -132,4 +139,3 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, yi += 1 y += cell_rows - From 2500c21b12a7184da4bb14d5b4ea0e47f8e9ab4a Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 22:07:28 +0200 Subject: [PATCH 21/25] Converted orientation and magnitude to typed memory view. Also changed gradient_columns and gradient_rows, hopefully in the right way. --- skimage/feature/_hoghistogram.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index bd304c64..9731251b 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -5,8 +5,8 @@ import numpy as np cimport numpy as cnp -cdef float cell_hog(cnp.float64_t[:, :] magnitude, - cnp.float64_t[:, :] orientation, +cdef float cell_hog(double[:, ::1] magnitude, + double[:, ::1] orientation, float orientation_start, float orientation_end, int cell_columns, int cell_rows, int column_index, int row_index, @@ -69,8 +69,8 @@ cdef float cell_hog(cnp.float64_t[:, :] magnitude, return total -def hog_histograms(cnp.float64_t[:, :] gradient_columns, - cnp.float64_t[:, :] gradient_rows, +def hog_histograms(double[:, ::1] gradient_columns, + double[:, ::1] gradient_rows, int cell_columns, int cell_rows, int size_columns, int size_rows, int number_of_cells_columns, int number_of_cells_rows, @@ -102,9 +102,9 @@ def hog_histograms(cnp.float64_t[:, :] gradient_columns, The histogram array which is modified in place. """ - cdef cnp.float64_t[:, :] magnitude = np.hypot(gradient_columns, + cdef double[:, ::1] magnitude = np.hypot(gradient_columns, gradient_rows) - cdef cnp.float64_t[:, :] orientation = \ + 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 float orientation_start, orientation_end, \ From 167c8eeeb6d67a457c87caa414fc35adad73e4dd Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Thu, 18 Jun 2015 22:08:34 +0200 Subject: [PATCH 22/25] I hadn't realized when I changed orientations to an array, that its actually just an int and the original code said range(orientations), so I changed it to np.arange(orientations). Hopefully the tests will now pass again --- skimage/feature/_hog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 743597b0..64bb1221 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -124,7 +124,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), from .. import draw radius = min(cx, cy) // 2 - 1 - orientations_arr = np.array(orientations) + orientations_arr = np.arange(orientations) dx_arr = radius * np.cos(orientations_arr / orientations_arr * np.pi) dy_arr = radius * np.sin(orientations_arr / orientations_arr * np.pi) From 51e683f665223346df17a29ada0388931747a6e1 Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Fri, 19 Jun 2015 08:51:54 +0200 Subject: [PATCH 23/25] I hadn't realized I was reusing cc in the loop, so it would crash after one iteration. And I had changed the second orientation, which probably wasn't correct --- skimage/feature/_hog.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/skimage/feature/_hog.py b/skimage/feature/_hog.py index 64bb1221..afb2b4e9 100644 --- a/skimage/feature/_hog.py +++ b/skimage/feature/_hog.py @@ -125,17 +125,15 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), radius = min(cx, cy) // 2 - 1 orientations_arr = np.arange(orientations) - dx_arr = radius * np.cos(orientations_arr / orientations_arr * np.pi) - dy_arr = radius * np.sin(orientations_arr / orientations_arr * np.pi) - - cc = cy + cy - cr = cx + cx + 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 * cc // 2, x * cr // 2]) - + centre = tuple([y * cr2 // 2, x * cc2 // 2]) rr, cc = draw.line(int(centre[0] - dx), int(centre[1] + dy), int(centre[0] + dx), From 691beaa59f2c668b6b0ac005adc458d7f87b4b4c Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Fri, 19 Jun 2015 15:49:48 +0200 Subject: [PATCH 24/25] Adding nogil to cell_hog --- skimage/feature/_hoghistogram.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 9731251b..496770b4 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -10,7 +10,7 @@ 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): + int size_columns, int size_rows) nogil: """Calculation of the cell's HOG value Parameters From cd30e010d5bd71b36bf172c72de53007d8c8a3fc Mon Sep 17 00:00:00 2001 From: Ivo Flipse Date: Fri, 19 Jun 2015 16:01:52 +0200 Subject: [PATCH 25/25] Also added a nogil context manager to hoghistograms --- skimage/feature/_hoghistogram.pyx | 41 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/skimage/feature/_hoghistogram.pyx b/skimage/feature/_hoghistogram.pyx index 496770b4..3f8899dd 100644 --- a/skimage/feature/_hoghistogram.pyx +++ b/skimage/feature/_hoghistogram.pyx @@ -116,26 +116,27 @@ def hog_histograms(double[:, ::1] gradient_columns, cr = cell_columns * number_of_cells_columns number_of_orientations_per_180 = 180. / number_of_orientations - # compute orientations integral images - for i in range(number_of_orientations): - # isolate orientations in this range - orientation_start = number_of_orientations_per_180 * (i + 1) - orientation_end = number_of_orientations_per_180 * i - x = x0 - y = y0 - yi = 0 - xi = 0 - - while y < cc: - xi = 0 + with nogil: + # compute orientations integral images + for i in range(number_of_orientations): + # isolate orientations in this range + orientation_start = number_of_orientations_per_180 * (i + 1) + orientation_end = number_of_orientations_per_180 * i x = x0 + y = y0 + yi = 0 + xi = 0 - 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) - xi += 1 - x += cell_columns + while y < cc: + xi = 0 + x = x0 - yi += 1 - y += cell_rows + 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) + xi += 1 + x += cell_columns + + yi += 1 + y += cell_rows