//Using statements omitted
///
/// This class manages a list of pixel coordinates that have an intensity bright enough
/// to be a laser dot. Once a frame has finished processing by the tracker class the
/// identified coordinates are grouped together into clumps.
///
public class PixelList
{
/*
* CONSTANTS
*/
///
/// the minimum number of pixels in a clump to be considered part of a laser dot
///
public const int NUM_PIXELS_NEEDED_FOR_LASER = 3;
/*
* CLASS PROPERTIES
*/
///
/// the list of bright pixel clumps
///
public List PixelClumps;
///
/// the maximum number of clumps
///
protected int maxNumClumps;
//possible pixel attributes
///
/// the list of bright pixel positions identified by the tracker
///
protected List brightPixels;
///
/// the raster size of the video stream
///
protected Size sourceSize;
///
/// the matrix of values flags indicating which pixels
/// have been identified as part of a pixel clump
///
protected Boolean[,] identified;
///
/// the matrix of laser dot weights (brightness) for each pixel
///
protected ushort[,] pixelWeights;
///
/// the video calibration data object
///
protected CalibrationData calibrationData;
///
/// the laser dot tracking object
///
protected LaserDotTracking curTracker;
/*
* CONSTRUCTOR
*/
///
/// constructor for a new pixel list manager object
///
/// the laser dot tracking object
/// the calibration data object
/// the raster width of the video feed
/// the raster height of the video feed
public PixelList(LaserDotTracking newTracker, CalibrationData newData, int sourceWidth, int sourceHeight)
{
curTracker = newTracker;
calibrationData = newData;
maxNumClumps = calibrationData.NumLasers * 2;
sourceSize = new Size(sourceWidth, height);
pixelWeights = new ushort[sourceSize.sourceHeight, sourceSize.Height];
identified = new Boolean[sourceSize.Width, sourceSize.Height];
brightPixels = new List(150);
PixelClumps = new List();
}
///
/// called when a new frame begins processing
///
public void beginFrame()
{
brightPixels.Clear();
for (int i = 0; i < sourceSize.Height; i++)
for (int j = 0; j < sourceSize.Width; j++)
{
//set all pixels to no laser weight
pixelWeights[j, i] = 0;
identified[j, i] = false;
}
}
///
/// adds a new entry to the list of pixels which are intense
/// enough to be considered a laser pointer
///
/// the pixel's position
/// the pixel's weight (brightness)
public void addPixel(Point pos, ushort weight)
{
pixelWeights[pos.X, pos.Y] = weight;
brightPixels.Add(pos);
}
/*
* PROCESS FINISHED FRAME
*/
///
/// called when a frame has finished processing
/// by the laser dot tracking object. Processes
/// the identified bright pixels to group them
/// into pixel clumps
///
public virtual void finishFrame()
{
PixelClumps.Clear();
for (int i = 0; i < brightPixels.Count; i++)
{
//if the current bright pixel has not already been identified as part of another pixel clump...
if (!identified[brightPixels[i].X, brightPixels[i].Y])
{
//create a new pixel clump
PixelClump curClump = new PixelClump(false);
//gets all the pixels that make up this clump
recursiveGetPixelClump(brightPixels[i].X, brightPixels[i].Y, ref curClump);
if (curClump.NumPixels == 0)
{
curClump.AveragePosition.X = 0;
curClump.AveragePosition.Y = 0;
curClump.AverageWeight = 0;
}
else
{
//find the average position of this clump
curClump.AveragePosition.X = curClump.PositionSum.X / curClump.NumPixels;
curClump.AveragePosition.Y = curClump.PositionSum.Y / curClump.NumPixels;
curClump.AveragePosition += calibrationData.CropOffset;
curClump.AverageWeight = curClump.TotalWeight / curClump.NumPixels;
}
//if the clump is large enough add it to the list of pixel clumps that represent laser dots
if (curClump.NumPixels >= PixelList.NUM_PIXELS_NEEDED_FOR_LASER)
PixelClumps.Add(curClump);
//limit the number of pixel clumps
//to prevent a stack overflow or OoM
if(PixelClumps.Count > maxNumClumps)
break;
}
}
}
///
/// recursively identifies all pixels in this
/// bright clump by flood filling algorithm
///
/// the current X location
/// the current Y location
/// the current clump of pixels
private void recursiveGetPixelClump(int X, int Y, ref PixelClump clump)
{
if (X >= 0 && X < sourceSize.Width &&
Y >= 0 && Y < sourceSize.Height &&
!identified[X, Y] &&
pixelWeights[X, Y] > 0)
{
//if in bounds and not yet a part of a pixel clump and pixel weight is large enough
identified[X, Y] = true;
clump.TotalWeight += pixelWeights[X, Y];
clump.PositionSum.X += X;
clump.PositionSum.Y += Y;
clump.NumPixels++;
// limit number of pixels which can make up a clump
// to prevent a stack overflow
if (clump.NumPixels > 400)
return;
//add neighbors
recursiveGetPixelClump(X + 1, Y - 1, ref clump); //right, top
recursiveGetPixelClump(X + 1, Y, ref clump); //right
recursiveGetPixelClump(X + 1, Y + 1, ref clump); //right, botom
recursiveGetPixelClump(X, Y - 1, ref clump); //top
recursiveGetPixelClump(X, Y + 1, ref clump); //bottom
recursiveGetPixelClump(X - 1, Y - 1, ref clump); //left, top
recursiveGetPixelClump(X - 1, Y, ref clump); //left
recursiveGetPixelClump(X - 1, Y + 1, ref clump); //left, botom
}
}
}