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.
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.
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");
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.
}
I created a full solution for the problem (relying only on
Bitmap.GetPixel(Int32,Int32)
).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