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