如何检测图像中的黑色子弹?

鉴于以下图像,我如何使用C#,EmguCV或AForge检测此图像中的黑色子弹(90子弹)?

在此处输入图像描述

我尝试使用GetPixel(x,y)方法,但它只检查逐个像素,它非常慢,我需要检测项目符号而不是像素。

算法/创意1您可以将图像划分为正方形,如下例所示: 在此处输入图像描述

使用此逻辑,您只需检查每20个像素。 只要知道第一个点的位置,就会知道每个其他点必须在同一水平线上(在您提供的样本中)。

示例算法看起来与此类似(请注意,它需要进一步改进):

 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 xFound = new List(); 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"); 

性能

除非您这样做是为了了解有关图像处理的更多信息,否则不要重新发明轮子。 只需使用emgucv(或类似的库)。 emgucv语法相当不友好(主要是因为它的底层Win32 OpenCV实现),但基本上它归结为

 Contour 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. } 

我为问题创建了一个完整的解决方案(仅依赖于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 l_new, l_old; { Bitmap bmp = Image.FromFile(PATH) as Bitmap; // Initialization l_old = new List(); l_new = new List(); l_new.Add(bmp); // Splitting while (l_new.Count > l_old.Count) { l_old = l_new; l_new = new List(); 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 SplitBitmapsVertically(List l_old) { Int32 x_start = -1; Bitmap bmp; Boolean colContainsData = false; List l = new List(); 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 SplitBitmapsHorizontally(List l_old) { Int32 y_start = -1; Bitmap bmp; Boolean rowContainsData = false; List l = new List(); 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; } } } 

处理图像大约需要半秒钟(见附图) 图像处理基准

我们的想法是将提供的图像迭代地分割成仅包含点子集的行和列,直到只包含一个点。

点可以任意地分布在图像上。 希望这可以帮助