View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.feature.dense.gradient.binning;
31  
32  import org.openimaj.citation.annotation.Reference;
33  import org.openimaj.citation.annotation.ReferenceType;
34  import org.openimaj.image.analysis.algorithm.histogram.WindowedHistogramExtractor;
35  import org.openimaj.image.analysis.algorithm.histogram.binning.SpatialBinningStrategy;
36  import org.openimaj.math.geometry.shape.Rectangle;
37  import org.openimaj.math.statistics.distribution.Histogram;
38  import org.openimaj.util.array.ArrayUtils;
39  
40  /**
41   * Implementation of an {@link SpatialBinningStrategy} that extracts normalised
42   * HOG features in the style defined by Dalal and Triggs. In this scheme,
43   * histograms are computed for fixed size cells (size in pixels), and are
44   * aggregated over fixed size blocks of cells. Blocks may overlap. The
45   * aggregated histogram for each block is then normalised before being appended
46   * into the final output histogram.
47   * 
48   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
49   */
50  @Reference(
51  		type = ReferenceType.Inproceedings,
52  		author = { "Dalal, Navneet", "Triggs, Bill" },
53  		title = "Histograms of Oriented Gradients for Human Detection",
54  		year = "2005",
55  		booktitle = "Proceedings of the 2005 IEEE Computer Society Conference on Computer Vision and Pattern Recognition (CVPR'05) - Volume 1 - Volume 01",
56  		pages = { "886", "", "893" },
57  		url = "http://dx.doi.org/10.1109/CVPR.2005.177",
58  		publisher = "IEEE Computer Society",
59  		series = "CVPR '05",
60  		customData = {
61  				"isbn", "0-7695-2372-2",
62  				"numpages", "8",
63  				"doi", "10.1109/CVPR.2005.177",
64  				"acmid", "1069007",
65  				"address", "Washington, DC, USA"
66  		})
67  public class FixedHOGStrategy implements SpatialBinningStrategy {
68  	/**
69  	 * Block normalisation schemes
70  	 * 
71  	 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
72  	 * 
73  	 */
74  	public enum BlockNormalisation {
75  		/**
76  		 * L1 normalisation
77  		 */
78  		L1 {
79  			@Override
80  			final void normalise(Histogram h, int blockArea) {
81  				h.normaliseL1();
82  			}
83  		},
84  		/**
85  		 * L2 normalisation
86  		 */
87  		L2 {
88  			@Override
89  			final void normalise(Histogram h, int blockArea) {
90  				// each cell is l2 normed, so it follows that the l2 norm of the
91  				// block is simply the values divided by the area
92  				ArrayUtils.divide(h.values, blockArea);
93  			}
94  		},
95  		/**
96  		 * L1 normalisation followed by element-wise sqrt
97  		 */
98  		L1sqrt {
99  			@Override
100 			final void normalise(Histogram h, int blockArea) {
101 				h.normaliseL1();
102 
103 				for (int x = 0; x < h.values.length; x++)
104 					h.values[x] = Math.sqrt(h.values[x]);
105 			}
106 		},
107 		/**
108 		 * L2 normalisation; clipping values above 0.2; L2 normalisation
109 		 */
110 		L2clip {
111 			@Override
112 			final void normalise(Histogram h, int blockArea) {
113 				// each cell is l2 normed, so it follows that the l2 norm of the
114 				// block is simply the values divided by the area
115 				double sumsq = 0;
116 				for (int x = 0; x < h.values.length; x++) {
117 					h.values[x] = h.values[x] / blockArea;
118 					if (h.values[x] > 0.2)
119 						h.values[x] = 0.2;
120 					sumsq += h.values[x] * h.values[x];
121 				}
122 
123 				final double invNorm = 1.0 / Math.sqrt(sumsq);
124 				for (int x = 0; x < h.values.length; x++) {
125 					h.values[x] *= invNorm;
126 				}
127 			}
128 		};
129 
130 		abstract void normalise(Histogram h, int blockArea);
131 	}
132 
133 	int cellWidth = 6;
134 	int cellHeight = 6;
135 	int cellsPerBlockX = 3;
136 	int cellsPerBlockY = 3;
137 	int blockStepX = 1;
138 	int blockStepY = 1;
139 	BlockNormalisation norm = BlockNormalisation.L2;
140 
141 	/**
142 	 * Construct with square cells of the given size (in pixels). Square blocks
143 	 * are constructed from the given number of cells in each dimension. Blocks
144 	 * overlap, and shift by 1 cell (i.e. overlap is cellsPerBlock - 1).
145 	 * 
146 	 * @param cellSize
147 	 *            the size of the cells in pixels
148 	 * @param cellsPerBlock
149 	 *            the number of cells per block
150 	 * @param norm
151 	 *            the normalisation scheme
152 	 */
153 	public FixedHOGStrategy(int cellSize, int cellsPerBlock, BlockNormalisation norm) {
154 		this(cellSize, cellsPerBlock, 1, norm);
155 	}
156 
157 	/**
158 	 * Construct with square cells of the given size (in pixels). Square blocks
159 	 * are constructed from the given number of cells in each dimension.
160 	 * 
161 	 * @param cellSize
162 	 *            the size of the cells in pixels
163 	 * @param cellsPerBlock
164 	 *            the number of cells per block
165 	 * @param blockStep
166 	 *            the amount to shift each block in terms of cells
167 	 * @param norm
168 	 *            the normalisation scheme
169 	 */
170 	public FixedHOGStrategy(int cellSize, int cellsPerBlock, int blockStep, BlockNormalisation norm) {
171 		this(cellSize, cellSize, cellsPerBlock, cellsPerBlock, blockStep, blockStep, norm);
172 	}
173 
174 	/**
175 	 * Construct with square cells of the given size (in pixels). Square blocks
176 	 * are constructed from the given number of cells in each dimension.
177 	 * 
178 	 * @param cellWidth
179 	 *            the width of the cells in pixels
180 	 * @param cellHeight
181 	 *            the height of the cells in pixels
182 	 * @param cellsPerBlockX
183 	 *            the number of cells per block in the x direction
184 	 * @param cellsPerBlockY
185 	 *            the number of cells per block in the y direction
186 	 * @param blockStepX
187 	 *            the amount to shift each block in terms of cells in the x
188 	 *            direction
189 	 * @param blockStepY
190 	 *            the amount to shift each block in terms of cells in the y
191 	 *            direction
192 	 * @param norm
193 	 *            the normalisation scheme
194 	 */
195 	public FixedHOGStrategy(int cellWidth, int cellHeight, int cellsPerBlockX, int cellsPerBlockY, int blockStepX,
196 			int blockStepY, BlockNormalisation norm)
197 	{
198 		super();
199 		this.cellWidth = cellWidth;
200 		this.cellHeight = cellHeight;
201 		this.cellsPerBlockX = cellsPerBlockX;
202 		this.cellsPerBlockY = cellsPerBlockY;
203 		this.norm = norm;
204 		this.blockStepX = blockStepX;
205 		this.blockStepY = blockStepY;
206 	}
207 
208 	@Override
209 	public Histogram extract(WindowedHistogramExtractor binnedData, Rectangle region, Histogram output) {
210 		final Histogram[][] cells = computeCells(binnedData, region);
211 		final Histogram[][] blocks = computeBlocks(cells);
212 
213 		final int blockSize = blocks[0][0].values.length;
214 		final int blockArea = cellsPerBlockX * cellsPerBlockY;
215 
216 		if (output == null || output.values.length != blocks[0].length * blocks.length * blockSize)
217 			output = new Histogram(blocks[0].length * blocks.length * blockSize);
218 
219 		for (int j = 0, k = 0; j < blocks.length; j++) {
220 			for (int i = 0; i < blocks[0].length; i++, k++) {
221 				norm.normalise(blocks[j][i], blockArea);
222 
223 				System.arraycopy(blocks[j][i].values, 0, output.values, k * blockSize, blockSize);
224 			}
225 		}
226 
227 		return output;
228 	}
229 
230 	private Histogram[][] computeBlocks(Histogram[][] cells) {
231 		final int numBlocksX = 1 + (cells[0].length - cellsPerBlockX) / this.blockStepX;
232 		final int numBlocksY = 1 + (cells.length - cellsPerBlockY) / this.blockStepY;
233 		final Histogram[][] blocks = new Histogram[numBlocksY][numBlocksX];
234 
235 		for (int y = 0; y < numBlocksY; y++) {
236 			for (int x = 0; x < numBlocksX; x++) {
237 				final Histogram[] blockData = new Histogram[cellsPerBlockX * cellsPerBlockY];
238 
239 				for (int j = 0, k = 0; j < cellsPerBlockY; j++) {
240 					for (int i = 0; i < cellsPerBlockX; i++) {
241 						blockData[k++] = cells[y * blockStepY + j][x * blockStepX + i];
242 					}
243 				}
244 
245 				blocks[y][x] = new Histogram(blockData);
246 			}
247 		}
248 		return blocks;
249 	}
250 
251 	private Histogram[][] computeCells(WindowedHistogramExtractor binnedData, Rectangle region) {
252 		final int numCellsX = (int) ((region.width + cellWidth / 2) / cellWidth);
253 		final int numCellsY = (int) ((region.height + cellHeight / 2) / cellHeight);
254 
255 		final Histogram[][] cells = new Histogram[numCellsY][numCellsX];
256 		for (int j = 0, y = (int) region.y; j < numCellsY; j++, y += cellHeight) {
257 			for (int i = 0, x = (int) region.x; i < numCellsX; i++, x += cellWidth) {
258 				cells[j][i] = binnedData.computeHistogram(x, y, cellWidth, cellHeight);
259 				cells[j][i].normaliseL2();
260 			}
261 		}
262 
263 		return cells;
264 	}
265 }