├── .github └── FUNDING.yml ├── HOG.py ├── HOG_implementation.py ├── README.md └── im_patch.jpg /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | # paypal: http://paypal.me/ahmedfgad # Replace with a single Patreon username 5 | open_collective: pygad 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://donate.stripe.com/eVa5kO866elKgM0144', 'http://paypal.me/ahmedfgad'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /HOG.py: -------------------------------------------------------------------------------- 1 | import skimage.io, skimage.color 2 | import numpy 3 | import matplotlib.pyplot 4 | 5 | def calculate_gradient(img, template): 6 | ts = template.size #Number of elements in the template (3). 7 | #New padded array to hold the resultant gradient image. 8 | new_img = numpy.zeros((img.shape[0]+ts-1, 9 | img.shape[1]+ts-1)) 10 | new_img[numpy.uint16((ts-1)/2.0):img.shape[0]+numpy.uint16((ts-1)/2.0), 11 | numpy.uint16((ts-1)/2.0):img.shape[1]+numpy.uint16((ts-1)/2.0)] = img 12 | result = numpy.zeros((new_img.shape)) 13 | 14 | for r in numpy.uint16(numpy.arange((ts-1)/2.0, img.shape[0]+(ts-1)/2.0)): 15 | for c in numpy.uint16(numpy.arange((ts-1)/2.0, 16 | img.shape[1]+(ts-1)/2.0)): 17 | curr_region = new_img[r-numpy.uint16((ts-1)/2.0):r+numpy.uint16((ts-1)/2.0)+1, 18 | c-numpy.uint16((ts-1)/2.0):c+numpy.uint16((ts-1)/2.0)+1] 19 | curr_result = curr_region * template 20 | score = numpy.sum(curr_result) 21 | result[r, c] = score 22 | #Result of the same size as the original image after removing the padding. 23 | result_img = result[numpy.uint16((ts-1)/2.0):result.shape[0]-numpy.uint16((ts-1)/2.0), 24 | numpy.uint16((ts-1)/2.0):result.shape[1]-numpy.uint16((ts-1)/2.0)] 25 | return result_img 26 | 27 | def gradient_magnitude(horizontal_gradient, vertical_gradient): 28 | horizontal_gradient_square = numpy.power(horizontal_gradient, 2) 29 | vertical_gradient_square = numpy.power(vertical_gradient, 2) 30 | sum_squares = horizontal_gradient_square + vertical_gradient_square 31 | grad_magnitude = numpy.sqrt(sum_squares) 32 | return grad_magnitude 33 | 34 | def gradient_direction(horizontal_gradient, vertical_gradient): 35 | grad_direction = numpy.arctan(vertical_gradient/(horizontal_gradient+0.00000001)) 36 | grad_direction = numpy.rad2deg(grad_direction) 37 | grad_direction = grad_direction%180 38 | return grad_direction 39 | 40 | def HOG_cell_histogram(cell_direction, cell_magnitude, hist_bins): 41 | HOG_cell_hist = numpy.zeros(shape=(hist_bins.size)) 42 | cell_size = cell_direction.shape[0] 43 | 44 | for row_idx in range(cell_size): 45 | for col_idx in range(cell_size): 46 | curr_direction = cell_direction[row_idx, col_idx] 47 | curr_magnitude = cell_magnitude[row_idx, col_idx] 48 | 49 | diff = numpy.abs(curr_direction - hist_bins) 50 | 51 | if curr_direction < hist_bins[0]: 52 | first_bin_idx = 0 53 | second_bin_idx = hist_bins.size-1 54 | elif curr_direction > hist_bins[-1]: 55 | first_bin_idx = hist_bins.size-1 56 | second_bin_idx = 0 57 | else: 58 | first_bin_idx = numpy.where(diff == numpy.min(diff))[0][0] 59 | temp = hist_bins[[(first_bin_idx-1)%hist_bins.size, (first_bin_idx+1)%hist_bins.size]] 60 | temp2 = numpy.abs(curr_direction - temp) 61 | res = numpy.where(temp2 == numpy.min(temp2))[0][0] 62 | if res == 0 and first_bin_idx != 0: 63 | second_bin_idx = first_bin_idx-1 64 | else: 65 | second_bin_idx = first_bin_idx+1 66 | 67 | first_bin_value = hist_bins[first_bin_idx] 68 | second_bin_value = hist_bins[second_bin_idx] 69 | HOG_cell_hist[first_bin_idx] = HOG_cell_hist[first_bin_idx] + (numpy.abs(curr_direction - first_bin_value)/(180.0/hist_bins.size)) * curr_magnitude 70 | HOG_cell_hist[second_bin_idx] = HOG_cell_hist[second_bin_idx] + (numpy.abs(curr_direction - second_bin_value)/(180.0/hist_bins.size)) * curr_magnitude 71 | return HOG_cell_hist 72 | 73 | -------------------------------------------------------------------------------- /HOG_implementation.py: -------------------------------------------------------------------------------- 1 | import skimage.io, skimage.color 2 | import numpy 3 | import matplotlib.pyplot 4 | import HOG 5 | 6 | img = skimage.io.imread("im_patch.jpg") 7 | img = skimage.color.rgb2gray(img) 8 | 9 | horizontal_mask = numpy.array([-1, 0, 1]) 10 | vertical_mask = numpy.array([[-1], 11 | [0], 12 | [1]]) 13 | 14 | horizontal_gradient = HOG.calculate_gradient(img, horizontal_mask) 15 | vertical_gradient = HOG.calculate_gradient(img, vertical_mask) 16 | 17 | grad_magnitude = HOG.gradient_magnitude(horizontal_gradient, vertical_gradient) 18 | grad_direction = HOG.gradient_direction(horizontal_gradient, vertical_gradient) 19 | 20 | grad_direction = grad_direction % 180 21 | hist_bins = numpy.array([10,30,50,70,90,110,130,150,170]) 22 | 23 | # Histogram of the first cell in the first block. 24 | cell_direction = grad_direction[:8, :8] 25 | cell_magnitude = grad_magnitude[:8, :8] 26 | HOG_cell_hist = HOG.HOG_cell_histogram(cell_direction, cell_magnitude, hist_bins) 27 | 28 | matplotlib.pyplot.bar(x=numpy.arange(9), height=HOG_cell_hist, align="center", width=0.8) 29 | matplotlib.pyplot.show() 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HOG 2 | Histogram of oriented gradients (HOG) Python implementation using NumPy from scratch. 3 | 4 | HOG is an image feature descripts to describe the image based on the gradients directions and magnitudes. At the current time, this project supports calculating the following: 5 | 1. Horizontal and vertical gradients. 6 | 2. Gradient magnituge. 7 | 3. Gradient direction. 8 | 4. Histogram for a given cell. 9 | 10 | In the future, the project will support the following: 11 | 1. Extracting histogram from all cells within the entire image patch. 12 | 2. Concatenating the histogrms of all cells within the same block. 13 | 3. Normalize the feature vector of each block. 14 | 4. Concatenating all feature vectors from all blocks. 15 | 16 | For contacting me: 17 | 18 | ahmed.f.gad@gmail.com 19 | 20 | ahmed.fawzy@ci.menofia.edu.eg 21 | 22 | LinkedIn: https://www.linkedin.com/in/ahmedfgad 23 | 24 | Facebook: https://www.facebook.com/ahmed.f.gadd 25 | 26 | Twitter: https://twitter.com/ahmedfgad 27 | 28 | KDnuggets: https://www.kdnuggets.com/author/ahmed-gad 29 | 30 | TowardsDataScience: https://towardsdatascience.com/@ahmedfgad 31 | -------------------------------------------------------------------------------- /im_patch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedfgad/HOGNumPy/a2116972b90b6c05c7bc601c27e0fed8cd70738d/im_patch.jpg --------------------------------------------------------------------------------