13#include "fits_debug.h"
14#include "fitsbahtinovdetector.h"
15#include "hough/houghline.h"
18#include <QElapsedTimer>
19#include <QtConcurrent>
49 FITSImage::Statistic
const &stats = m_ImageData->getStatistics();
50 switch (stats.dataType)
52#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
54 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<int16_t>,
this, boundary);
57 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<uint16_t>,
this, boundary);
60 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<int32_t>,
this, boundary);
63 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<uint32_t>,
this, boundary);
66 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<float>,
this, boundary);
69 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<int64_t>,
this, boundary);
72 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<double>,
this, boundary);
76 return QtConcurrent::run(&FITSBahtinovDetector::findBahtinovStar<uint8_t>,
this, boundary);
79 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<int16_t>, boundary);
82 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<uint16_t>, boundary);
85 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<int32_t>, boundary);
88 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<uint32_t>, boundary);
91 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<float>, boundary);
94 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<int64_t>, boundary);
97 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<double>, boundary);
101 return QtConcurrent::run(
this, &FITSBahtinovDetector::findBahtinovStar<uint8_t>, boundary);
107bool FITSBahtinovDetector::findBahtinovStar(
const QRect &boundary)
113 int subX = qMax(0, boundary.
isNull() ? 0 : boundary.
x());
114 int subY = qMax(0, boundary.
isNull() ? 0 : boundary.
y());
115 int subW = (boundary.
isNull() ? m_ImageData->width() : boundary.
width());
116 int subH = (boundary.
isNull() ? m_ImageData->height() : boundary.
height());
118 int BBP = m_ImageData->getBytesPerPixel();
119 uint16_t dataWidth = m_ImageData->width();
122 uint32_t size = subW * subH;
123 uint32_t offset = subX + subY * dataWidth;
126 auto * buffer =
new uint8_t[size * BBP];
130 memcpy(buffer, m_ImageData->getImageBuffer(), size * BBP);
134 uint8_t * dataPtr = buffer;
135 const uint8_t * origDataPtr = m_ImageData->getImageBuffer();
137 for (
int height = subY; height < (subY + subH); height++)
139 uint32_t lineOffset = (subX + height * dataWidth) * BBP;
140 memcpy(dataPtr, origDataPtr + lineOffset, subW * BBP);
141 dataPtr += (subW * BBP);
146 FITSImage::Statistic stats;
149 stats.dataType = m_ImageData->getStatistics().dataType;
150 stats.bytesPerPixel = BBP;
151 stats.samples_per_channel = size;
152 FITSData* boundedImage =
new FITSData();
153 boundedImage->restoreStatistics(stats);
154 boundedImage->
setProperty(
"dataType", stats.dataType);
157 boundedImage->setImageBuffer(buffer);
159 boundedImage->calculateStats(
true);
165 const int steps = 180;
166 double radPerStep = M_PI / steps;
170 for (
int angle = 0; angle < steps; angle++)
173 BahtinovLineAverage lineAverage = calculateMaxAverage<T>(boundedImage, angle);
175 lineAveragesPerAngle.
insert(angle, lineAverage);
178 qCDebug(KSTARS_FITS) <<
"Getting max average for all 180 rotations took" << timer1.
elapsed() <<
"milliseconds";
187 for (
int index1 = 0; index1 < 3; index1++)
189 double maxAverage = 0.0;
190 double maxAngle = 0.0;
191 int maxAverageOffset = 0;
192 for (
int angle = 0; angle < steps; angle++)
194 BahtinovLineAverage lineAverage = lineAveragesPerAngle[angle];
195 if (lineAverage.average > maxAverage)
197 maxAverage = lineAverage.average;
198 maxAverageOffset = lineAverage.offset;
202 HoughLine* pHoughLine =
new HoughLine(maxAngle * radPerStep, maxAverageOffset, subW, subH, maxAverage);
203 if (pHoughLine !=
nullptr)
205 bahtinov_angles.
append(pHoughLine);
209 int minBahtinovAngleOffset = 18;
210 for (
int subAngle = maxAngle - minBahtinovAngleOffset; subAngle < maxAngle + minBahtinovAngleOffset; subAngle++)
212 int angleInRange = subAngle;
213 if (angleInRange < 0)
217 if (angleInRange >= 180)
221 lineAveragesPerAngle.
remove(angleInRange);
227 if (bahtinov_angles.
size() >= 3)
229 HoughLine::getSortedTopThreeLines(bahtinov_angles, top3Lines);
232 qCDebug(KSTARS_FITS) <<
"Sorted bahtinov angles:";
235 ln->printHoughLine();
242 HoughLine::IntersectResult result = oneLine->
Intersect(*otherLine, intersection);
243 if (result == HoughLine::INTERESECTING)
246 qCDebug(KSTARS_FITS) <<
"Intersection: " << intersection.
x() <<
", " << intersection.
y();
252 if (midLine->DistancePointLine(intersection, intersectionOnMidLine, distance))
254 qCDebug(KSTARS_FITS) <<
"Distance between intersection and midline is " <<
distance
255 <<
" at mid line point " << intersectionOnMidLine.
x() <<
", "
256 << intersectionOnMidLine.
y();
260 int maxR = qMin(subW - 1, subH - 1) / 2;
261 BahtinovEdge*
center =
new BahtinovEdge();
263 center->x = subX + intersection.
x();
264 center->y = subY + intersection.
y();
268 center->offset.setX(subX + intersectionOnMidLine.
x());
269 center->offset.setY(subY + intersectionOnMidLine.
y());
270 oneLine->Offset(subX, subY);
271 midLine->Offset(subX, subY);
272 otherLine->Offset(subX, subY);
273 center->line.append(*oneLine);
274 center->line.append(*midLine);
275 center->line.append(*otherLine);
276 starCenters.
append(center);
280 qCWarning(KSTARS_FITS) <<
"Closest point does not fall within the line segment.";
285 qCWarning(KSTARS_FITS) <<
"Lines are not intersecting (result: " << result <<
")";
290 for (
int index = 0; index < bahtinov_angles.
size(); index++)
292 HoughLine* pLineAverage = bahtinov_angles[index];
293 if (pLineAverage !=
nullptr)
298 bahtinov_angles.
clear();
301 lineAveragesPerAngle.
clear();
305 m_ImageData->setStarCenters(starCenters);
311BahtinovLineAverage FITSBahtinovDetector::calculateMaxAverage(
const FITSData *data,
int angle)
313 int BBP = data->getBytesPerPixel();
314 int size = data->getStatistics().samples_per_channel;
315 int width = data->width();
316 int height = data->height();
317 int numChannels = data->channels();
319 BahtinovLineAverage lineAverage;
320 auto * rotimage =
new T[size * BBP];
322 rotateImage(data, angle, rotimage);
325 auto * rotBuffer =
reinterpret_cast<T *
>(rotimage);
329 int NUMBER_OF_AVERAGE_ROWS = getValue(
"NUMBER_OF_AVERAGE_ROWS", 1).
toInt();
330 if (NUMBER_OF_AVERAGE_ROWS % 2 == 0)
332 NUMBER_OF_AVERAGE_ROWS--;
333 qCWarning(KSTARS_FITS) <<
"Warning, number of rows must be an odd number, correcting number of rows to "
334 << NUMBER_OF_AVERAGE_ROWS;
337 if (NUMBER_OF_AVERAGE_ROWS < 1)
339 NUMBER_OF_AVERAGE_ROWS = 1;
340 qCWarning(KSTARS_FITS) <<
"Warning, number of rows must be positive correcting number of rows to "
341 << NUMBER_OF_AVERAGE_ROWS;
344 for (
int y = 0; y < height; y++)
346 int yMin = y - ((NUMBER_OF_AVERAGE_ROWS - 1) / 2);
347 int yMax = y + ((NUMBER_OF_AVERAGE_ROWS - 1) / 2);
349 unsigned long multiRowSum = 0;
351 for (
int y1 = yMin; y1 <= yMax; y1++)
362 if (y2 < 0 || y2 >= height)
364 qCWarning(KSTARS_FITS) <<
"Y still out of bounds: 0 <=" << y2 <<
"<" << height;
367 for (
int x1 = 0; x1 < width; x1++)
369 int index = y2 * width + x1;
370 unsigned long channelAverage = 0;
371 for (
int i = 0; i < numChannels; i++)
373 int offset = size * i;
374 channelAverage += rotBuffer[index + offset];
376 multiRowSum += qRound(channelAverage /
static_cast<double>(numChannels));
381 double average = multiRowSum /
static_cast<double>(width * NUMBER_OF_AVERAGE_ROWS);
382 if (average > lineAverage.average)
384 lineAverage.average = average;
385 lineAverage.offset = y;
405bool FITSBahtinovDetector::rotateImage(
const FITSData *data,
int angle, T * rotImage)
407 int BBP = data->getBytesPerPixel();
408 int size = data->getStatistics().samples_per_channel;
409 int width = data->width();
410 int height = data->height();
411 int numChannels = data->channels();
417 if (rotImage ==
nullptr)
419 qWarning() <<
"No memory allocated for rotated image buffer!";
432 hx = qFloor((width + 1) / 2.0);
433 hy = qFloor((height + 1) / 2.0);
435 bufferSize = size * numChannels * BBP;
436 memset(rotImage, 0, bufferSize);
438 auto * rotBuffer =
reinterpret_cast<T *
>(rotImage);
439 auto * buffer =
reinterpret_cast<const T *
>(data->getImageBuffer());
441 double innerCircleRadius = (0.5 * qSqrt(2.0) * qMin(hx, hy));
442 double angleInRad = angle * M_PI / 180.0;
443 double sinAngle = qSin(angleInRad);
444 double cosAngle = qCos(angleInRad);
445 int leftEdge = qCeil(hx - innerCircleRadius);
446 int rightEdge = qFloor(hx + innerCircleRadius);
447 int topEdge = qCeil(hy - innerCircleRadius);
448 int bottomEdge = qFloor(hy + innerCircleRadius);
450 for (
int i = 0; i < numChannels; i++)
452 int offset = size * i;
453 for (
int x1 = leftEdge; x1 < rightEdge; x1++)
455 for (
int y1 = topEdge; y1 < bottomEdge; y1++)
462 double xnew = x2 * cosAngle - y2 * sinAngle;
463 double ynew = x2 * sinAngle + y2 * cosAngle;
469 int orgIndex = y1 * height + x1;
470 int newIndex = qRound(y2) * height + qRound(x2);
472 if (newIndex >= 0 && newIndex <
static_cast<int>(bufferSize))
474 rotBuffer[newIndex + offset] = buffer[orgIndex + offset];
Line representation for HoughTransform Based on the java implementation found on http://vase....
IntersectResult Intersect(const HoughLine &other_line, QPointF &intersection)
Sources for intersection and distance calculations came from http://paulbourke.net/geometry/pointline...
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
qint64 elapsed() const const
void append(QList< T > &&value)
qsizetype size() const const
iterator insert(const Key &key, const T &value)
size_type remove(const Key &key)
bool setProperty(const char *name, QVariant &&value)
bool isEmpty() const const
bool isNull() const const
QTextStream & center(QTextStream &stream)
QFuture< T > run(Function function,...)
int toInt(bool *ok) const const