How to detect black Bullets in Image?

梦想的初衷 提交于 2019-12-08 08:12:53

问题


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

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


回答1:


Algorithm/Idea 1 You can divide your image in squares as shown in this sample:

With this logic you only have to check every 20th pixel. As soon as you know where the first dot is, you will know that every other dot have to be in the same horizontal line (in your provided sample).

A sample algorithm would look similar to this (please note that it need further improvement):

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");

I added an output so you can check which dots got detected (all containing red pixels).

Further improvement would be to find the center of a dot. After that you should know the width (and height) and can start at first upper left dot with an offset of the dots width.

Algorithm 2 The second algorithm analyses each pixel and is a lot easier to implement. As soon as there's a black pixel it checks if there was a black pixel in the same vertical or horizontal line before and skips in this case until there is no black pixel in line.

Further improvement would be to store the height of the first dot and to make the condition in the middle of the snippet more beautiful.

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");




回答2:


Unless you are doing this to learn more about image processing, do not re-invent the wheel. Just use emgucv (or a similar library). The emgucv syntax is rather unfriendly (mostly because of its underlying Win32 OpenCV implementation), but basically it comes down to

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 full solution for 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 roughly half a second to process the image (see attached picture)

The idea is to iteratively split the provided image into rows and colums that do only contain a subset of dots until there is exactly one dot contained.

The dots may be arbitrarily spread over the image. Hope this helps



来源:https://stackoverflow.com/questions/30944998/how-to-detect-black-bullets-in-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!