/// <summary> /// Gets the sum of the areas of the rectangular features in an integral image. /// </summary> /// public double GetSum(SIntegralImage image, int x, int y) { double sum = 0.0; if (!Tilted) { // Compute the sum for a standard feature foreach (HaarRectangle 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 (HaarRectangle rect in Rectangles) { sum += image.GetSumT(x + rect.ScaledX, y + rect.ScaledY, rect.ScaledWidth, rect.ScaledHeight) * rect.ScaledWeight; } } return(sum); }
/// <summary> /// Detects the presence of an object in a given window. /// </summary> /// public bool Compute(SIntegralImage image, Rectangle rectangle) { int x = rectangle.X; int y = rectangle.Y; int w = rectangle.Width; int h = rectangle.Height; double mean = image.GetSum(x, y, w, h) * invArea; double factor = image.GetSum2(x, y, w, h) * invArea - (mean * mean); factor = (factor >= 0) ? Math.Sqrt(factor) : 1; // For each classification stage in the cascade foreach (HaarCascadeStage stage in cascade.Stages) { // Check if the stage has rejected the image if (stage.Classify(image, x, y, factor) == false) { return(false); // The image has been rejected. } } // If the object has gone all stages and has not // been rejected, the object has been detected. return(true); // The image has been detected. }
/// <summary> /// Classifies an image as having the searched object or not. /// </summary> public bool Classify(SIntegralImage image, int x, int y, double factor) { double value = 0; // For each feature in the feature tree of the current stage, foreach (HaarFeatureNode[] tree in Trees) { int current = 0; do { // Get the feature node from the tree HaarFeatureNode node = tree[current]; // Evaluate the node's feature double sum = node.Feature.GetSum(image, x, y); // And increase the value accumulator if (sum < node.Threshold * factor) { value += node.LeftValue; current = node.LeftNodeIndex; } else { value += node.RightValue; current = node.RightNodeIndex; } } while (current > 0); // Stop early if we have already surpassed the stage threshold value. //if (value > this.Threshold) return true; } // After we have evaluated the output for the // current stage, we will check if the value // is still lesser than the stage threshold. if (value < this.Threshold) { // If it is, the stage has rejected the current // image and it doesn't contains our object. return(false); } else { // The stage has accepted the current image return(true); } }
/// <summary> /// Performs object detection on the given frame. /// </summary> /// public Rectangle[] ProcessFrame(UnmanagedImage image) { // Creates an integral image representation of the frame SIntegralImage integralImage = SIntegralImage.FromBitmap( image, channel, classifier.Cascade.HasTiltedFeatures); // Creates a new list of detected objects. this.detectedObjects.Clear(); int width = integralImage.Width; int height = integralImage.Height; // Update parameters only if different size if (steps == null || width != lastWidth || height != lastHeight) { update(width, height); } Rectangle window = Rectangle.Empty; // For each scaling step foreach (float scaling in steps) { // Set the classifier window scale classifier.Scale = scaling; // Get the scaled window size window.Width = (int)(baseWidth * scaling); window.Height = (int)(baseHeight * scaling); // Check if the window is lesser than the minimum size if (window.Width < minSize.Width && window.Height < minSize.Height && window.Width > maxSize.Width && window.Height > maxSize.Height) { // If we are searching in greater to smaller mode, if (scalingMode == ObjectDetectorScalingMode.GreaterToSmaller) { goto EXIT; // it won't get bigger, so we should stop. } else { continue; // continue until it gets greater. } } // Grab some scan loop parameters int xStep = window.Width >> 3; int yStep = window.Height >> 3; int xEnd = width - window.Width; int yEnd = height - window.Height; // Check if we should run in parallel or sequential if (!UseParallelProcessing) { // Sequential mode. Scan the integral image searching // for objects in the window without parallelization. // For every pixel in the window column for (int y = 0; y < yEnd; y += yStep) { window.Y = y; // For every pixel in the window row for (int x = 0; x < xEnd; x += xStep) { window.X = x; if (searchMode == ObjectDetectorSearchMode.NoOverlap && overlaps(window)) { continue; // We have already detected something here, moving along. } // Try to detect and object inside the window if (classifier.Compute(integralImage, window)) { // an object has been detected detectedObjects.Add(window); if (searchMode == ObjectDetectorSearchMode.Single) { goto EXIT; // Stop on first object found } } } } } else { // Parallel mode. Scan the integral image searching // for objects in the window with parallelization. ConcurrentBag <Rectangle> bag = new ConcurrentBag <Rectangle>(); int numSteps = (int)Math.Ceiling((double)yEnd / yStep); // For each pixel in the window column Parallel.For(0, numSteps, (j, options) => { int y = j * yStep; // Create a local window reference Rectangle localWindow = window; localWindow.Y = y; // For each pixel in the window row for (int x = 0; x < xEnd; x += xStep) { if (options.ShouldExitCurrentIteration) { return; } localWindow.X = x; // Try to detect and object inside the window if (classifier.Compute(integralImage, localWindow)) { // an object has been detected bag.Add(localWindow); if (searchMode == ObjectDetectorSearchMode.Single) { options.Stop(); } } } }); // If required, avoid adding overlapping objects at // the expense of extra computation. Otherwise, only // add objects to the detected objects collection. switch (searchMode) { case ObjectDetectorSearchMode.NoOverlap: foreach (Rectangle obj in bag.Where(obj => !overlaps(obj))) { detectedObjects.Add(obj); } break; case ObjectDetectorSearchMode.Single: if (bag.TryPeek(out window)) { detectedObjects.Add(window); goto EXIT; } break; default: foreach (Rectangle obj in bag) { detectedObjects.Add(obj); } break; } } } EXIT: Rectangle[] objects = detectedObjects.ToArray(); checkSteadiness(objects); lastObjects = objects; return(objects); // Returns the array of detected objects. }