public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit) { IsTrackedSuccessfully = false; ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid( (int)TrackedObject.LastKnownX, (int)TrackedObject.LastKnownY, CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius); if (centroid != null) { PSFFit psfFit; AstrometryContext.Current.StarMap.GetPSFFit( centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit); if (psfFit != null) { double ra, de; astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de); double maxPosDiffArcSec = astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage, astrometricFit.Image.CenterXImage + CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius, astrometricFit.Image.CenterYImage); if (!double.IsNaN(TrackedObject.RAHours)) { double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de); if (posDif > maxPosDiffArcSec) { // NOTE: Not a valid measurement Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif)); return; } } TrackedObject.RAHours = ra / 15.0; TrackedObject.DEDeg = de; TrackedObject.LastKnownX = psfFit.XCenter; TrackedObject.LastKnownY = psfFit.YCenter; TrackedObject.PSFFit = psfFit; IsTrackedSuccessfully = true; } } }
private void ImproveSolution(LeastSquareFittedAstrometry fit, double coeff, int i, int j, int k) { m_SolutionSolver = new PlateConstantsSolver(m_PlateConfig); double ra0, de0; fit.GetRADEFromImageCoords(m_PlateConfig.CenterXImage, m_PlateConfig.CenterXImage, out ra0, out de0); m_SolutionSolver.InitPlateSolution(ra0, de0); List<IStar> consideredStars = new List<IStar>(); List<ulong> nonStellarStars = new List<ulong>(); foreach (IStar star in m_CelestialAllStars) { if (star.Mag < m_MinMag || star.Mag > m_MaxMag) continue; if (m_DetermineAutoLimitMagnitude && !double.IsNaN(m_DetectedLimitingMagnitude) && star.Mag > m_DetectedLimitingMagnitude) { #if ASTROMETRY_DEBUG Trace.Assert(false); #endif continue; } double x, y; fit.GetImageCoordsFromRADE(star.RADeg, star.DEDeg, out x, out y); if (x < 0 || x > m_PlateConfig.ImageWidth || y < 0 || y > m_PlateConfig.ImageHeight) continue; ImagePixel pixel = null; PSFFit psfFit; PSFFit asymPsfFit = null; //if (m_FitSettings.CenterDetectionMethod == StarCenterDetection.PSFFit) { if (m_Settings.PyramidRemoveNonStellarObject) m_StarMap.GetPSFFit((int)x, (int)y, PSFFittingMethod.NonLinearAsymetricFit, out asymPsfFit); pixel = m_StarMap.GetPSFFit((int)x, (int)y, PSFFittingMethod.NonLinearFit, out psfFit); } //else if (m_FitSettings.CenterDetectionMethod == StarCenterDetection.Centroid) { // NOTE: Centroid detection is way faster and PSF will not lead to big improvement considering the threshold for star matching //pixel = m_StarMap.GetCentroid((int)x, (int)y, (int)Math.Ceiling(m_Settings.SearchArea)); } if (pixel != null && psfFit.Certainty >= CorePyramidConfig.Default.MinDetectionLimitForSolutionImprovement / coeff) { consideredStars.Add(star); double distance = fit.GetDistanceInArcSec(pixel.XDouble, pixel.YDouble, x, y); if (distance < CorePyramidConfig.Default.MaxPreliminaryResidualForSolutionImprovement / coeff) { #if ASTROMETRY_DEBUG Trace.Assert(!double.IsNaN(pixel.XDouble)); Trace.Assert(!double.IsNaN(pixel.YDouble)); #endif if ( Math.Sqrt((x - pixel.XDouble) * (x - pixel.XDouble) + (y - pixel.YDouble) * (y - pixel.YDouble)) > CoreAstrometrySettings.Default.SearchArea) continue; pixel.SignalNoise = psfFit.Certainty; pixel.Brightness = psfFit.Brightness; m_SolutionSolver.AddStar(pixel, star); if (m_Settings.PyramidRemoveNonStellarObject && ( asymPsfFit.FWHM < m_Settings.MinReferenceStarFWHM || asymPsfFit.FWHM > m_Settings.MaxReferenceStarFWHM || asymPsfFit.ElongationPercentage > m_Settings.MaximumPSFElongation) ) { nonStellarStars.Add(star.StarNo); } } } } double ffl = fit.FitInfo.FittedFocalLength; m_ImprovedSolution = m_SolutionSolver.SolveWithLinearRegression(m_Settings, out m_FirstImprovedSolution); //if (m_ImprovedSolution != null && m_ImprovedSolution.FitInfo.AllStarPairs.Count < 12) //{ // // Attempt to reject errorous solutions with small number of fitted stars // int totalMatched = 0; // var largeFeatures = m_StarMap.Features.Where(x => x.MaxBrightnessPixels > 1).ToList(); // if (largeFeatures.Count > 5) // { // foreach (var feature in largeFeatures) // { // var ftrCenter = feature.GetCenter(); // // TODO: PFS Fit on the feature? // var matchedFittedStar = m_ImprovedSolution.FitInfo.AllStarPairs.FirstOrDefault( // x => // Math.Sqrt(Math.Pow(x.x - ftrCenter.XDouble, 2) + Math.Pow(x.x - ftrCenter.XDouble, 2)) < // 2); // if (matchedFittedStar != null) totalMatched++; // } // double percentLargeFeaturesMatched = 1.0*totalMatched/largeFeatures.Count; // if (percentLargeFeaturesMatched < 0.75) // { // if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceInfo()) // Trace.WriteLine(string.Format("Only {0:0.0}% ({1} features) of the large {2} features have been matched, where 75% is required.", percentLargeFeaturesMatched*100, totalMatched, largeFeatures.Count)); // // At least 75% of the bright features from the video need to be matched to stars for the solution to be accepted // m_ImprovedSolution = null; // } // } //} if (m_ImprovedSolution != null) { m_ImprovedSolution.FitInfo.FittedFocalLength = ffl; if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceWarning()) Trace.WriteLine(string.Format("Improved solution: {0} considered stars, UsedInSolution: {1}, ExcludedForHighResidual: {2}", m_ImprovedSolution.FitInfo.AllStarPairs.Count(), m_ImprovedSolution.FitInfo.AllStarPairs.Count(x => x.FitInfo.UsedInSolution), m_ImprovedSolution.FitInfo.AllStarPairs.Count(x => x.FitInfo.ExcludedForHighResidual))); // Fit was successful, exclude all unused non stellar objects so they // don't interfere with the included/excluded stars improved solution tests m_ImprovedSolution.FitInfo.AllStarPairs.RemoveAll(p => (p.FitInfo.ExcludedForHighResidual || !p.FitInfo.UsedInSolution) && nonStellarStars.Contains(p.StarNo)); // NOTE: How excluding stars for FWHM/Elongation may lead to incorrectly accepted solutions that include small number of stars // because the majority haven't been used for the fit. This is why we have another solution check here. if (m_ImprovedSolution.FitInfo.AllStarPairs.Count > 3) { List<PlateConstStarPair> usedStarPairs = m_ImprovedSolution.FitInfo.AllStarPairs.Where(p => p.FitInfo.UsedInSolution).ToList(); double maxIncludedMag = usedStarPairs.Max(p => p.Mag); int nonIncludedConsideredStars = consideredStars.Count(s => s.Mag <= maxIncludedMag) - usedStarPairs.Count; if (nonIncludedConsideredStars > CorePyramidConfig.Default.MaxFWHMExcludedImprovemntStarsCoeff * usedStarPairs.Count) { LogUnsuccessfulFitImage(m_ImprovedSolution, i, j, k, string.Format("More than {0:0.0}% of the stars ({1} stars) down to mag {2:0.00} have not been matched. Attempted stars: {3}, Coeff: {4:0.00}", CorePyramidConfig.Default.MaxFWHMExcludedImprovemntStarsCoeff * 100, nonIncludedConsideredStars, maxIncludedMag, m_SolutionSolver.Pairs.Count, nonIncludedConsideredStars / m_SolutionSolver.Pairs.Count)); m_ImprovedSolution = null; return; } List<double> intensities = usedStarPairs.Select(s => (double)s.Intensity).ToList(); List<double> mags = usedStarPairs.Select(s => s.Mag).ToList(); if (usedStarPairs.Count > 3) { LinearRegression reg = new LinearRegression(); int pointsAdded = 0; for (int ii = 0; ii < intensities.Count; ii++) { if (intensities[ii] > 0) { reg.AddDataPoint(intensities[ii], Math.Log10(mags[ii])); pointsAdded++; } } if (pointsAdded > 3) { reg.Solve(); if (Math.Pow(10, reg.StdDev) > CorePyramidConfig.Default.MagFitTestMaxStdDev || reg.A > CorePyramidConfig.Default.MagFitTestMaxInclination) { LogUnsuccessfulFitImage(m_ImprovedSolution, i, j, k, string.Format("Failing solution for failed magnitude fit. Intensity(Log10(Magntude)) = {1} * x + {2}, StdDev = {0:0.0000}, ChiSquare = {3:0.000}", Math.Pow(10, reg.StdDev), reg.A, reg.B, reg.ChiSquare)); m_ImprovedSolution = null; return; } } } } } }
public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit) { IsTrackedSuccessfully = false; int searchRadius = (int)Math.Ceiling(Math.Max(m_LastMovementPixels, CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius)); PSFFit psfFit = null; int startingX = (int)TrackedObject.LastKnownX; int startingY = (int)TrackedObject.LastKnownY; if (m_RepeatedIntergationPositions * 4 < m_PastFrameNos.Count) { var expectedPos = GetExpectedPosition(frameNo); if (expectedPos != null) { startingX = expectedPos.X; startingY = expectedPos.Y; } } var nearbyFeatures = starMap.GetFeaturesInRadius(startingX, startingY, searchRadius).ToArray(); var nonStarNearbyFeature = new List <StarMapFeature>(); foreach (var feature in nearbyFeatures) { var center = feature.GetCenter(); var referenceStarFeatures = astrometricFit.FitInfo.AllStarPairs.Where(x => x.FitInfo.UsedInSolution).ToList(); var refStar = referenceStarFeatures.FirstOrDefault(s => Math.Sqrt((s.x - center.X) * (s.x - center.X) + (s.y - center.Y) * (s.y - center.Y)) < 2); double raf, def; astrometricFit.GetRADEFromImageCoords(center.XDouble, center.YDouble, out raf, out def); var pastKnownStar = m_LastFrameStars.FirstOrDefault(s => AngleUtility.Elongation(s.RADeg, s.DEDeg, raf, def) * 3600.0 < 2); if (refStar == null && pastKnownStar == null) { nonStarNearbyFeature.Add(feature); } } if (nonStarNearbyFeature.Count > 0) { StarMapFeature closestFeature = nonStarNearbyFeature[0]; var lastKnownCenter = new ImagePixel(TrackedObject.LastKnownX, TrackedObject.LastKnownY); var smallestDistance = lastKnownCenter.DistanceTo(closestFeature.GetCenter()); for (int i = 1; i < nonStarNearbyFeature.Count; i++) { var distance = lastKnownCenter.DistanceTo(nonStarNearbyFeature[i].GetCenter()); if (distance < smallestDistance) { smallestDistance = distance; closestFeature = nonStarNearbyFeature[i]; } } if (closestFeature != null) { var center = closestFeature.GetCenter(); AstrometryContext.Current.StarMap.GetPSFFit(center.X, center.Y, PSFFittingMethod.NonLinearFit, out psfFit); } } if (psfFit == null) { // The expected location cannot be matched with any brighter feature so it is likely a faint object // with no brighter objects around. Lets find the brightest (faint) object in the are and use it ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid(startingX, startingY, searchRadius); if (centroid != null) { AstrometryContext.Current.StarMap.GetPSFFit(centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit); } } if (psfFit != null) { double ra, de; astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de); double maxPosDiffArcSec = astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage, astrometricFit.Image.CenterXImage + Math.Max(m_LastMovementPixels, CoreAstrometrySettings.Default.MaxAllowedDefaultMotionInPixels), astrometricFit.Image.CenterYImage); if (!double.IsNaN(TrackedObject.RAHours)) { double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de); if (posDif > maxPosDiffArcSec) { // NOTE: Not a valid measurement Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif)); return; } } TrackedObject.RAHours = ra / 15.0; TrackedObject.DEDeg = de; if (TrackedObject.PSFFit != null) { m_LastMovementPixels = 1.2 * ImagePixel.ComputeDistance(TrackedObject.LastKnownX, psfFit.XCenter, TrackedObject.LastKnownY, psfFit.YCenter); } TrackedObject.LastKnownX = psfFit.XCenter; TrackedObject.LastKnownY = psfFit.YCenter; TrackedObject.PSFFit = psfFit; var lastKnownCenter = new ImagePixel(TrackedObject.LastKnownX, TrackedObject.LastKnownY); var thisFrameStars = astrometricFit.FitInfo.AllStarPairs.Where(x => lastKnownCenter.DistanceTo(x.x, x.y) > 2 * psfFit.FWHM).ToList(); if (thisFrameStars.Count > 0) { m_LastFrameStars = thisFrameStars.Select(x => new Star(x.StarNo, x.RADeg, x.DEDeg, x.Mag) as IStar).ToList(); } IsTrackedSuccessfully = true; } if (psfFit != null && psfFit.XCenter > 0 && psfFit.YCenter > 0) { m_PastFramePosX.Add(psfFit.XCenter); m_PastFramePosY.Add(psfFit.YCenter); m_PastFrameNos.Add(frameNo); } }
public Bitmap ResolveObjects( TangraConfig.PhotometryReductionMethod photometryReductionMethod, TangraConfig.PsfQuadrature psfQuadrature, TangraConfig.PsfFittingMethod psfFittingMethod, TangraConfig.BackgroundMethod backgroundMethod, TangraConfig.PreProcessingFilter filter, Guid magnitudeBandId, Rectangle osdRectangleToExclude, Rectangle rectToInclude, bool limitByInclusion, IAstrometrySettings astrometrySettings, ObjectResolverSettings objectResolverSettings) { m_AstrometrySettings = astrometrySettings; StarMap starMap = new StarMap( astrometrySettings.PyramidRemoveNonStellarObject, astrometrySettings.MinReferenceStarFWHM, astrometrySettings.MaxReferenceStarFWHM, astrometrySettings.MaximumPSFElongation, astrometrySettings.LimitReferenceStarDetection); starMap.FindBestMap(StarMapInternalConfig.Default, m_Image, osdRectangleToExclude, rectToInclude, limitByInclusion); float r0 = 0; m_MagnitudeFit = StarMagnitudeFit.PerformFit( m_AstrometryController, m_VideoController, m_Image.Pixelmap.BitPixCamera, m_Image.Pixelmap.MaxSignalValue, m_Astrometry.FitInfo, photometryReductionMethod, psfQuadrature, psfFittingMethod, backgroundMethod, filter, m_Stars, magnitudeBandId, 1.0f, TangraConfig.KnownCameraResponse.Undefined, null, null, null, ref r0); m_BackgroundFlux = m_MagnitudeFit.GetBackgroundIntencity(); m_BackgroundMag = m_MagnitudeFit.GetMagnitudeForIntencity(m_BackgroundFlux); if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) { Trace.WriteLine(string.Format("Plate FWHM: {0}", 2 * Math.Sqrt(Math.Log(2)) * r0)); } PeakPixelResolver resolver = new PeakPixelResolver(m_Image); resolver.ResolvePeakPixels(osdRectangleToExclude, rectToInclude, limitByInclusion, objectResolverSettings.ExcludeEdgeAreaPixels, objectResolverSettings.MinDistanceBetweenPeakPixels); List <double> identifiedMagnitudes = new List <double>(); List <double> identifiedR0s = new List <double>(); m_IdentifiedObjects.Clear(); m_UidentifiedObjects.Clear(); foreach (KeyValuePair <int, int> peakPixel in resolver.PeakPixels.Keys) { int x = peakPixel.Key; int y = peakPixel.Value; bool isSaturated; double intencity = m_MagnitudeFit.GetIntencity(new ImagePixel(255, x, y), out isSaturated); double magnitude = m_MagnitudeFit.GetMagnitudeForIntencity(intencity); if (magnitude < m_MaxMagForAstrometry) { double RADeg, DEDeg; PSFFit fit; starMap.GetPSFFit(x, y, PSFFittingMethod.NonLinearFit, out fit); if (fit.IMax < 0) { continue; } if (fit.IMax < fit.I0) { continue; } if (fit.Certainty < objectResolverSettings.MinCertainty) { continue; } if (fit.FWHM < objectResolverSettings.MinFWHM) { continue; } if (fit.IMax - fit.I0 < objectResolverSettings.MinAmplitude) { continue; } m_Astrometry.GetRADEFromImageCoords(fit.XCenter, fit.YCenter, out RADeg, out DEDeg); // All stars closer than 2 arcsec to this position List <IStar> matchingStars = m_Stars.Where(s => Math.Abs(AngleUtility.Elongation(s.RADeg, s.DEDeg, RADeg, DEDeg) * 3600.0) < objectResolverSettings.MaxStarMatchMagDif).ToList(); bool identified = false; if (matchingStars.Count >= 1) { foreach (IStar star in matchingStars) { if (objectResolverSettings.MaxStarMatchMagDif >= Math.Abs(magnitude - star.Mag)) { // The star is identified. Do we care more? if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) { Trace.WriteLine(string.Format("STAR ({0}, {1}) No -> {2}; Mag -> {3} (Expected: {4}); R0 = {5}", x, y, star.StarNo, magnitude.ToString("0.00"), star.Mag.ToString("0.00"), fit.R0.ToString("0.0"))); } identifiedMagnitudes.Add(magnitude); identifiedR0s.Add(fit.R0); m_IdentifiedObjects.Add(fit, star); identified = true; break; } } } if (matchingStars.Count == 0 || !identified) { // The object is not in the star database // TODO: Test for hot pixel. Match to hot pixel profile from the brightest pixel in the area m_UidentifiedObjects.Add(fit, magnitude); } } else { // Don't bother with too faint objects } } if (m_IdentifiedObjects.Count > 0) { double mean = identifiedR0s.Average(); double variance = 0; foreach (double rr0 in identifiedR0s) { variance += (rr0 - mean) * (rr0 - mean); } variance = Math.Sqrt(variance / (m_IdentifiedObjects.Count - 1)); double minR0 = mean - variance; double maxR0 = mean + variance; identifiedMagnitudes.Sort(); double maxStarMag = identifiedMagnitudes[Math.Max(0, (int)Math.Truncate(0.9 * identifiedMagnitudes.Count))]; if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) { Trace.WriteLine(string.Format("Max Star Mag: {0}; R0 ({1}, {2})", maxStarMag.ToString("0.00"), minR0.ToString("0.0"), maxR0.ToString("0.0"))); } // NOTE: The R0 exclusion may ignore bright comets ! m_UnknownObjects = m_UidentifiedObjects .Where(p => p.Value < maxStarMag && p.Key.R0 >= minR0 && p.Key.R0 <= maxR0) .ToDictionary(p => p.Key, p => p.Value); if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) { foreach (PSFFit obj in m_UnknownObjects.Keys) { Trace.WriteLine(string.Format("UNK: ({0}, {1}) Mag -> {2}; R0 = {3}", obj.XCenter.ToString("0.0"), obj.YCenter.ToString("0.0"), m_UnknownObjects[obj].ToString("0.00"), obj.R0.ToString("0.0"))); } } } Bitmap bitmap = m_Image.Pixelmap.CreateDisplayBitmapDoNotDispose(); using (Graphics g = Graphics.FromImage(bitmap)) { foreach (PSFFit star in m_IdentifiedObjects.Keys) { float x = (float)star.XCenter; float y = (float)star.YCenter; g.DrawEllipse(Pens.GreenYellow, x - 5, y - 5, 10, 10); } foreach (PSFFit star in m_UnknownObjects.Keys) { float x = (float)star.XCenter; float y = (float)star.YCenter; g.DrawEllipse(Pens.Tomato, x - 8, y - 8, 16, 16); } g.Save(); } return(bitmap); }