Chương 3 THỰC NGHIỆM
3.1. Chương trình phát hiện chuyển động
3.1.2. Các thuật toán áp dụng:
Bài toán đưa ra là một ảnh gốc 24bpp RGB được gọi là frame hiện tại (image), một bản sao của nó (currentFrame) và một frame video trước (backgroundFrame). Trước hết là tìm các vùng mà ở đó 2 frame có sự khác nhau trên 1 bit. Trong đó sử dung 2 bộ lọc Difference và Threshold để cho kết quả tốt hơn.
// create filters
Difference differenceFilter = new Difference( );
IFilter thresholdFilter = new Threshold( 15 );
// set backgroud frame as an overlay for difference filter
differenceFilter.OverlayImage = backgroundFrame;
// apply the filters
Bitmap tmp1 = differenceFilter.Apply( currentFrame ); Bitmap tmp2 = thresholdFilter.Apply( tmp1 );
Để loại bỏ đi những pixel nhiểu ngẫu nhiên trong chương trình sử dụng bộ lọc Erosion, khi đó những vùng này chỉ xuất hiện những chuyển động thực sự.
// create filter
IFilter erosionFilter = new Erosion( );
// apply the filter
Bitmap tmp3 = erosionFilter.Apply( tmp2 );
Để phát hiện ra có sự chuyển động trong chương trình này làm sáng lên những vùng chuyển động đó.
// extract red channel from the original image
IFilter extrachChannel = new ExtractChannel( RGB.R ); Bitmap redChannel = extrachChannel.Apply( image );
// merge red channel with motion regions
Merge mergeFilter = new Merge( ); mergeFilter.OverlayImage = tmp3;
Bitmap tmp4 = mergeFilter.Apply( redChannel );
// replace red channel in the original image
ReplaceChannel replaceChannel = new ReplaceChannel( RGB.R ); replaceChannel.ChannelImage = tmp4;
Bitmap tmp5 = replaceChannel.Apply( image );
Hình 3.2. Phương pháp so sánh sự khác biệt
Qua bức ảnh trên cho thấy có một sự hạn chế với cách tiếp cận này. Đó là nếu đối tượng chuyển động một cách từ từ, êm ả thì kết quả thể hiện chỉ là sự thay đổi nhỏ giữa frame này với frame kia. Nếu đối tượng chuyển động châm hoặc dừng lại thì thuật toán này trở nên không hiệu quả.
Trong chương trình này bổ sung thêm phần cập nhật nền để làm cho việc phát hiện chuyển động được chính xác hơn:
// create filter
MoveTowards moveTowardsFilter = new MoveTowards( );
// move background towards current frame
moveTowardsFilter.OverlayImage = currentFrame;
Bitmap tmp = moveTowardsFilter.Apply( backgroundFrame );
// dispose old background
backgroundFrame.Dispose( ); backgroundFrame = tmp;
// create processing filters sequence
FiltersSequence processingFilter = new FiltersSequence( ); processingFilter.Add( new Difference( backgroundFrame ) );
processingFilter.Add( new Threshold( 15 ) );
processingFilter.Add( new Opening( ) ); processingFilter.Add( new Edges( ) );
// apply the filter
Bitmap tmp1 = processingFilter.Apply( currentFrame );
// extract red channel from the original image
IFilter extrachChannel = new ExtractChannel( RGB.R ); Bitmap redChannel = extrachChannel.Apply( image );
// merge red channel with moving object borders
Merge mergeFilter = new Merge( ); mergeFilter.OverlayImage = tmp1;
Bitmap tmp2 = mergeFilter.Apply( redChannel );
// replace red channel in the original image
ReplaceChannel replaceChannel = new ReplaceChannel( RGB.R ); replaceChannel.ChannelImage = tmp2;
Hình 3.3. Phương pháp trừ nền
Khi đó sẽ cho kết quả tốt hơn, ở đây có thể sử dụng bộ lọc Pixellate để đạt được hiệu quả cao hơn
// create filter
IFilter pixellateFilter = new Pixellate( );
// apply the filter
Bitmap newImage = pixellateFilter( image );
Sau đó chuyển frame nền đến frame hiện tại.
// create processing filters sequence
FiltersSequence processingFilter = new FiltersSequence( ); processingFilter.Add( new Difference( backgroundFrame ) );
processingFilter.Add( new Threshold( 15 ) );
processingFilter.Add( new Dilatation( ) ); processingFilter.Add( new Edges( ) );
// apply the filter
Bitmap tmp1 = processingFilter.Apply( currentFrame );
Hình 3.4. Cập nhật nền
BlobCounter blobCounter = new BlobCounter( ); ...
// get object rectangles
blobCounter.ProcessImage( thresholdedImage );
Rectangle[] rects = BlobCounter.GetObjectRectangles( );
// create graphics object from initial image
Graphics g = Graphics.FromImage( image ); // draw each rectangle
{
foreach ( Rectangle rc in rects ) {
g.DrawRectangle( pen, rc );
if ( ( rc.Width > 15 ) && ( rc.Height > 15 ) ) {
// here we can higligh large objects with something else
} } }
g.Dispose( );
Khi đưa thêm dòng code trên thì sẽ cho thêm đặc tính là xuất hiện các con số trên đối tượng chuyện động.
Hình 3.5. Đánh số các đối tượng chuyển động
Cảnh báo chuyển động: Để làm cho chương trình trở nên thú vị hơn, cảnh báo chuyển động được đưa vào trong các thuật toán phát hiện chuyển động.
// Calculate white pixels
private int CalculateWhitePixels( Bitmap image ) {
int count = 0;
// lock difference image
BitmapData data = image.LockBits( new Rectangle( 0, 0, width, height ),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed ); int offset = data.Stride - width;
unsafe {
byte * ptr = (byte *) data.Scan0.ToPointer( ); for ( int y = 0; y < height; y++ )
{
for ( int x = 0; x < width; x++, ptr++ ) { count += ( (*ptr) >> 7 ); } ptr += offset; } }
// unlock image
image.UnlockBits( data ); return count;
}
Lưu Video: Có nhiều cách khác nhau để xử lý sự kiện cảnh báo chuyển động: vẽ một hình chữ nhật xung quang đối tượng trong video, hoặc phát ra âm thanh đẻ cảnh báo. Nhưng tất nhiên, tiện lợi nhất là lưu lại phát hiện chuyển động. Trong ứng dụng minh hoạ này sử dụng lớp AVIWrite, nó sử dụng Video For Windows để lưu các file AVI. Sau đây là một minh hoạ nhỏ của việc sử dụng lớp này để ghi file AVI nhỏ:
SaveFileDialog sfd = new SaveFileDialog( ); if ( sfd.ShowDialog( ) == DialogResult.OK ) {
AVIWriter writer = new AVIWriter( "wmv3" ); try
{
writer.Open( sfd.FileName, 320, 240 ); Bitmap bmp = new Bitmap( 320, 240, PixelFormat.Format24bppRgb );
for ( int i = 0; i < 100; i++ ) { bmp.SetPixel( i, i, Color.FromArgb( i, 0, 255 - i ) ); writer.AddFrame( bmp ); } bmp.Dispose( ); } catch ( ApplicationException ex ) { } writer.Dispose( ); }
Lưu ý: trong vi dụ nhỏ này và trong ứng dụng demo tôi sử dụng codec Windows Media Video 9 VCM.