10#include "guidealgorithms.h"
15#include "ekos_guide_debug.h"
16#include "ekos/auxiliary/stellarsolverprofileeditor.h"
18#include "fitsviewer/fitsdata.h"
20#define SMART_THRESHOLD 0
21#define SEP_THRESHOLD 1
22#define CENTROID_THRESHOLD 2
23#define AUTO_THRESHOLD 3
25#define SEP_MULTISTAR 5
29#define SMART_FRAME_WIDTH 4
31#define SMART_CUT_FACTOR 0.1
40 Peak(
int x_,
int y_,
float val_) : x(x_), y(y_), val(val_) { }
41 bool operator<(
const Peak &rhs)
const
56void psf_conv(
float *dst,
const float *src,
int width,
int height)
61 const double PSF[] = { 0.906, 0.584, 0.365, .117, .049, -0.05, -.064, -.074, -.094 };
84 for (
int y = psf_size; y < height - psf_size; y++)
86 for (
int x = psf_size; x < width - psf_size; x++)
88 float A, B1, B2, C1, C2, C3, D1, D2, D3;
90#define PX(dx, dy) *(src + width * (y + (dy)) + x + (dx))
92 B1 = PX(+0, -1) + PX(+0, +1) + PX(+1, +0) + PX(-1, +0);
93 B2 = PX(-1, -1) + PX(+1, -1) + PX(-1, +1) + PX(+1, +1);
94 C1 = PX(+0, -2) + PX(-2, +0) + PX(+2, +0) + PX(+0, +2);
95 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);
96 C3 = PX(-2, -2) + PX(+2, -2) + PX(-2, +2) + PX(+2, +2);
97 D1 = PX(+0, -3) + PX(-3, +0) + PX(+3, +0) + PX(+0, +3);
98 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);
99 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,
100 +1) + PX(+4, +1) + PX(-4, +2) + PX(-3, +2) + PX(+3, +2) + PX(+4, +2);
105 uptr = src + width * (y - 4) + (x - 4);
106 for (i = 0; i < 9; i++)
109 uptr = src + width * (y - 3) + (x - 4);
110 for (i = 0; i < 3; i++)
113 for (i = 0; i < 3; i++)
116 uptr = src + width * (y + 3) + (x - 4);
117 for (i = 0; i < 3; i++)
120 for (i = 0; i < 3; i++)
123 uptr = src + width * (y + 4) + (x - 4);
124 for (i = 0; i < 9; i++)
127 double mean = (A + B1 + B2 + C1 + C2 + C3 + D1 + D2 + D3) / 81.0;
128 double PSF_fit = PSF[0] * (A - mean) + PSF[1] * (B1 - 4.0 * mean) + PSF[2] * (B2 - 4.0 * mean) +
129 PSF[3] * (C1 - 4.0 * mean) + PSF[4] * (C2 - 8.0 * mean) + PSF[5] * (C3 - 4.0 * mean) +
130 PSF[6] * (D1 - 4.0 * mean) + PSF[7] * (D2 - 8.0 * mean) + PSF[8] * (D3 - 44.0 * mean);
132 dst[width * y + x] = (float) PSF_fit;
137void GetStats(
double *mean,
double *stdev,
int width,
const float *img,
const QRect &win)
146 const float *p0 = img + win.
top() * width + win.
left();
147 for (
int y = 0; y < win.
height(); y++)
150 for (
const float *p = p0; p <
end; p++)
152 double const x = (double) * p;
156 q += (x - a0) * (x - a);
164 *stdev = sqrt(q / km1);
167void RemoveItems(std::set<Peak> &stars,
const std::set<int> &to_erase)
170 for (std::set<Peak>::iterator it = stars.begin(); it != stars.end(); n++)
172 if (to_erase.find(n) != to_erase.end())
174 std::set<Peak>::iterator
next = it;
197 uint32_t imgSize = imageData->width() * imageData->height();
198 float *imgFloat =
new float[imgSize];
200 if (imgFloat ==
nullptr)
202 qCritical() <<
"Not enough memory for float image array!";
206 switch (imageData->getStatistics().dataType)
210 uint8_t
const *buffer = imageData->getImageBuffer();
211 for (uint32_t i = 0; i < imgSize; i++)
212 imgFloat[i] = buffer[i];
218 int16_t
const *buffer =
reinterpret_cast<int16_t
const *
>(imageData->getImageBuffer());
219 for (uint32_t i = 0; i < imgSize; i++)
220 imgFloat[i] = buffer[i];
226 uint16_t
const *buffer =
reinterpret_cast<uint16_t const*
>(imageData->getImageBuffer());
227 for (uint32_t i = 0; i < imgSize; i++)
228 imgFloat[i] = buffer[i];
234 int32_t
const *buffer =
reinterpret_cast<int32_t const*
>(imageData->getImageBuffer());
235 for (uint32_t i = 0; i < imgSize; i++)
236 imgFloat[i] = buffer[i];
242 uint32_t
const *buffer =
reinterpret_cast<uint32_t const*
>(imageData->getImageBuffer());
243 for (uint32_t i = 0; i < imgSize; i++)
244 imgFloat[i] = buffer[i];
250 float const *buffer =
reinterpret_cast<float const*
>(imageData->getImageBuffer());
251 for (uint32_t i = 0; i < imgSize; i++)
252 imgFloat[i] = buffer[i];
258 int64_t
const *buffer =
reinterpret_cast<int64_t const*
>(imageData->getImageBuffer());
259 for (uint32_t i = 0; i < imgSize; i++)
260 imgFloat[i] = buffer[i];
266 double const *buffer =
reinterpret_cast<double const*
>(imageData->getImageBuffer());
267 for (uint32_t i = 0; i < imgSize; i++)
268 imgFloat[i] = buffer[i];
291 constexpr int extraEdgeAllowance = 0;
293 smoothed->applyFilter(FITS_MEDIAN);
295 int searchRegion = trackingBox.
width();
297 int subW = smoothed->width();
298 int subH = smoothed->height();
299 int size = subW * subH;
302 float *conv = createFloatImage(smoothed);
306 float *tmp =
new float[size] {};
307 psf_conv(tmp, conv, subW, subH);
313 enum { CONV_RADIUS = 4 };
316 QRect convRect(CONV_RADIUS, CONV_RADIUS, dw - 2 * CONV_RADIUS, dh - 2 * CONV_RADIUS);
318 enum { TOP_N = 100 };
319 std::set<Peak> stars;
321 double global_mean, global_stdev;
322 GetStats(&global_mean, &global_stdev, subW, conv, convRect);
326 const double threshold = 0.1;
331 for (
int y = convRect.top() + srch; y <= convRect.bottom() - srch; y++)
333 for (
int x = convRect.left() + srch; x <= convRect.right() - srch; x++)
335 float val = conv[dw * y + x];
340 for (
int j = -srch; j <= srch; j++)
342 for (
int i = -srch; i <= srch; i++)
344 if (i == 0 && j == 0)
346 if (conv[dw * (y + j) + (x + i)] > val)
359 double local_mean, local_stdev;
360 QRect localRect(x - local, y - local, 2 * local + 1, 2 * local + 1);
361 localRect = localRect.intersected(convRect);
362 GetStats(&local_mean, &local_stdev, subW, conv, localRect);
365 double h = (val - local_mean) / global_stdev;
375 int imgx = x * downsample + downsample / 2;
376 int imgy = y * downsample + downsample / 2;
378 stars.insert(Peak(imgx, imgy, h));
379 if (stars.size() > TOP_N)
380 stars.erase(stars.begin());
389 const int minlimitsq = 5 * 5;
391 for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a)
393 std::set<Peak>::const_iterator b = a;
395 for (; b != stars.end(); ++b)
397 int dx = a->x - b->x;
398 int dy = a->y - b->y;
399 int d2 = dx * dx + dy * dy;
415 std::set<int> to_erase;
417 const int fullw = searchRegion + extra;
418 for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a)
420 std::set<Peak>::const_iterator b = a;
422 for (; b != stars.end(); ++b)
424 int dx = abs(a->x - b->x);
425 int dy = abs(a->y - b->y);
426 if (dx <= fullw && dy <= fullw)
430 if (b->val / a->val >= 5.0)
437 to_erase.insert(std::distance(stars.begin(), a));
438 to_erase.insert(std::distance(stars.begin(), b));
443 RemoveItems(stars, to_erase);
448 enum { MIN_EDGE_DIST = 40 };
449 int edgeDist = MIN_EDGE_DIST;
450 if (edgeDist < searchRegion)
451 edgeDist = searchRegion;
452 edgeDist += extraEdgeAllowance;
454 std::set<Peak>::iterator it = stars.begin();
455 while (it != stars.end())
457 std::set<Peak>::iterator
next = it;
459 if (it->x <= edgeDist || it->x >= subW - edgeDist ||
460 it->y <= edgeDist || it->y >= subH - edgeDist)
470 for (std::set<Peak>::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it)
487 const int algorithmIndex,
488 const int videoWidth,
489 const int videoHeight,
490 const QRect &trackingBox)
492 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,
495 GuiderUtils::Vector ret(-1, -1, -1);
497 double resx, resy, mass, threshold, pval;
498 T
const *psrc =
nullptr;
499 T
const *porigin =
nullptr;
502 if (trackingBox.
isValid() ==
false)
503 return GuiderUtils::Vector(-1, -1, -1);
507 qCWarning(KSTARS_EKOS_GUIDE) <<
"Cannot process a nullptr image.";
508 return GuiderUtils::Vector(-1, -1, -1);
511 if (algorithmIndex == SEP_THRESHOLD)
513 QVariantMap settings;
514 settings[
"optionsProfileIndex"] = Options::guideOptionsProfile();
515 settings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::GuideProfiles);
516 imageData->setSourceExtractorSettings(settings);
517 QFuture<bool> result = imageData->findStars(ALGORITHM_SEP, trackingBox);
521 imageData->getHFR(HFR_MEDIAN);
522 const Edge star = imageData->getSelectedHFRStar();
523 ret = GuiderUtils::Vector(star.x, star.y, 0);
526 ret = GuiderUtils::Vector(-1, -1, -1);
531 T
const *pdata =
reinterpret_cast<T const*
>(imageData->getImageBuffer());
533 qCDebug(KSTARS_EKOS_GUIDE) <<
"Tracking Square " << trackingBox;
535 double square_square = trackingBox.
width() * trackingBox.
width();
537 psrc = porigin = pdata + trackingBox.
y() * videoWidth + trackingBox.
x();
540 threshold = mass = 0;
543 switch (algorithmIndex)
545 case CENTROID_THRESHOLD:
547 int width = trackingBox.
width();
548 int height = trackingBox.
width();
549 float i0, i1, i2, i3, i4, i5, i6, i7, i8;
553 double average, fit, bestFit = 0;
558 for (
int x = minx; x < maxx; x++)
559 for (
int y = miny; y < maxy; y++)
561 i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0;
563 p = psrc + (y - 4) * videoWidth + xM4;
573 p = psrc + (y - 3) * videoWidth + xM4;
583 p = psrc + (y - 2) * videoWidth + xM4;
593 p = psrc + (y - 1) * videoWidth + xM4;
603 p = psrc + (y + 0) * videoWidth + xM4;
613 p = psrc + (y + 1) * videoWidth + xM4;
623 p = psrc + (y + 2) * videoWidth + xM4;
633 p = psrc + (y + 3) * videoWidth + xM4;
643 p = psrc + (y + 4) * videoWidth + xM4;
653 average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0;
654 fit = P0 * (i0 - average) + P1 * (i1 - 4 * average) + P2 * (i2 - 4 * average) +
655 P3 * (i3 - 4 * average) + P4 * (i4 - 8 * average) + P5 * (i5 - 4 * average) +
656 P6 * (i6 - 4 * average) + P7 * (i7 - 8 * average) + P8 * (i8 - 48 * average);
670 for (
int y = iy - 4; y <= iy + 4; y++)
672 p = psrc + y * width + ix - 4;
673 for (
int x = ix - 4; x <= ix + 4; x++)
683 ret = (GuiderUtils::Vector(trackingBox.
x(), trackingBox.
y(), 0) + GuiderUtils::Vector(sumX / total, sumY / total, 0));
688 return GuiderUtils::Vector(-1, -1, -1);
692 case SMART_THRESHOLD:
694 point_t bbox_lt = { trackingBox.
x() - SMART_FRAME_WIDTH, trackingBox.
y() - SMART_FRAME_WIDTH };
695 point_t bbox_rb = { trackingBox.
x() + trackingBox.
width() + SMART_FRAME_WIDTH,
696 trackingBox.
y() + trackingBox.
width() + SMART_FRAME_WIDTH
705 if (bbox_rb.x > videoWidth)
706 bbox_rb.x = videoWidth;
707 if (bbox_rb.y > videoHeight)
708 bbox_rb.y = videoHeight;
711 int box_wd = bbox_rb.x - bbox_lt.x;
712 int box_ht = trackingBox.
y() - bbox_lt.y;
714 if (box_wd > 0 && box_ht > 0)
716 pix_cnt += box_wd * box_ht;
717 for (j = bbox_lt.y; j < trackingBox.
y(); ++j)
719 offset = j * videoWidth;
720 for (i = bbox_lt.x; i < bbox_rb.x; ++i)
722 pptr = pdata + offset + i;
728 box_wd = trackingBox.
x() - bbox_lt.x;
729 box_ht = trackingBox.
width();
730 if (box_wd > 0 && box_ht > 0)
732 pix_cnt += box_wd * box_ht;
733 for (j = trackingBox.
y(); j < trackingBox.
y() + box_ht; ++j)
735 offset = j * videoWidth;
736 for (i = bbox_lt.x; i < trackingBox.
x(); ++i)
738 pptr = pdata + offset + i;
744 box_wd = bbox_rb.x - trackingBox.
x() - trackingBox.
width();
745 box_ht = trackingBox.
width();
746 if (box_wd > 0 && box_ht > 0)
748 pix_cnt += box_wd * box_ht;
749 for (j = trackingBox.
y(); j < trackingBox.
y() + box_ht; ++j)
751 offset = j * videoWidth;
752 for (i = trackingBox.
x() + trackingBox.
width(); i < bbox_rb.x; ++i)
754 pptr = pdata + offset + i;
760 box_wd = bbox_rb.x - bbox_lt.x;
761 box_ht = bbox_rb.y - trackingBox.
y() - trackingBox.
width();
762 if (box_wd > 0 && box_ht > 0)
764 pix_cnt += box_wd * box_ht;
765 for (j = trackingBox.
y() + trackingBox.
width(); j < bbox_rb.y; ++j)
767 offset = j * videoWidth;
768 for (i = bbox_lt.x; i < bbox_rb.x; ++i)
770 pptr = pdata + offset + i;
777 for (j = 0; j < trackingBox.
width(); ++j)
779 for (i = 0; i < trackingBox.
width(); ++i)
788 threshold /= (double)pix_cnt;
791 if (max_val > threshold)
792 threshold += (max_val - threshold) * SMART_CUT_FACTOR;
800 for (j = 0; j < trackingBox.
width(); ++j)
802 for (i = 0; i < trackingBox.
width(); ++i)
809 threshold /= square_square;
819 for (j = 0; j < trackingBox.
width(); ++j)
821 for (i = 0; i < trackingBox.
width(); ++i)
824 pval = *pptr - threshold;
825 pval = pval < 0 ? 0 : pval;
827 resx += (double)i * pval;
828 resy += (double)j * pval;
841 ret = GuiderUtils::Vector(trackingBox.
x(), trackingBox.
y(), 0) + GuiderUtils::Vector(resx, resy, 0);
847 const int algorithmIndex,
848 const int videoWidth,
849 const int videoHeight,
850 const QRect &trackingBox)
852 switch (imageData->dataType())
855 return findLocalStarPosition<uint8_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
858 return findLocalStarPosition<int16_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
861 return findLocalStarPosition<uint16_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
864 return findLocalStarPosition<int32_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
867 return findLocalStarPosition<uint32_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
870 return findLocalStarPosition<float>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
873 return findLocalStarPosition<int64_t>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
876 return findLocalStarPosition<double>(imageData, algorithmIndex, videoWidth, videoHeight, trackingBox);
882 return GuiderUtils::Vector(-1, -1, -1);
QAction * repeat(const QObject *recvr, const char *slot, QObject *parent)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
void append(QList< T > &&value)
bool isValid() const const
bool isNull() const const
QTextStream & center(QTextStream &stream)