使用 C# 根据颜色和大小检测和跟踪实时摄像头视频中的对象
使用 C# 检测和跟踪实时网络摄像头视频中的对象。
引言
您可以实时选择一种颜色,它会跟踪该颜色的对象并为您提供其位置。我为此使用了 Aforge 库。我还使用了 .NET Framework 4.0。这是一个 C# 桌面应用程序,它可以达到每秒 25 帧。您可以随时更改颜色大小,绘制点的颜色也会随之改变。
背景
我在 CodeProject 上看到了一个非常有趣的 项目,名为制作 乐高倾斜摄像头。借助这个项目,我想到可以制作一个实时跟踪器,其中颜色和对象的大小也可以实时更改。并可以用该颜色在位图中绘制该对象的移动轨迹。我使用了他们代码的一部分,但为了提高准确性,我使用了单独的颜色过滤器。
使用代码
步骤非常简单
- 从网络摄像头捕获视频帧
- 按给定颜色进行过滤(此处使用欧几里得过滤)
- 转换为灰度
- 按给定大小查找对象
- 查找最大的对象
- 在位图中绘制对象位置
首先,我将解释我的软件是如何工作的。它可以以默认颜色黑色和 120 的颜色范围开始跟踪,但您可以实时更改它。要更改颜色,请使用颜色对话框选择一种颜色,请记住按“确定”,并在实心颜色框中检查您的所需颜色是否存在。
要选择范围并定义对象的最小尺寸,请参见下图,这里有三个数字上调/下调列表。objectsize
以像素为单位计算。现在关于视图
有四个视图,其中三个是 Aforge 视频源控件,另一个是 PictureBox
。第一个显示正常视频,第二个显示所有检测到的对象,第三个框仅显示最大的对象,第四个框仅在最大对象位置绘制一个 0 符号。请看下图以获得更清晰的视图
我还添加了一个 RichTextBox
,用于显示 biggestrectangle
位置的像素值。现在关于连接代码,我使用普通的 Aforge 连接代码。
为此,您需要下载 Aforge DLL,并添加一个非常好的视频控件来显示视频。您可以将其添加到工具箱中。只需单击工具箱上的空白区域,然后选择“项”->“浏览 DLL”->“添加控件”。
using AForge;
using AForge.Video.DirectShow;
然后初始化
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count == 0)
throw new ApplicationException();
foreach (FilterInfo device in videoDevices)
{
VideoCaptureDevice videoSource = new VideoCaptureDevice(device.MonikerString);
videoSource.DesiredFrameSize = new Size(320, 240);
videoSource.DesiredFrameRate = 15;
videoSourcePlayer1.VideoSource = videoSource;
videoSourcePlayer1.Start();
}
对于 videoplayercontrol
,在其 misc 中添加一个名为的事件
private void videoSourcePlayer1_NewFrame(object sender, ref Bitmap image)
{
//work with image
}
为了理解过滤是如何工作的,我将通过一张水母的图片来解释。这里,我以红色中心颜色为例。在我提供的软件中,用户可以选择他所需的颜色和大小。
在该函数中,我通过编写欧几里得过滤来应用我的颜色检测代码,然后使用 blob counter 来提取数据。我将在下面详细解释。
要查看 EuclideanColorFiltering
的工作原理,请先查看
我们将应用一个颜色过滤器。使用 euclideanfiltering
是一个非常简单的代码
// create filter
EuclideanColorFiltering filter = new EuclideanColorFiltering( );
// set center colol and radius
filter.CenterColor = Color.FromArgb( 215, 30, 30 );
filter.Radius = 100;
// apply the filter
filter.ApplyInPlace( image );
现在看看效果
嗯,为了理解它是如何工作的,请仔细查看代码:
filter.CenterColor = Color.FromArgb( 215, 30, 30 );
filter.Radius = 100;
第一行选择选定的颜色值。大家都知道颜色值的范围是 0 到 255。使用 filter.CenterColor = Color.FromArgb( 215, 30, 30 );
我指定我的中心颜色将是一种受红色影响的颜色,因为这里的红色值为 215,绿色和蓝色值为 30,而 filter.Radius = 100
意味着所有接近 100 的颜色值都在我指定的颜色范围内。现在我的过滤器会过滤像素,并决定哪些颜色在指定中心和半径的 RGB 球体内部/外部——它会保留球体内部/外部的颜色像素,其余填充为指定颜色。
现在对于对象检测,我使用位图数据并使用 lockbits
方法。要清楚地理解此方法,请在此 处 查看。然后,我们将其转换为灰度算法,然后解锁。
BitmapData objectsData = image.LockBits(new Rectangle(0, 0,
image.Width, image.Height),ImageLockMode.ReadOnly, image.PixelFormat);
// grayscaling
UnmanagedImage grayImage = grayscaleFilter.Apply(new UnmanagedImage(objectsData));
// unlock image
image.UnlockBits(objectsData);
现在对于对象,我们使用 blobcounter
。这是 Aforge 提供的一个非常强大的类。
blobCounter.MinWidth = 5;
blobCounter.MinHeight = 5;
blobCounter.FilterBlobs = true;
blobCounter.ProcessImage(grayImage);
Rectangle[] rects = blobCounter.GetObjectRectangles();
foreach(Rectangle recs in rects)
if (rects.Length > 0)
{
foreach (Rectangle objectRect in rects)
{
Graphics g = Graphics.FromImage(image);
using (Pen pen = new Pen(Color.FromArgb(160, 255, 160), 5))
{
g.DrawRectangle(pen, objectRect);
}
g.Dispose();
}
}
blobCounter.MinWidth
和 blobCounter.MinHeight
定义了对象的最小像素尺寸,而 blobCounter.GetObjectRectangles()
返回所有对象的矩形位置,并使用 graphics
类,我在图像上绘制一个矩形。
现在,如果您只想获取最大的对象,有一个方法可以做到这一点
blobCounter.MinWidth = 5;
blobCounter.MinHeight = 5;
blobCounter.FilterBlobs = true;
blobCounter.ObjectsOrder = ObjectsOrder.Size;
blobCounter.ProcessImage(grayImage);
Rectangle[] rects = blobCounter.GetObjectRectangles();
foreach(Rectangle recs in rects)
if (rects.Length > 0)
{
Rectangle objectRect = rects[0];
Graphics g = Graphics.FromImage(image);
using (Pen pen = new Pen(Color.FromArgb(160, 255, 160), 5))
{
g.DrawRectangle(pen, objectRect);
}
g.Dispose();
}
现在图像看起来是这样的

但是,如果您想提取它,可以使用以下代码
Bitmap bmp = new Bitmap(rects[0].Width, rects[0].Height);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(c, 0, 0, rects[0], GraphicsUnit.Pixel);
这样您就可以获得您的对象了

为了绘制位图,我不得不使用多线程。在 videosurecplayer
事件中调用了该线程。
void p(object r)
{
try
{//create image
Bitmap b = new Bitmap(pictureBox1.Image);
Rectangle a = (Rectangle)r;
Pen pen1 = new Pen(Color.FromArgb(160, 255, 160), 3);
Graphics g2 = Graphics.FromImage(b);
pen1 = new Pen(color, 3);
SolidBrush b5 = new SolidBrush(color);
Font f = new Font(Font, FontStyle.Bold);
g2.DrawString("o", f, b5, a.Location);//draw the position
g2.Dispose();
pictureBox1.Image = (System.Drawing.Image)b;
this.Invoke((MethodInvoker)delegate
{
richTextBox1.Text = a.Location.ToString() +
"\n" + richTextBox1.Text + "\n"; ;
});
}
catch (Exception faa)
{
Thread.CurrentThread.Abort();
}
Thread.CurrentThread.Abort();
}
由于跨线程,我必须使用 invoke 来写入 textbox
。为了发送矩形对象,我使用了一个参数化线程。线程 aa
调用函数 p()
来在位图上绘制并写入文本框。
if (rects.Length > 0)
{
Rectangle objectRect = rects[0];
// draw rectangle around derected object
Graphics g = Graphics.FromImage(image);
using (Pen pen = new Pen(Color.FromArgb(160, 255, 160), 5))
{
g.DrawRectangle(pen, objectRect);
}
g.Dispose();
int objectX = objectRect.X + objectRect.Width / 2 - image.Width / 2;
int objectY = image.Height / 2 - (objectRect.Y + objectRect.Height / 2);
ParameterizedThreadStart t = new ParameterizedThreadStart(p);
Thread aa = new Thread(t);
aa.Start(rects[0]);
}
关注点
这里必须实现参数化线程和跨线程,还需要应用 invoke 方法进行写入,通过良好的多线程,我们可以提高 framerate
。关于过滤,欧几里得过滤比 HSLfiltering
或 Colorfiltering
,yrbrcr
过滤更准确。
感谢 Andrew Kirillov 的帮助。