14#include <QtConcurrent>
16#include "fits_debug.h"
17#include "fitsgradientdetector.h"
22 FITSImage::Statistic
const &stats = m_ImageData->getStatistics();
23 switch (stats.dataType)
25#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
28 return QtConcurrent::run(&FITSGradientDetector::findSources<uint8_t>,
this, boundary);
31 return QtConcurrent::run(&FITSGradientDetector::findSources<int16_t>,
this, boundary);
34 return QtConcurrent::run(&FITSGradientDetector::findSources<uint16_t>,
this, boundary);
37 return QtConcurrent::run(&FITSGradientDetector::findSources<int32_t>,
this, boundary);
40 return QtConcurrent::run(&FITSGradientDetector::findSources<uint16_t>,
this, boundary);
43 return QtConcurrent::run(&FITSGradientDetector::findSources<float>,
this, boundary);
46 return QtConcurrent::run(&FITSGradientDetector::findSources<int64_t>,
this, boundary);
49 return QtConcurrent::run(&FITSGradientDetector::findSources<double>,
this, boundary);
53 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<uint8_t>, boundary);
56 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<int16_t>, boundary);
59 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<uint16_t>, boundary);
62 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<int32_t>, boundary);
65 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<uint16_t>, boundary);
68 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<float>, boundary);
71 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<int64_t>, boundary);
74 return QtConcurrent::run(
this, &FITSGradientDetector::findSources<double>, boundary);
80bool FITSGradientDetector::findSources(
const QRect &boundary)
82 int subX = qMax(0, boundary.
isNull() ? 0 : boundary.
x());
83 int subY = qMax(0, boundary.
isNull() ? 0 : boundary.
y());
84 int subW = (boundary.
isNull() ? m_ImageData->width() : boundary.
width());
85 int subH = (boundary.
isNull() ? m_ImageData->height() : boundary.
height());
87 int BBP = m_ImageData->getBytesPerPixel();
89 uint16_t dataWidth = m_ImageData->width();
92 uint32_t size = subW * subH;
93 uint32_t offset = subX + subY * dataWidth;
96 auto * buffer =
new uint8_t[size * BBP];
99 memcpy(buffer, m_ImageData->getImageBuffer(), size * BBP);
102 uint8_t * dataPtr = buffer;
103 uint8_t
const * origDataPtr = m_ImageData->getImageBuffer();
104 uint32_t lineOffset = 0;
106 for (
int height = subY; height < (subY + subH); height++)
108 lineOffset = (subX + height * dataWidth) * BBP;
109 memcpy(dataPtr, origDataPtr + lineOffset, subW * BBP);
110 dataPtr += (subW * BBP);
115 auto * boundedImage =
new FITSData();
116 FITSImage::Statistic stats;
119 stats.dataType = m_ImageData->getStatistics().dataType;
120 stats.bytesPerPixel = m_ImageData->getStatistics().bytesPerPixel;
121 stats.samples_per_channel = size;
123 boundedImage->restoreStatistics(stats);
126 boundedImage->setImageBuffer(buffer);
128 boundedImage->calculateStats(
true);
131 boundedImage->applyFilter(FITS_MEDIAN);
132 boundedImage->applyFilter(FITS_HIGH_CONTRAST);
140 sobel<T>(boundedImage, gradients, directions);
144 int maxID = partition(subW, subH, gradients, ids);
152 typedef struct massInfo
162 for (
int y = 0; y < subH; y++)
164 for (
int x = 0; x < subW; x++)
166 int index = x + y * subW;
168 int regionID = ids[index];
171 float pixel = gradients[index];
173 masses[regionID].totalMass += pixel;
174 masses[regionID].massX += x * pixel;
175 masses[regionID].massY += y * pixel;
182 int maxTotalMass = masses[1].totalMass;
183 double totalMassRatio = 1e6;
184 for (
auto key : masses.
keys())
186 massInfo oneMass = masses.
value(key);
187 if (oneMass.totalMass > maxTotalMass)
189 totalMassRatio = oneMass.totalMass / maxTotalMass;
190 maxTotalMass = oneMass.totalMass;
197 if (maxID > 10 && totalMassRatio < 1.5)
202 center->x = masses[maxRegionID].massX / masses[maxRegionID].totalMass + 0.5;
203 center->y = masses[maxRegionID].massY / masses[maxRegionID].totalMass + 0.5;
207 int maxR = qMin(subW - 1, subH - 1) / 2;
209 for (
int r = maxR; r > 1; r--)
213 for (
float theta = 0; theta < 2 * M_PI; theta += (2 * M_PI) / 36.0)
215 int testX =
center->x + std::cos(theta) * r;
216 int testY =
center->y + std::sin(theta) * r;
219 if (testX < 0 || testX >= subW || testY < 0 || testY >= subH)
222 if (gradients[testX + testY * subW] > 0)
236 qCDebug(KSTARS_FITS) <<
"FITS: Weighted Center is X: " <<
center->x <<
" Y: " <<
center->y <<
" Width: " <<
center->width;
248 double FSum = 0, HF = 0, TF = 0;
249 const double resolution = 1.0 / 20.0;
251 int cen_y = qRound(
center->y);
259 const T * origBuffer =
reinterpret_cast<T
const *
>(m_ImageData->getImageBuffer()) + offset;
261 for (
double x = leftEdge; x <= rightEdge; x += resolution)
263 double slice = resolution * (origBuffer[
static_cast<int>(floor(x)) + cen_y * dataWidth]);
271 int subPixelCenter = (
center->width / resolution) / 2;
274 TF = subPixels[subPixelCenter];
278 for (
int k = 1; k < subPixelCenter; k++)
280 TF += subPixels[subPixelCenter + k];
281 TF += subPixels[subPixelCenter - k];
288 center->HFR = (k - 1 + ((HF - lastTF) / (TF - lastTF)) * 2) * resolution;
303 starCenters.
append(center);
304 m_ImageData->setStarCenters(starCenters);
306 qCDebug(KSTARS_FITS) <<
"Flux: " << FSum <<
" Half-Flux: " << HF <<
" HFR: " <<
center->HFR;
321 FITSImage::Statistic
const &stats = data->getStatistics();
324 gradient.
resize(stats.samples_per_channel);
325 direction.
resize(stats.samples_per_channel);
327 for (
int y = 0; y < stats.height; y++)
329 size_t yOffset = y * stats.width;
330 const T * grayLine =
reinterpret_cast<T
const *
>(data->getImageBuffer()) + yOffset;
332 const T * grayLine_m1 = y < 1 ? grayLine : grayLine - stats.width;
333 const T * grayLine_p1 = y >= stats.height - 1 ? grayLine : grayLine + stats.width;
335 float * gradientLine = gradient.
data() + yOffset;
336 float * directionLine = direction.
data() + yOffset;
338 for (
int x = 0; x < stats.width; x++)
340 int x_m1 = x < 1 ? x : x - 1;
341 int x_p1 = x >= stats.width - 1 ? x : x + 1;
343 int gradX = grayLine_m1[x_p1] + 2 * grayLine[x_p1] + grayLine_p1[x_p1] - grayLine_m1[x_m1] -
344 2 * grayLine[x_m1] - grayLine_p1[x_m1];
346 int gradY = grayLine_m1[x_m1] + 2 * grayLine_m1[x] + grayLine_m1[x_p1] - grayLine_p1[x_m1] -
347 2 * grayLine_p1[x] - grayLine_p1[x_p1];
349 gradientLine[x] = qAbs(gradX) + qAbs(gradY);
377 if (gradX == 0 && gradY == 0)
378 directionLine[x] = 0;
380 directionLine[x] = 3;
383 qreal a = 180. * atan(qreal(gradY) / gradX) / M_PI;
385 if (a >= -22.5 && a < 22.5)
386 directionLine[x] = 0;
387 else if (a >= 22.5 && a < 67.5)
388 directionLine[x] = 2;
389 else if (a >= -67.5 && a < -22.5)
390 directionLine[x] = 1;
392 directionLine[x] = 3;
402 for (
int y = 1; y < height - 1; y++)
404 for (
int x = 1; x < width - 1; x++)
406 int index = x + y * width;
407 float val = gradient[index];
408 if (val > 0 && ids[index] == 0)
410 trace(width, height, ++
id, gradient, ids, x, y);
422 int yOffset = y * width;
423 float * cannyLine = image.
data() + yOffset;
424 int * idLine = ids.
data() + yOffset;
431 for (
int j = -1; j < 2; j++)
435 if (nextY < 0 || nextY >= height)
438 float * cannyLineNext = cannyLine + j * width;
440 for (
int i = -1; i < 2; i++)
444 if (i == j || nextX < 0 || nextX >= width)
447 if (cannyLineNext[nextX] > 0)
450 trace(width, height,
id, image, ids, nextX, nextY);
void append(QList< T > &&value)
void reserve(qsizetype size)
void resize(qsizetype size)
qsizetype size() const const
QList< Key > keys() const const
T value(const Key &key, const T &defaultValue) const const
bool isNull() const const
QTextStream & center(QTextStream &stream)
QFuture< T > run(Function function,...)