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

时间:2015-06-19 18:30:56

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

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

enter image description here

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

3 个答案:

答案 0 :(得分:4)

算法/创意1 您可以将图像划分为正方形,如下例所示: enter image description here

使用此逻辑,您只需检查每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<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

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

处理图像大约需要半秒钟(见附图)Benchmark for Image-Processing

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

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