File indexing completed on 2024-05-12 15:23:42
0001 /* Algorithms used in the internal guider. 0002 Copyright (C) 2012 Andrew Stepanenko 0003 0004 Modified by Jasem Mutlaq for KStars. 0005 SPDX-FileCopyrightText: Jasem Mutlaq <mutlaqja@ikarustech.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "guidealgorithms.h" 0011 0012 #include <set> 0013 #include <QObject> 0014 0015 #include "ekos_guide_debug.h" 0016 #include "ekos/auxiliary/stellarsolverprofileeditor.h" 0017 #include "Options.h" 0018 #include "fitsviewer/fitsdata.h" 0019 0020 #define SMART_THRESHOLD 0 0021 #define SEP_THRESHOLD 1 0022 #define CENTROID_THRESHOLD 2 0023 #define AUTO_THRESHOLD 3 0024 #define NO_THRESHOLD 4 0025 #define SEP_MULTISTAR 5 0026 0027 // smart threshold algorithm param 0028 // width of outer frame for background calculation 0029 #define SMART_FRAME_WIDTH 4 0030 // cut-factor above average threshold 0031 #define SMART_CUT_FACTOR 0.1 0032 0033 struct Peak 0034 { 0035 int x; 0036 int y; 0037 float val; 0038 0039 Peak() = default; 0040 Peak(int x_, int y_, float val_) : x(x_), y(y_), val(val_) { } 0041 bool operator<(const Peak &rhs) const 0042 { 0043 return val < rhs.val; 0044 } 0045 }; 0046 0047 // JM: Why not use QPoint? 0048 typedef struct 0049 { 0050 int x, y; 0051 } point_t; 0052 0053 namespace 0054 { 0055 0056 void psf_conv(float *dst, const float *src, int width, int height) 0057 { 0058 //dst.Init(src.Size); 0059 0060 // A B1 B2 C1 C2 C3 D1 D2 D3 0061 const double PSF[] = { 0.906, 0.584, 0.365, .117, .049, -0.05, -.064, -.074, -.094 }; 0062 0063 //memset(dst.px, 0, src.NPixels * sizeof(float)); 0064 0065 /* PSF Grid is: 0066 D3 D3 D3 D3 D3 D3 D3 D3 D3 0067 D3 D3 D3 D2 D1 D2 D3 D3 D3 0068 D3 D3 C3 C2 C1 C2 C3 D3 D3 0069 D3 D2 C2 B2 B1 B2 C2 D2 D3 0070 D3 D1 C1 B1 A B1 C1 D1 D3 0071 D3 D2 C2 B2 B1 B2 C2 D2 D3 0072 D3 D3 C3 C2 C1 C2 C3 D3 D3 0073 D3 D3 D3 D2 D1 D2 D3 D3 D3 0074 D3 D3 D3 D3 D3 D3 D3 D3 D3 0075 0076 1@A 0077 4@B1, B2, C1, C3, D1 0078 8@C2, D2 0079 44 * D3 0080 */ 0081 0082 int psf_size = 4; 0083 0084 for (int y = psf_size; y < height - psf_size; y++) 0085 { 0086 for (int x = psf_size; x < width - psf_size; x++) 0087 { 0088 float A, B1, B2, C1, C2, C3, D1, D2, D3; 0089 0090 #define PX(dx, dy) *(src + width * (y + (dy)) + x + (dx)) 0091 A = PX(+0, +0); 0092 B1 = PX(+0, -1) + PX(+0, +1) + PX(+1, +0) + PX(-1, +0); 0093 B2 = PX(-1, -1) + PX(+1, -1) + PX(-1, +1) + PX(+1, +1); 0094 C1 = PX(+0, -2) + PX(-2, +0) + PX(+2, +0) + PX(+0, +2); 0095 C2 = PX(-1, -2) + PX(+1, -2) + PX(-2, -1) + PX(+2, -1) + PX(-2, +1) + PX(+2, +1) + PX(-1, +2) + PX(+1, +2); 0096 C3 = PX(-2, -2) + PX(+2, -2) + PX(-2, +2) + PX(+2, +2); 0097 D1 = PX(+0, -3) + PX(-3, +0) + PX(+3, +0) + PX(+0, +3); 0098 D2 = PX(-1, -3) + PX(+1, -3) + PX(-3, -1) + PX(+3, -1) + PX(-3, +1) + PX(+3, +1) + PX(-1, +3) + PX(+1, +3); 0099 D3 = PX(-4, -2) + PX(-3, -2) + PX(+3, -2) + PX(+4, -2) + PX(-4, -1) + PX(+4, -1) + PX(-4, +0) + PX(+4, +0) + PX(-4, 0100 +1) + PX(+4, +1) + PX(-4, +2) + PX(-3, +2) + PX(+3, +2) + PX(+4, +2); 0101 #undef PX 0102 int i; 0103 const float *uptr; 0104 0105 uptr = src + width * (y - 4) + (x - 4); 0106 for (i = 0; i < 9; i++) 0107 D3 += *uptr++; 0108 0109 uptr = src + width * (y - 3) + (x - 4); 0110 for (i = 0; i < 3; i++) 0111 D3 += *uptr++; 0112 uptr += 3; 0113 for (i = 0; i < 3; i++) 0114 D3 += *uptr++; 0115 0116 uptr = src + width * (y + 3) + (x - 4); 0117 for (i = 0; i < 3; i++) 0118 D3 += *uptr++; 0119 uptr += 3; 0120 for (i = 0; i < 3; i++) 0121 D3 += *uptr++; 0122 0123 uptr = src + width * (y + 4) + (x - 4); 0124 for (i = 0; i < 9; i++) 0125 D3 += *uptr++; 0126 0127 double mean = (A + B1 + B2 + C1 + C2 + C3 + D1 + D2 + D3) / 81.0; 0128 double PSF_fit = PSF[0] * (A - mean) + PSF[1] * (B1 - 4.0 * mean) + PSF[2] * (B2 - 4.0 * mean) + 0129 PSF[3] * (C1 - 4.0 * mean) + PSF[4] * (C2 - 8.0 * mean) + PSF[5] * (C3 - 4.0 * mean) + 0130 PSF[6] * (D1 - 4.0 * mean) + PSF[7] * (D2 - 8.0 * mean) + PSF[8] * (D3 - 44.0 * mean); 0131 0132 dst[width * y + x] = (float) PSF_fit; 0133 } 0134 } 0135 } 0136 0137 void GetStats(double *mean, double *stdev, int width, const float *img, const QRect &win) 0138 { 0139 // Determine the mean and standard deviation 0140 double sum = 0.0; 0141 double a = 0.0; 0142 double q = 0.0; 0143 double k = 1.0; 0144 double km1 = 0.0; 0145 0146 const float *p0 = img + win.top() * width + win.left(); 0147 for (int y = 0; y < win.height(); y++) 0148 { 0149 const float *end = p0 + win.height(); 0150 for (const float *p = p0; p < end; p++) 0151 { 0152 double const x = (double) * p; 0153 sum += x; 0154 double const a0 = a; 0155 a += (x - a) / k; 0156 q += (x - a0) * (x - a); 0157 km1 = k; 0158 k += 1.0; 0159 } 0160 p0 += width; 0161 } 0162 0163 *mean = sum / km1; 0164 *stdev = sqrt(q / km1); 0165 } 0166 0167 void RemoveItems(std::set<Peak> &stars, const std::set<int> &to_erase) 0168 { 0169 int n = 0; 0170 for (std::set<Peak>::iterator it = stars.begin(); it != stars.end(); n++) 0171 { 0172 if (to_erase.find(n) != to_erase.end()) 0173 { 0174 std::set<Peak>::iterator next = it; 0175 ++next; 0176 stars.erase(it); 0177 it = next; 0178 } 0179 else 0180 ++it; 0181 } 0182 } 0183 0184 float *createFloatImage(const QSharedPointer<FITSData> &target) 0185 { 0186 QSharedPointer<FITSData> imageData; 0187 0188 /************* 0189 if (target.isNull()) 0190 imageData = m_ImageData; 0191 else 0192 *********/ // shouldn't be null 0193 imageData = target; 0194 0195 // #1 Convert to float array 0196 // We only process 1st plane if it is a color image 0197 uint32_t imgSize = imageData->width() * imageData->height(); 0198 float *imgFloat = new float[imgSize]; 0199 0200 if (imgFloat == nullptr) 0201 { 0202 qCritical() << "Not enough memory for float image array!"; 0203 return nullptr; 0204 } 0205 0206 switch (imageData->getStatistics().dataType) 0207 { 0208 case TBYTE: 0209 { 0210 uint8_t const *buffer = imageData->getImageBuffer(); 0211 for (uint32_t i = 0; i < imgSize; i++) 0212 imgFloat[i] = buffer[i]; 0213 } 0214 break; 0215 0216 case TSHORT: 0217 { 0218 int16_t const *buffer = reinterpret_cast<int16_t const *>(imageData->getImageBuffer()); 0219 for (uint32_t i = 0; i < imgSize; i++) 0220 imgFloat[i] = buffer[i]; 0221 } 0222 break; 0223 0224 case TUSHORT: 0225 { 0226 uint16_t const *buffer = reinterpret_cast<uint16_t const*>(imageData->getImageBuffer()); 0227 for (uint32_t i = 0; i < imgSize; i++) 0228 imgFloat[i] = buffer[i]; 0229 } 0230 break; 0231 0232 case TLONG: 0233 { 0234 int32_t const *buffer = reinterpret_cast<int32_t const*>(imageData->getImageBuffer()); 0235 for (uint32_t i = 0; i < imgSize; i++) 0236 imgFloat[i] = buffer[i]; 0237 } 0238 break; 0239 0240 case TULONG: 0241 { 0242 uint32_t const *buffer = reinterpret_cast<uint32_t const*>(imageData->getImageBuffer()); 0243 for (uint32_t i = 0; i < imgSize; i++) 0244 imgFloat[i] = buffer[i]; 0245 } 0246 break; 0247 0248 case TFLOAT: 0249 { 0250 float const *buffer = reinterpret_cast<float const*>(imageData->getImageBuffer()); 0251 for (uint32_t i = 0; i < imgSize; i++) 0252 imgFloat[i] = buffer[i]; 0253 } 0254 break; 0255 0256 case TLONGLONG: 0257 { 0258 int64_t const *buffer = reinterpret_cast<int64_t const*>(imageData->getImageBuffer()); 0259 for (uint32_t i = 0; i < imgSize; i++) 0260 imgFloat[i] = buffer[i]; 0261 } 0262 break; 0263 0264 case TDOUBLE: 0265 { 0266 double const *buffer = reinterpret_cast<double const*>(imageData->getImageBuffer()); 0267 for (uint32_t i = 0; i < imgSize; i++) 0268 imgFloat[i] = buffer[i]; 0269 } 0270 break; 0271 0272 default: 0273 delete[] imgFloat; 0274 return nullptr; 0275 } 0276 0277 return imgFloat; 0278 } 0279 0280 } // namespace 0281 0282 // Based on PHD2 algorithm 0283 QList<Edge*> GuideAlgorithms::detectStars(const QSharedPointer<FITSData> &imageData, const QRect &trackingBox) 0284 { 0285 //Debug.Write(wxString::Format("Star::AutoFind called with edgeAllowance = %d searchRegion = %d\n", extraEdgeAllowance, searchRegion)); 0286 0287 // run a 3x3 median first to eliminate hot pixels 0288 //usImage smoothed; 0289 //smoothed.CopyFrom(image); 0290 //Median3(smoothed); 0291 constexpr int extraEdgeAllowance = 0; 0292 const QSharedPointer<FITSData> smoothed(new FITSData(imageData)); 0293 smoothed->applyFilter(FITS_MEDIAN); 0294 0295 int searchRegion = trackingBox.width(); 0296 0297 int subW = smoothed->width(); 0298 int subH = smoothed->height(); 0299 int size = subW * subH; 0300 0301 // convert to floating point 0302 float *conv = createFloatImage(smoothed); 0303 0304 // run the PSF convolution 0305 { 0306 float *tmp = new float[size] {}; 0307 psf_conv(tmp, conv, subW, subH); 0308 delete [] conv; 0309 // Swap 0310 conv = tmp; 0311 } 0312 0313 enum { CONV_RADIUS = 4 }; 0314 int dw = subW; // width of the downsampled image 0315 int dh = subH; // height of the downsampled image 0316 QRect convRect(CONV_RADIUS, CONV_RADIUS, dw - 2 * CONV_RADIUS, dh - 2 * CONV_RADIUS); // region containing valid data 0317 0318 enum { TOP_N = 100 }; // keep track of the brightest stars 0319 std::set<Peak> stars; // sorted by ascending intensity 0320 0321 double global_mean, global_stdev; 0322 GetStats(&global_mean, &global_stdev, subW, conv, convRect); 0323 0324 //Debug.Write(wxString::Format("AutoFind: global mean = %.1f, stdev %.1f\n", global_mean, global_stdev)); 0325 0326 const double threshold = 0.1; 0327 //Debug.Write(wxString::Format("AutoFind: using threshold = %.1f\n", threshold)); 0328 0329 // find each local maximum 0330 int srch = 4; 0331 for (int y = convRect.top() + srch; y <= convRect.bottom() - srch; y++) 0332 { 0333 for (int x = convRect.left() + srch; x <= convRect.right() - srch; x++) 0334 { 0335 float val = conv[dw * y + x]; 0336 bool ismax = false; 0337 if (val > 0.0) 0338 { 0339 ismax = true; 0340 for (int j = -srch; j <= srch; j++) 0341 { 0342 for (int i = -srch; i <= srch; i++) 0343 { 0344 if (i == 0 && j == 0) 0345 continue; 0346 if (conv[dw * (y + j) + (x + i)] > val) 0347 { 0348 ismax = false; 0349 break; 0350 } 0351 } 0352 } 0353 } 0354 if (!ismax) 0355 continue; 0356 0357 // compare local maximum to mean value of surrounding pixels 0358 const int local = 7; 0359 double local_mean, local_stdev; 0360 QRect localRect(x - local, y - local, 2 * local + 1, 2 * local + 1); 0361 localRect = localRect.intersected(convRect); 0362 GetStats(&local_mean, &local_stdev, subW, conv, localRect); 0363 0364 // this is our measure of star intensity 0365 double h = (val - local_mean) / global_stdev; 0366 0367 if (h < threshold) 0368 { 0369 // Debug.Write(wxString::Format("AG: local max REJECT [%d, %d] PSF %.1f SNR %.1f\n", imgx, imgy, val, SNR)); 0370 continue; 0371 } 0372 0373 // coordinates on the original image 0374 int downsample = 1; 0375 int imgx = x * downsample + downsample / 2; 0376 int imgy = y * downsample + downsample / 2; 0377 0378 stars.insert(Peak(imgx, imgy, h)); 0379 if (stars.size() > TOP_N) 0380 stars.erase(stars.begin()); 0381 } 0382 } 0383 0384 //for (std::set<Peak>::const_reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) 0385 //qCDebug(KSTARS_EKOS_GUIDE) << "AutoFind: local max [" << it->x << "," << it->y << "]" << it->val; 0386 0387 // merge stars that are very close into a single star 0388 { 0389 const int minlimitsq = 5 * 5; 0390 repeat: 0391 for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) 0392 { 0393 std::set<Peak>::const_iterator b = a; 0394 ++b; 0395 for (; b != stars.end(); ++b) 0396 { 0397 int dx = a->x - b->x; 0398 int dy = a->y - b->y; 0399 int d2 = dx * dx + dy * dy; 0400 if (d2 < minlimitsq) 0401 { 0402 // very close, treat as single star 0403 //Debug.Write(wxString::Format("AutoFind: merge [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); 0404 // erase the dimmer one 0405 stars.erase(a); 0406 goto repeat; 0407 } 0408 } 0409 } 0410 } 0411 0412 // exclude stars that would fit within a single searchRegion box 0413 { 0414 // build a list of stars to be excluded 0415 std::set<int> to_erase; 0416 const int extra = 5; // extra safety margin 0417 const int fullw = searchRegion + extra; 0418 for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) 0419 { 0420 std::set<Peak>::const_iterator b = a; 0421 ++b; 0422 for (; b != stars.end(); ++b) 0423 { 0424 int dx = abs(a->x - b->x); 0425 int dy = abs(a->y - b->y); 0426 if (dx <= fullw && dy <= fullw) 0427 { 0428 // stars closer than search region, exclude them both 0429 // but do not let a very dim star eliminate a very bright star 0430 if (b->val / a->val >= 5.0) 0431 { 0432 //Debug.Write(wxString::Format("AutoFind: close dim-bright [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); 0433 } 0434 else 0435 { 0436 //Debug.Write(wxString::Format("AutoFind: too close [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); 0437 to_erase.insert(std::distance(stars.begin(), a)); 0438 to_erase.insert(std::distance(stars.begin(), b)); 0439 } 0440 } 0441 } 0442 } 0443 RemoveItems(stars, to_erase); 0444 } 0445 0446 // exclude stars too close to the edge 0447 { 0448 enum { MIN_EDGE_DIST = 40 }; 0449 int edgeDist = MIN_EDGE_DIST;//pConfig->Profile.GetInt("/StarAutoFind/MinEdgeDist", MIN_EDGE_DIST); 0450 if (edgeDist < searchRegion) 0451 edgeDist = searchRegion; 0452 edgeDist += extraEdgeAllowance; 0453 0454 std::set<Peak>::iterator it = stars.begin(); 0455 while (it != stars.end()) 0456 { 0457 std::set<Peak>::iterator next = it; 0458 ++next; 0459 if (it->x <= edgeDist || it->x >= subW - edgeDist || 0460 it->y <= edgeDist || it->y >= subH - edgeDist) 0461 { 0462 //Debug.Write(wxString::Format("AutoFind: too close to edge [%d, %d] %.1f\n", it->x, it->y, it->val)); 0463 stars.erase(it); 0464 } 0465 it = next; 0466 } 0467 } 0468 0469 QList<Edge*> centers; 0470 for (std::set<Peak>::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) 0471 { 0472 Edge *center = new Edge; 0473 center->x = it->x; 0474 center->y = it->y; 0475 center->val = it->val; 0476 centers.append(center); 0477 } 0478 0479 delete [] conv; 0480 0481 return centers; 0482 } 0483 0484 0485 template <typename T> 0486 GuiderUtils::Vector GuideAlgorithms::findLocalStarPosition(QSharedPointer<FITSData> &imageData, 0487 const int algorithmIndex, 0488 const int videoWidth, 0489 const int videoHeight, 0490 const QRect &trackingBox) 0491 { 0492 static const double P0 = 0.906, P1 = 0.584, P2 = 0.365, P3 = 0.117, P4 = 0.049, P5 = -0.05, P6 = -0.064, P7 = -0.074, 0493 P8 = -0.094; 0494 0495 GuiderUtils::Vector ret(-1, -1, -1); 0496 int i, j; 0497 double resx, resy, mass, threshold, pval; 0498 T const *psrc = nullptr; 0499 T const *porigin = nullptr; 0500 T const *pptr; 0501 0502 if (trackingBox.isValid() == false) 0503 return GuiderUtils::Vector(-1, -1, -1); 0504 0505 if (imageData.isNull()) 0506 { 0507 qCWarning(KSTARS_EKOS_GUIDE) << "Cannot process a nullptr image."; 0508 return GuiderUtils::Vector(-1, -1, -1); 0509 } 0510 0511 if (algorithmIndex == SEP_THRESHOLD) 0512 { 0513 QVariantMap settings; 0514 settings["optionsProfileIndex"] = Options::guideOptionsProfile(); 0515 settings["optionsProfileGroup"] = static_cast<int>(Ekos::GuideProfiles); 0516 imageData->setSourceExtractorSettings(settings); 0517 QFuture<bool> result = imageData->findStars(ALGORITHM_SEP, trackingBox); 0518 result.waitForFinished(); 0519 if (result.result()) 0520 { 0521 imageData->getHFR(HFR_MEDIAN); 0522 const Edge star = imageData->getSelectedHFRStar(); 0523 ret = GuiderUtils::Vector(star.x, star.y, 0); 0524 } 0525 else 0526 ret = GuiderUtils::Vector(-1, -1, -1); 0527 0528 return ret; 0529 } 0530 0531 T const *pdata = reinterpret_cast<T const*>(imageData->getImageBuffer()); 0532 0533 qCDebug(KSTARS_EKOS_GUIDE) << "Tracking Square " << trackingBox; 0534 0535 double square_square = trackingBox.width() * trackingBox.width(); 0536 0537 psrc = porigin = pdata + trackingBox.y() * videoWidth + trackingBox.x(); 0538 0539 resx = resy = 0; 0540 threshold = mass = 0; 0541 0542 // several threshold adaptive smart algorithms 0543 switch (algorithmIndex) 0544 { 0545 case CENTROID_THRESHOLD: 0546 { 0547 int width = trackingBox.width(); 0548 int height = trackingBox.width(); 0549 float i0, i1, i2, i3, i4, i5, i6, i7, i8; 0550 int ix = 0, iy = 0; 0551 int xM4; 0552 T const *p; 0553 double average, fit, bestFit = 0; 0554 int minx = 0; 0555 int maxx = width; 0556 int miny = 0; 0557 int maxy = height; 0558 for (int x = minx; x < maxx; x++) 0559 for (int y = miny; y < maxy; y++) 0560 { 0561 i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0; 0562 xM4 = x - 4; 0563 p = psrc + (y - 4) * videoWidth + xM4; 0564 i8 += *p++; 0565 i8 += *p++; 0566 i8 += *p++; 0567 i8 += *p++; 0568 i8 += *p++; 0569 i8 += *p++; 0570 i8 += *p++; 0571 i8 += *p++; 0572 i8 += *p++; 0573 p = psrc + (y - 3) * videoWidth + xM4; 0574 i8 += *p++; 0575 i8 += *p++; 0576 i8 += *p++; 0577 i7 += *p++; 0578 i6 += *p++; 0579 i7 += *p++; 0580 i8 += *p++; 0581 i8 += *p++; 0582 i8 += *p++; 0583 p = psrc + (y - 2) * videoWidth + xM4; 0584 i8 += *p++; 0585 i8 += *p++; 0586 i5 += *p++; 0587 i4 += *p++; 0588 i3 += *p++; 0589 i4 += *p++; 0590 i5 += *p++; 0591 i8 += *p++; 0592 i8 += *p++; 0593 p = psrc + (y - 1) * videoWidth + xM4; 0594 i8 += *p++; 0595 i7 += *p++; 0596 i4 += *p++; 0597 i2 += *p++; 0598 i1 += *p++; 0599 i2 += *p++; 0600 i4 += *p++; 0601 i8 += *p++; 0602 i8 += *p++; 0603 p = psrc + (y + 0) * videoWidth + xM4; 0604 i8 += *p++; 0605 i6 += *p++; 0606 i3 += *p++; 0607 i1 += *p++; 0608 i0 += *p++; 0609 i1 += *p++; 0610 i3 += *p++; 0611 i6 += *p++; 0612 i8 += *p++; 0613 p = psrc + (y + 1) * videoWidth + xM4; 0614 i8 += *p++; 0615 i7 += *p++; 0616 i4 += *p++; 0617 i2 += *p++; 0618 i1 += *p++; 0619 i2 += *p++; 0620 i4 += *p++; 0621 i8 += *p++; 0622 i8 += *p++; 0623 p = psrc + (y + 2) * videoWidth + xM4; 0624 i8 += *p++; 0625 i8 += *p++; 0626 i5 += *p++; 0627 i4 += *p++; 0628 i3 += *p++; 0629 i4 += *p++; 0630 i5 += *p++; 0631 i8 += *p++; 0632 i8 += *p++; 0633 p = psrc + (y + 3) * videoWidth + xM4; 0634 i8 += *p++; 0635 i8 += *p++; 0636 i8 += *p++; 0637 i7 += *p++; 0638 i6 += *p++; 0639 i7 += *p++; 0640 i8 += *p++; 0641 i8 += *p++; 0642 i8 += *p++; 0643 p = psrc + (y + 4) * videoWidth + xM4; 0644 i8 += *p++; 0645 i8 += *p++; 0646 i8 += *p++; 0647 i8 += *p++; 0648 i8 += *p++; 0649 i8 += *p++; 0650 i8 += *p++; 0651 i8 += *p++; 0652 i8 += *p++; 0653 average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0; 0654 fit = P0 * (i0 - average) + P1 * (i1 - 4 * average) + P2 * (i2 - 4 * average) + 0655 P3 * (i3 - 4 * average) + P4 * (i4 - 8 * average) + P5 * (i5 - 4 * average) + 0656 P6 * (i6 - 4 * average) + P7 * (i7 - 8 * average) + P8 * (i8 - 48 * average); 0657 if (bestFit < fit) 0658 { 0659 bestFit = fit; 0660 ix = x; 0661 iy = y; 0662 } 0663 } 0664 0665 if (bestFit > 50) 0666 { 0667 double sumX = 0; 0668 double sumY = 0; 0669 double total = 0; 0670 for (int y = iy - 4; y <= iy + 4; y++) 0671 { 0672 p = psrc + y * width + ix - 4; 0673 for (int x = ix - 4; x <= ix + 4; x++) 0674 { 0675 double w = *p++; 0676 sumX += x * w; 0677 sumY += y * w; 0678 total += w; 0679 } 0680 } 0681 if (total > 0) 0682 { 0683 ret = (GuiderUtils::Vector(trackingBox.x(), trackingBox.y(), 0) + GuiderUtils::Vector(sumX / total, sumY / total, 0)); 0684 return ret; 0685 } 0686 } 0687 0688 return GuiderUtils::Vector(-1, -1, -1); 0689 } 0690 break; 0691 // Alexander's Stepanenko smart threshold algorithm 0692 case SMART_THRESHOLD: 0693 { 0694 point_t bbox_lt = { trackingBox.x() - SMART_FRAME_WIDTH, trackingBox.y() - SMART_FRAME_WIDTH }; 0695 point_t bbox_rb = { trackingBox.x() + trackingBox.width() + SMART_FRAME_WIDTH, 0696 trackingBox.y() + trackingBox.width() + SMART_FRAME_WIDTH 0697 }; 0698 int offset = 0; 0699 0700 // clip frame 0701 if (bbox_lt.x < 0) 0702 bbox_lt.x = 0; 0703 if (bbox_lt.y < 0) 0704 bbox_lt.y = 0; 0705 if (bbox_rb.x > videoWidth) 0706 bbox_rb.x = videoWidth; 0707 if (bbox_rb.y > videoHeight) 0708 bbox_rb.y = videoHeight; 0709 0710 // calc top bar 0711 int box_wd = bbox_rb.x - bbox_lt.x; 0712 int box_ht = trackingBox.y() - bbox_lt.y; 0713 int pix_cnt = 0; 0714 if (box_wd > 0 && box_ht > 0) 0715 { 0716 pix_cnt += box_wd * box_ht; 0717 for (j = bbox_lt.y; j < trackingBox.y(); ++j) 0718 { 0719 offset = j * videoWidth; 0720 for (i = bbox_lt.x; i < bbox_rb.x; ++i) 0721 { 0722 pptr = pdata + offset + i; 0723 threshold += *pptr; 0724 } 0725 } 0726 } 0727 // calc left bar 0728 box_wd = trackingBox.x() - bbox_lt.x; 0729 box_ht = trackingBox.width(); 0730 if (box_wd > 0 && box_ht > 0) 0731 { 0732 pix_cnt += box_wd * box_ht; 0733 for (j = trackingBox.y(); j < trackingBox.y() + box_ht; ++j) 0734 { 0735 offset = j * videoWidth; 0736 for (i = bbox_lt.x; i < trackingBox.x(); ++i) 0737 { 0738 pptr = pdata + offset + i; 0739 threshold += *pptr; 0740 } 0741 } 0742 } 0743 // calc right bar 0744 box_wd = bbox_rb.x - trackingBox.x() - trackingBox.width(); 0745 box_ht = trackingBox.width(); 0746 if (box_wd > 0 && box_ht > 0) 0747 { 0748 pix_cnt += box_wd * box_ht; 0749 for (j = trackingBox.y(); j < trackingBox.y() + box_ht; ++j) 0750 { 0751 offset = j * videoWidth; 0752 for (i = trackingBox.x() + trackingBox.width(); i < bbox_rb.x; ++i) 0753 { 0754 pptr = pdata + offset + i; 0755 threshold += *pptr; 0756 } 0757 } 0758 } 0759 // calc bottom bar 0760 box_wd = bbox_rb.x - bbox_lt.x; 0761 box_ht = bbox_rb.y - trackingBox.y() - trackingBox.width(); 0762 if (box_wd > 0 && box_ht > 0) 0763 { 0764 pix_cnt += box_wd * box_ht; 0765 for (j = trackingBox.y() + trackingBox.width(); j < bbox_rb.y; ++j) 0766 { 0767 offset = j * videoWidth; 0768 for (i = bbox_lt.x; i < bbox_rb.x; ++i) 0769 { 0770 pptr = pdata + offset + i; 0771 threshold += *pptr; 0772 } 0773 } 0774 } 0775 // find maximum 0776 double max_val = 0; 0777 for (j = 0; j < trackingBox.width(); ++j) 0778 { 0779 for (i = 0; i < trackingBox.width(); ++i) 0780 { 0781 pptr = psrc + i; 0782 if (*pptr > max_val) 0783 max_val = *pptr; 0784 } 0785 psrc += videoWidth; 0786 } 0787 if (pix_cnt != 0) 0788 threshold /= (double)pix_cnt; 0789 0790 // cut by 10% higher then average threshold 0791 if (max_val > threshold) 0792 threshold += (max_val - threshold) * SMART_CUT_FACTOR; 0793 0794 //log_i("smart thr. = %f cnt = %d", threshold, pix_cnt); 0795 break; 0796 } 0797 // simple adaptive threshold 0798 case AUTO_THRESHOLD: 0799 { 0800 for (j = 0; j < trackingBox.width(); ++j) 0801 { 0802 for (i = 0; i < trackingBox.width(); ++i) 0803 { 0804 pptr = psrc + i; 0805 threshold += *pptr; 0806 } 0807 psrc += videoWidth; 0808 } 0809 threshold /= square_square; 0810 break; 0811 } 0812 // no threshold subtracion 0813 default: 0814 { 0815 } 0816 } 0817 0818 psrc = porigin; 0819 for (j = 0; j < trackingBox.width(); ++j) 0820 { 0821 for (i = 0; i < trackingBox.width(); ++i) 0822 { 0823 pptr = psrc + i; 0824 pval = *pptr - threshold; 0825 pval = pval < 0 ? 0 : pval; 0826 0827 resx += (double)i * pval; 0828 resy += (double)j * pval; 0829 0830 mass += pval; 0831 } 0832 psrc += videoWidth; 0833 } 0834 0835 if (mass == 0) 0836 mass = 1; 0837 0838 resx /= mass; 0839 resy /= mass; 0840 0841 ret = GuiderUtils::Vector(trackingBox.x(), trackingBox.y(), 0) + GuiderUtils::Vector(resx, resy, 0); 0842 0843 return ret; 0844 } 0845 0846 GuiderUtils::Vector GuideAlgorithms::findLocalStarPosition(QSharedPointer<FITSData> &imageData, 0847 const int algorithmIndex, 0848 const int videoWidth, 0849 const int videoHeight, 0850 const QRect &trackingBox) 0851 { 0852 switch (imageData->dataType()) 0853 { 0854 case TBYTE: 0855 return findLocalStarPosition<uint8_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0856 0857 case TSHORT: 0858 return findLocalStarPosition<int16_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0859 0860 case TUSHORT: 0861 return findLocalStarPosition<uint16_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0862 0863 case TLONG: 0864 return findLocalStarPosition<int32_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0865 0866 case TULONG: 0867 return findLocalStarPosition<uint32_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0868 0869 case TFLOAT: 0870 return findLocalStarPosition<float>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0871 0872 case TLONGLONG: 0873 return findLocalStarPosition<int64_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0874 0875 case TDOUBLE: 0876 return findLocalStarPosition<double>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox); 0877 0878 default: 0879 break; 0880 } 0881 0882 return GuiderUtils::Vector(-1, -1, -1); 0883 }