// ###################################################################### void BitObject::computeSecondMoments() { ASSERT(isValid()); int w = itsObjectMask.getWidth(); int h = itsObjectMask.getHeight(); // The bounding box is stored in image coordinates, and so is the centroid. For // computing the second moments, however we need the centroid in object coords. float cenX = itsCentroidXY.x() - itsBoundingBox.left(); float cenY = itsCentroidXY.y() - itsBoundingBox.top(); // compute the second moments std::vector<float> diffX(w), diffX2(w), diffY(h), diffY2(h); for (int y = 0; y < h; ++y) { diffY[y] = y - cenY; diffY2[y] = diffY[y] * diffY[y]; } for (int x = 0; x < w; ++x) { diffX[x] = x - cenX; diffX2[x] = diffX[x] * diffX[x]; } Image<byte>::const_iterator optr = itsObjectMask.begin(); for (int y = 0; y < h; ++y) for(int x = 0; x < w; ++x) { if (*optr != 0) { itsUxx += diffX2[x]; itsUyy += diffY2[y]; itsUxy += (diffX[x] * diffY[y]); } ++optr; } itsUxx /= itsArea; itsUyy /= itsArea; itsUxy /= itsArea; // compute the parameters d, e and f for the ellipse: // d*x^2 + 2*e*x*y + f*y^2 <= 1 float coeff = 0.F; if((itsUxx * itsUyy - itsUxy * itsUxy) > 0) coeff = 1 / (4 * (itsUxx * itsUyy - itsUxy * itsUxy)); float d = coeff * itsUyy; float e = -coeff * itsUxy; float f = coeff * itsUxx; // from these guys, compute the parameters d and f for the // ellipse when it is rotated so that x is the main axis // and figure out the angle of rotation for this. float expr = sqrt(4*e*e + squareOf(d - f)); float d2 = 0.5 * (d + f + expr); float f2 = 0.5 * (d + f - expr); // the angle is defined in clockwise (image) coordinates: // -- is 0 // \ is 45 // | is 90 // / is 135 if( d != f) itsOriAngle = 90 * atan(2 * e / (d - f)) / M_PI; else itsOriAngle = 0.F; if (itsUyy > itsUxx) itsOriAngle += 90.0F; if (itsOriAngle < 0.0F) itsOriAngle += 180.0F; // this checks if itsOriAngle is nan if (itsOriAngle != itsOriAngle) itsOriAngle = 0.0F; // now get the length of the major and the minor axes: if(f2) itsMajorAxis = 2 / sqrt(f2); if(d2) itsMinorAxis = 2 / sqrt(d2); if(itsMinorAxis) itsElongation = itsMajorAxis / itsMinorAxis; else itsElongation = 0.F; // We're done haveSecondMoments = true; return; }
Feature HOGFeatureExtractor::operator()(const CByteImage& img_) const { /******** BEGIN TODO ********/ // Compute the Histogram of Oriented Gradients feature // Steps are: // 1) Compute gradients in x and y directions. We provide the // derivative kernel proposed in the paper in _kernelDx and // _kernelDy. // 2) Compute gradient magnitude and orientation // 3) Add contribution each pixel to HOG cells whose // support overlaps with pixel. Each cell has a support of size // _cellSize and each histogram has _nAngularBins. // 4) Normalize HOG for each cell. One simple strategy that is // is also used in the SIFT descriptor is to first threshold // the bin values so that no bin value is larger than some // threshold (we leave it up to you do find this value) and // then re-normalize the histogram so that it has norm 1. A more // elaborate normalization scheme is proposed in Dalal & Triggs // paper but we leave that as extra credit. // // Useful functions: // convertRGB2GrayImage, TypeConvert, WarpGlobal, Convolve int xCells = ceil(1.*img_.Shape().width / _cellSize); int yCells = ceil(1.*img_.Shape().height / _cellSize); CFloatImage HOGHist(xCells, yCells, _nAngularBins); HOGHist.ClearPixels(); CByteImage gray(img_.Shape()); CFloatImage grayF(img_.Shape().width, img_.Shape().height, 1); convertRGB2GrayImage(img_, gray); TypeConvert(gray, grayF); CFloatImage diffX( img_.Shape()), diffY( img_.Shape()); Convolve(grayF, diffX, _kernelDx); Convolve(grayF, diffY, _kernelDy); CFloatImage grad(grayF.Shape()), grad2(grayF.Shape()); CFloatImage angl(grayF.Shape()), angl2(grayF.Shape()); for (int y = 0; y <grayF.Shape().height; y++){ for (int x = 0; x<grayF.Shape().width; x++) { grad2.Pixel(x,y,0) = (diffX.Pixel(x,y,0) * diffX.Pixel(x,y,0) + diffY.Pixel(x,y,0) * diffY.Pixel(x,y,0)); angl2.Pixel(x,y,0) = atan(diffY.Pixel(x,y,0) / abs(diffY.Pixel(x,y,0))); } } // Bilinear Filter ConvolveSeparable(grad2, grad, ConvolveKernel_121,ConvolveKernel_121,1); ConvolveSeparable(angl2, angl, ConvolveKernel_121,ConvolveKernel_121,1); //WriteFile(diffX, "angle.tga"); //WriteFile(diffY, "angleG.tga"); for (int y = 0; y <grayF.Shape().height; y++){ for (int x = 0; x<grayF.Shape().width; x++) { // Fit in the bins int a = angl.Pixel(x,y,0) / 3.14 * (_nAngularBins) + _nAngularBins/2; // Histogram HOGHist.Pixel(floor(1.*x / _cellSize), floor(1.*y / _cellSize), a) += grad.Pixel(x,y,0); } } // Normalization float threshold = 0.7; for (int y = 0; y < yCells; y++){ for (int x = 0; x < xCells; x++){ float total = 0; for (int a = 0; a < _nAngularBins; a++) { if (HOGHist.Pixel(x,y,a) > threshold) HOGHist.Pixel(x,y,a) = threshold; // Sum for normalization total += HOGHist.Pixel(x,y,a); } for (int a = 0;a< _nAngularBins; a++) { HOGHist.Pixel(x,y,a) /= total; } } } return HOGHist; /******** END TODO ********/ }