Skip to content

Commit

Permalink
initial HOG descriptor code
Browse files Browse the repository at this point in the history
  • Loading branch information
harthur committed May 17, 2012
0 parents commit cb7f762
Show file tree
Hide file tree
Showing 11 changed files with 525 additions and 0 deletions.
113 changes: 113 additions & 0 deletions README.md
@@ -0,0 +1,113 @@
# hog-descriptor
hog-descriptor extracts a [Histogram of Oriented Gradients](http://en.wikipedia.org/wiki/Histogram_of_oriented_gradients) descriptor from an image (canvas):

```javascript
var hog = require("hog-descriptor");

var descriptor = hog.extractHOG(canvas);

console.log(descriptor); // [0.455, 0.003, 0.987, ...]
```

# Install

For node.js:

```bash
npm install hog-descriptor
```

# API

`extractHOG()` takes options for the cell size (default is 6 pixels), block size (default is 2 cells), number of bins per orientation histogram (default is 6), and block normalization method (one of `"L1"` (default), `"L1-sqrt"`, and `"L2"`):

```javascript
var options = {
cellSize: 6, // length of cell in px
blockSize: 2, // length of block in number of cells
bins: 6, // bins per histogram
norm: 'L1' // block normalization method
}

var descriptor = hog.extractHOG(canvas, options);
```

# Other Goodies

In the process of computing a HOG descriptor, a bunch of other intermediate things have to be computed, like the image gradient, so these steps are also provided as secret goodies on the library:

#### intensities

Get a 2d array of the pixel intensities (normalized to fall between `0` and `1`):

```javascript
var intensities = hog.intensities(canvas);
```

The return array is indexed first by row (y direction) then by column (x direction).

#### gradients

Get a 2d array of the image gradient at each pixel of the canvas with respect to the vertical and horizontal directions using a `[-1, 0, 1]` filter:

```javascript
var gradients = hog.gradients(canvas);
```

Return looks like this:

```javascript
{
x: [[0.0084, 0.354] /* , ... */],
y: [[0.056, 0.7888] /* , ... */]
}
```

#### gradientVectors

Get a 2d array of the gradient vectors at each pixel of the canvas:

```javascript
var vectors = hog.gradientVectors(canvas);
```

Return value is the vector at each pixel with `mag` and `orient` for magnitude and orientation (in radians):

```javascript
[[{ mag: 0.4, orient: -1.52} /*, ... */]]
```

#### drawGreyscale

Greyscales a canvas:

```javascript
hog.drawGreyscale(canvas)
```
![x-gradient](https://raw.github.com/harthur/hog-descriptor/master/test/greyscale.jpg)

#### drawGradient

Draws the gradient of the canvas with respect to 'x' or 'y' direction:

```javascript
hog.drawGradient(canvas, 'x')
```

![x-gradient](https://raw.github.com/harthur/hog-descriptor/master/test/gradient-x.jpg)

```javascript
hog.drawGradient(canvas, 'y')
```

![y-gradient](https://raw.github.com/harthur/hog-descriptor/master/test/gradient-y.jpg)

#### drawMagnitude

Draws the magnitude of the gradient vectors over the canvas:

```javascript
hog.drawMagnitude(canvas)
```

![magnitude](https://raw.github.com/harthur/hog-descriptor/master/test/magnitude.jpg)
101 changes: 101 additions & 0 deletions hog.js
@@ -0,0 +1,101 @@
var _ = require("underscore"),
processing = require("./processing"),
norms = require("./norms");

_(module.exports).extend(processing);

exports.extractHOG = function(canvas, options) {
options = options || {};
var cellSize = options.cellSize || 6;
var blockSize = options.blockSize || 2;
var bins = options.bins || 6;
var normalize = norms[options.norm || "L1"];

var vectors = processing.gradientVectors(canvas);

var cellsWide = Math.floor(canvas.width / cellSize);
var cellsHigh = Math.floor(canvas.height / cellSize);
var histograms = new Array(cellsHigh);

for (var i = 0; i < cellsHigh; i++) {
histograms[i] = new Array(cellsWide);

for (var j = 0; j < cellsWide; j++) {
var cell = getSquare(vectors, j * cellSize, i * cellSize, cellSize);
histograms[i][j] = getHistogram(cell, bins);
}
}
var blocks = getNormalizedBlocks(histograms, blockSize, normalize);
return _(blocks).flatten();
}

function getNormalizedBlocks(histograms, blockSize, normalize) {
var blocks = [];
var blocksHigh = histograms.length - blockSize + 1;
var blocksWide = histograms[0].length - blockSize + 1;

for (var y = 0; y < blocksHigh; y++) {
for (var x = 0; x < blocksWide; x++) {
var block = getBlock(histograms, x, y, blockSize);
normalize(block);
blocks.push(block);
}
}
return blocks;
}

function getBlock(matrix, x, y, length) {
var square = [];
for (var i = y; i < y + length; i++) {
var row = matrix[i];
for (var j = x; j < x + length; j++) {
square.push(row[j]);
}
}
return _(square).flatten();
}


function getHistogram(cell, bins) {
var histogram = zeros(bins);
var size = cell.length;

for (var i = 0; i < size; i++) {
for (var j = 0; j < size; j++) {
var vector = cell[i][j];
var bin = binFor(vector.orient, bins);
histogram[bin] += vector.mag;
}
}
return histogram;
}

function getSquare(elements, x, y, size) {
var square = new Array(size);

for (var i = 0; i < size; i++) {
square[i] = new Array(size);

for (var j = 0; j < size; j++) {
square[i][j] = elements[y + i][x + j];
}
}
return square;
}

function binFor(radians, bins) {
var angle = radians * (180 / Math.PI);
if (angle < 0) {
angle += 180;
}
var bin = Math.floor(angle / 181 * bins);
return bin;
}

function zeros(size) {
var array = new Array(size);
for (var i = 0; i < size; i++) {
array[i] = 0;
}
return array;
}
38 changes: 38 additions & 0 deletions norms.js
@@ -0,0 +1,38 @@
var epsilon = 0.00001;

module.exports = {
L1: function(vector) {
var norm = 0;
for (var i = 0; i < vector.length; i++) {
norm += Math.abs(vector[i]);
}
var denom = norm + epsilon;

for (var i = 0; i < vector.length; i++) {
vector[i] /= denom;
}
},

'L1-sqrt': function(vector) {
var norm = 0;
for (var i = 0; i < vector.length; i++) {
norm += Math.abs(vector[i]);
}
var denom = norm + epsilon;

for (var i = 0; i < vector.length; i++) {
vector[i] = Math.sqrt(vector[i] / denom);
}
},

L2: function(vector) {
var sum = 0;
for (var i = 0; i < vector.length; i++) {
sum += Math.pow(vector[i], 2);
}
var denom = Math.sqrt(sum + epsilon);
for (var i = 0; i < vector.length; i++) {
vector[i] /= denom;
}
}
}
15 changes: 15 additions & 0 deletions package.json
@@ -0,0 +1,15 @@
{
"name": "hog-descriptor",
"description": "Histogram of Oriented Gradients (HOG) descriptor extractor",
"version": "0.1.0",
"author": "Heather Arthur <fayearthur@gmail.com>",
"main": "./hog",
"repository": {
"type": "git",
"url": "http://github.com/harthur/hog-descriptor.git"
},
"dependencies": {
"underscore" : "*"
},
"keywords": ["image processing", "computer vision"]
}

0 comments on commit cb7f762

Please sign in to comment.