【问题标题】:How to detect black Bullets in Image?如何检测图像中的黑色子弹?
【发布时间】:2015-06-19 18:30:56
【问题描述】:

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

我尝试使用GetPixel(x,y) 方法,但它只能逐像素检查,速度非常慢,我需要检测子弹而不是像素。

【问题讨论】:

  • 您可以编写一个算法,它会跳过几个像素,直到找到黑色像素。在此之后,您必须为下一个黑色像素定义允许的偏移量,以便您可以识别它是否连接到要识别的字符
  • 我试过这个算法,但它速度慢而且准确,我要求一些经过测试的算法可以做到这一点
  • 在 emgu 中搜索 FindContours。它可能会帮助你
  • 定义检测。你需要计数,中点的位置,..?
  • 您可以将图像拆分为 9 张图像(例如)。然后使用 Tpl 中的算法。 Parallel.For

标签: c# c#-4.0 image-processing emgucv aforge


【解决方案1】:

算法/想法 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 第二种算法分析每个像素,并且更容易实现。一旦有黑色像素,它就会检查之前在同一垂直或水平线上是否有黑色像素,并在这种情况下跳过,直到没有黑色像素在线。

进一步的改进是存储第一个点的高度并使sn-p中间的条件更漂亮。

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】:

    除非您这样做是为了了解有关图像处理的更多信息,否则不要重新发明轮子。只需使用 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.
    }
    

    【讨论】:

    • 这个方法+1。只是想补充一点,在使用此方法之前应该反转图像,因为它会在黑色背景上寻找白色前景像素。
    【解决方案3】:

    我为这个问题创建了一个完整的解决方案(仅依赖于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;
            }
        }
    }
    

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

    这个想法是迭代地将提供的图像拆分为仅包含点子集的行和列,直到恰好包含一个点。

    这些点可以任意分布在图像上。希望这会有所帮助

    【讨论】:

    • 一个好主意,即使使用 O(n^4) 周期效率有点低
    猜你喜欢
    • 2012-02-15
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 2011-09-22
    相关资源
    最近更新 更多