001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.objectdetection.haar.training; 031 032import java.util.List; 033 034import org.openimaj.image.analysis.algorithm.SummedSqTiltAreaTable; 035import org.openimaj.image.objectdetection.haar.HaarFeature; 036import org.openimaj.ml.classification.LabelledDataProvider; 037import org.openimaj.util.array.ArrayUtils; 038import org.openimaj.util.function.Operation; 039import org.openimaj.util.parallel.Parallel; 040 041public class CachedTrainingData implements LabelledDataProvider { 042 float[][] responses; 043 boolean[] classes; 044 int[][] sortedIndices; 045 List<HaarFeature> features; 046 int width, height; 047 048 float computeWindowVarianceNorm(SummedSqTiltAreaTable sat) { 049 final int w = width - 2; 050 final int h = height - 2; 051 052 final int x = 1; // shift by 1 scaled px to centre box 053 final int y = 1; 054 055 final float sum = sat.sum.pixels[y + h][x + w] + sat.sum.pixels[y][x] - 056 sat.sum.pixels[y + h][x] - sat.sum.pixels[y][x + w]; 057 final float sqSum = sat.sqSum.pixels[y + w][x + w] + sat.sqSum.pixels[y][x] - 058 sat.sqSum.pixels[y + w][x] - sat.sqSum.pixels[y][x + w]; 059 060 final float cachedInvArea = 1.0f / (w * h); 061 final float mean = sum * cachedInvArea; 062 float wvNorm = sqSum * cachedInvArea - mean * mean; 063 wvNorm = (float) ((wvNorm > 0) ? Math.sqrt(wvNorm) : 1); 064 065 return wvNorm; 066 } 067 068 public CachedTrainingData(final List<SummedSqTiltAreaTable> positive, final List<SummedSqTiltAreaTable> negative, 069 final List<HaarFeature> features) 070 { 071 this.width = positive.get(0).sum.width - 1; 072 this.height = positive.get(0).sum.height - 1; 073 074 this.features = features; 075 final int nfeatures = features.size(); 076 077 classes = new boolean[positive.size() + negative.size()]; 078 responses = new float[nfeatures][classes.length]; 079 sortedIndices = new int[nfeatures][]; 080 // for (int f = 0; f < nfeatures; f++) { 081 082 Parallel.forIndex(0, nfeatures, 1, new Operation<Integer>() { 083 084 @Override 085 public void perform(Integer f) { 086 final HaarFeature feature = features.get(f); 087 int count = 0; 088 089 for (final SummedSqTiltAreaTable t : positive) { 090 final float wvNorm = computeWindowVarianceNorm(t); 091 responses[f][count] = feature.computeResponse(t, 0, 0) / wvNorm; 092 classes[count] = true; 093 ++count; 094 } 095 096 for (final SummedSqTiltAreaTable t : negative) { 097 final float wvNorm = computeWindowVarianceNorm(t); 098 responses[f][count] = feature.computeResponse(t, 0, 0) / wvNorm; 099 classes[count] = false; 100 ++count; 101 } 102 103 sortedIndices[f] = ArrayUtils.indexSort(responses[f]); 104 } 105 }); 106 } 107 108 @Override 109 public float[] getFeatureResponse(int dimension) { 110 return responses[dimension]; 111 } 112 113 @Override 114 public boolean[] getClasses() { 115 return classes; 116 } 117 118 @Override 119 public int numInstances() { 120 return classes.length; 121 } 122 123 @Override 124 public int numDimensions() { 125 return responses.length; 126 } 127 128 @Override 129 public float[] getInstanceFeature(int idx) { 130 final float[] feature = new float[responses.length]; 131 132 for (int i = 0; i < feature.length; i++) { 133 feature[i] = responses[i][idx]; 134 } 135 136 return feature; 137 } 138 139 @Override 140 public int[] getSortedResponseIndices(int d) { 141 return sortedIndices[d]; 142 } 143 144 public HaarFeature getFeature(int dimension) { 145 return features.get(dimension); 146 } 147}