Subversion Repositories Projects

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using WebCamLib;

namespace Touchless.Vision.Camera
{
   public enum CameraProperty : int
   {
      Pan_degrees = WebCamLib.CameraProperty.Pan_degrees,
      Tilt_degrees = WebCamLib.CameraProperty.Tilt_degrees,
      Roll_degrees = WebCamLib.CameraProperty.Roll_degrees,
      Zoom_mm = WebCamLib.CameraProperty.Zoom_mm,
      Exposure_lgSec = WebCamLib.CameraProperty.Exposure_lgSec,
      Iris_10f = WebCamLib.CameraProperty.Iris_10f,
      FocalLength_mm = WebCamLib.CameraProperty.FocalLength_mm,
      Flash = WebCamLib.CameraProperty.Flash,
      Brightness = WebCamLib.CameraProperty.Brightness,
      Contrast = WebCamLib.CameraProperty.Brightness,
      Hue = WebCamLib.CameraProperty.Contrast,
      Saturation = WebCamLib.CameraProperty.Saturation,
      Sharpness = WebCamLib.CameraProperty.Sharpness,
      Gamma = WebCamLib.CameraProperty.Gamma,
      ColorEnable = WebCamLib.CameraProperty.ColorEnable,
      WhiteBalance = WebCamLib.CameraProperty.WhiteBalance,
      BacklightCompensation = WebCamLib.CameraProperty.BacklightCompensation,
      Gain = WebCamLib.CameraProperty.Gain,
   }

   public sealed class CameraPropertyValue : IComparable<CameraPropertyValue>, IEquatable<CameraPropertyValue>
   {
      public CameraPropertyValue( bool isPercentageValue, int value, bool isAuto )
      {
         IsPercentageValue = isPercentageValue;
         Value = value;
         IsAuto = isAuto;
      }

      public int Value
      {
         get;
         set;
      }

      private bool isAuto;

      public bool IsAuto
      {
         get
         {
            return isAuto;
         }

         set
         {
            isAuto = value;
         }
      }

      public bool IsManual
      {
         get
         {
            return !IsAuto;
         }

         set
         {
            IsAuto = !value;
         }
      }

      private bool isPercentageValue;

      public bool IsActualValue
      {
         get
         {
            return !IsPercentageValue;
         }

         set
         {
            IsPercentageValue = !value;
         }
      }

      public bool IsPercentageValue
      {
         get
         {
            return isPercentageValue;
         }

         set
         {
            isPercentageValue = value;
         }
      }

      #region ICompare<CameraPropertyValue> Members

      public int CompareTo( CameraPropertyValue other )
      {
         int result = 0;

         if( IsActualValue && other.IsPercentageValue )
            result = -1;
         else if( IsPercentageValue && other.IsActualValue )
            result = 1;
         else
         {
            if( Value < other.Value )
               result = -1;
            else if( Value > other.Value )
               result = 1;
            else
            {
               if( IsAuto && other.IsManual )
                  result = -1;
               else if( IsManual && other.IsAuto )
                  result = 1;
            }
         }

         return result;
      }

      #endregion

      #region IEquatable<CameraPropertyValue> Members

      public bool Equals( CameraPropertyValue other )
      {
         return Object.ReferenceEquals( this, other ) || CompareTo( other ) == 0;
      }

      #endregion

      public override bool Equals( Object obj )
      {
         bool result;

         if( !( result = Object.ReferenceEquals( this, obj ) ) )
         {
            CameraPropertyValue other = obj as CameraPropertyValue;

            if( result = other != null )
               result = Equals( other );
         }

         return result;
      }
   }

   public sealed class CameraPropertyRange : IComparable<CameraPropertyRange>, IEquatable<CameraPropertyRange>
   {
      public CameraPropertyRange( int minimum, int maximum, int step, int defaults, bool isAuto )
      {
         Minimum = minimum;
         Maximum = maximum;
         Step = step;
         Defaults = defaults;
         IsAuto = isAuto;
      }

      public int Minimum
      {
         get;
         private set;
      }

      public int Maximum
      {
         get;
         private set;
      }

      public int Range
      {
         get
         {
            return Maximum - Minimum;
         }
      }

      public int DomainSize
      {
         get
         {
            return Range + 1;
         }
      }

      public int Step
      {
         get;
         private set;
      }

      public int Defaults
      {
         get;
         private set;
      }

      public bool IsAuto
      {
         get;
         private set;
      }

      public bool IsManual
      {
         get
         {
            return !IsAuto;
         }
      }

      #region ICompare<CameraPropertyRange> Members

      public int CompareTo( CameraPropertyRange other )
      {
         int result = 0;

         if( Minimum < other.Minimum )
            result = -1;
         else if( Minimum > other.Minimum )
            result = 1;
         else
         {
            if( Maximum < other.Maximum )
               result = -1;
            else if( Maximum > other.Maximum )
               result = 1;
            else
            {
               if( Step < other.Step )
                  result = -1;
               else if( Step > other.Step )
                  result = 1;
               else
               {
                  if( Defaults < other.Defaults )
                     result = -1;
                  else if( Defaults > other.Defaults )
                     result = 1;
                  else
                  {
                     if( IsAuto && other.IsManual )
                        result = -1;
                     else if( IsManual && other.IsAuto )
                        result = 1;
                  }
               }
            }
         }

         return result;
      }

      #endregion

      #region IEquatable<CameraPropertyRange> Members

      public bool Equals( CameraPropertyRange other )
      {
         return Object.ReferenceEquals( this, other ) || CompareTo( other ) == 0;
      }

      #endregion

      public override bool Equals( Object obj )
      {
         bool result;

         if( !( result = Object.ReferenceEquals( this, obj ) ) )
         {
            CameraPropertyRange other = obj as CameraPropertyRange;

            if( result = other != null )
               result = Equals( other );
         }

         return result;
      }
   }

   public sealed class CameraPropertyCapabilities : IComparable<CameraPropertyCapabilities>, IEquatable<CameraPropertyCapabilities>
   {
      internal CameraPropertyCapabilities( Camera camera, WebCamLib.CameraPropertyCapabilities capabilities )
      {
         Camera = camera;
         InternalCapabilities = capabilities;
      }

      public Camera Camera
      {
         get;
         private set;
      }

      internal WebCamLib.CameraPropertyCapabilities InternalCapabilities
      {
         get;
         private set;
      }

      public bool IsSupported
      {
         get
         {
            return InternalCapabilities.IsSupported;
         }
      }

      public bool IsFullySupported
      {
         get
         {
            return InternalCapabilities.IsFullySupported;
         }
      }

      public bool IsGetSupported
      {
         get
         {
            return InternalCapabilities.IsGetSupported;
         }
      }

      public bool IsSetSupported
      {
         get
         {
            return InternalCapabilities.IsSetSupported;
         }
      }

      public bool IsGetRangeSupported
      {
         get
         {
            return InternalCapabilities.IsGetRangeSupported;
         }
      }

      #region IComparable<CameraPropertyCapabilities> Members
      // sort order: IsGetSupported, IsSetSupported, IsGetRangeSupported; this exists and other doesn't first/less for all keys
      public int CompareTo( CameraPropertyCapabilities other )
      {
         int result = 0;

         if( IsGetSupported && !other.IsGetSupported )
            result = -1;
         else if( !IsGetSupported && other.IsGetSupported )
            result = 1;
         else
         {
            if( IsSetSupported && !other.IsSetSupported )
               result = -1;
            else if( !IsSetSupported && other.IsSetSupported )
               result = 1;
            else
            {
               if( IsGetRangeSupported && !other.IsGetRangeSupported )
                  result = -1;
               else if( !IsGetRangeSupported && other.IsGetRangeSupported )
                  result = 1;
            }
         }

         return result;
      }
      #endregion

      #region IEquatable<CameraPropertyCapabilities>
      public bool Equals( CameraPropertyCapabilities other )
      {
         return CompareTo( other ) == 0;
      }
      #endregion

      public override bool Equals( object obj )
      {
         bool result;

         CameraPropertyCapabilities capabilities = obj as CameraPropertyCapabilities;

         if( result = capabilities != null )
            result = Equals( capabilities );

         return result;
      }
   }

   public sealed class CaptureSize : IComparable<CaptureSize>, IEquatable<CaptureSize>
   {
      public CaptureSize( int width, int height, int colorDepth )
      {
         Width = width;
         Height = height;
         ColorDepth = colorDepth;
      }

      public int Width
      {
         get;
         private set;
      }

      public int Height
      {
         get;
         private set;
      }

      public int ColorDepth
      {
         get;
         private set;
      }

      public override String ToString()
      {
         return String.Format( "{0} x {1} @ {2}", Width, Height, ColorDepth );
      }

      #region IComparable<CaptureSize> Members

      public int CompareTo( CaptureSize other )
      {
         int result;

         if( Width < other.Width )
            result = -1;
         else if( Width > other.Width )
            result = 1;
         else
         {
            if( Height < other.Height )
               result = -1;
            else if( Height > other.Height )
               result = 1;
            else
            {
               if( ColorDepth < other.ColorDepth )
                  result = -1;
               else if( ColorDepth > other.ColorDepth )
                  result = 1;
               else
                  result = 0;
            }
         }

         return result;
      }

      #endregion

      #region IEquatable<CaptureSize> Members

      public bool Equals( CaptureSize other )
      {
         return Object.ReferenceEquals( this, other ) || CompareTo( other ) == 0;
      }

      #endregion

      public override bool Equals( Object obj )
      {
         bool result;

         if( !( result = Object.ReferenceEquals( this, obj ) ) )
         {
            CaptureSize other = obj as CaptureSize;
            if( result = other != null )
               result = Equals( other );
         }

         return result;
      }

      public override int GetHashCode()
      {
         int result = 0;

         result ^= Width;
         result ^= Height << 11;
         result ^= ColorDepth << 23;

         return result;
      }
   }

   /// <summary>
   /// Represents a camera in use by the Touchless system
   /// </summary>
   public class Camera : IDisposable
   {
      public const int IgnoredBitsPerPixel = -1;

      private readonly Object CameraMethodsLock = new Object();

      private readonly CameraMethods _cameraMethods;
      private RotateFlipType _rotateFlip = RotateFlipType.RotateNoneFlipNone;

      public Camera( CameraMethods cameraMethods, string name, int index )
      {
         _name = name;
         _index = index;

         lock( CameraMethodsLock )
         {
            _cameraMethods = cameraMethods;
            _cameraMethods.OnImageCapture += CaptureCallbackProc;
         }
      }

      public string Name
      {
         get
         {
            return _name;
         }
      }

      /// <summary>
      /// Defines the frames per second limit that is in place, -1 means no limit
      /// </summary>
      public int Fps
      {
         get
         {
            return _fpslimit;
         }
         set
         {
            _fpslimit = value;
            _timeBetweenFrames = ( 1000.0 / _fpslimit );
         }
      }

      /// <summary>
      /// Determines the width of the image captured
      /// </summary>
      public int CaptureWidth
      {
         get
         {
            return _width;
         }
         set
         {
            _width = value;
         }
      }

      /// <summary>
      /// Defines the height of the image captured
      /// </summary>
      public int CaptureHeight
      {
         get
         {
            return _height;
         }
         set
         {
            _height = value;
         }
      }

      /// <summary>
      /// Defines the bits per pixel of image captured.
      /// </summary>
      public int CaptureBitsPerPixel
      {
         get
         {
            return _bpp;
         }

         set
         {
            _bpp = value;
         }
      }

      public bool HasFrameLimit
      {
         get
         {
            return _fpslimit != -1;
         }
      }

      public bool FlipHorizontal
      {
         get
         {
            return RotateFlip == RotateFlipType.RotateNoneFlipX || RotateFlip == RotateFlipType.Rotate180FlipNone;
         }

         set
         {
            if( value && FlipVertical )
            {
               RotateFlip = RotateFlipType.Rotate180FlipNone;
            }
            else if( value && !FlipVertical )
            {
               RotateFlip = RotateFlipType.RotateNoneFlipX;
            }
            else if( !value && FlipVertical )
            {
               RotateFlip = RotateFlipType.Rotate180FlipX;
            }
            else if( !value && !FlipVertical )
            {
               RotateFlip = RotateFlipType.RotateNoneFlipNone;
            }
         }
      }

      public bool FlipVertical
      {
         get
         {
            return RotateFlip == RotateFlipType.Rotate180FlipX || RotateFlip == RotateFlipType.Rotate180FlipNone;
         }

         set
         {
            if( value && FlipHorizontal )
            {
               RotateFlip = RotateFlipType.Rotate180FlipNone;
            }
            else if( value && !FlipHorizontal )
            {
               RotateFlip = RotateFlipType.Rotate180FlipX;
            }
            else if( !value && FlipHorizontal )
            {
               RotateFlip = RotateFlipType.RotateNoneFlipX;
            }
            else if( !value && !FlipHorizontal )
            {
               RotateFlip = RotateFlipType.RotateNoneFlipNone;
            }
         }
      }

      /// <summary>
      /// Command for rotating and flipping incoming images
      /// </summary>
      public RotateFlipType RotateFlip
      {
         get
         {
            return _rotateFlip;
         }
         set
         {
            // Swap height/width when rotating by 90 or 270
            if( ( int ) _rotateFlip % 2 != ( int ) value % 2 )
            {
               int temp = CaptureWidth;
               CaptureWidth = CaptureHeight;
               CaptureHeight = temp;
            }
            _rotateFlip = value;
         }
      }

      #region IDisposable Members

      /// <summary>
      /// Cleanup function for the camera
      /// </summary>
      public void Dispose()
      {
         StopCapture();
      }

      #endregion

      /// <summary>
      /// Returns the last image acquired from the camera
      /// </summary>
      /// <returns>A bitmap of the last image acquired from the camera</returns>
      public Bitmap GetCurrentImage()
      {
         Bitmap b = null;
         lock( _bitmapLock )
         {
            if( _bitmap == null )
            {
               return null;
            }

            b = new Bitmap( _bitmap );
         }

         return b;
      }

      public void ShowPropertiesDialog()
      {
         lock( CameraMethodsLock )
         {
            _cameraMethods.DisplayCameraPropertiesDialog( _index );
         }
      }

      public CameraInfo GetCameraInfo()
      {
         lock( CameraMethodsLock )
         {
            return _cameraMethods.GetCameraInfo( _index );
         }
      }

      #region Camera Properties
      public bool IsCameraPropertySupported( CameraProperty property )
      {
         bool result = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.IsPropertySupported( ( WebCamLib.CameraProperty ) property, ref result );
         }

         return result;
      }

      public bool SetCameraProperty( CameraProperty property, CameraPropertyValue value )
      {
         bool result;

         if( value.IsActualValue )
            result = SetCameraProperty_value( property, value.Value, value.IsAuto );
         else // if( value.IsPercentageValue )
            result = SetCameraProperty_percentage( property, value.Value, value.IsAuto );

         return result;
      }

      public bool SetCameraProperty( CameraProperty property, bool isActualValue, int value )
      {
         bool result = false;

         if( isActualValue )
            result = SetCameraProperty_value( property, value );
         else // is percentage value
            result = SetCameraProperty_percentage( property, value );

         return result;
      }

      public bool SetCameraProperty( CameraProperty property, bool isActualValue, int value, bool auto )
      {
         bool result = false;

         if( isActualValue )
            result = SetCameraProperty_value( property, value, auto );
         else // is percentage value
            result = SetCameraProperty_percentage( property, value, auto );

         return result;
      }

      public bool SetCameraProperty_value( CameraProperty property, bool auto )
      {
         return SetCameraProperty_value( property, 0, auto );
      }

      // Assume manual control
      public bool SetCameraProperty_value( CameraProperty property, int value )
      {
         return SetCameraProperty_value( property, value, false );
      }

      public bool SetCameraProperty_value( CameraProperty property, int value, bool auto )
      {
         bool result = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.SetProperty_value( ( WebCamLib.CameraProperty ) property, value, auto, ref result );
         }

         return result;
      }

      public bool SetCameraProperty_percentage( CameraProperty property, bool auto )
      {
         return SetCameraProperty_percentage( property, 0, auto );
      }

      // Assume manual control
      public bool SetCameraProperty_percentage( CameraProperty property, int percentage )
      {
         return SetCameraProperty_percentage( property, percentage, false );
      }

      public bool SetCameraProperty_percentage( CameraProperty property, int percentage, bool auto )
      {
         bool result = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.SetProperty_percentage( ( WebCamLib.CameraProperty ) property, percentage, auto, ref result );
         }

         return result;
      }

      public CameraPropertyValue GetCameraProperty( CameraProperty property, bool isActualValue )
      {
         CameraPropertyValue result;

         if( isActualValue )
            result = GetCameraProperty_value( property );
         else // is percentage value
            result = GetCameraProperty_percentage( property );

         return result;
      }

      public CameraPropertyValue GetCameraProperty_value( CameraProperty property )
      {
         CameraPropertyValue result;

         bool successful = false;

         int value = -1;
         bool isAuto = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.GetProperty_value( ( WebCamLib.CameraProperty ) property, ref value, ref isAuto, ref successful );
         }

         if( successful )
            result = new CameraPropertyValue( false, value, isAuto );
         else
            result = null;

         return result;
      }

      public CameraPropertyValue GetCameraProperty_percentage( CameraProperty property )
      {
         CameraPropertyValue result;

         bool successful = false;

         int value = -1;
         bool isAuto = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.GetProperty_percentage( ( WebCamLib.CameraProperty ) property, ref value, ref isAuto, ref successful );
         }

         if( successful )
            result = new CameraPropertyValue( true, value, isAuto );
         else
            result = null;

         return result;
      }

      public CameraPropertyRange GetCameraPropertyRange( CameraProperty property )
      {
         CameraPropertyRange result;

         bool successful = false;

         int minimum, maximum, step, defaults;
         bool isAuto;

         minimum = maximum = step = defaults = -1;
         isAuto = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.GetPropertyRange( ( WebCamLib.CameraProperty ) property, ref minimum, ref maximum, ref step, ref defaults, ref isAuto, ref successful );
         }

         if( successful )
            result = new CameraPropertyRange( minimum, maximum, step, defaults, isAuto );
         else
            result = null;

         return result;
      }

      public bool CameraPropertyHasRange( CameraProperty property )
      {
         bool result = false;

         _cameraMethods.PropertyHasRange( ( WebCamLib.CameraProperty ) property, ref result );

         return result;
      }

      public bool ValidateCameraProperty( CameraProperty property, int value )
      {
         bool result = false;

         _cameraMethods.ValidatePropertyValue( ( WebCamLib.CameraProperty ) property, value, ref result );

         return result;
      }

      public IDictionary<CameraProperty, CameraPropertyCapabilities> CameraPropertyCapabilities
      {
         get
         {
            IDictionary<CameraProperty, CameraPropertyCapabilities> result = new Dictionary<CameraProperty, CameraPropertyCapabilities>( _cameraMethods.PropertyCapabilities.Count );

            foreach( WebCamLib.CameraProperty property in _cameraMethods.PropertyCapabilities.Keys )
            {
               CameraProperty prop = ( CameraProperty ) property;
               CameraPropertyCapabilities capabilities = new CameraPropertyCapabilities( this, _cameraMethods.PropertyCapabilities[ property ] );

               result.Add( prop, capabilities );
            }

            return result;
         }
      }
      #endregion

      public IList<CaptureSize> CaptureSizes
      {
         get
         {
            IList<Tuple<int, int, int>> rawSizes = new List<Tuple<int, int, int>>();

            lock( CameraMethodsLock )
            {
               _cameraMethods.GetCaptureSizes( _index, rawSizes );
            }

            IList<CaptureSize> result = new List<CaptureSize>( rawSizes.Count );
            foreach( Tuple<int, int, int> size in rawSizes )
            {
               CaptureSize newSize = new CaptureSize( size.Item1, size.Item2, size.Item3 );
               result.Add( newSize );
            }

            return result;
         }
      }

      /// <summary>
      /// Event fired when an image from the camera is captured
      /// </summary>
      public event EventHandler<CameraEventArgs> OnImageCaptured;

      /// <summary>
      /// Returns the camera name as the ToString implementation
      /// </summary>
      /// <returns>The name of the camera</returns>
      public override string ToString()
      {
         return _name;
      }

      #region Internal Implementation

      private readonly object _bitmapLock = new object();
      private readonly int _index;
      private readonly string _name;
      private Bitmap _bitmap;
      private DateTime _dtLastCap = DateTime.MinValue;
      private int _fpslimit = -1;
      private int _height = 240;
      private double _timeBehind;
      private double _timeBetweenFrames;
      private int _width = 320;
      private int _bpp = 24;

      internal bool StartCapture()
      {
         bool result = false;

         lock( CameraMethodsLock )
         {
            _cameraMethods.StartCamera( _index, ref _width, ref _height, ref _bpp, ref result );
            _cameraMethods.OnImageCapture += CaptureCallbackProc;
         }

         return result;
      }

      internal void StopCapture()
      {
         lock( CameraMethodsLock )
         {
            _cameraMethods.StopCamera();
            _cameraMethods.OnImageCapture -= CaptureCallbackProc;
         }
      }

      /// <summary>
      /// Here is where the images come in as they are collected, as fast as they can and on a background thread
      /// </summary>
      private void CaptureCallbackProc( int dataSize, byte[] data )
      {
         // Do the magic to create a bitmap
         int stride = _width * 3;
         GCHandle handle = GCHandle.Alloc( data, GCHandleType.Pinned );
         var scan0 = handle.AddrOfPinnedObject();
         scan0 += ( _height - 1 ) * stride;
         var b = new Bitmap( _width, _height, -stride, PixelFormat.Format24bppRgb, scan0 );
         b.RotateFlip( _rotateFlip );
         // NOTE: It seems that bntr has made that resolution property work properly
         var copyBitmap = ( Bitmap ) b.Clone();
         // Copy the image using the Thumbnail function to also resize if needed
         //var copyBitmap = (Bitmap)b.GetThumbnailImage(_width, _height, null, IntPtr.Zero);

         // Now you can free the handle
         handle.Free();

         ImageCaptured( copyBitmap );
      }

      private void ImageCaptured( Bitmap bitmap )
      {
         DateTime dtCap = DateTime.Now;

         // Always save the bitmap
         lock( _bitmapLock )
         {
            _bitmap = bitmap;
         }

         // FPS affects the callbacks only
         if( _fpslimit != -1 )
         {
            if( _dtLastCap != DateTime.MinValue )
            {
               double milliseconds = ( ( dtCap.Ticks - _dtLastCap.Ticks ) / TimeSpan.TicksPerMillisecond ) * 1.15;
               if( milliseconds + _timeBehind >= _timeBetweenFrames )
               {
                  _timeBehind = ( milliseconds - _timeBetweenFrames );
                  if( _timeBehind < 0.0 )
                  {
                     _timeBehind = 0.0;
                  }
               }
               else
               {
                  _timeBehind = 0.0;
                  return; // ignore the frame
               }
            }
         }

            var handler = OnImageCaptured;

         if ( handler != null )
         {
            var fps = ( int ) ( 1 / dtCap.Subtract( _dtLastCap ).TotalSeconds );
            handler.Invoke( this, new CameraEventArgs( bitmap, fps ) );
         }

         _dtLastCap = dtCap;
      }

      #endregion
   }

   /// <summary>
   /// Camera specific EventArgs that provides the Image being captured
   /// </summary>
   public class CameraEventArgs : EventArgs
   {
      /// <summary>
      /// Current Camera Image
      /// </summary>
      public Bitmap Image
      {
         get
         {
            return _image;
         }
      }

      public int CameraFps
      {
         get
         {
            return _cameraFps;
         }
      }

      #region Internal Implementation

      private readonly int _cameraFps;
      private readonly Bitmap _image;

      internal CameraEventArgs( Bitmap i, int fps )
      {
         _image = i;
         _cameraFps = fps;
      }

      #endregion
   }
}