Click here to Skip to main content
15,895,777 members
Articles / Artificial Intelligence

AForge.NET open source framework

Rate me:
Please Sign up or sign in to vote.
4.97/5 (150 votes)
16 May 2007GPL311 min read 831.5K   48.3K   346  
The article describes an open source C# framework for researchers in the areas of Computer Vision and Artificial Intelligence - image processing, neural networks, genetic algorithms, etc.
// AForge Image Processing Library
//
// Copyright � Andrew Kirillov, 2005-2007
// andrew.kirillov@gmail.com
//

namespace AForge.Imaging.Filters
{
	using System;
	using System.Drawing;
	using System.Drawing.Imaging;

	/// <summary>
	/// Masked filter - filter an image using binary mask image.
	/// </summary>
    /// 
    /// <remarks></remarks>
    /// 
	public class MaskedFilter : IFilter
	{
		private Bitmap	mask;
		private IFilter	filter1 = null;
		private IFilter	filter2 = null;

        /// <summary>
        /// Binary mask image
        /// </summary>
        /// 
		public Bitmap Mask
		{
			get { return mask; }
			set { mask = value; }
		}

        /// <summary>
        /// Filter to be applied to image regions, which correspond to
        /// black pixels of the specified mask.
        /// </summary>
        /// 
		public IFilter Filter1
		{
			get { return filter1; }
			set { filter1 = value; }
		}

        /// <summary>
        /// Filter to be applied to image regios, which correspons to
        /// white pixels of the specified mask.
        /// </summary>
        /// 
        /// <remarks>If the filter is not set, then original source images
        /// will be used for white pixels of the specified mask.</remarks>
        /// 
		public IFilter Filter2
		{
			get { return filter2; }
			set { filter2 = value; }
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MaskedFilter"/> class
        /// </summary>
        /// 
        /// <param name="mask">Binary mask image</param>
        /// <param name="filter1">Filter for black regions of the mask</param>
        /// 
        public MaskedFilter( Bitmap mask, IFilter filter1 )
		{
			this.mask = mask;
			this.filter1 = filter1;
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MaskedFilter"/> class
        /// </summary>
        /// 
        /// <param name="mask">Binary mask image</param>
        /// <param name="filter1">Filter for black regions of the mask</param>
        /// <param name="filter2">Filter for white regions of the mask</param>
        /// 
		public MaskedFilter( Bitmap mask, IFilter filter1, IFilter filter2 )
		{
			this.mask = mask;
			this.filter1 = filter1;
			this.filter2 = filter2;
		}

        /// <summary>
        /// Apply filter to an image
        /// </summary>
        /// 
        /// <param name="image">Source image to apply filter to</param>
        /// 
        /// <returns>Returns filter's result obtained by applying the filter to
        /// the source image</returns>
        /// 
        /// <remarks>The method keeps the source image unchanged and returns the
        /// the result of image processing filter as new image.</remarks> 
        ///
        public Bitmap Apply( Bitmap image )
        {
            // lock source bitmap data
            BitmapData imageData = image.LockBits(
                new Rectangle( 0, 0, image.Width, image.Height ),
                ImageLockMode.ReadOnly,
                ( image.PixelFormat == PixelFormat.Format8bppIndexed ) ?
                PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb );

            // apply the filter
            Bitmap dstImage = Apply( imageData );

            // unlock source image
            image.UnlockBits( imageData );

            return dstImage;
        }

        /// <summary>
        /// Apply filter to an image
        /// </summary>
        /// 
        /// <param name="imageData">Source image to apply filter to</param>
        /// 
        /// <returns>Returns filter's result obtained by applying the filter to
        /// the source image</returns>
        /// 
        /// <remarks>The filter accepts birmap data as input and returns the result
        /// of image processing filter as new image. The source image data are kept
        /// unchanged.</remarks>
        /// 
        public Bitmap Apply( BitmapData imageData )
        {
            // get mask dimension
            int width = mask.Width;
            int height = mask.Height;

            // check source image size
            if ( ( width != imageData.Width ) || ( height != imageData.Height ) )
            {
                // sorry, but source image must have the same dimension as mask image
                throw new ArgumentException( "Source image has wrong dimension (does not equal to mask dimension" );
            }

            // apply first filter
            Bitmap dstImg1 = filter1.Apply( imageData );

            // check dimension of the result image
            if ( ( width != dstImg1.Width ) || ( height != dstImg1.Height ) )
            {
                dstImg1.Dispose( );
                // we are not handling such situations yet
                throw new ApplicationException( "Filters should not change image dimension" );
            }

            Bitmap dstImg2 = null;
            // apply second filter, if it was specified
            if ( filter2 != null )
            {
                dstImg2 = filter2.Apply( imageData );
                // check dimension of the result image
                if ( ( width != dstImg2.Width ) || ( height != dstImg2.Height ) )
                {
                    dstImg1.Dispose( );
                    dstImg2.Dispose( );
                    // we are not handling such situations yet
                    throw new ApplicationException( "Filters should not change image dimension" );
                }
            }

            // lock second image or get source instead of it
            BitmapData dstData2 = ( dstImg2 == null ) ? imageData :
                dstImg2.LockBits(
                    new Rectangle( 0, 0, width, height ), ImageLockMode.ReadOnly,
                    ( dstImg2.PixelFormat == PixelFormat.Format8bppIndexed ) ?
				        PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb );

            // check pixel formats
            if ( dstImg1.PixelFormat != dstData2.PixelFormat )
            {
                IFilter f = new GrayscaleToRGB( );

                // convert 1st image to RGB format
                if ( dstImg1.PixelFormat == PixelFormat.Format8bppIndexed )
                {
                    Bitmap t = f.Apply( dstImg1 );
                    dstImg1.Dispose( );
                    dstImg1 = t;
                }

                // convert 2nd image to RGB format
                if ( dstData2.PixelFormat == PixelFormat.Format8bppIndexed )
                {
                    Bitmap t = f.Apply( dstData2 );

                    // dispose temporary image
                    if ( dstImg2 != null )
                    {
                        dstImg2.UnlockBits( dstData2 );
                        dstImg2.Dispose( );
                    }

                    dstImg2 = t;
                    // lock second image again
                    dstData2 = dstImg2.LockBits(
                            new Rectangle( 0, 0, width, height ), ImageLockMode.ReadOnly,
                            PixelFormat.Format24bppRgb );
                }
            }

            // lock 1st image - result image
            BitmapData dstData1 = dstImg1.LockBits(
                new Rectangle( 0, 0, width, height ),
                ImageLockMode.ReadWrite, dstImg1.PixelFormat );

            // lock mask bitmap data
            BitmapData maskData = mask.LockBits(
                new Rectangle( 0, 0, width, height ),
                ImageLockMode.ReadOnly, mask.PixelFormat );

            int pixelSize   = ( dstImg1.PixelFormat == PixelFormat.Format8bppIndexed ) ? 1 : 3;
            int offset      = dstData1.Stride - width * pixelSize;
            int maskInc     = ( mask.PixelFormat == PixelFormat.Format8bppIndexed ) ? 1 : 3;
            int maskOffset  = maskData.Stride - width * maskInc;

            // do the job
            unsafe
            {
                byte* dst = (byte*) dstData1.Scan0.ToPointer( );
                byte* src = (byte*) dstData2.Scan0.ToPointer( );
                byte* m = (byte*) maskData.Scan0.ToPointer( );

                // for each line
                for ( int y = 0; y < height; y++ )
                {
                    // for each pixel
                    for ( int x = 0; x < width; x++, m += maskInc )
                    {
                        if ( *m != 0 )
                        {
                            for ( int i = 0; i < pixelSize; i++, src++, dst++ )
                            {
                                *dst = *src;
                            }
                        }
                        else
                        {
                            src += pixelSize;
                            dst += pixelSize;
                        }
                    }
                    src += offset;
                    dst += offset;
                    m += maskOffset;
                }
            }

            // unlock 1st image and mask
            dstImg1.UnlockBits( dstData1 );
            mask.UnlockBits( maskData );

            // dispose 2nd image
            if ( dstImg2 != null )
            {
                dstImg2.UnlockBits( dstData2 );
                dstImg2.Dispose( );
            }

            // return result image
            return dstImg1;
        }
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer IBM
United Kingdom United Kingdom
Started software development at about 15 years old and it seems like now it lasts most part of my life. Fortunately did not spend too much time with Z80 and BK0010 and switched to 8086 and further. Similar with programming languages – luckily managed to get away from BASIC and Pascal to things like Assembler, C, C++ and then C#. Apart from daily programming for food, do it also for hobby, where mostly enjoy areas like Computer Vision, Robotics and AI. This led to some open source stuff like AForge.NET, Computer Vision Sandbox, cam2web, ANNT, etc.

Comments and Discussions