File indexing completed on 2024-04-14 14:10:38
0001 /* 0002 SPDX-FileCopyrightText: 2004 Jasem Mutlaq 0003 SPDX-FileCopyrightText: 2020 Eric Dejouhanet <eric.dejouhanet@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 Some code fragments were adapted from Peter Kirchgessner's FITS plugin 0008 SPDX-FileCopyrightText: Peter Kirchgessner <http://members.aol.com/pkirchg> 0009 */ 0010 0011 #include <math.h> 0012 #include <cmath> 0013 #include <QtConcurrent> 0014 0015 #include "fits_debug.h" 0016 #include "fitsthresholddetector.h" 0017 #include "fitsdata.h" 0018 0019 //void FITSThresholdDetector::configure(const QString &setting, const QVariant &value) 0020 //{ 0021 // if (!setting.compare("THRESHOLD_PERCENTAGE", Qt::CaseInsensitive)) 0022 // if (value.canConvert<int>()) 0023 // THRESHOLD_PERCENTAGE = value.value<int>(); 0024 //} 0025 0026 QFuture<bool> FITSThresholdDetector::findSources(QRect const &boundary) 0027 { 0028 FITSImage::Statistic const &stats = m_ImageData->getStatistics(); 0029 switch (stats.dataType) 0030 { 0031 case TSHORT: 0032 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<int16_t>, boundary); 0033 0034 case TUSHORT: 0035 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<uint16_t>, boundary); 0036 0037 case TLONG: 0038 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<int32_t>, boundary); 0039 0040 case TULONG: 0041 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<uint32_t>, boundary); 0042 0043 case TFLOAT: 0044 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<float>, boundary); 0045 0046 case TLONGLONG: 0047 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<int64_t>, boundary); 0048 0049 case TDOUBLE: 0050 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<double>, boundary); 0051 0052 case TBYTE: 0053 default: 0054 return QtConcurrent::run(this, &FITSThresholdDetector::findOneStar<uint8_t>, boundary); 0055 } 0056 0057 } 0058 0059 template <typename T> 0060 bool FITSThresholdDetector::findOneStar(const QRect &boundary) const 0061 { 0062 FITSImage::Statistic const &stats = m_ImageData->getStatistics(); 0063 0064 int subX = boundary.x(); 0065 int subY = boundary.y(); 0066 int subW = subX + boundary.width(); 0067 int subH = subY + boundary.height(); 0068 0069 float massX = 0, massY = 0, totalMass = 0; 0070 0071 auto * buffer = reinterpret_cast<T const *>(m_ImageData->getImageBuffer()); 0072 0073 double THRESHOLD_PERCENTAGE = getValue("THRESHOLD_PERCENTAGE", 120.0).toDouble(); 0074 // TODO replace magic number with something more useful to understand 0075 double threshold = stats.mean[0] * THRESHOLD_PERCENTAGE / 100.0; 0076 0077 for (int y = subY; y < subH; y++) 0078 { 0079 for (int x = subX; x < subW; x++) 0080 { 0081 T pixel = buffer[x + y * stats.width]; 0082 if (pixel > threshold) 0083 { 0084 totalMass += pixel; 0085 massX += x * pixel; 0086 massY += y * pixel; 0087 } 0088 } 0089 } 0090 0091 qCDebug(KSTARS_FITS) << "FITS: Weighted Center is X: " << massX / totalMass << " Y: " << massY / totalMass; 0092 0093 auto * center = new Edge; 0094 center->width = -1; 0095 center->x = massX / totalMass + 0.5; 0096 center->y = massY / totalMass + 0.5; 0097 center->HFR = 1; 0098 0099 // Maximum Radius 0100 int maxR = qMin(subW - 1, subH - 1) / 2; 0101 0102 // Critical threshold 0103 double critical_threshold = threshold * 0.7; 0104 double running_threshold = threshold; 0105 0106 while (running_threshold >= critical_threshold) 0107 { 0108 for (int r = maxR; r > 1; r--) 0109 { 0110 int pass = 0; 0111 0112 for (float theta = 0; theta < 2 * M_PI; theta += (2 * M_PI) / 10.0) 0113 { 0114 int testX = center->x + std::cos(theta) * r; 0115 int testY = center->y + std::sin(theta) * r; 0116 0117 // if out of bound, break; 0118 if (testX < subX || testX > subW || testY < subY || testY > subH) 0119 break; 0120 0121 if (buffer[testX + testY * stats.width] > running_threshold) 0122 pass++; 0123 } 0124 0125 //qDebug() << Q_FUNC_INFO << "Testing for radius " << r << " passes # " << pass << " @ threshold " << running_threshold; 0126 //if (pass >= 6) 0127 if (pass >= 5) 0128 { 0129 center->width = r * 2; 0130 break; 0131 } 0132 } 0133 0134 if (center->width > 0) 0135 break; 0136 0137 // Increase threshold fuzziness by 10% 0138 running_threshold -= running_threshold * 0.1; 0139 } 0140 0141 // If no stars were detected 0142 if (center->width == -1) 0143 { 0144 delete center; 0145 return false; 0146 } 0147 0148 // 30% fuzzy 0149 //center->width += center->width*0.3 * (running_threshold / threshold); 0150 0151 double FSum = 0, HF = 0, TF = 0, min = stats.min[0]; 0152 const double resolution = 1.0 / 20.0; 0153 0154 int cen_y = qRound(center->y); 0155 0156 double rightEdge = center->x + center->width / 2.0; 0157 double leftEdge = center->x - center->width / 2.0; 0158 0159 QVector<double> subPixels; 0160 subPixels.reserve(center->width / resolution); 0161 0162 for (double x = leftEdge; x <= rightEdge; x += resolution) 0163 { 0164 //subPixels[x] = resolution * (image_buffer[static_cast<int>(floor(x)) + cen_y * stats.width] - min); 0165 double slice = resolution * (buffer[static_cast<int>(floor(x)) + cen_y * stats.width] - min); 0166 FSum += slice; 0167 subPixels.append(slice); 0168 } 0169 0170 // Half flux 0171 HF = FSum / 2.0; 0172 0173 //double subPixelCenter = center->x - fmod(center->x,resolution); 0174 int subPixelCenter = (center->width / resolution) / 2; 0175 0176 // Start from center 0177 TF = subPixels[subPixelCenter]; 0178 double lastTF = TF; 0179 // Integrate flux along radius axis until we reach half flux 0180 //for (double k=resolution; k < (center->width/(2*resolution)); k += resolution) 0181 for (int k = 1; k < subPixelCenter; k++) 0182 { 0183 TF += subPixels[subPixelCenter + k]; 0184 TF += subPixels[subPixelCenter - k]; 0185 0186 if (TF >= HF) 0187 { 0188 // We have two ways to calculate HFR. The first is the correct method but it can get quite variable within 10% due to random fluctuations of the measured star. 0189 // The second method is not truly HFR but is much more resistant to noise. 0190 0191 // #1 Approximate HFR, accurate and reliable but quite variable to small changes in star flux 0192 center->HFR = (k - 1 + ((HF - lastTF) / (TF - lastTF)) * 2) * resolution; 0193 0194 // #2 Not exactly HFR, but much more stable 0195 //center->HFR = (k*resolution) * (HF/TF); 0196 break; 0197 } 0198 0199 lastTF = TF; 0200 } 0201 0202 QList<Edge*> starCenters; 0203 starCenters.append(center); 0204 m_ImageData->setStarCenters(starCenters); 0205 0206 return true; 0207 }