00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include "Features/HOG.H"
00040 #include "Image/DrawOps.H"
00041 #include "Image/MathOps.H"
00042 #include "Image/Kernels.H"
00043 #include "Image/CutPaste.H"
00044 #include "Image/ColorOps.H"
00045 #include "Image/FilterOps.H"
00046 #include "Image/ShapeOps.H"
00047 #include "SIFT/FeatureVector.H"
00048 #include "GUI/DebugWin.H"
00049
00050 #include <stdio.h>
00051
00052 HOG::HOG()
00053 {
00054 }
00055
00056 HOG::~HOG()
00057 {
00058 }
00059
00060 ImageSet<float> HOG::getFeatures(const Image<PixRGB<byte> >& img, int numBins)
00061 {
00062 int itsNumOrientations = 18;
00063
00064 Image<float> mag, ori;
00065 getMaxGradient(img, mag, ori, itsNumOrientations);
00066
00067 ImageSet<float> histogram = getOriHistogram(mag, ori, itsNumOrientations, numBins);
00068
00069 ImageSet<double> features = computeFeatures(histogram);
00070
00071 return features;
00072 }
00073
00074 Image<PixRGB<byte> > HOG::getHistogramImage(const ImageSet<float>& hist, const int lineSize)
00075 {
00076 if (hist.size() == 0)
00077 return Image<PixRGB<byte> >();
00078
00079
00080 Image<float> img(hist[0].getDims()*lineSize, ZEROS);
00081
00082
00083 for(uint feature=0; feature<9; feature++)
00084 {
00085 float ori = (float)feature/M_PI + (M_PI/2);
00086 for(int y=0; y<hist[feature].getHeight(); y++)
00087 {
00088 for(int x=0; x<hist[feature].getWidth(); x++)
00089 {
00090 float histVal = hist[feature].getVal(x,y);
00091
00092
00093
00094 if (hist[feature+9].getVal(x,y) > histVal)
00095 histVal = hist[feature+9].getVal(x,y);
00096 if (hist[feature+18].getVal(x,y) > histVal)
00097 histVal = hist[feature+18].getVal(x,y);
00098 if (histVal < 0) histVal = 0;
00099
00100 drawLine(img, Point2D<int>((lineSize/2) + x*lineSize,
00101 (lineSize/2) + y*lineSize),
00102 -ori, lineSize,
00103 histVal);
00104 }
00105 }
00106 }
00107
00108 inplaceNormalize(img, 0.0F, 255.0F);
00109
00110 return toRGB(img);
00111 }
00112
00113 ImageSet<double> HOG::computeFeatures(const ImageSet<float>& hist)
00114 {
00115
00116
00117 Image<double> norm = getHistogramEnergy(hist);
00118
00119 const int w = norm.getWidth();
00120 const int h = norm.getHeight();
00121
00122 const int numFeatures = hist.size() +
00123 hist.size()/2 +
00124 4 +
00125 1;
00126
00127 const int featuresW = std::max(w-2, 0);
00128 const int featuresH = std::max(h-2, 0);
00129
00130 ImageSet<double> features(numFeatures, Dims(featuresW, featuresH), ZEROS);
00131
00132 Image<double>::const_iterator normPtr = norm.begin();
00133 Image<double>::const_iterator ptr;
00134
00135
00136 const double eps = 0.0001;
00137
00138 for(int y=0; y<featuresH; y++)
00139 for(int x=0; x<featuresW; x++)
00140 {
00141
00142
00143 ptr = normPtr + (y+1)*w + x+1;
00144 const double n1 = 1.0 / sqrt(*ptr + *(ptr+1) +
00145 *(ptr+w) + *(ptr+w+1) +
00146 eps);
00147 ptr = normPtr + y*w + x+1;
00148 const double n2 = 1.0 / sqrt(*ptr + *(ptr+1) +
00149 *(ptr+w) + *(ptr+w+1) +
00150 eps);
00151 ptr = normPtr + (y+1)*w + x;
00152 const double n3 = 1.0 / sqrt(*ptr + *(ptr+1) +
00153 *(ptr+w) + *(ptr+w+1) +
00154 eps);
00155 ptr = normPtr + y*w + x;
00156 const double n4 = 1.0 / sqrt(*ptr + *(ptr+1) +
00157 *(ptr+w) + *(ptr+w+1) +
00158 eps);
00159
00160
00161 double t1 = 0, t2 = 0, t3 = 0, t4 = 0;
00162
00163
00164 uint featureId = 0;
00165 for(uint ori=0; ori < hist.size(); ori++)
00166 {
00167 Image<float>::const_iterator histPtr = hist[ori].begin();
00168 const float histVal = histPtr[(y+1)*w + x+1];
00169 double h1 = std::min(histVal * n1, 0.2);
00170 double h2 = std::min(histVal * n2, 0.2);
00171 double h3 = std::min(histVal * n3, 0.2);
00172 double h4 = std::min(histVal * n4, 0.2);
00173
00174 t1 += h1; t2 += h2; t3 += h3; t4 += h4;
00175
00176 Image<double>::iterator featuresPtr = features[featureId++].beginw();
00177 featuresPtr[y*featuresW + x] = 0.5 * (h1 + h2 + h3 + h4);
00178 }
00179
00180
00181 int halfOriSize = hist.size()/2;
00182 for(int ori=0; ori < halfOriSize; ori++)
00183 {
00184 Image<float>::const_iterator histPtr1 = hist[ori].begin();
00185 Image<float>::const_iterator histPtr2 = hist[ori+halfOriSize].begin();
00186 const double sum = histPtr1[(y+1)*w + x+1] + histPtr2[(y+1)*w + x+1];
00187 double h1 = std::min(sum * n1, 0.2);
00188 double h2 = std::min(sum * n2, 0.2);
00189 double h3 = std::min(sum * n3, 0.2);
00190 double h4 = std::min(sum * n4, 0.2);
00191
00192 Image<double>::iterator featuresPtr = features[featureId++].beginw();
00193 featuresPtr[y*featuresW + x] = 0.5 * (h1 + h2 + h3 + h4);
00194 }
00195
00196
00197 Image<double>::iterator featuresPtr = features[featureId++].beginw();
00198 featuresPtr[y*featuresW + x] = 0.2357 * t1;
00199
00200 featuresPtr = features[featureId++].beginw();
00201 featuresPtr[y*featuresW + x] = 0.2357 * t2;
00202
00203 featuresPtr = features[featureId++].beginw();
00204 featuresPtr[y*featuresW + x] = 0.2357 * t3;
00205
00206 featuresPtr = features[featureId++].beginw();
00207 featuresPtr[y*featuresW + x] = 0.2357 * t4;
00208
00209
00210
00211 featuresPtr = features[featureId++].beginw();
00212 featuresPtr[y*featuresW + x] = 0;
00213
00214 }
00215
00216
00217
00218 return features;
00219
00220 }
00221
00222 Image<double> HOG::getHistogramEnergy(const ImageSet<float>& hist)
00223 {
00224 if (hist.size() == 0)
00225 return Image<double>();
00226
00227 Image<double> norm(hist[0].getDims(), ZEROS);
00228
00229
00230 int halfOriSize = hist.size()/2;
00231
00232 for(int ori=0; ori<halfOriSize; ori++)
00233 {
00234 Image<float>::const_iterator src1Ptr = hist[ori].begin();
00235 Image<float>::const_iterator src2Ptr = hist[ori+halfOriSize].begin();
00236
00237 Image<double>::iterator normPtr = norm.beginw();
00238 Image<double>::const_iterator normPtrEnd = norm.end();
00239
00240 while(normPtr < normPtrEnd)
00241 {
00242 *(normPtr++) += (*src1Ptr + *src2Ptr) * (*src1Ptr + *src2Ptr);
00243 src1Ptr++;
00244 src2Ptr++;
00245 }
00246 }
00247
00248 return norm;
00249 }
00250
00251 ImageSet<float> HOG::getOriHistogram(const Image<float>& mag, const Image<float>& ori, int numOrientations, int numBins)
00252 {
00253 Dims blocksDims = Dims(
00254 (int)round((double)mag.getWidth()/double(numBins)),
00255 (int)round((double)mag.getHeight()/double(numBins)));
00256
00257 ImageSet<float> hist(numOrientations, blocksDims, ZEROS);
00258
00259 Image<float>::const_iterator magPtr = mag.begin(), oriPtr = ori.begin();
00260
00261
00262 int w = blocksDims.w()*numBins;
00263 int h = blocksDims.h()*numBins;
00264 int magW = mag.getWidth();
00265 int magH = mag.getHeight();
00266 int histWidth = blocksDims.w();
00267 int histHeight = blocksDims.h();
00268
00269 for (int y = 1; y < h-1; y ++)
00270 for (int x = 1; x < w-1; x ++)
00271 {
00272
00273 double xp = ((double)x+0.5)/(double)numBins - 0.5;
00274 double yp = ((double)y+0.5)/(double)numBins - 0.5;
00275 int ixp = (int)floor(xp);
00276 int iyp = (int)floor(yp);
00277 double vx0 = xp-ixp;
00278 double vy0 = yp-iyp;
00279 double vx1 = 1.0-vx0;
00280 double vy1 = 1.0-vy0;
00281
00282
00283
00284 int magX = std::min(x, magW-2);
00285 int magY = std::min(y, magH-2);
00286 double mag = magPtr[magY*magW + magX];
00287 int ori = int(oriPtr[magY*magW + magX]);
00288
00289 Image<float>::iterator histPtr = hist[ori].beginw();
00290
00291 if (ixp >= 0 && iyp >= 0)
00292 histPtr[iyp*histWidth + ixp] += vx1*vy1*mag;
00293
00294 if (ixp+1 < histWidth && iyp >= 0)
00295 histPtr[iyp*histWidth + ixp+1] += vx0*vy1*mag;
00296
00297 if (ixp >= 0 && iyp+1 < histHeight)
00298 histPtr[(iyp+1)*histWidth + ixp] += vx1*vy0*mag;
00299
00300 if (ixp+1 < histWidth && iyp+1 < histHeight)
00301 histPtr[(iyp+1)*histWidth + ixp+1] += vx0*vy0*mag;
00302 }
00303
00304 return hist;
00305 }
00306
00307 void HOG::getMaxGradient(const Image<PixRGB<byte> >& img,
00308 Image<float>& mag, Image<float>& ori,
00309 int numOrientations)
00310 {
00311 if (numOrientations != 0 &&
00312 numOrientations > 18)
00313 LFATAL("Can only support up to 18 orientations for now.");
00314
00315 mag.resize(img.getDims()); ori.resize(img.getDims());
00316
00317 Image<PixRGB<byte> >::const_iterator src = img.begin();
00318 Image<float>::iterator mPtr = mag.beginw(), oPtr = ori.beginw();
00319 const int w = mag.getWidth(), h = mag.getHeight();
00320
00321 float zero = 0;
00322
00323
00324 for (int i = 0; i < w; i ++) { *mPtr ++ = zero; *oPtr ++ = zero; }
00325 src += w;
00326
00327
00328 for (int j = 1; j < h-1; j ++)
00329 {
00330
00331 *mPtr ++ = zero; *oPtr ++ = zero; ++ src;
00332
00333
00334 for (int i = 1; i < w-1; i ++)
00335 {
00336 PixRGB<int> valx = src[1] - src[-1];
00337 PixRGB<int> valy = src[w] - src[-w];
00338
00339
00340 double mag1 = (valx.red()*valx.red()) + (valy.red()*valy.red());
00341 double mag2 = (valx.green()*valx.green()) + (valy.green()*valy.green());
00342 double mag3 = (valx.blue()*valx.blue()) + (valy.blue()*valy.blue());
00343
00344 double mag = mag1;
00345 double dx = valx.red();
00346 double dy = valy.red();
00347
00348
00349 if (mag2 > mag)
00350 {
00351 dx = valx.green();
00352 dy = valy.green();
00353 mag = mag2;
00354 }
00355 if (mag3 > mag)
00356 {
00357 dx = valx.blue();
00358 dy = valy.blue();
00359 mag = mag3;
00360 }
00361
00362 *mPtr++ = sqrt(mag);
00363 if (numOrientations > 0)
00364 {
00365
00366 double bestDot = 0;
00367 int bestOri = 0;
00368 for (int ori = 0; ori < numOrientations/2; ori++) {
00369 double dot = itsUU[ori]*dx + itsVV[ori]*dy;
00370 if (dot > bestDot) {
00371 bestDot = dot;
00372 bestOri = ori;
00373 } else if (-dot > bestDot) {
00374 bestDot = -dot;
00375 bestOri = ori+(numOrientations/2);
00376 }
00377 }
00378 *oPtr++ = bestOri;
00379
00380 } else {
00381 *oPtr++ = atan2(dy, dx);
00382 }
00383 ++ src;
00384 }
00385
00386
00387 *mPtr ++ = zero; *oPtr ++ = zero; ++ src;
00388 }
00389
00390
00391 for (int i = 0; i < w; i ++) { *mPtr ++ = zero; *oPtr ++ = zero; }
00392 }
00393
00394
00395
00396
00397
00398