Blob counter - counts objects in image, which are separated by black background.

The class counts and extracts stand alone objects in images using connected components labeling algorithm.

The algorithm treats all pixels with values less or equal to BackgroundThreshold as background, but pixels with higher values are treated as objects' pixels.

For blobs' searching the class supports 8 bpp indexed grayscale images and 24/32 bpp color images that are at least two pixels wide. Images that are one pixel wide can be processed if they are rotated first, or they can be processed with RecursiveBlobCounter. See documentation about BlobCounterBase for information about which pixel formats are supported for extraction of blobs.

Sample usage:

// create an instance of blob counter algorithm BlobCounter bc = new BlobCounter(); // process binary image bc.ProcessImage(image); // process blobs foreach (Rectangle rect in bc.GetObjectsRectangles()) { // ... }
Наследование: BlobCounterBase
Пример #1
2
        public void FindContourTest2()
        {
            Bitmap bmp = Accord.Imaging.Image.Clone(Properties.Resources.hand2);

            BlobCounter bc = new BlobCounter(bmp);
            bc.ObjectsOrder = ObjectsOrder.Size;
            Blob[] blobs = bc.GetObjectsInformation();
            bc.ExtractBlobsImage(bmp, blobs[0], true);
            List<IntPoint> expected = bc.GetBlobsEdgePoints(blobs[0]);
            Bitmap blob = blobs[0].Image.ToManagedImage();

            BorderFollowing bf = new BorderFollowing();
            List<IntPoint> actual = bf.FindContour(blob);

            foreach (IntPoint point in expected)
                Assert.IsTrue(actual.Contains(point));

            IntPoint prev = actual[0];
            for (int i = 1; i < actual.Count; i++)
            {
                IntPoint curr = actual[i];
                Assert.IsTrue(System.Math.Abs(prev.X - curr.X) <= 1 &&
                              System.Math.Abs(prev.Y - curr.Y) <= 1);
                prev = curr;
            }

            IntPoint first = actual[0];
            IntPoint last = actual[actual.Count - 1];
            Assert.IsTrue(System.Math.Abs(first.X - last.X) <= 1 &&
                          System.Math.Abs(first.Y - last.Y) <= 1);
        }
Пример #2
0
        public void FindDefectsTest()
        {
            Bitmap bmp = Accord.Imaging.Image.Clone(Properties.Resources.hand);

            Bitmap gray = Accord.Imaging.Filters.Grayscale.CommonAlgorithms.BT709.Apply(bmp);

            BlobCounter bc = new BlobCounter(gray);
            bc.ObjectsOrder = ObjectsOrder.Size;
            Blob[] blobs = bc.GetObjectsInformation();
            bc.ExtractBlobsImage(bmp, blobs[0], true);

            Bitmap blob = blobs[0].Image.ToManagedImage();

            BorderFollowing bf = new BorderFollowing();
            List<IntPoint> contour = bf.FindContour(blob);

            GrahamConvexHull graham = new GrahamConvexHull();
            List<IntPoint> hull = graham.FindHull(contour);

            ConvexHullDefects hullDefects = new ConvexHullDefects(10);
            List<ConvexityDefect> defects = hullDefects.FindDefects(contour, hull);

          /*  PointsMarker marker = new PointsMarker(hull, Color.Green, 10);
            marker.ApplyInPlace(blob);
            ImageBox.Show(blob);
            */

            Assert.AreEqual(2, defects.Count);
            Assert.AreEqual(new IntPoint(130, 10), contour[defects[0].Start]);
            Assert.AreEqual(new IntPoint(93, 109), contour[defects[0].Point]);
            Assert.AreEqual(new IntPoint(64, 9), contour[defects[0].End]);
            Assert.AreEqual(99.549179077148438, defects[0].Depth, 1e-5);
            Assert.IsFalse(double.IsNaN(defects[0].Depth));
            //    Assert.AreEqual(9912.9531239366424, defects[0].Area);

            Assert.AreEqual(new IntPoint(49, 18), contour[defects[1].Start]);
            Assert.AreEqual(new IntPoint(61, 106), contour[defects[1].Point]);
            Assert.AreEqual(new IntPoint(18, 127), contour[defects[1].End]);
            Assert.AreEqual(35.615153852366504, defects[1].Depth, 1e-5);
            Assert.IsFalse(double.IsNaN(defects[1].Depth));
            //    Assert.AreEqual(2293.7535682510002, defects[1].Area);

        }
Пример #3
0
        // Process image
        private void ProcessImage(Bitmap bitmap)
        {
            // lock image
            BitmapData bitmapData = bitmap.LockBits(
                new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadWrite, bitmap.PixelFormat);

            // step 1 - turn background to black
            ColorFiltering colorFilter = new ColorFiltering();

            colorFilter.Red = new IntRange(0, 64);
            colorFilter.Green = new IntRange(0, 64);
            colorFilter.Blue = new IntRange(0, 64);
            colorFilter.FillOutsideRange = false;

            colorFilter.ApplyInPlace(bitmapData);

            // step 2 - locating objects
            BlobCounter blobCounter = new BlobCounter();

            blobCounter.FilterBlobs = true;
            blobCounter.MinHeight = 5;
            blobCounter.MinWidth = 5;

            blobCounter.ProcessImage(bitmapData);
            Blob[] blobs = blobCounter.GetObjectsInformation();
            bitmap.UnlockBits(bitmapData);

            // step 3 - check objects' type and highlight
            SimpleShapeChecker shapeChecker = new SimpleShapeChecker();

            Graphics g = Graphics.FromImage(bitmap);
            Pen yellowPen = new Pen(Color.Yellow, 2); // circles
            Pen redPen = new Pen(Color.Red, 2);       // quadrilateral
            Pen brownPen = new Pen(Color.Brown, 2);   // quadrilateral with known sub-type
            Pen greenPen = new Pen(Color.Green, 2);   // known triangle
            Pen bluePen = new Pen(Color.Blue, 2);     // triangle

            for (int i = 0, n = blobs.Length; i < n; i++)
            {
                List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]);

                Accord.Point center;
                float radius;

                // is circle ?
                if (shapeChecker.IsCircle(edgePoints, out center, out radius))
                {
                    g.DrawEllipse(yellowPen,
                        (float)(center.X - radius), (float)(center.Y - radius),
                        (float)(radius * 2), (float)(radius * 2));
                }
                else
                {
                    List<IntPoint> corners;

                    // is triangle or quadrilateral
                    if (shapeChecker.IsConvexPolygon(edgePoints, out corners))
                    {
                        // get sub-type
                        PolygonSubType subType = shapeChecker.CheckPolygonSubType(corners);

                        Pen pen;

                        if (subType == PolygonSubType.Unknown)
                        {
                            pen = (corners.Count == 4) ? redPen : bluePen;
                        }
                        else
                        {
                            pen = (corners.Count == 4) ? brownPen : greenPen;
                        }

                        g.DrawPolygon(pen, ToPointsArray(corners));
                    }
                }
            }

            yellowPen.Dispose();
            redPen.Dispose();
            greenPen.Dispose();
            bluePen.Dispose();
            brownPen.Dispose();
            g.Dispose();

            // put new image to clipboard
            Clipboard.SetDataObject(bitmap);
            // and to picture box
            pictureBox.Image = bitmap;

            UpdatePictureBoxPosition();
        }
Пример #4
0
        private void BtnCalcAccord_Click(object sender, RoutedEventArgs e)
        {
            if (imageLoaded)
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                Accord.Imaging.UnmanagedImage unmanagedImage1 = Accord.Imaging.UnmanagedImage.FromManagedImage(GrayScaleImage);
                Accord.Imaging.BlobCounter    bc = new Accord.Imaging.BlobCounter
                {
                    BackgroundThreshold  = Color.Black,
                    CoupledSizeFiltering = true,
                    FilterBlobs          = true,
                    MinHeight            = 30,
                    MinWidth             = 30,
                    MaxHeight            = 100,
                    MaxWidth             = 100
                };


                bc.ProcessImage(GrayScaleImage);


                Bitmap indexMap = AForge.Imaging.Image.Clone(GrayScaleImage);

                for (int x = 0; x < indexMap.Width; x++)
                {
                    for (int y = 0; y < indexMap.Height; y++)
                    {
                        indexMap.SetPixel(x, y, System.Drawing.Color.Black);
                    }
                }


                System.Drawing.Rectangle[] rects = bc.GetObjectsRectangles();
                // process blobs
                BreadBlob[] breadBlob1     = new BreadBlob[bc.ObjectsCount];
                int         blobArrayIndex = 0;
                int         blobPt         = Convert.ToInt16(txbBlobNum.Text);
                int         blobThreshold  = Convert.ToInt16(txbBlobThreshold.Text);
                if (blobPt > bc.ObjectsCount)
                {
                    blobPt = bc.ObjectsCount - 1;
                }
                StaticsCalculator MuffinStatistics = new StaticsCalculator();

                Graphics g = Graphics.FromImage(indexMap);

                List <Accord.Imaging.Blob> blobList = new List <Accord.Imaging.Blob>();


                foreach (Accord.Imaging.Blob blob in bc.GetObjects(GrayScaleImage, false))
                {
                    blobList.Add(blob);

                    breadBlob1[blobArrayIndex] = new BreadBlob();
                    breadBlob1[blobArrayIndex].TopDownThreshold = blobThreshold;
                    byte[,] blobArray = new byte[blob.Rectangle.Width, blob.Rectangle.Height];

                    for (int x = blob.Rectangle.Left; x < blob.Rectangle.Right; x++)
                    {
                        for (int y = blob.Rectangle.Top; y < blob.Rectangle.Bottom; y++)
                        {
                            System.Drawing.Color tempPixelColor = GrayScaleImage.GetPixel(x, y);
                            blobArray[x - blob.Rectangle.Left, y - blob.Rectangle.Top] = tempPixelColor.G;
                        }
                    }

                    breadBlob1[blobArrayIndex].PixelArray = blobArray;

                    breadBlob1[blobArrayIndex].X = blob.Rectangle.X;
                    breadBlob1[blobArrayIndex].Y = blob.Rectangle.Y;

                    if (blobArrayIndex == blobPt)
                    {
                        System.Drawing.Rectangle tempRect = blob.Rectangle;
                        tempRect.X      -= 1;
                        tempRect.Y      -= 1;
                        tempRect.Width  += 2;
                        tempRect.Height += 2;

                        Accord.Imaging.Drawing.Rectangle(unmanagedImage1, tempRect, System.Drawing.Color.Yellow);
                    }

                    if (breadBlob1[blobArrayIndex].IsTop())
                    {
                        Accord.Imaging.Drawing.Rectangle(unmanagedImage1, blob.Rectangle, System.Drawing.Color.Green);
                    }
                    else
                    {
                        Accord.Imaging.Drawing.Rectangle(unmanagedImage1, blob.Rectangle, System.Drawing.Color.Red);
                    }

                    RectangleF rectf = new RectangleF(blob.Rectangle.X, blob.Rectangle.Y, blob.Rectangle.Width, blob.Rectangle.Height);

                    g.SmoothingMode     = SmoothingMode.AntiAlias;
                    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    g.PixelOffsetMode   = PixelOffsetMode.HighQuality;
                    g.DrawString(Convert.ToString(blob.ID - 1), new Font("Arial", 5), System.Drawing.Brushes.White, rectf);

                    lblBlobHeight.Content = blob.Rectangle.Height;
                    lblBlobWidth.Content  = blob.Rectangle.Width;

                    blobArrayIndex++;
                }

                lblAccordStdDev.Content = blobList[blobPt].ColorStdDev.B;
                BitmapImage indexMap_temp = ToBitmapImage(indexMap);
                g.Flush();
                // conver to managed image if it is required to display it at some point of time
                Bitmap managedImage = unmanagedImage1.ToManagedImage();

                // create filter
                Add filter = new Add(indexMap);
                // apply the filter
                Bitmap      resultImage    = filter.Apply(managedImage);
                BitmapImage GrayImage_temp = ToBitmapImage(resultImage);

                imgGray.Source = GrayImage_temp;

                stopwatch.Stop();
                lblTime.Content = stopwatch.ElapsedMilliseconds;

                lblBlobCount.Content = bc.ObjectsCount;


                lblLib.Content        = "Accord";
                lblVariance.Content   = breadBlob1[blobPt].GetVariance(BreadBlob.VarianceType.All);
                lblX.Content          = breadBlob1[blobPt].X;
                lblY.Content          = breadBlob1[blobPt].Y;
                lblQ1Variance.Content = "";
                lblQ2Variance.Content = "";
                lblQ3Variance.Content = "";
                lblQ4Variance.Content = "";
                lblQAverage.Content   = breadBlob1[blobPt].GetVariance(BreadBlob.VarianceType.QAverage);
                //lblAllMuffinStat.Content = MuffinStatistics.StandardDeviation;
            }
        }
Пример #5
0
        public void GetBlobs2(string inputFile, string outputPath)
        {
            UnmanagedImage skewedImg = null;
            UnmanagedImage bwInvImg  = null;

            {
                var    bmp          = new Bitmap(inputFile);
                var    img          = UnmanagedImage.FromManagedImage(bmp);
                var    grayImg      = Accord.Imaging.Filters.Grayscale.CommonAlgorithms.BT709.Apply(img);
                var    bwImg        = new Accord.Imaging.Filters.OtsuThreshold().Apply(grayImg);
                var    skewChecker  = new DocumentSkewChecker();
                double angle        = skewChecker.GetSkewAngle(bwImg);
                var    rotateFilter = new Accord.Imaging.Filters.RotateBilinear(-angle);
                skewedImg = rotateFilter.Apply(img);
                bwImg.Dispose();
                grayImg.Dispose();
                img.Dispose();
                bmp.Dispose();
            }

            {
                var grayImg       = Accord.Imaging.Filters.Grayscale.CommonAlgorithms.BT709.Apply(skewedImg);
                var bwImg         = new Accord.Imaging.Filters.OtsuThreshold().Apply(grayImg);
                var openingFilter = new Accord.Imaging.Filters.Opening();
                openingFilter.ApplyInPlace(bwImg);
                bwInvImg = new Accord.Imaging.Filters.Invert().Apply(bwImg);
                bwImg.Dispose();
                grayImg.Dispose();
            }


            var blobProc = new Accord.Imaging.BlobCounter();

            blobProc.ProcessImage(bwInvImg);
            var blobs = blobProc.GetObjectsInformation().ToList();

            foreach (Accord.Imaging.Blob blob in blobs.OrderBy(b => b.Rectangle.Left).ThenBy(b => b.Rectangle.Top))
            {
                Console.WriteLine("{0} {1}", blob.Rectangle.ToString(), blob.Area.ToString());
            }

            //Layout parameters
            var expectedLineMarkerSize = new System.Drawing.Size(25, 10); //new System.Drawing.Size(35, 15);
            var expectedCellSize       = new System.Drawing.Size(15, 10); //new System.Drawing.Size(20, 13);
            int expectedNumlineMarkers = 19;                              // 23;
            int tolerance = 3;
            //Limits to determine in a cell is marked
            double fullnessOk     = .75;
            double fullnessUnsure = .65;
            var    questions      = new List <Tuple <int, Accord.Imaging.Blob, List <Accord.Imaging.Blob> > >();

            {
                var lineMarkers = blobs.Where(b =>
                {
                    if (b.Rectangle.Width < expectedLineMarkerSize.Width - tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Width > expectedLineMarkerSize.Width + tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Height < expectedLineMarkerSize.Height - tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Height > expectedLineMarkerSize.Height + tolerance)
                    {
                        return(false);
                    }
                    return(true);
                })
                                  .OrderBy(b => b.Rectangle.Left)
                                  .ThenBy(b => b.Rectangle.Top)
                                  .ToList();
                if (lineMarkers.Count() != expectedNumlineMarkers)
                {
                    throw new Exception(string.Format("Can't locate all line markers. Expected {0}, found {1}", expectedNumlineMarkers, lineMarkers.Count));
                }
                var cells = blobs.Where(b =>
                {
                    if (b.Rectangle.Width < expectedCellSize.Width - tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Width > expectedCellSize.Width + tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Height < expectedCellSize.Height - tolerance)
                    {
                        return(false);
                    }
                    if (b.Rectangle.Height > expectedCellSize.Height + tolerance)
                    {
                        return(false);
                    }
                    return(true);
                }).ToList();

                int idxLine = 1;
                foreach (var lineMarker in lineMarkers.OrderBy(b => b.CenterOfGravity.Y))
                {
                    var cellsOfLine = cells.Where(b => Math.Abs(b.CenterOfGravity.Y - lineMarker.CenterOfGravity.Y) <= tolerance)
                                      .Take(5)
                                      .ToList()
                                      .OrderBy(b => b.CenterOfGravity.X)
                                      .ToList();
                    questions.Add(new Tuple <int, Accord.Imaging.Blob, List <Accord.Imaging.Blob> >(idxLine, lineMarker, cellsOfLine));
                    idxLine++;
                }
            }

            {
                var bmp = skewedImg.ToManagedImage();
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    foreach (var question in questions)
                    {
                        g.FillRectangle(new SolidBrush(Color.Blue), question.Item2.Rectangle);
                        g.DrawString(question.Item1.ToString(), new System.Drawing.Font("Arial", 8), new SolidBrush(Color.White), question.Item2.Rectangle);
                        int column = 1;
                        foreach (Accord.Imaging.Blob blob in question.Item3.OrderBy(b => b.Rectangle.Left).ThenBy(b => b.Rectangle.Top))
                        {
                            if (System.Diagnostics.Debugger.IsAttached)
                            {
                                Console.WriteLine("Line {0}, Column {1}, Fullness {2}", question.Item1, column, Math.Round(blob.Fullness, 2));
                            }
                            if (blob.Fullness >= fullnessOk)
                            {
                                g.DrawRectangle(new Pen(Color.Green, 2), blob.Rectangle);
                            }
                            else if (blob.Fullness >= fullnessUnsure)
                            {
                                g.DrawRectangle(new Pen(Color.Yellow, 2), blob.Rectangle);
                            }
                            else
                            {
                                g.DrawRectangle(new Pen(Color.Red, 2), blob.Rectangle);
                            }
                            column++;
                        }
                    }
                }
                // bmp.Save(outp);
            }
        }
Пример #6
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="HslBlobTracker"/> class.
        /// </summary>
        /// 
        /// <param name="filter">The filter.</param>
        /// 
        public HslBlobTracker(HSLFiltering filter)
        {
            this.filter = filter;
            this.blobCounter = new BlobCounter();
            this.trackingObject = new TrackingObject();

            blobCounter.CoupledSizeFiltering = false;
            blobCounter.FilterBlobs = true;
        }
Пример #7
0
        private void captureHand(UnmanagedImage mask, Rectangle rect, PictureBox pbArm, PictureBox pbHand)
        {
            Crop c = new Crop(rect);
            var handImage = c.Apply(mask);

            var ps = handImage.Collect16bppPixelValues(handImage.CollectActivePixels());

            if (ps.Length > 0)
            {
                ushort max = Matrix.Max(ps);

                LevelsLinear16bpp levels = new LevelsLinear16bpp();
                levels.InGray = new IntRange(0, max);
                levels.OutGray = new IntRange(0, 65535);
                levels.ApplyInPlace(handImage);


                // pbArm.Image = handImage.ToManagedImage();


                double cutoff = 30000;
                Threshold th = new Threshold((int)cutoff);
                var handMask = th.Apply(handImage);

                var handMask8bit = Accord.Imaging.Image.Convert16bppTo8bpp(handMask.ToManagedImage());

                BlobCounter bch = new BlobCounter();
                bch.ObjectsOrder = ObjectsOrder.Area;
                bch.ProcessImage(handMask8bit);
                var blob = bch.GetObjectsInformation();

                if (blob.Length > 0)
                {
                    Intersect inters = new Intersect();
                    inters.UnmanagedOverlayImage = handMask;
                    inters.ApplyInPlace(handImage);

                    Crop ch = new Crop(blob[0].Rectangle);
                    handImage = ch.Apply(handImage);

                    ResizeNearestNeighbor res = new ResizeNearestNeighbor(25, 25);
                    handImage = res.Apply(handImage);

                    var leftHand = Accord.Imaging.Image.Convert16bppTo8bpp(handImage.ToManagedImage());

                    pbHand.Image = leftHand;
                }
            }
        }
Пример #8
0
        private void videoSourcePlayer1_NewFrame(object sender, ref Bitmap image)
        {
            Invert inv = new Invert();
            inv.ApplyInPlace(image);

            UnmanagedImage ui = UnmanagedImage.FromManagedImage(image);

            pictureBox1.Image = image;


            if (controller.Tracker.TrackingObject == null)
                return;

            if (controller.Tracker.TrackingObject.IsEmpty)
                return;

            var rect = controller.Tracker.TrackingObject.Rectangle;
            Crop crop = new Crop(rect);

            UnmanagedImage head = crop.Apply(ui);

            var points = new List<IntPoint>() { new IntPoint(head.Width / 2, head.Height / 2) };
            var pps = head.Collect16bppPixelValues(points);

            double mean = pps.Mean();

            double cutoff = mean + 15;
            Threshold t = new Threshold((int)cutoff);
            var mask = t.Apply(ui);



            LevelsLinear16bpp levels = new LevelsLinear16bpp();
            levels.InGray = new IntRange((int)cutoff, 65535);
            levels.OutGray = new IntRange(0, 65535);
            levels.ApplyInPlace(ui);


            var mask8bit = Accord.Imaging.Image.Convert16bppTo8bpp(mask.ToManagedImage());



            BlobCounter bc = new BlobCounter();
            bc.ObjectsOrder = ObjectsOrder.Area;
            bc.ProcessImage(mask8bit);
            var blobs = bc.GetObjectsInformation();

            inv.ApplyInPlace(image);
            Intersect intersect = new Intersect();
            intersect.UnmanagedOverlayImage = mask;
            mask = intersect.Apply(ui);

            List<Rectangle> rects = new List<Rectangle>();

            // Extract the uppermost largest blobs.
            for (int i = 0; i < blobs.Length; i++)
            {
                double dx = (blobs[i].Rectangle.Top - controller.Tracker.TrackingObject.Center.Y);
                double d = (dx * dx) / controller.Tracker.TrackingObject.Area;
                if (d < 2 && blobs[i].Area > 1000)
                    rects.Add(blobs[i].Rectangle);
            }

            rects.Sort(compare);

            if (rects.Count > 0)
            {
                captureHand(mask, rects[0], pbLeftArm, pbLeftHand);
            }
            if (rects.Count > 1)
            {
                captureHand(mask, rects[1], pbRightArm, pbRightHand);

            }

            RectanglesMarker marker = new RectanglesMarker(rects);
            marker.MarkerColor = Color.White;
            marker.ApplyInPlace(mask8bit);

            image = mask.ToManagedImage();
        }