//Using statements omitted
///
/// The LaserDotTracking class handles processing of video frames to identify laser dot positions.
/// It works with the calibration classes to control what pixels are identified as laser dots as well
/// as the PixelList and PixelClump classes to make these points meaningful.
///
public class LaserDotTracking
{
/*
* CONSTANTS
*/
///
/// modifier for the red channel's contribution to brightness
///
public const ushort RED_MODIFIER = 4;
///
/// modifier for the green channel's contribution to brightness
///
public const ushort GREEN_MODIFIER = 2;
///
/// modifier for the blue channel's contribution to brightness
///
public const ushort BLUE_MODIFIER = 2;
//red * 4 + green * 2 + blue * 2
public const int MAX_POSSIBLE_BRIGHTNESS = 255 * (RED_MODIFIER +
GREEN_MODIFIER +
BLUE_MODIFIER);
/*
* CLASS PROPERTIES
*/
///
/// the object to store video calibration data
///
private CalibrationData calibrationData;
///
/// the raster size of the video feed
///
private Size sourceSize;
///
/// the number of lasers identified in the last frame
///
private int numLasersOnScreen;
///
/// the list of laser dots being tracked
///
private List lasers;
///
/// bright pixel clump list
///
private PixelList brightPixelList;
///
/// the video frame used for thresholding
///
private UnsafeBitmap uThresholdFrame;
/*
* CONSTRUCTOR
*/
///
/// constructor for the tracking object
///
/// the calibration object data
/// the array of laser dots
public LaserDotTracking(CalibrationData newData, List newLasers)
{
calibrationData = newData;
lasers = newLasers;
}
///
/// unloads the laser dot tracker object
///
public void unload()
{
uThresholdFrame.Dispose();
lasers.Clear();
lasers = null;
brightPixelList = null;
calibrationData = null;
}
//encapsulators omitted...
/*
* FRAME PROCESSING
*/
///
/// process a new frame of the input video
///
public void ProcessFrame(Bitmap tmpImage1)
{
//Get width and height of source video
if (brightPixelList == null ||
sourceSize.Width != tmpImage1.Width ||
sourceSize.Height != tmpImage1.Height)
{
sourceSize.Width = tmpImage1.Width;
sourceSize.Height = tmpImage1.Height;
brightPixelList = new PixelList(this, calibrationData,
sourceSize.Width, sourceSize.Height);
}
//clear pixel list and prepare to process new frame
brightPixelList.beginFrame();
//copy image content to unsafe bitmap object
UnsafeBitmap uBitmap = new UnsafeBitmap(tmpImage1);
//lock memory
uBitmap.LockBitmap();
if (!uThresholdFrame.Locked)
this.uThresholdFrame.LockBitmap();
//integer variables avoid byte overflow
int dRed; int dGreen; int dBlue;
//for each scan line
for (int y = 0; y < sourceSize.Height; y++)
{
//for each pixel in the scan line
for (int x = 0; x < sourceSize.Width; x++)
{
PixelData thresholdPixel = this.uThresholdFrame.GetPixel(x, y);
PixelData newPixel = uBitmap.GetPixel(x, y);
//subtract brightnessThreshold pixel color from current pixel color
dRed = (newPixel.red - thresholdPixel.red);
dGreen = (newPixel.green - thresholdPixel.green);
dBlue = (newPixel.blue - thresholdPixel.blue);
//enforce zero minimum for adjusted color
if (dRed < 0)
dRed = 0;
if (dGreen < 0)
dGreen = 0;
if (dBlue < 0)
dBlue = 0;
//pixel brightness function
int brightness = (RED_MODIFIER * dRed) +
(GREEN_MODIFIER * dGreen) +
(BLUE_MODIFIER * dBlue);
//if pixel is bright enough to be considered
// a laser dot add it to the pixel list
if (brightness > calibrationData.Threshold)
brightPixelList.addPixel(new Point(x, y), (ushort)(brightness));
}
}
//identify laser pixel clumps
brightPixelList.finishFrame();
//unlock bitmap
uBitmap.UnlockBitmap();
//dispose frame
uBitmap.Bitmap.Dispose();
tmpImage1.Dispose();
//process clumps and match them with laser dots from the previous frame
foreach(LaserDot laser in lasers)
if(laser.onScreen)
laser.setClumpDistances( ref brightPixelList.PixelClumps );
// iteratively approach a best solution
// for laser dot to pixel clump matching
Boolean changeMade = true;
int iteration = 0;
while (changeMade && iteration < 10)
{
changeMade = false;
foreach (LaserDot laser in lasers)
{
if (laser.claimBestClump())
changeMade = true;
}
iteration++;
}
//update position from best pixel clump
foreach (LaserDot laser in lasers)
laser.updateFromClaim();
//check for new lasers that were not visible in the previous frame
foreach(LaserDot laser in lasers)
laser.checkForNewLaser( ref brightPixelList.PixelClumps );
//total number of lasers dots in the current frame
numLasersOnScreen = 0;
for (int i = 0; i < lasers.Count; i++)
{
if (lasers[i].onScreen)
numLasersOnScreen++;
}
}
}