Warning, file /education/kstars/kstars/ekos/focus/focusfwhm.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2023
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QList>
0010 #include "../fitsviewer/fitsstardetector.h"
0011 #include "fitsviewer/fitsview.h"
0012 #include "fitsviewer/fitsdata.h"
0013 #include "curvefit.h"
0014 #include "../ekos.h"
0015 #include <ekos_focus_debug.h>
0016 
0017 
0018 namespace Ekos
0019 {
0020 
0021 // This class performs FWHM processing on the passed in FITS image
0022 
0023 class FocusFWHM
0024 {
0025     public:
0026 
0027         FocusFWHM(Mathematics::RobustStatistics::ScaleCalculation scaleCalc);
0028         ~FocusFWHM();
0029 
0030         template <typename T>
0031         void processFWHM(const T &imageBuffer, const QList<Edge *> &focusStars, const QSharedPointer<FITSData> &imageData,
0032                          std::unique_ptr<CurveFitting> &starFitting,
0033                          double *FWHM, double *weight)
0034         {
0035             CurveFitting::StarParams starParams, starParams2;
0036             std::vector<double> FWHMs, R2s;
0037 
0038             auto skyBackground = imageData->getSkyBackground();
0039             auto stats = imageData->getStatistics();
0040 
0041             // Setup a vector for each of the stars to be processed
0042             QVector<StarBox> stars;
0043             StarBox star;
0044 
0045             for (int s = 0; s < focusStars.size(); s++)
0046             {
0047                 int starSize = focusStars[s]->numPixels;
0048 
0049                 // If the star size is invalid then ignore this star
0050                 if (starSize <= 0)
0051                     continue;
0052 
0053                 // factor scales a box around the star to use in fitting the Gaussian
0054                 // too big and it wastes processing resource and since there is no deblending, it will also result
0055                 // in more star exclusions. Too small and the gaussian won't fit properly. On the Simulator 1.4 is good.
0056                 constexpr double factor = 1.4;
0057                 int width = factor * 2 * sqrt(starSize / M_PI);
0058                 int height = width;
0059 
0060                 // Width x height box centred on star centroid
0061                 star.star = s;
0062                 star.isValid = true;
0063                 star.start.first = focusStars[s]->x - width / 2.0;
0064                 star.end.first = focusStars[s]->x + width / 2.0;
0065                 star.start.second = focusStars[s]->y - height / 2.0;
0066                 star.end.second = focusStars[s]->y + height / 2.0;
0067 
0068                 // Check star box does not go over image edge, drop star if so
0069                 if (star.start.first < 0 || star.end.first > stats.width || star.start.second < 0 || star.end.second > stats.height)
0070                     continue;
0071 
0072                 stars.push_back(star);
0073             }
0074 
0075             // Ideally we would deblend where another star encroaches into this star's box
0076             // For now we'll just exclude stars in this situation by marking isValid as false
0077             for (int s1 = 0; s1 < stars.size(); s1++)
0078             {
0079                 if (!stars[s1].isValid)
0080                     continue;
0081 
0082                 for (int s2 = s1 + 1; s2 < stars.size(); s2++)
0083                 {
0084                     if (!stars[s2].isValid)
0085                         continue;
0086 
0087                     if (boxOverlap(stars[s1].start, stars[s1].end, stars[s2].start, stars[s2].end))
0088                     {
0089                         stars[s1].isValid = false;
0090                         stars[s2].isValid = false;
0091                     }
0092                 }
0093             }
0094 
0095             // We have the list of stars to process now so iterate through the list fitting a curve, etc
0096             for (int s = 0; s < stars.size(); s++)
0097             {
0098                 if (!stars[s].isValid)
0099                     continue;
0100 
0101                 starParams.background = skyBackground.mean;
0102                 starParams.peak = focusStars[stars[s].star]->val;
0103                 starParams.centroid_x = focusStars[stars[s].star]->x - stars[s].start.first;
0104                 starParams.centroid_y = focusStars[stars[s].star]->y - stars[s].start.second;
0105                 starParams.HFR = focusStars[stars[s].star]->HFR;
0106                 starParams.theta = 0.0;
0107                 starParams.FWHMx = -1;
0108                 starParams.FWHMy = -1;
0109                 starParams.FWHM = -1;
0110 
0111                 starFitting->fitCurve3D(imageBuffer, stats.width, stars[s].start, stars[s].end, starParams, CurveFitting::FOCUS_GAUSSIAN,
0112                                         false);
0113                 if (starFitting->getStarParams(CurveFitting::FOCUS_GAUSSIAN, &starParams2))
0114                 {
0115                     starParams2.centroid_x += stars[s].start.first;
0116                     starParams2.centroid_y += stars[s].start.second;
0117                     double R2 = starFitting->calculateR2(CurveFitting::FOCUS_GAUSSIAN);
0118                     if (R2 >= 0.25)
0119                     {
0120                         // Filter stars - 0.25 works OK on Sim
0121                         FWHMs.push_back(starParams2.FWHM);
0122                         R2s.push_back(R2);
0123 
0124                         qCDebug(KSTARS_EKOS_FOCUS) << "Star" << s << " R2=" << R2
0125                                                    << " x=" << focusStars[s]->x << " vs " << starParams2.centroid_x
0126                                                    << " y=" << focusStars[s]->y << " vs " << starParams2.centroid_y
0127                                                    << " HFR=" << focusStars[s]->HFR << " FWHM=" << starParams2.FWHM
0128                                                    << " Background=" << skyBackground.mean << " vs " << starParams2.background
0129                                                    << " Peak=" << focusStars[s]->val << "vs" << starParams2.peak;
0130                     }
0131                 }
0132             }
0133 
0134             if (FWHMs.size() == 0)
0135             {
0136                 *FWHM = INVALID_STAR_MEASURE;
0137                 *weight = 0.0;
0138             }
0139             else
0140             {
0141                 // There are many ways to compute a robust location. Using data on the simulator there wasn't much to choose
0142                 // between the Location measures available in RobustStatistics, so will use basic 2 Sigma Clipping
0143                 double FWHM_sc = Mathematics::RobustStatistics::ComputeLocation(Mathematics::RobustStatistics::LOCATION_SIGMACLIPPING,
0144                                  FWHMs, 2.0);
0145                 double R2_median = Mathematics::RobustStatistics::ComputeLocation(Mathematics::RobustStatistics::LOCATION_MEDIAN,
0146                                    R2s, 2.0);
0147                 double FWHMweight = Mathematics::RobustStatistics::ComputeWeight(m_ScaleCalc, FWHMs);
0148 
0149                 qCDebug(KSTARS_EKOS_FOCUS) << "Original Stars=" << focusStars.size()
0150                                            << " Processed=" << stars.size()
0151                                            << " Solved=" << FWHMs.size()
0152                                            << " R2 min/max/median=" << *std::min_element(R2s.begin(), R2s.end())
0153                                            << "/" << *std::max_element(R2s.begin(), R2s.end())
0154                                            << "/" << R2_median
0155                                            << " FWHM=" << FWHM_sc
0156                                            << " Weight=" << FWHMweight;
0157 
0158                 *FWHM = FWHM_sc;
0159                 *weight = FWHMweight;
0160             }
0161         }
0162 
0163         static double constexpr INVALID_STAR_MEASURE = -1.0;
0164 
0165     private:
0166 
0167         bool boxOverlap(const QPair<int, int> b1Start, const QPair<int, int> b1End, const QPair<int, int> b2Start,
0168                         const QPair<int, int> b2End);
0169 
0170         // Structure to hold parameters for box around a star for FWHM calcs
0171         struct StarBox
0172         {
0173             int star;
0174             bool isValid;
0175             QPair<int, int> start; // top left of box. x = first element, y = second element
0176             QPair<int, int> end; // bottom right of box. x = first element, y = second element
0177         };
0178 
0179         Mathematics::RobustStatistics::ScaleCalculation m_ScaleCalc;
0180 };
0181 }