/// <summary> /// Gets the sum of the areas of the rectangular features in an integral image /// </summary> /// <param name="image"></param> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public double GetSum(ImageHelper image, int x, int y) { double sum = 0.0; if (!Tilted) { // Compute the sum for a standard feature foreach (RectangleHelper rect in Rectangles) { sum += image.GetSum(x + rect.scaledX, y + rect.scaledY, rect.scaledWidth, rect.scaledHeight) * rect.scaledWeight; } } else { // Compute the sum for a rotated feature foreach (RectangleHelper rect in Rectangles) { sum += image.GetSumT(x + rect.scaledX, y + rect.scaledY, rect.scaledWidth, rect.scaledHeight) * rect.scaledWeight; } } return sum; }
/// <summary> /// This is where we do the detection for an image passing in the interest window /// </summary> /// <param name="pImage"></param> /// <param name="pWindow"></param> /// <returns></returns> public bool Compute(ImageHelper pImage, Rectangle pWindow) { // grab the dimensions int x = pWindow.X; int y = pWindow.Y; int w = pWindow.Width; int h = pWindow.Height; // figure out the details of the window in the image double mean = pImage.GetSum(x, y, w, h) * invArea; double factor = pImage.GetSum2(x, y, w, h) * invArea - (mean * mean); // do the math to adjust the factor factor = (factor >= 0) ? Math.Sqrt(factor) : 1; // For each classification stage in the cascade foreach (Stage stage in cascade.stages) { // Check to see if the image was rejected if (stage.Classify(pImage, x, y, factor) == false) { return false; // If it was, we tell the caller that we were rejected. } } // If we've gone through all the stages and were not rejected, the object was detected inside the window return true; }
/// <summary> /// Constructor passing in an unmanaged image, a channel, and the flag to compute a tilted image /// </summary> /// <param name="pImage"></param> /// <param name="pChannel"></param> /// <param name="pComputeTilted"></param> /// <returns></returns> public static ImageHelper FromBitmap(AForge.Imaging.UnmanagedImage pImage, int pChannel, bool pComputeTilted) { // check image format, we'll have the same issue with bitmaps here as we did before if (!(pImage.PixelFormat == PixelFormat.Format8bppIndexed || pImage.PixelFormat == PixelFormat.Format24bppRgb || pImage.PixelFormat == PixelFormat.Format32bppArgb)) { throw new AForge.Imaging.UnsupportedImageFormatException("Only grayscale and 24 bpp RGB images are supported."); } // find the pixel size int pixelSize = System.Drawing.Image.GetPixelFormatSize(pImage.PixelFormat) / 8; // get source image size int pWidth = pImage.Width; int pHeight = pImage.Height; int pStride = pImage.Stride; int pOffset = pStride - pWidth * pixelSize; // create integral image ImageHelper im = new ImageHelper(pWidth, pHeight, pComputeTilted); int* nSum = im.intImage, sSum = im.squaredImage, tSum = im.tiltedImage; // compute the adjusted widths and heights for the other two operations int nWidth = im.nWidth, nHeight = im.nHeight; int tWidth = im.tWidth, tHeight = im.tHeight; // We're going to have an issue if the channel doesn't support 8 bpp if (pImage.PixelFormat == PixelFormat.Format8bppIndexed && pChannel != 0) throw new ArgumentException("Only the first channel is available for 8 bpp images.", "channel"); // grab the image pointer src byte* srcStart = (byte*)pImage.ImageData.ToPointer() + pChannel; // do the job byte* src = srcStart; // for each line for (int y = 1; y <= pHeight; y++) { int yy = nWidth * (y); int y1 = nWidth * (y - 1); // for each pixel for (int x = 1; x <= pWidth; x++, src += pixelSize) { // set p1 to the src // and square it for p2 int p1 = *src; int p2 = p1 * p1; // find the indexes for each sum int r = yy + (x); int a = yy + (x - 1); int b = y1 + (x); int c = y1 + (x - 1); // and sum them up! nSum[r] = p1 + nSum[a] + nSum[b] - nSum[c]; sSum[r] = p2 + sSum[a] + sSum[b] - sSum[c]; } src += pOffset; } // if we're computing the tilted image as well, there is a lot more going on if (pComputeTilted) { // grab the source src = srcStart; // Left-to-right, top-to-bottom pass for (int y = 1; y <= pHeight; y++, src += pOffset) { // compute the new yy and y1 int yy = tWidth * (y); int y1 = tWidth * (y - 1); for (int x = 2; x < pWidth + 2; x++, src += pixelSize) { // find the indexes int a = y1 + (x - 1); int b = yy + (x - 1); int c = y1 + (x - 2); int r = yy + (x); // and sum them up tSum[r] = *src + tSum[a] + tSum[b] - tSum[c]; } } { int yy = tWidth * (pHeight); int y1 = tWidth * (pHeight + 1); for (int x = 2; x < pWidth + 2; x++, src += pixelSize) { int a = yy + (x - 1); int c = yy + (x - 2); int b = y1 + (x - 1); int r = y1 + (x); tSum[r] = tSum[a] + tSum[b] - tSum[c]; } } // Now do it Right-to-left, and bottom-to-top for (int y = pHeight; y >= 0; y--) { // compute the new values int yy = tWidth * (y); int y1 = tWidth * (y + 1); for (int x = pWidth + 1; x >= 1; x--) { // grab the indexes int r = yy + (x); int b = y1 + (x - 1); // and add them up tSum[r] += tSum[b]; } } for (int y = pHeight + 1; y >= 0; y--) { // grab the new value int yy = tWidth * (y); for (int x = pWidth + 1; x >= 2; x--) { // compute the indexes int r = yy + (x); int b = yy + (x - 2); // and add them up tSum[r] -= tSum[b]; } } } // finally return the restult... All the math should be done by this point return im; }
/// <summary> /// Classifies an image as having the searched object or not. /// </summary> public bool Classify(ImageHelper pImage, int row, int column, double pFactor) { // Keep track of how close to the threshold we are. double achievedThreshold = 0; // For each feature in the feature tree of the current stage, foreach (FeatureNode[] node in nodes) { int current = 0; do { // Get the feature node from the current branch in the node tree FeatureNode feature = node[current]; // Evaluate the node's feature double sum = feature.mFeature.GetSum(pImage, row, column); // And increase the value accumulator if (sum < feature.mThreshold * pFactor) { achievedThreshold += feature.mLeftValue; current = feature.mLeft; } else { achievedThreshold += feature.mRightValue; current = feature.mRight; } } while (current > 0); } // If the achieved threshold is greater than the desired threshold we have found a face!! if (achievedThreshold <= this.threshold) { // Otherwise our image doesn't contain what we're looking for. return false; } else { // We had a match! return true; } }