How to identify black bullets in an image?

Given the following image: How can I detect black bullets (90 bullets) in this image using C #, EmguCV or AForge?

enter image description here

I tried to use the method GetPixel(x,y), but it only checks pixel by pixel, it is very slow, and I need to detect bullets, not pixels.

+1
source share
3 answers

Algorithm / Idea 1 You can divide the image into squares, as shown in this example: enter image description here

With this logic, you only need to check every 20th pixel. As soon as you find out where the first point is, you will find out that every other point must be in the same horizontal line (in the provided sample).

( , ):

Bitmap myBitmap = new Bitmap ("input.png");

int skipX = 12;
int skipY = 12;

int detectedDots = 0;

for (int x = 0; x < myBitmap.Width; x += skipX) {
    for (int y = 0; y < myBitmap.Height; y += skipY) {
        Color pixelColor = myBitmap.GetPixel (x, y);

        if (pixelColor.R + pixelColor.G + pixelColor.B == 0) {
            myBitmap.SetPixel (x, y, Color.Red);
            detectedDots++;
        }
    }
}

myBitmap.Save ("output.png");

Console.WriteLine (detectedDots + " dots detected");

, , ( ).

. ( ) .

2 . , , , .

.

Stopwatch watch = new Stopwatch(); watch.Start();

Bitmap myBitmap = new Bitmap ("input.png");

int dotsDetected = 0;


List<int> xFound = new List<int>();


for (int x = 0; x < myBitmap.Width; x++) {
    bool yFound = false;
    bool dotFound = false;
    for (int y = 0; y < myBitmap.Height; y++) {
        Color pixelColor = myBitmap.GetPixel (x, y);
        if (pixelColor.R + pixelColor.G + pixelColor.B == 0) {
            dotFound = true;

            if (yFound)
                continue;

            if (xFound.Contains (y) 
                || xFound.Contains (y + 1) 
                || xFound.Contains (y + 2) 
                || xFound.Contains (y + 3)
                || xFound.Contains (y + 4)
                || xFound.Contains (y - 1) 
                || xFound.Contains (y - 2) 
                || xFound.Contains (y - 3)
                || xFound.Contains (y - 4)) {
                yFound = true;
                continue;
            }

            xFound.Add (y);
            //myBitmap.SetPixel (x, y, Color.Red);

            dotsDetected++;
            yFound = true;
        } else
            yFound = false;
    }

    if(!dotFound) //no dot found in this line
        xFound.Clear();
}

//myBitmap.Save ("output.png");

watch.Stop();

Console.WriteLine("Picture analyzed in " + watch.Elapsed.TotalSeconds.ToString("#,##0.0000"));

Console.WriteLine (dotsDetected + " dots detected");

Performance

+4

, , . emgucv ( ). emgucv ( - Win32 OpenCV),

Contour<Point> contour = img.FindContours(CV_CHAIN_APPROX_TC89_L1, RETR_TYPE.CV_RETR_LIST);

for (; contour != null; contour = contour.HNext)
{
    // You now have the contours. These have characteristics like a boundingRect, which is an easy way to approach the center of a circle.
}
+3

I created a complete solution to the problem (relying only on Bitmap.GetPixel(Int32,Int32)).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;

namespace StackOverflow
{
    public static class Program
    {
        static void Main(string[] args)
        {
            const String PATH = @"C:\sim\sbro6.png";

            Stopwatch watch = new Stopwatch(); watch.Start();
            List<Bitmap> l_new, l_old;

            {

                Bitmap bmp = Image.FromFile(PATH) as Bitmap;

                // Initialization
                l_old = new List<Bitmap>();
                l_new = new List<Bitmap>(); l_new.Add(bmp);

                // Splitting
                while (l_new.Count > l_old.Count)
                {
                    l_old = l_new; l_new = new List<Bitmap>();

                    l_new.AddRange(SplitBitmapsVertically(SplitBitmapsHorizontally(l_old)));
                }

                // for (Int32 i = 0; i < l_new.Count; i++)
                // {
                //     l_new[i].Save(@"C:\sim\bitmap_" + i + ".bmp");
                // }

            }

            watch.Stop();

            Console.WriteLine("Picture analyzed in ".PadRight(59,'.') + " " + watch.Elapsed.TotalSeconds.ToString("#,##0.0000"));
            Console.WriteLine("Dots found ".PadRight(59, '.') + " " + l_new.Count);
            Console.WriteLine();
            Console.WriteLine("[ENTER] terminates ...");
            Console.ReadLine();

        }

        static List<Bitmap> SplitBitmapsVertically(List<Bitmap> l_old)
        {
            Int32 x_start = -1; Bitmap bmp; Boolean colContainsData = false;
            List<Bitmap> l = new List<Bitmap>();

            foreach(Bitmap b in l_old)
            {
                for (Int32 x = 0; x < b.Width; x++)
                {
                    colContainsData = false;

                    for (Int32 y = 0; y < b.Height; y++)
                    {
                        if (b.GetPixel(x, y).ToArgb() != Color.White.ToArgb())
                        {
                            colContainsData = true;
                        }
                    }

                    if (colContainsData) if (x_start < 0) { x_start = x; }
                    if (!colContainsData || (x == (b.Width - 1))) if (x_start >= 0)
                        {
                            bmp = new Bitmap(x - x_start, b.Height);

                            for (Int32 x_tmp = x_start; x_tmp < x; x_tmp++)
                                for (Int32 y_tmp = 0; y_tmp < b.Height; y_tmp++)
                                {
                                    bmp.SetPixel(x_tmp - x_start, y_tmp, b.GetPixel(x_tmp, y_tmp));
                                }

                            l.Add(bmp); x_start = -1;
                        }
                }
            }

            return l;
        }
        static List<Bitmap> SplitBitmapsHorizontally(List<Bitmap> l_old)
        {
            Int32 y_start = -1; Bitmap bmp; Boolean rowContainsData = false;
            List<Bitmap> l = new List<Bitmap>();

            foreach (Bitmap b in l_old)
            {
                for (Int32 y = 0; y < b.Height; y++)
                {
                    rowContainsData = false;

                    for (Int32 x = 0; x < b.Width; x++)
                    {
                        if (b.GetPixel(x, y).ToArgb() != Color.White.ToArgb())
                        {
                            rowContainsData = true;
                        }
                    }

                    if (rowContainsData) if (y_start < 0) { y_start = y; }
                    if (!rowContainsData || (y == (b.Height - 1))) if (y_start >= 0)
                        {
                            bmp = new Bitmap(b.Width, y - y_start);

                            for (Int32 x_tmp = 0; x_tmp < b.Width; x_tmp++)
                                for (Int32 y_tmp = y_start; y_tmp < y; y_tmp++)
                                {
                                    bmp.SetPixel(x_tmp, y_tmp - y_start, b.GetPixel(x_tmp, y_tmp));
                                }

                            l.Add(bmp); y_start = -1;
                        }
                }
            }

            return l;
        }
    }
}

It takes about half a second to process the image (see attached image) Benchmark for image processing

The idea is to iteratively split the provided image into rows and columns that contain only a subset of the points, until exactly one point remains.

Points can be arbitrarily distributed over the image. Hope this helps

+1
source

All Articles