/// <summary> /// Creates a mask from an image. All light sources are detected via a hysteresis algorithm and flagged in the mask. /// </summary> /// <param name="Input">Input image data.</param> /// <param name="Position">Data position in the image.</param> /// <param name="Properties">Bag of mask data.</param> static void GenerateMask(double[,] Input, ImageSegmentPosition Position, MaskProperties Properties) { int Width = Input.GetLength(1); int Height = Input.GetLength(0); int i, j; PixelPoint pxp = new PixelPoint(); /* Compute masking thresholds */ double UpperThreshold = Properties.UTM * Properties.StDev + Properties.Mean; double LowerThreshold = Properties.LTM * Properties.StDev + Properties.Mean; for (i = 0; i < Height; i++) { for (j = 0; j < Width; j++) { pxp.X = j + Position.Alignment.X; pxp.Y = i + Position.Alignment.Y; if (pxp.Y >= Properties.MaskData.Length) { break; } if (pxp.X >= Properties.MaskData[(int)pxp.Y].Length) { break; } if (Properties.MaskData[(int)pxp.Y][(int)pxp.X]) { continue; } if (Input[i, j] > UpperThreshold) { BitmapFill(Properties.MaskData, Input, Position.Alignment, pxp, LowerThreshold, Properties.MaskRadiusMultiplier, Properties.ExtraMaskRadius, out Filtering.Star? Star); if (Star != null) { Filtering.Star S = Star.Value; S.EqCenter = Position.WCS.GetEquatorialPoint(S.PixCenter); lock (Properties.StarList) Properties.StarList.FixedStarList.Add(S); } } } } }
/// <summary> /// Runs the hysteresis connected component detection algorithm for light sources. At the end also applies the extra circular masking. /// </summary> /// <param name="Mask">Mask array.</param> /// <param name="MaskData">Masking image data.</param> /// <param name="Alignment">Position of data in the image.</param> /// <param name="DPoint">Starting point for the connected component algorithm.</param> /// <param name="LowerThreshold">Lower hysteresis threshold.</param> /// <param name="RadiusMultiplier">Ratio between extra masking circle radius and light source radius.</param> /// <param name="ExtraRadius">Extra radius for the masking circle.</param> /// <param name="Star">The potential output star.</param> static void BitmapFill(BitArray[] Mask, double[,] MaskData, PixelPoint Alignment, PixelPoint DPoint, double LowerThreshold, double RadiusMultiplier, double ExtraRadius, out Filtering.Star?Star) { Queue <PixelPoint> PointQ = new Queue <PixelPoint>(); PointQ.Enqueue(DPoint); double XMean = 0, YMean = 0, XSquare = 0, YSquare = 0, XY = 0; int PCount = 0; double Flux = 0; while (PointQ.Count > 0) { PixelPoint pt = PointQ.Dequeue(); if (pt.X < 0 || pt.X >= Mask[0].Length) { continue; } if (pt.Y < 0 || pt.Y >= Mask.Length) { continue; } if (Mask[(int)pt.Y][(int)pt.X]) { continue; } double dX = pt.X - Alignment.X; double dY = pt.Y - Alignment.Y; dX = Math.Round(dX); dY = Math.Round(dY); if (dX < 0 || dX >= MaskData.GetLength(1)) { continue; } if (dY < 0 || dY >= MaskData.GetLength(0)) { continue; } if (MaskData[(int)dY, (int)dX] > LowerThreshold) { Mask[(int)pt.Y][(int)pt.X] = true; PointQ.Enqueue(new PixelPoint() { X = pt.X - 1, Y = pt.Y }); PointQ.Enqueue(new PixelPoint() { X = pt.X + 1, Y = pt.Y }); PointQ.Enqueue(new PixelPoint() { X = pt.X, Y = pt.Y - 1 }); PointQ.Enqueue(new PixelPoint() { X = pt.X, Y = pt.Y + 1 }); XMean += pt.X; YMean += pt.Y; XSquare += pt.X * pt.X; YSquare += pt.Y * pt.Y; XY += pt.X * pt.Y; PCount++; Flux += MaskData[(int)dY, (int)dX]; } } /* Computes size of and shape of the light source */ XMean /= PCount; YMean /= PCount; XSquare /= PCount; YSquare /= PCount; XY /= PCount; XSquare -= XMean * XMean; YSquare -= YMean * YMean; XY -= XMean * YMean; double Radius = Math.Sqrt(XSquare + YSquare); SourceEllipse Shape = new SourceEllipse(XSquare, XY, YSquare); /* If not to irregular, suppose it is a star and apply extra masking. */ if (Shape.SemiaxisMajor < 3 * Shape.SemiaxisMinor) { FillMarginsExtra(Mask, new PixelPoint() { X = XMean, Y = YMean }, Radius * RadiusMultiplier + ExtraRadius); Star = new Filtering.Star() { Shape = Shape, PixCenter = new PixelPoint() { X = XMean, Y = YMean }, PixRadius = Radius, Flux = Flux }; } else { Star = null; } }