// TODO: Use the imeplementation in the MainForm instead private void DrawHighResFeature(ImagePixel pixel, PlateConstStarPair selectedPair, LeastSquareFittedAstrometry astrometry) { int x0 = pixel.X; int y0 = pixel.Y; if (x0 < 15) { x0 = 15; } if (y0 < 15) { y0 = 15; } if (x0 > AstrometryContext.Current.FullFrame.Width - 15) { x0 = AstrometryContext.Current.FullFrame.Width - 15; } if (y0 > AstrometryContext.Current.FullFrame.Height - 15) { y0 = AstrometryContext.Current.FullFrame.Height - 15; } int bytes; int bytesPerPixel; int selIdx; Bitmap featureBitmap = new Bitmap(31 * 8, 31 * 8, PixelFormat.Format24bppRgb); m_VideoController.UpdateZoomedImage(featureBitmap, pixel); BitmapData zoomedData = featureBitmap.LockBits(new Rectangle(0, 0, 31 * 8, 31 * 8), ImageLockMode.ReadWrite, featureBitmap.PixelFormat); try { bytes = zoomedData.Stride * featureBitmap.Height; byte[] zoomedValues = new byte[bytes]; Marshal.Copy(zoomedData.Scan0, zoomedValues, 0, bytes); bytesPerPixel = AstrometryContext.Current.BytesPerPixel; byte saturatedR = TangraConfig.Settings.Color.Saturation.R; byte saturatedG = TangraConfig.Settings.Color.Saturation.G; byte saturatedB = TangraConfig.Settings.Color.Saturation.B; selIdx = 0; for (int y = 0; y < 31; y++) { for (int x = 0; x < 31; x++) { for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { int zoomedX = 8 * x + i; int zoomedY = 8 * y + j; int zoomedIdx = zoomedData.Stride * zoomedY + zoomedX * bytesPerPixel; if (zoomedValues[zoomedIdx] > TangraConfig.Settings.Photometry.Saturation.Saturation8Bit) { // Saturation detected zoomedValues[zoomedIdx] = saturatedR; zoomedValues[zoomedIdx + 1] = saturatedG; zoomedValues[zoomedIdx + 2] = saturatedB; } } } selIdx++; } } Marshal.Copy(zoomedValues, 0, zoomedData.Scan0, bytes); } finally { featureBitmap.UnlockBits(zoomedData); } Pen starPen = catalogStarPen; double xFitUnscaled = pixel.XDouble; double yFitUnscaled = pixel.YDouble; if (selectedPair != null && selectedPair.FitInfo.UsedInSolution) { starPen = referenceStarPen; astrometry.GetImageCoordsFromRADE(selectedPair.RADeg, selectedPair.DEDeg, out xFitUnscaled, out yFitUnscaled); } else if (selectedPair != null && selectedPair.FitInfo.ExcludedForHighResidual) { starPen = rejectedStarPen; astrometry.GetImageCoordsFromRADE(selectedPair.RADeg, selectedPair.DEDeg, out xFitUnscaled, out yFitUnscaled); } else { starPen = unrecognizedStarPen; } double xFit = (8 * (xFitUnscaled - x0 + 16)) - 4; double yFit = (8 * (yFitUnscaled - y0 + 16)) - 4; }
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 LeastSquareFittedAstrometry SolvePlateConstantsPhase4(double pyramidLimitMag) { double ra0deg, de0Deg; m_SolvedPlate.GetRADEFromImageCoords(m_SolvedPlate.Image.CenterXImage, m_SolvedPlate.Image.CenterYImage, out ra0deg, out de0Deg); FocalLengthFit distFit = m_ConstantsSolver.ComputeFocalLengthFit(); m_Context.PlateConfig.EffectiveFocalLength = m_Context.FieldSolveContext.FocalLength; distFit.GetFocalParameters(m_Context.PlateConfig.EffectiveFocalLength, out m_Context.PlateConfig.EffectivePixelWidth, out m_Context.PlateConfig.EffectivePixelHeight); DistanceBasedAstrometrySolver distMatch = new DistanceBasedAstrometrySolver( m_AstrometryController, m_Context.PlateConfig, m_AstrometrySettings, m_Context.FieldSolveContext.CatalogueStars, m_Context.FieldSolveContext.RADeg, m_Context.FieldSolveContext.DEDeg, m_Context.FieldSolveContext.DetermineAutoLimitMagnitude); distMatch.SetMinMaxMagOfStarsForAstrometry(CorePyramidConfig.Default.DefaultMinAstrometryMagnitude, 18); distMatch.SetMinMaxMagOfStarsForPyramidAlignment(CorePyramidConfig.Default.DefaultMinPyramidMagnitude, pyramidLimitMag); Trace.WriteLine(string.Format("Stars for alignment in range: {0:0.0} - {1:0.0} mag", CorePyramidConfig.Default.DefaultMinPyramidMagnitude, pyramidLimitMag)); Dictionary <PSFFit, IStar> manualStars = null; var threeStarFit = m_Context.PreliminaryFit as ThreeStarAstrometry; if (threeStarFit != null) { manualStars = new Dictionary <PSFFit, IStar>(); foreach (var kvp in threeStarFit.UserStars) { PSFFit psfFit; m_Context.StarMap.GetPSFFit(kvp.Key.X, kvp.Key.Y, PSFFittingMethod.NonLinearFit, out psfFit); if (psfFit != null && psfFit.IsSolved) { manualStars.Add(psfFit, kvp.Value); } } } distMatch.InitNewMatch(m_Context.StarMap, PyramidMatchType.PlateSolve, manualStars); #if ASTROMETRY_DEBUG Dictionary <int, ulong> debugInfo = new Dictionary <int, ulong>(); var starList = m_Context.SecondAstrometricFit.FitInfo.AllStarPairs .Where(p => !p.FitInfo.ExcludedForHighResidual); foreach (var x in starList) { if (!debugInfo.ContainsKey(x.FeatureId)) { debugInfo.Add(x.FeatureId, x.StarNo); } } foreach (var f in m_Context.StarMap.Features) { Trace.WriteLine(string.Format("{0} - {1}", f.FeatureId, debugInfo.ContainsKey(f.FeatureId) ? "INCLUDED" : "MISSING")); } distMatch.SetDebugData(new DistanceBasedAstrometrySolver.PyramidDebugContext() { ResolvedStars = debugInfo, ResolvedFocalLength = m_Context.SecondAstrometricFit.FitInfo.FittedFocalLength }); #endif LeastSquareFittedAstrometry fit = null; LeastSquareFittedAstrometry improvedFit = null; PlateConstantsSolver solver; distMatch.PerformMatch(out fit, out improvedFit, out solver); m_Context.DistanceBasedFit = fit; m_Context.ImprovedDistanceBasedFit = improvedFit; if (fit != null) { m_Context.PlateConstants = new TangraConfig.PersistedPlateConstants { EffectiveFocalLength = m_Context.PlateConfig.EffectiveFocalLength, EffectivePixelWidth = m_Context.PlateConfig.EffectivePixelWidth, EffectivePixelHeight = m_Context.PlateConfig.EffectivePixelHeight }; m_Context.ConstantsSolver = solver; if (improvedFit != null) { foreach (var pair in improvedFit.FitInfo.AllStarPairs) { if (pair.FitInfo.ExcludedForHighResidual) { continue; } Trace.WriteLine(string.Format("{6}; {0}; {1}; {2}; {3}; ({4}\", {5}\")", pair.RADeg, pair.DEDeg, pair.x.ToString("0.00"), pair.y.ToString("0.00"), pair.FitInfo.ResidualRAArcSec.ToString("0.00"), pair.FitInfo.ResidualDEArcSec.ToString("0.00"), pair.StarNo)); double x, y; improvedFit.GetImageCoordsFromRADE(pair.RADeg, pair.DEDeg, out x, out y); double dist = improvedFit.GetDistanceInArcSec(x, y, pair.x, pair.y); } } } else { m_Context.PlateConstants = null; } return(fit); }