//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 } } }