简介:本来想用红外线对射传感器做,苦于最近手头比较紧,就改用摄像头了。
Aforge是一套.NET开发的开源图像,视频处理库,涵盖面广的邪乎,我这里用到的部分只是冰山里的一滴水。
原理: 利用Aforge的图像差异算法,对比每帧图像和它上一帧图像的差异度,如果达到标准,计数器开始计数,当变化量达到一个值的时候,就开始录像,将接下来的每一帧写入一个AVI文件。同理,在开始录像的同时启动一个静止量的计数器。当静止指标达到限量后,停止录像。
主要代码:
public partial class Form1 : Form
{
FilterInfoCollection videoDevices;
private string device;
private float motionAlarmLevel = 0.015f;
private List<float> motionHistory = new List<float>();
MotionDetector detector = new MotionDetector(
new TwoFramesDifferenceDetector(),
new MotionAreaHighlighting());
private int flash = 0;
private bool beginREC = false;
private bool StopREC = true;
private int statCount = 0;
private int MoveCount = 0;
private const int MaxStat = 10;
private AVIWriter writer;
public Form1()
{
Control.CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
#region 初始化设备列表
try
{
// enumerate video devices
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count == 0)
throw new ApplicationException();
// add all devices to combo
foreach (FilterInfo device in videoDevices)
{
devicesCombo.Items.Add(device.Name);
}
devicesCombo.Items.Insert(0, "");
}
catch (ApplicationException)
{
devicesCombo.Items.Add("No local capture devices");
devicesCombo.Enabled = false;
}
devicesCombo.SelectedIndex = 0;
#endregion
}
private void devicesCombo_SelectedIndexChanged(object sender, EventArgs e)
{
device = videoDevices[devicesCombo.SelectedIndex].MonikerString;
// create video source
VideoCaptureDevice videoSource = new VideoCaptureDevice(device);
// open it
OpenVideoSource(videoSource);
}
// Open video source
private void OpenVideoSource(IVideoSource source)
{
// set busy cursor
this.Cursor = Cursors.WaitCursor;
// stop current video source
videoSourcePlayer.SignalToStop();
videoSourcePlayer.WaitForStop();
// start new video source
videoSourcePlayer.VideoSource = source;
videoSourcePlayer.Start();
//// reset statistics
//statIndex = statReady = 0;
//// start timer
//timer.Start();
this.Cursor = Cursors.Default;
}
private void videoSourcePlayer_NewFrame(object sender, ref Bitmap image)
{
if (detector != null)
{
float motionLevel = detector.ProcessFrame(image);
//if (motionLevel > motionAlarmLevel)
//{
// // flash for 2 seconds
// flash = (int)(2 * (1000 / alarmTimer.Interval));
//}
//// check objects' count
//if (detector.MotionProcessingAlgorithm is BlobCountingObjectsProcessing)
//{
// BlobCountingObjectsProcessing countingDetector = (BlobCountingObjectsProcessing)detector.MotionProcessingAlgorithm;
// objectsCountLabel.Text = "Objects: " + countingDetector.ObjectsCount.ToString();
//}
//else
//{
// objectsCountLabel.Text = "";
//}
int t1 = (int)(motionAlarmLevel * 500);
int t2 = (int)(0.075 * 500);
if (motionLevel * 500 > t1)
{
if (StopREC)
{
MoveCount++;
}
}
else
{
if (beginREC)
{
statCount++;
}
}
try
{
if (MoveCount == MaxStat)
{
if (StopREC)
{
StopREC = false;
beginREC = true;
writer = new AVIWriter("wmv3");
writer.Open(@"I:\log"+System.DateTime.Now.ToString("yyyyMMddhhmmss")+".avi", 640, 480);
this.listBox1.Items.Insert(0, "REC NOW!");
writer.AddFrame(image);
}
else
{
writer.AddFrame(image);
this.listBox1.Items.Insert(0, "RECING!");
}
}
if (statCount == MaxStat)
{
if (beginREC)
{
StopREC = true;
beginREC = false;
statCount = 0;
MoveCount = 0;
writer.Close();
this.listBox1.Items.Insert(0, "REC STOP!");
}
}
}
catch (Exception ex)
{
this.listBox1.Items.Insert(0, ex.Message);
}
if (motionLevel * 500 > t1)
{
this.listBox1.Items.Insert(0,"t1");
}
if (motionLevel * 500 > t2)
{
this.listBox1.Items.Insert(0, "t2");
}
motionHistory.Add(motionLevel);
//try
//{
// this.listBox1.Items.Insert(0, motionLevel.ToString());
//}
//catch (Exception ex)
//{
//}
if (motionHistory.Count > 300)
{
motionHistory.RemoveAt(0);
}
//if (showMotionHistoryToolStripMenuItem.Checked)
// DrawMotionHistory(image);
}
DrawMotionHistory(image);
}
private void DrawMotionHistory(Bitmap image)
{
Color greenColor = Color.FromArgb(128, 0, 255, 0);
Color yellowColor = Color.FromArgb(128, 255, 255, 0);
Color redColor = Color.FromArgb(128, 255, 0, 0);
BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite, image.PixelFormat);
int t1 = (int)(motionAlarmLevel * 500);
int t2 = (int)(0.075 * 500);
for (int i = 1, n = motionHistory.Count; i <= n; i++)
{
int motionBarLength = (int)(motionHistory[n - i] * 500);
if (motionBarLength == 0)
continue;
if (motionBarLength > 50)
motionBarLength = 50;
Drawing.Line(bitmapData,
new IntPoint(image.Width - i, image.Height - 1),
new IntPoint(image.Width - i, image.Height - 1 - motionBarLength),
greenColor);
if (motionBarLength > t1)
{
Drawing.Line(bitmapData,
new IntPoint(image.Width - i, image.Height - 1 - t1),
new IntPoint(image.Width - i, image.Height - 1 - motionBarLength),
yellowColor);
}
if (motionBarLength > t2)
{
Drawing.Line(bitmapData,
new IntPoint(image.Width - i, image.Height - 1 - t2),
new IntPoint(image.Width - i, image.Height - 1 - motionBarLength),
redColor);
}
}
image.UnlockBits(bitmapData);
}
private void button2_Click(object sender, EventArgs e)
{
StopREC = true;
beginREC = false;
statCount = 0;
MoveCount = 0;
writer.Close();
this.listBox1.Items.Insert(0, "REC STOP!");
this.Close();
}
}
请注意:AForge.Video.VFW.AVIWriter的大部分功能需要使用:Win32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero);但是这个方法在某些电脑上会报错。究其原因,可能是由于缺少directshow,所以我重写了AVIWRITE的部分方法,去掉了视频压缩的部分。如下:
public void Open( string fileName, int width, int height )
{
// close previous file
Close( );
lock ( this )
{
// calculate stride
stride = width * 3;
if ( ( stride % 4 ) != 0 )
stride += ( 4 - stride % 4 );
// create new file
if ( Win32.AVIFileOpen( out file, fileName, Win32.OpenFileMode.Create | Win32.OpenFileMode.Write, IntPtr.Zero ) != 0 )
throw new ApplicationException( "Failed opening file" );
this.width = width;
this.height = height;
// describe new stream
Win32.AVISTREAMINFO info = new Win32.AVISTREAMINFO( );
info.type = Win32.mmioFOURCC( "vids" );
info.handler = Win32.mmioFOURCC( codec );
info.scale = 1;
info.rate = rate;
info.suggestedBufferSize = stride * height;
// create stream
if (Win32.AVIFileCreateStream(file, out stream, ref info) != 0)
throw new ApplicationException("Failed creating stream");
// describe compression options
Win32.AVICOMPRESSOPTIONS options = new Win32.AVICOMPRESSOPTIONS( );
options.handler = Win32.mmioFOURCC( codec );
options.quality = quality;
// uncomment if video settings dialog is required to show
// Win32.AVISaveOptions( stream, ref options );
// create compressed stream
//if (Win32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero) != 0)
// throw new ApplicationException("Failed creating compressed stream");
//Win32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero);
// describe frame format
Win32.BITMAPINFOHEADER bitmapInfoHeader = new Win32.BITMAPINFOHEADER( );
bitmapInfoHeader.size = Marshal.SizeOf( bitmapInfoHeader.GetType( ) );
bitmapInfoHeader.width = width;
bitmapInfoHeader.height = height;
bitmapInfoHeader.planes = 1;
bitmapInfoHeader.bitCount = 24;
bitmapInfoHeader.sizeImage = 0;
bitmapInfoHeader.compression = 0; // BI_RGB
// set frame format
if (Win32.AVIStreamSetFormat(stream, 0, ref bitmapInfoHeader, Marshal.SizeOf(bitmapInfoHeader.GetType())) != 0)
throw new ApplicationException("Failed creating compressed stream");
// alloc unmanaged memory for frame
buffer = Marshal.AllocHGlobal( stride * height );
if ( buffer == IntPtr.Zero )
throw new ApplicationException( "Insufficient memory for internal buffer" );
position = 0;
}
}
public void AddFrame( Bitmap frameImage )
{
lock ( this )
{
// check if AVI file was properly opened
if ( buffer == IntPtr.Zero )
throw new ApplicationException( "AVI file should be successfully opened before writing" );
// check image dimension
//if ((frameImage.Width != width) || (frameImage.Height != height))
// throw new ApplicationException("Invalid image dimension");
// lock bitmap data
BitmapData imageData = frameImage.LockBits(
new Rectangle( 0, 0, width, height ),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb );
// copy image data
int srcStride = imageData.Stride;
int dstStride = stride;
int src = imageData.Scan0.ToInt32( ) + srcStride * ( height - 1 );
int dst = buffer.ToInt32( );
for ( int y = 0; y < height; y++ )
{
Win32.memcpy( dst, src, dstStride );
dst += dstStride;
src -= srcStride;
}
// unlock bitmap data
frameImage.UnlockBits( imageData );
// write to stream
if (Win32.AVIStreamWrite(stream, position, 1, buffer,
stride * height, 0, IntPtr.Zero, IntPtr.Zero ) != 0 )
throw new ApplicationException( "Failed adding frame" );
position++;
}
}