Skip to content
Justen Hyde edited this page Mar 13, 2014 · 15 revisions

libpsinc

Basic use of libpsinc is as straightforward creating a Camera object, hooking into the Acquired event and calling Initialise to set the camera in motion:

	this.camera = new Camera();
	this.camera.Acquired += i => this.Render(i as System.Drawing.Bitmap);
	this.camera.Initialise();

A more complete example of use can be found in the example application in the repository (and remember to call Dispose() on the camera when you've finished with it).

Connecting to cameras

By default the Camera class will attempt to connect to the first compatible camera that it finds on any USB bus when you call Camera.Initialise(). It's useful to have hooked into the ConnectionChanged event on the Camera before this point, as there is no guarantee that the Camera has actually connected when the Initialise call exits. Instead you should perform any post-connection configuration of the camera when the ConnectionChanged event fires.

	this.camera = new Camera();
	...
	this.camera.ConnectionChanged += c => {
		if (c)
		{
			//We can only set values on the camera after it's connected...
			this.camera.Aliases.AutoGain.Value	= 1;
			this.camera.Aliases.AutoExposure.Value	= 1; 
		}
	};
	this.camera.Initialise();

Connecting to specific cameras

It can be useful in systems with multiple cameras to be able to connect to a specific device. Camera.Initialise() takes two parameters to help with this - a bus index and a serial string.

If the bus is non-zero, the Camera will only look for devices on the bus indexed. If you only have two cameras and they each get plugged into a different bus, this may be all you need to use. For systems with multiple cameras on the same bus, or where there is no guarantee which bus the desired camera will be connected to, you can set a serial string. This takes the form of a regular expression and the first camera found presenting a serial descriptor which matches the expression in the string will be connected to.

The serial descriptor is a concatenation of the unique serial number of the camera with the mutable name of the camera. By either noting the serial number of a given camera or by setting the camera name to a known value, an appropriate serial regex can be used to connect to the camera:

	this.camera1 = new Camera();
	this.camera2 = new Camera();
	...
        //camera1 will always connect to the camera with the name "main"
	this.camera1.Initialise(".*:main");
        //camera2 will always connect to the camera with the name "secondary"
	this.camera2.Initialise(".*:secondary");

Use the SerialNumber Device to read the fixed serial number of the camera, or use the Name Device to set or read the camera name (which is written to non-volatile memory in the camera).

Controlling the camera

libpsinc exposes all of the features of the imaging chip to be modified as you see fit. The details of the features available is beyond the scope of this documentation - take a look at the xml file for the chip you're using for a full listing of what's available.

Common features

There are some features that are so fundamental that they're supported in some form by all imaging chips that could be built into the camera range. Rather than have to modify the application code should feature names vary between imaging chips, these can be accessed through the Aliases property of the Camera object. These also tend to be the most useful features of the camera, so simply exploring the features available through the Aliases property is usually a quicker and easier way to get to grips with a camera than turning to the raw XML description of the whole feature set.

	// Regardless of the imaging chip in the camera, 
	// these instructions will enable automatic gain and exposure control.
	this.camera.Aliases.AutoGain.Value	= 1;
	this.camera.Aliases.AutoExposure.Value	= 1;
	// (Plus you get code completion working on your side)

The one common feature that isn't accessed through the Aliases property is the flash. The flash is accessible directly through Camera.Flash. This is a simple numeric value - set it to zero to turn the flash off. The higher the number, the brighter the flash.

Chip-specific features

More esoteric features of a chip must be accessed using the feature name as declared in the embedded xml. To use one of these features, first look it up in the xml file and take note of the feature name for use thus:

	this.camera.Features["Black Level Calibration Value"].Value = 100;

Both Common and Chip-specific Features are written to the camera when they are set, but rather than causing a read from the camera whenever they are read a local cache of the feature values is maintained. When the camera connection is made, all features are synchronised with the camera and from that point on should normally only be changed by the current connection (so the cache should remain synchronised). If you suspect a feature may have changed on the hardware for some reason you can call Refresh() on the feature in question prior to reading its value, though unless a feature is specifically stated to behave in such a way this is unlikely to occur.

Capturing images

Once the camera is initialised, it will begin capturing images immediately. All you need to do to get access to the captures images is to handle the Acquired event:

	this.camera.Acquired += i => this.OnCapture(i as Bitmap);

And do whatever you want with the image you just received.

Custom image handlers

By default libpsinc produces images as System.Drawing.Bitmap objects. For many applications this is perfectly adequate, but if you'd rather receive your images in some other format you just need to produce an image handler for libpsinc to use when converting the data from the camera into an object. When you receive the Acquired event, simply cast the image to the appropriate type:

// An image handler for Gtk applications that produces a Pixbuf image
public class GtkImageHandler : ImageHandler
{
	public override object Decode(ColourMode colour, byte [] buffer, int width, int height)
	{
		Pixbuf result	= null;
		int w 			= colour == ColourMode.Monochrome ? width : width - 4;
		int h			= colour == ColourMode.Monochrome ? height : height - 4;
		
		if (w > 0 && h > 0)
		{
			result = new Pixbuf(Colorspace.Rgb, false, 8, w, h);
			
			unsafe 
			{
				switch (colour)
				{
					case ColourMode.Monochrome:		this.DecodeMono(buffer, (byte *)result.Pixels, width, height, result.Rowstride);			break;
					case ColourMode.BayerGrey:		this.DecodeGrey(buffer, (byte *)result.Pixels, width, height, result.Rowstride);			break;
					case ColourMode.BayerColour:	this.DecodeColour(buffer, (byte *)result.Pixels, width, height, result.Rowstride, true);	break;
				}
			}
		}
			
		return result;
	}
}

///And then, when setting up your camera...
class Example
{
	protected Camera camera = new Camera();
	public Example()
	{
		this.camera.ImageHandler = new GtkImageHandler();
		this.camera.Acquired += i => this.Render(i as Gdk.Pixbuf);
		this.camera.Initialise();
	}
...
}

Pausing and resuming

If you don't want to keep streaming images from the camera constantly, you can pause it then resume when you're ready to receive more data:

	//Pause the camera when we're not using it...
	this.camera.Pause();

	//Do something time consuming that doesn't require any image capture
	this.DoComplicatedStuff();

	//...and then resume again when we're ready
	this.camera.Resume();

Note that while the camera is paused you won't get timely ConnectionChanged events as these are emitted by the main thread of the camera.

Devices

Devices are either physical peripherals that can be connected to the camera or are "virtual" devices that provide some added functionality or information about the camera hardware. libpsinc provides some helper classes that make using many of these Devices trivial - and some are trivial to use to begin with.

Devices with helper "handlers"

These devices can easily by used by instantiating the relevant class from the libpsinc.device namespace, though can still be accessed directly through the Camera.Devices property should you so wish.

To attach a device handler to the camera, simply create the device and pass the Camera it resides on in to the constructor. Once the camera is connected, the device is live:

	this.camera = new Camera();
	this.led = new LEDArray(this.camera);
	this.camera.ConnectionChanged += (c) => {
		if(c)
		{  
			//the LED Array device can now be controlled...
			this.led.PrimaryMode = LEDArray.Mode.Sweep;
			this.led.PrimaryColour = LEDArray.Colour.Red;
			this.led.Flush();
		}
	};
	this.camera.Initialise();

Devices that don't have handlers are generally either so simple that they don't need them or are intended for internal use (see the Query device for an example).

Devices with handlers available are:

  • Prox reader A prox card reader. Hook into the CardPresented event to receive the data read from a prox card presented to the card reader. You can set the amount of data read through the ReadLength property. You can set the read length before or after connection is complete; it will be flushed through to the camera on each iteration of the polling thread after connection. Remember to dispose the prox object when you've finished with it in order for it to exit the thread.
	this.prox = new Prox(this.camera);

	// ReadLength is the number of 16-byte pages to read in addition to the
	// initial 20-byte page which contains the unique card ID.
	this.prox.ReadLength = 2;

	// ID contains the unique ID of the card, converted to an int representation.
	// data contains all of the data read from the card.
	this.prox.CardPresented+= (ID, data) => { Console.WriteLine("Card " + ID +" presented");};
  • Electronic lock The electronic lock output on the camera can be controlled through the SimpleLock helper class. Simply construct it and after camera connection is complete you can send either Lock or Unlock signals:
	this.lock = new SimpleLock(this.camera)
	...
	// After the camera is connected you can then issue lock...
	this.lock.Lock();
	// ...And unlock signals
	this.lock.Unlock();
  • LED array The LED array, if fitted, is a bank of five multicoloured LEDs. They can be issued with a mode and colour, both of which are applied to all LEDs, and an override mode and override colour. The override mode specifies which LEDs to override with the specified colour. This allows you to, for example, have a sweeping red light (configured using the mode and colour) with a constant central blue LED (configured using the override mode and colour). Modes are not written to the camera when set; instead you should call Flush() when you have configured all four properties as desired:-
	this.camera = new Camera();
	this.led = new LEDArray(this.camera);
	this.camera.ConnectionChanged += (c) => {
		if(c)
		{  
			// The LED Array device can now be controlled
			// so let's set it up as described above...
			this.led.PrimaryMode = LEDArray.Mode.Sweep;
			this.led.PrimaryColour = LEDArray.Colour.Red;
			this.led.OverrideMode = LEDArray.Override.Central;
			this.led.OverrideColour = LEDArray.Colour.Blue;
			this.led.Flush();
		}
	};
  • LED pair The LED pair, if fitted, is a pair of multicolour LEDs. This is a similar, but much simpler, signalling device to the LED Array and is also simpler to use. The colour of both LEDs is set in one call through the Device:
	this.led = new LEDPair(this.camera);
	...
	// After connection, when appropriate...
	this.led.flush(LEDPair.Colour.Red, LEDPair.Colour.Green);
  • Serial number The serial number device simply returns the immutable serial string of the device, formatted nicely:
	this.serialNumber = new SerialNumber(this.camera);
	...
	// After connection, when appropriate...
	Console.WriteLine(this.serialNumber.Read());

###Devices without handlers### The following devices do not have handlers; you must access them directly through the Devices property of the Camera:

  • Device name This is the user-settable device name. When written, this is stored on the camera and used alongside the immutable serial number to modify the USB descriptors the camera provides so that cameras can be easily identified and selected at connection.
	//Remember to wait until the camera is connected before performing Device access
	string name = this.camera.Devices["Name"].ReadString();
  • Storage
  • Defaults
  • Query
Clone this wiki locally