鉴于以下图像,如何使用C#,EmguCV或AForge在此图像中检测黑色子弹(90子弹)?
我尝试使用GetPixel(x,y)
方法,但它只检查逐个像素,它非常慢,我需要检测项目符号而不是像素。
答案 0 :(得分:4)
算法/创意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<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");
答案 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;
}
}
}
处理图像大约需要半秒钟(见附图)
我们的想法是将提供的图像迭代地分割成只包含点子集的行和列,直到只包含一个点。
点可以任意地分布在图像上。希望这有帮助