11#include "fitsbahtinovdetector.h"
12#include "fitsthresholddetector.h"
13#include "fitsgradientdetector.h"
14#include "fitscentroiddetector.h"
15#include "fitssepdetector.h"
19#include "kstarsdata.h"
23#include "skymapcomposite.h"
24#include "auxiliary/ksnotification.h"
25#include "auxiliary/robuststatistics.h"
28#include <QApplication>
30#include <QtConcurrent>
31#include <QImageReader>
33#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
38#if !defined(KSTARS_LITE) && defined(HAVE_LIBRAW)
39#include <libraw/libraw.h>
49#include <fits_debug.h>
51#define ZOOM_DEFAULT 100.0
54#define ZOOM_LOW_INCR 10
55#define ZOOM_HIGH_INCR 50
63const QStringList RAWFormats = {
"cr2",
"cr3",
"crw",
"nef",
"raf",
"dng",
"arw",
"orf" };
65bool FITSData::readableFilename(
const QString &filename)
68 QString extension = info.completeSuffix().toLower();
72 RAWFormats.contains(extension);
79 debayerParams.method = DC1394_BAYER_METHOD_NEAREST;
80 debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
81 debayerParams.offsetX = debayerParams.offsetY = 0;
84 m_CumulativeFrequency.resize(3);
85 m_HistogramBinWidth.resize(3);
86 m_HistogramFrequency.resize(3);
87 m_HistogramIntensity.resize(3);
94 debayerParams.method = DC1394_BAYER_METHOD_NEAREST;
95 debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
96 debayerParams.offsetX = debayerParams.offsetY = 0;
100 this->m_Mode =
other->m_Mode;
101 this->m_Statistics.channels =
other->m_Statistics.channels;
102 memcpy(&m_Statistics, &(
other->m_Statistics),
sizeof(m_Statistics));
103 m_ImageBuffer =
new uint8_t[m_Statistics.samples_per_channel * m_Statistics.channels * m_Statistics.bytesPerPixel];
105 m_Statistics.samples_per_channel * m_Statistics.channels * m_Statistics.bytesPerPixel);
118 if (m_WCSHandle !=
nullptr)
121 m_WCSHandle =
nullptr;
126 if (starCenters.
count() > 0)
130 if (m_SkyObjects.
count() > 0)
132 m_SkyObjects.
clear();
139 m_PackBuffer =
nullptr;
155 m_PackBuffer =
nullptr;
162bool FITSData::loadFromBuffer(
const QByteArray &buffer)
166 return privateLoad(buffer);
173 m_Extension = info.completeSuffix().toLower();
174 qCDebug(
KSTARS_FITS) <<
"Loading file " << m_Filename;
191bool FITSData::privateLoad(
const QByteArray &buffer)
195 cacheEccentricity = -1;
198 return loadFITSImage(buffer);
200 return loadXISFImage(buffer);
202 return loadCanonicalImage(buffer);
203 else if (RAWFormats.contains(m_Extension))
204 return loadRAWImage(buffer);
209bool FITSData::loadFITSImage(
const QByteArray &buffer,
const bool isCompressed)
214 m_HistogramConstructed =
false;
216 if (m_Extension.
contains(
".fz") || isCompressed)
225 m_compressedFilename = m_Filename;
245 void *data =
reinterpret_cast<void *
>(m_PackBuffer);
261 m_PackBuffer =
nullptr;
262 m_LastError =
i18n(
"Failed to unpack compressed fits");
267 m_isTemporary =
true;
268 m_isCompressed =
true;
269 m_Statistics.size = fptr->Fptr->logfilesize;
281 m_Statistics.size =
QFile(m_Filename).
size();
286 void *
temp_buffer =
const_cast<void *
>(
reinterpret_cast<const void *
>(buffer.
data()));
302 m_PackBuffer =
nullptr;
309 m_PackBuffer =
nullptr;
317 loadCommon(m_Filename);
318 qCDebug(
KSTARS_FITS) <<
"Image is compressed. Reloading...";
319 return loadFITSImage(buffer,
true);
322 if (m_Statistics.ndim < 2)
324 m_LastError =
i18n(
"1D FITS images are not supported in KStars.");
327 m_PackBuffer =
nullptr;
331 switch (m_FITSBITPIX)
334 m_Statistics.dataType =
TBYTE;
335 m_Statistics.bytesPerPixel =
sizeof(uint8_t);
340 m_Statistics.dataType =
TUSHORT;
341 m_Statistics.bytesPerPixel =
sizeof(int16_t);
344 m_Statistics.dataType =
TUSHORT;
345 m_Statistics.bytesPerPixel =
sizeof(uint16_t);
350 m_Statistics.dataType =
TULONG;
351 m_Statistics.bytesPerPixel =
sizeof(int32_t);
354 m_Statistics.dataType =
TULONG;
355 m_Statistics.bytesPerPixel =
sizeof(uint32_t);
358 m_Statistics.dataType =
TFLOAT;
359 m_Statistics.bytesPerPixel =
sizeof(float);
363 m_Statistics.bytesPerPixel =
sizeof(int64_t);
366 m_Statistics.dataType =
TDOUBLE;
367 m_Statistics.bytesPerPixel =
sizeof(double);
370 m_LastError =
i18n(
"Bit depth %1 is not supported.", m_FITSBITPIX);
375 if (m_Statistics.ndim < 3)
380 m_LastError =
i18n(
"Image has invalid dimensions %1x%2",
naxes[0],
naxes[1]);
383 m_PackBuffer =
nullptr;
387 m_Statistics.width =
naxes[0];
388 m_Statistics.height =
naxes[1];
389 m_Statistics.samples_per_channel = m_Statistics.width * m_Statistics.height;
390 roiCenter.
setX(m_Statistics.width / 2);
391 roiCenter.
setY(m_Statistics.height / 2);
392 if(m_Statistics.width % 2)
393 roiCenter.
setX(roiCenter.
x() + 1);
394 if(m_Statistics.height % 2)
395 roiCenter.
setY(roiCenter.
y() + 1);
399 m_Statistics.channels =
naxes[2];
403 if ( (m_Mode != FITS_NORMAL && m_Mode != FITS_CALIBRATE) || !Options::auto3DCube())
404 m_Statistics.channels = 1;
406 m_ImageBufferSize = m_Statistics.samples_per_channel * m_Statistics.channels * m_Statistics.bytesPerPixel;
407 m_ImageBuffer =
new uint8_t[m_ImageBufferSize];
408 if (m_ImageBuffer ==
nullptr)
410 qCWarning(
KSTARS_FITS) <<
"FITSData: Not enough memory for image_buffer channel. Requested: "
411 << m_ImageBufferSize <<
" bytes.";
414 m_PackBuffer =
nullptr;
421 long nelements = m_Statistics.samples_per_channel * m_Statistics.channels;
433 if (getRecordValue(
"DATE-OBS", value) && value.
isValid())
441 if (
naxes[2] == 1 && m_Statistics.channels == 1 && Options::autoDebayer() && checkDebayer())
444 if (m_isTemporary && m_TemporaryDataFile.
open())
446 m_TemporaryDataFile.
write(buffer);
447 m_TemporaryDataFile.
close();
448 m_Filename = m_TemporaryDataFile.
fileName();
452 calculateStats(
false,
false);
455 calculateStats(
false,
false);
457 if (m_Mode == FITS_NORMAL || m_Mode == FITS_ALIGN)
460 starsSearched =
false;
465bool FITSData::loadXISFImage(
const QByteArray &buffer)
467 m_HistogramConstructed =
false;
486 m_LastError =
i18n(
"File contain no images");
490 const LibXISF::Image &image =
xisfReader.getImage(0);
492 switch (image.sampleFormat())
494 case LibXISF::Image::UInt8:
495 m_Statistics.dataType =
TBYTE;
496 m_Statistics.bytesPerPixel =
sizeof(LibXISF::UInt8);
497 m_FITSBITPIX =
TBYTE;
499 case LibXISF::Image::UInt16:
500 m_Statistics.dataType =
TUSHORT;
501 m_Statistics.bytesPerPixel =
sizeof(LibXISF::UInt16);
504 case LibXISF::Image::UInt32:
505 m_Statistics.dataType =
TULONG;
506 m_Statistics.bytesPerPixel =
sizeof(LibXISF::UInt32);
509 case LibXISF::Image::Float32:
510 m_Statistics.dataType =
TFLOAT;
511 m_Statistics.bytesPerPixel =
sizeof(LibXISF::Float32);
515 m_LastError =
i18n(
"Sample format %1 is not supported.", LibXISF::Image::sampleFormatString(image.sampleFormat()).c_str());
520 m_Statistics.width = image.width();
521 m_Statistics.height = image.height();
522 m_Statistics.samples_per_channel = m_Statistics.width * m_Statistics.height;
523 m_Statistics.channels = image.channelCount();
524 m_Statistics.size = buffer.
size();
525 roiCenter.
setX(m_Statistics.width / 2);
526 roiCenter.
setY(m_Statistics.height / 2);
527 if(m_Statistics.width % 2)
528 roiCenter.
setX(roiCenter.
x() + 1);
529 if(m_Statistics.height % 2)
530 roiCenter.
setY(roiCenter.
y() + 1);
532 m_HeaderRecords.clear();
538 if (getRecordValue(
"DATE-OBS", value) && value.
isValid())
544 m_ImageBufferSize = image.imageDataSize();
545 m_ImageBuffer =
new uint8_t[m_ImageBufferSize];
546 std::memcpy(m_ImageBuffer, image.imageData(), m_ImageBufferSize);
548 calculateStats(
false,
false);
551 catch (LibXISF::Error &error)
553 m_LastError =
i18n(
"XISF file open error: ") +
error.what();
569 LibXISF::Image image;
570 image.setGeometry(m_Statistics.width, m_Statistics.height, m_Statistics.channels);
571 if (m_Statistics.channels > 1)
572 image.setColorSpace(LibXISF::Image::RGB);
574 switch (m_FITSBITPIX)
577 image.setSampleFormat(LibXISF::Image::UInt8);
580 image.setSampleFormat(LibXISF::Image::UInt16);
583 image.setSampleFormat(LibXISF::Image::UInt32);
586 image.setSampleFormat(LibXISF::Image::Float32);
589 m_LastError =
i18n(
"Bit depth %1 is not supported.", m_FITSBITPIX);
594 std::memcpy(image.imageData(), m_ImageBuffer, m_ImageBufferSize);
602 catch (LibXISF::Error &
err)
604 m_LastError =
i18n(
"Error saving XISF image") +
err.what();
613bool FITSData::loadCanonicalImage(
const QByteArray &buffer)
620 qCCritical(
KSTARS_FITS) <<
"Failed to open image.";
624 m_Statistics.size = buffer.
size();
630 qCCritical(
KSTARS_FITS) <<
"Failed to open image.";
643 switch (m_FITSBITPIX)
646 m_Statistics.dataType =
TBYTE;
647 m_Statistics.bytesPerPixel =
sizeof(uint8_t);
651 m_Statistics.dataType =
TUSHORT;
652 m_Statistics.bytesPerPixel =
sizeof(int16_t);
655 m_Statistics.dataType =
TUSHORT;
656 m_Statistics.bytesPerPixel =
sizeof(uint16_t);
660 m_Statistics.dataType =
TULONG;
661 m_Statistics.bytesPerPixel =
sizeof(int32_t);
664 m_Statistics.dataType =
TULONG;
665 m_Statistics.bytesPerPixel =
sizeof(uint32_t);
668 m_Statistics.dataType =
TFLOAT;
669 m_Statistics.bytesPerPixel =
sizeof(float);
673 m_Statistics.bytesPerPixel =
sizeof(int64_t);
676 m_Statistics.dataType =
TDOUBLE;
677 m_Statistics.bytesPerPixel =
sizeof(double);
680 m_LastError =
QString(
"Bit depth %1 is not supported.").
arg(m_FITSBITPIX);
685 m_Statistics.width =
static_cast<uint16_t
>(
imageFromFile.width());
686 m_Statistics.height =
static_cast<uint16_t
>(
imageFromFile.height());
688 m_Statistics.samples_per_channel = m_Statistics.width * m_Statistics.height;
690 m_ImageBufferSize = m_Statistics.samples_per_channel * m_Statistics.channels *
static_cast<uint16_t
>
691 (m_Statistics.bytesPerPixel);
692 m_ImageBuffer =
new uint8_t[m_ImageBufferSize];
693 if (m_ImageBuffer ==
nullptr)
695 m_LastError =
i18n(
"FITSData: Not enough memory for image_buffer channel. Requested: %1 bytes ", m_ImageBufferSize);
701 if (m_Statistics.channels == 1)
716 int imax = m_Statistics.samples_per_channel * 4 - 4;
717 for (
int i = 0; i <=
imax; i += 4)
725 calculateStats(
false,
false);
729bool FITSData::loadRAWImage(
const QByteArray &buffer)
731#if !defined(KSTARS_LITE) && !defined(HAVE_LIBRAW)
732 m_LastError =
i18n(
"Unable to find dcraw and cjpeg. Please install the required tools to convert CR2/NEF to JPEG.");
756 if ((
ret =
RawProcessor.open_buffer(
const_cast<void *
>(
reinterpret_cast<const void *
>(buffer.
data())),
764 m_Statistics.size = buffer.
size();
792 m_Statistics.bytesPerPixel = image->bits / 8;
794 if (m_Statistics.bytesPerPixel == 1)
795 m_Statistics.dataType =
TBYTE;
797 m_Statistics.dataType =
TUSHORT;
798 m_Statistics.width = image->width;
799 m_Statistics.height = image->height;
800 m_Statistics.channels = image->colors;
801 m_Statistics.samples_per_channel = m_Statistics.width * m_Statistics.height;
803 m_ImageBufferSize = m_Statistics.samples_per_channel * m_Statistics.channels * m_Statistics.bytesPerPixel;
804 m_ImageBuffer =
new uint8_t[m_ImageBufferSize];
805 if (m_ImageBuffer ==
nullptr)
807 m_LastError =
i18n(
"FITSData: Not enough memory for image_buffer channel. Requested: %1 bytes ", m_ImageBufferSize);
815 auto source_buffer =
reinterpret_cast<uint8_t *
>(image->data);
818 if (image->colors == 1)
829 int imax = m_Statistics.samples_per_channel * 3 - 3;
830 for (
int i = 0; i <=
imax; i += 3)
839 calculateStats(
false,
false);
851 if (
ext ==
"jpg" ||
ext ==
"png")
855 getMinMax(&min, &max);
861 else if (channels() == 1)
866 for (
int i = 0; i < 256; i++)
874 double dataMin = m_Statistics.mean[0] - m_Statistics.stddev[0];
875 double dataMax = m_Statistics.mean[0] + m_Statistics.stddev[0] * 3;
881 switch (m_Statistics.dataType)
922 qCInfo(
KSTARS_FITS) <<
"Saved image file:" << m_Filename;
928 m_LastError =
i18n(
"Saving compressed files is not supported.");
943 rotCounter = flipHCounter = flipVCounter = 0;
983 nelements = m_Statistics.samples_per_channel * m_Statistics.channels;
1004 long naxis = m_Statistics.channels == 1 ? 2 : 3;
1005 long naxes[3] = {m_Statistics.width, m_Statistics.height,
naxis};
1047 if (m_Statistics.mean[0] > 0)
1058 if (m_Statistics.median[0] > 0)
1069 if (m_Statistics.stddev[0] > 0)
1071 fits_write_key(fptr,
TDOUBLE,
"STDDEV1", &m_Statistics.stddev[0],
"Standard Deviation Channel 1", &status);
1074 fits_write_key(fptr,
TDOUBLE,
"STDDEV2", &m_Statistics.stddev[1],
"Standard Deviation Channel 2", &status);
1075 fits_write_key(fptr,
TDOUBLE,
"STDDEV3", &m_Statistics.stddev[2],
"Standard Deviation Channel 3", &status);
1080 for (
int i = 10; i < m_HeaderRecords.count(); i++)
1082 QString key = m_HeaderRecords[i].key;
1083 const char *comment = m_HeaderRecords[i].comment.toLatin1().constBegin();
1084 QVariant value = m_HeaderRecords[i].value;
1086 switch (value.
type())
1128 int rot = 0, mirror = 0;
1131 rot = (90 * rotCounter) % 360;
1135 if (flipHCounter % 2 != 0 || flipVCounter % 2 != 0)
1138 if ((
rot != 0) || (mirror != 0))
1139 rotWCSFITS(
rot, mirror);
1141 rotCounter = flipHCounter = flipVCounter = 0;
1154 qCInfo(
KSTARS_FITS) <<
"Saved FITS file:" << m_Filename;
1159void FITSData::clearImageBuffers()
1161 delete[] m_ImageBuffer;
1162 m_ImageBuffer =
nullptr;
1163 if(m_ImageRoiBuffer !=
nullptr )
1165 delete[] m_ImageRoiBuffer;
1166 m_ImageRoiBuffer =
nullptr;
1172void FITSData::makeRoiBuffer(
QRect roi)
1177 uint32_t channelSize =
roi.height() *
roi.width();
1178 if(channelSize > m_Statistics.samples_per_channel || channelSize == 1)
1182 if(m_ImageRoiBuffer !=
nullptr )
1184 delete[] m_ImageRoiBuffer;
1185 m_ImageRoiBuffer =
nullptr;
1187 int xoffset =
roi.topLeft().x() - 1;
1188 int yoffset =
roi.topLeft().y() - 1;
1189 uint32_t bpp = m_Statistics.bytesPerPixel;
1190 m_ImageRoiBuffer =
new uint8_t[
roi.height()*
roi.width()*m_Statistics.channels * m_Statistics.bytesPerPixel]();
1191 for(
int n = 0 ; n < m_Statistics.channels ; n++)
1193 for(
int i = 0; i <
roi.height(); i++)
1195 size_t i1 = n * channelSize * bpp + i *
roi.width() * bpp;
1196 size_t i2 = n * m_Statistics.samples_per_channel * bpp + (yoffset + i) * width() * bpp + xoffset * bpp;
1197 memcpy(&m_ImageRoiBuffer[i1],
1203 memcpy(&m_ROIStatistics, &m_Statistics,
sizeof(FITSImage::Statistic));
1204 m_ROIStatistics.samples_per_channel =
roi.height() *
roi.width();
1205 m_ROIStatistics.width =
roi.width();
1206 m_ROIStatistics.height =
roi.height();
1207 calculateStats(
false,
true);
1209void FITSData::calculateStats(
bool refresh,
bool roi)
1214 calculateMinMax(refresh);
1215 calculateMedian(refresh);
1218 if (refresh ==
false && fptr)
1222 if (
fits_read_key_dbl(fptr,
"MEAN1", &m_Statistics.mean[0],
nullptr, &status) == 0)
1229 if (
fits_read_key_dbl(fptr,
"STDDEV1", &m_Statistics.stddev[0],
nullptr, &status) == 0)
1241 switch (m_Statistics.dataType)
1280 m_Statistics.SNR = m_Statistics.mean[0] / m_Statistics.stddev[0];
1284 calculateMinMax(refresh,
roi);
1285 calculateMedian(refresh,
roi);
1287 switch (m_ROIStatistics.dataType)
1327void FITSData::calculateMinMax(
bool refresh,
bool roi)
1337 if (fptr !=
nullptr && !refresh)
1342 if (
fits_read_key_dbl(fptr,
"DATAMIN", &(m_Statistics.min[0]),
nullptr, &status) == 0)
1344 else if (
fits_read_key_dbl(fptr,
"MIN1", &(m_Statistics.min[0]),
nullptr, &status) == 0)
1353 if (
fits_read_key_dbl(fptr,
"DATAMAX", &(m_Statistics.max[0]),
nullptr, &status) == 0)
1355 else if (
fits_read_key_dbl(fptr,
"MAX1", &(m_Statistics.max[0]),
nullptr, &status) == 0)
1363 if (
nfound == 2 && !(m_Statistics.min[0] == 0 && m_Statistics.max[0] == 0))
1367 m_Statistics.min[0] = 1.0E30;
1368 m_Statistics.max[0] = -1.0E30;
1370 m_Statistics.min[1] = 1.0E30;
1371 m_Statistics.max[1] = -1.0E30;
1373 m_Statistics.min[2] = 1.0E30;
1374 m_Statistics.max[2] = -1.0E30;
1376 switch (m_Statistics.dataType)
1416 m_ROIStatistics.min[0] = 1.0E30;
1417 m_ROIStatistics.max[0] = -1.0E30;
1419 m_ROIStatistics.min[1] = 1.0E30;
1420 m_ROIStatistics.max[1] = -1.0E30;
1422 m_ROIStatistics.min[2] = 1.0E30;
1423 m_ROIStatistics.max[2] = -1.0E30;
1425 switch (m_Statistics.dataType)
1466void FITSData::calculateMedian(
bool refresh,
bool roi)
1476 if (fptr !=
nullptr && !refresh)
1479 if (
fits_read_key_dbl(fptr,
"MEDIAN1", &m_Statistics.median[0],
nullptr, &status) == 0)
1490 m_Statistics.median[RED_CHANNEL] = 0;
1491 m_Statistics.median[GREEN_CHANNEL] = 0;
1492 m_Statistics.median[BLUE_CHANNEL] = 0;
1494 switch (m_Statistics.dataType)
1534 m_ROIStatistics.median[RED_CHANNEL] = 0;
1535 m_ROIStatistics.median[GREEN_CHANNEL] = 0;
1536 m_ROIStatistics.median[BLUE_CHANNEL] = 0;
1538 switch (m_ROIStatistics.dataType)
1579template <
typename T>
1580void FITSData::calculateMedian(
bool roi)
1582 auto * buffer =
reinterpret_cast<T *
>(
roi ? m_ImageRoiBuffer : m_ImageBuffer);
1584 uint32_t
medianSize =
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel;
1595 std::vector<int32_t> samples;
1598 for (uint8_t n = 0; n < m_Statistics.channels; n++)
1600 auto *
oneChannel = buffer + n * (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel);
1601 for (uint32_t
upto = 0;
upto < (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel);
1604 auto median = Mathematics::RobustStatistics::ComputeLocation(Mathematics::RobustStatistics::LOCATION_MEDIAN, samples);
1605 roi ? m_ROIStatistics.median[n] = median : m_Statistics.median[n] = median;
1609template <
typename T>
1610QPair<T, T> FITSData::getParitionMinMax(uint32_t start, uint32_t stride,
bool roi)
1612 auto * buffer =
reinterpret_cast<T *
>(
roi ? m_ImageRoiBuffer : m_ImageBuffer);
1613 T min = std::numeric_limits<T>::max();
1614 T max = std::numeric_limits<T>::min();
1616 uint32_t
end = start + stride;
1618 for (uint32_t i = start; i <
end; i++)
1620 min =
qMin(buffer[i], min);
1621 max =
qMax(buffer[i], max);
1631template <
typename T>
1632void FITSData::calculateMinMax(
bool roi)
1634 T min = std::numeric_limits<T>::max();
1635 T max = std::numeric_limits<T>::min();
1641 for (
int n = 0; n < m_Statistics.channels; n++)
1643 uint32_t
cStart = n * (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel);
1646 uint32_t
tStride = (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel) /
nThreads;
1649 uint32_t
fStride =
tStride + ((
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel) -
1670 min =
qMin(result.first, min);
1671 max =
qMax(result.second, max);
1676 m_Statistics.min[n] = min;
1677 m_Statistics.max[n] = max;
1681 m_ROIStatistics.min[n] = min;
1682 m_ROIStatistics.max[n] = max;
1695 SumData(
double s,
double sq,
int n) : sum(s), squaredSum(
sq), numSamples(n) {}
1696 SumData() : sum(0), squaredSum(0), numSamples(0) {}
1699template <
typename T>
1700SumData getSumAndSquaredSum(uint32_t start, uint32_t stride, uint8_t *buff)
1702 auto * buffer =
reinterpret_cast<T *
>(buff);
1703 const uint32_t
end = start + stride;
1705 double squaredSum = 0;
1706 for (uint32_t i = start; i <
end; i++)
1708 double sample = buffer[i];
1712 const double numSamples =
end - start;
1713 return SumData(sum, squaredSum, numSamples);
1716template <
typename T>
1717void FITSData::calculateStdDev(
bool roi )
1722 for (
int n = 0; n < m_Statistics.channels; n++)
1724 uint32_t
cStart = n * (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel);
1727 uint32_t
tStride = (
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel) /
nThreads;
1730 uint32_t
fStride =
tStride + ((
roi ? m_ROIStatistics.samples_per_channel : m_Statistics.samples_per_channel) -
1742 uint8_t *buff =
roi ? m_ImageRoiBuffer : m_ImageBuffer;
1750 double numSamples = 0;
1753 const SumData result = futures[i].result();
1756 numSamples += result.numSamples;
1759 if (numSamples <= 0)
continue;
1760 const double mean = sum / numSamples;
1761 const double variance =
squared_sum / numSamples - mean * mean;
1764 m_Statistics.mean[n] = mean;
1765 m_Statistics.stddev[n] =
sqrt(variance);
1769 m_ROIStatistics.mean[n] = mean;
1770 m_ROIStatistics.stddev[n] =
sqrt(variance);
1778 kernel.fill(0.0, size * size);
1781 int fOff = (size - 1) / 2;
1782 double normal = 1.0 / (2.0 * M_PI * sigma * sigma);
1783 for (
int y = -
fOff; y <=
fOff; y++)
1785 for (
int x = -
fOff; x <=
fOff; x++)
1787 double distance = ((y * y) + (x * x)) / (2.0 * sigma * sigma);
1788 int index = (y +
fOff) * size + (x +
fOff);
1793 for (
int y = 0; y < size; y++)
1795 for (
int x = 0; x < size; x++)
1797 int index = y * size + x;
1805template <
typename T>
1808 T *
imagePtr =
reinterpret_cast<T *
>(m_ImageBuffer);
1818 for (
int offsetY = 0; offsetY < m_Statistics.height; offsetY++)
1820 for (
int offsetX = 0; offsetX < m_Statistics.width; offsetX++)
1825 int byteOffset = offsetY * m_Statistics.width + offsetX;
1832 if ((offsetY +
filterY) >= 0 && (offsetY +
filterY) < m_Statistics.height
1833 && ((offsetX +
filterX) >= 0 && (offsetX +
filterX) < m_Statistics.width ))
1850template <
typename T>
1851void FITSData::gaussianBlur(
int kernelSize,
double sigma)
1869void FITSData::setMinMax(
double newMin,
double newMax, uint8_t channel)
1871 m_Statistics.min[channel] =
newMin;
1872 m_Statistics.max[channel] =
newMax;
1875bool FITSData::parseHeader()
1877 char * header =
nullptr;
1887 m_HeaderRecords.clear();
1890 for (
int i = 0; i <
nkeys; i++)
1934bool FITSData::getRecordValue(
const QString &key,
QVariant &value)
const
1936 auto result = std::find_if(m_HeaderRecords.begin(), m_HeaderRecords.end(), [&key](
const Record &
oneRecord)
1938 return (oneRecord.key == key && oneRecord.value.isValid());
1941 if (result != m_HeaderRecords.end())
1943 value = (*result).
value;
1949bool FITSData::parseSolution(FITSImage::Solution &solution)
const
1956 solution.fieldWidth = solution.fieldHeight = solution.pixscale = solution.ra = solution.dec = -1;
1959 if (getRecordValue(
"OBJCTRA", value))
1965 else if (getRecordValue(
"RA", value))
1971 if (getRecordValue(
"OBJCTDEC", value))
1977 else if (getRecordValue(
"DEC", value))
1986 if (getRecordValue(
"SCALE", value))
1991 double focal_length = -1;
1992 if (getRecordValue(
"FOCALLEN", value))
1999 if (getRecordValue(
"PIXSIZE1", value))
2004 if (getRecordValue(
"PIXSIZE2", value))
2011 if (getRecordValue(
"XBINNING", value))
2016 if (getRecordValue(
"YBINNING", value))
2027 solution.pixscale = scale;
2029 solution.fieldWidth = (m_Statistics.width * scale) / 60.0;
2031 solution.fieldHeight = (m_Statistics.height * scale * (
pixsize2 /
pixsize1)) / 60.0;
2033 else if (focal_length > 0)
2036 solution.fieldWidth = ((206264.8062470963552 * m_Statistics.width * (
pixsize1 / 1000.0)) / (focal_length *
binx)) / 60.0;
2038 solution.fieldHeight = ((206264.8062470963552 * m_Statistics.height * (
pixsize2 / 1000.0)) / (focal_length *
biny)) / 60.0;
2040 solution.pixscale = (206264.8062470963552 * (
pixsize1 / 1000.0)) / (focal_length *
binx);
2054 starAlgorithm = algorithm;
2056 starCenters.
clear();
2057 starsSearched =
true;
2063 m_StarDetector.
reset(
new FITSSEPDetector(
this));
2064 m_StarDetector->setSettings(m_SourceExtractorSettings);
2065 if (m_Mode == FITS_NORMAL && trackingBox.
isNull())
2067 if (Options::quickHFR())
2070 const int w = getStatistics().width;
2071 const int h = getStatistics().height;
2072 QRect middle(
static_cast<int>(w * 0.25),
static_cast<int>(h * 0.25), w / 2, h / 2);
2073 m_StarFindFuture = m_StarDetector->findSources(
middle);
2074 return m_StarFindFuture;
2077 m_StarFindFuture = m_StarDetector->findSources(trackingBox);
2078 return m_StarFindFuture;
2081 case ALGORITHM_GRADIENT:
2084 m_StarDetector.
reset(
new FITSGradientDetector(
this));
2085 m_StarDetector->setSettings(m_SourceExtractorSettings);
2086 m_StarFindFuture = m_StarDetector->findSources(trackingBox);
2087 return m_StarFindFuture;
2090 case ALGORITHM_CENTROID:
2093 m_StarDetector.
reset(
new FITSCentroidDetector(
this));
2094 m_StarDetector->setSettings(m_SourceExtractorSettings);
2096 if (!isHistogramConstructed())
2097 constructHistogram();
2098 m_StarDetector->configure(
"JMINDEX", m_JMIndex);
2099 m_StarFindFuture = m_StarDetector->findSources(trackingBox);
2100 return m_StarFindFuture;
2104 m_StarDetector.
reset(
new FITSCentroidDetector(
this));
2105 m_StarFindFuture =
starDetector->findSources(trackingBox);
2106 return m_StarFindFuture;
2110 case ALGORITHM_THRESHOLD:
2112 m_StarDetector.
reset(
new FITSThresholdDetector(
this));
2113 m_StarDetector->setSettings(m_SourceExtractorSettings);
2114 m_StarDetector->configure(
"THRESHOLD_PERCENTAGE", Options::focusThreshold());
2115 m_StarFindFuture = m_StarDetector->findSources(trackingBox);
2116 return m_StarFindFuture;
2119 case ALGORITHM_BAHTINOV:
2121 m_StarDetector.
reset(
new FITSBahtinovDetector(
this));
2122 m_StarDetector->setSettings(m_SourceExtractorSettings);
2123 m_StarDetector->configure(
"NUMBER_OF_AVERAGE_ROWS", Options::focusMultiRowAverage());
2124 m_StarFindFuture = m_StarDetector->findSources(trackingBox);
2125 return m_StarFindFuture;
2132 if (mask.isNull() ==
false)
2134 starCenters.
erase(std::remove_if(starCenters.
begin(), starCenters.
end(),
2137 return (mask->isVisible(edge->x, edge->y) == false);
2138 }), starCenters.
end());
2141 return starCenters.
count();
2145double FITSData::getHFR(HFRType type)
2147 if (starCenters.
empty())
2150 if (cacheHFR >= 0 && cacheHFRType == type)
2153 m_SelectedHFRStar.invalidate();
2155 if (type == HFR_MAX)
2160 for (
int i = 0; i < starCenters.
count(); i++)
2162 if (starCenters[i]->HFR >
maxVal)
2165 maxVal = starCenters[i]->HFR;
2169 m_SelectedHFRStar = *starCenters[
maxIndex];
2170 cacheHFR = starCenters[
maxIndex]->HFR;
2171 cacheHFRType =
type;
2174 else if (type == HFR_HIGH)
2177 int minX = width() / 10;
2178 int minY = height() / 10;
2183 return (oneStar->x < minX || oneStar->x > maxX || oneStar->y < minY || oneStar->y > maxY);
2184 }), starCenters.
end());
2186 if (starCenters.
empty())
2189 m_SelectedHFRStar = *starCenters[
static_cast<int>(starCenters.
size() * 0.05)];
2190 cacheHFR = m_SelectedHFRStar.HFR;
2191 cacheHFRType =
type;
2194 else if (type == HFR_MEDIAN)
2196 std::nth_element(starCenters.
begin(), starCenters.
begin() + starCenters.
size() / 2, starCenters.
end());
2197 m_SelectedHFRStar = *starCenters[starCenters.
size() / 2];
2199 cacheHFR = m_SelectedHFRStar.HFR;
2200 cacheHFRType =
type;
2210 for (
auto center : starCenters)
2217 std::vector<double>
HFRs;
2219 for (
auto center : starCenters)
2223 if (type == HFR_AVERAGE)
2230 if (m_SkyBackground.mean <= 0.0 ||
center->val < m_SkyBackground.mean)
2233 qCDebug(
KSTARS_FITS) <<
"HFR Adj, sky background " << m_SkyBackground.mean <<
" star peak " <<
center->val <<
2241 qCDebug(
KSTARS_FITS) <<
"HFR Adj, brightness adjusted from " <<
center->HFR <<
" to " <<
center->HFR * factor;
2246 auto m = Mathematics::RobustStatistics::ComputeLocation(Mathematics::RobustStatistics::LOCATION_SIGMACLIPPING,
2250 cacheHFRType = HFR_AVERAGE;
2254double FITSData::getHFR(
int x,
int y,
double scale)
2256 if (starCenters.
empty())
2259 for (
int i = 0; i < starCenters.
count(); i++)
2261 const int maxDist = std::max(2,
static_cast<int>(0.5 + 2 * starCenters[i]->width / scale));
2262 const int dx = std::fabs(starCenters[i]->x - x);
2263 const int dy = std::fabs(starCenters[i]->y - y);
2266 m_SelectedHFRStar = *starCenters[i];
2267 return starCenters[i]->HFR;
2274double FITSData::getEccentricity()
2276 if (starCenters.
empty())
2278 if (cacheEccentricity >= 0)
2279 return cacheEccentricity;
2280 std::vector<float>
eccs;
2281 for (
const auto &s : starCenters)
2282 eccs.push_back(s->ellipticity);
2298 if (type == FITS_NONE)
2311 case FITS_AUTO_STRETCH:
2313 for (
int i = 0; i < 3; i++)
2315 dataMin[i] = m_Statistics.mean[i] - m_Statistics.stddev[i];
2316 dataMax[i] = m_Statistics.mean[i] + m_Statistics.stddev[i] * 3;
2321 case FITS_HIGH_CONTRAST:
2323 for (
int i = 0; i < 3; i++)
2325 dataMin[i] = m_Statistics.mean[i] + m_Statistics.stddev[i];
2326 dataMax[i] = m_Statistics.mean[i] + m_Statistics.stddev[i] * 3;
2331 case FITS_HIGH_PASS:
2333 for (
int i = 0; i < 3; i++)
2335 dataMin[i] = m_Statistics.mean[i];
2344 switch (m_Statistics.dataType)
2348 for (
int i = 0; i < 3; i++)
2359 for (
int i = 0; i < 3; i++)
2371 for (
int i = 0; i < 3; i++)
2382 for (
int i = 0; i < 3; i++)
2393 for (
int i = 0; i < 3; i++)
2404 for (
int i = 0; i < 3; i++)
2415 for (
int i = 0; i < 3; i++)
2427 for (
int i = 0; i < 3; i++)
2449template <
typename T>
2453 T * image =
nullptr;
2459 image =
reinterpret_cast<T *
>(m_ImageBuffer);
2464 for (
int i = 0; i < 3; i++)
2466 min[i] = (*targetMin)[i] < std::numeric_limits<T>::min() ? std::numeric_limits<T>::min() : (*
targetMin)[i];
2467 max[i] = (*targetMax)[i] > std::numeric_limits<T>::max() ? std::numeric_limits<T>::max() : (*
targetMax)[i];
2474 uint32_t width = m_Statistics.width;
2475 uint32_t height = m_Statistics.height;
2483 case FITS_AUTO_STRETCH:
2484 case FITS_HIGH_CONTRAST:
2487 case FITS_HIGH_PASS:
2493 if (type == FITS_LOG)
2495 for (
int i = 0; i < 3; i++)
2496 coeff[i] = max[i] / std::log(1 + max[i]);
2498 else if (type == FITS_SQRT)
2500 for (
int i = 0; i < 3; i++)
2504 for (
int n = 0; n < m_Statistics.channels; n++)
2506 if (type == FITS_HIGH_PASS)
2507 min[n] = m_Statistics.mean[n];
2509 uint32_t
cStart = n * m_Statistics.samples_per_channel;
2519 if (type == FITS_LOG)
2527 a =
qBound(min[n],
static_cast<T
>(round(
coeff[n] * std::log(1 +
qBound(min[n], a, max[n])))), max[n]);
2533 else if (type == FITS_SQRT)
2541 a =
qBound(min[n],
static_cast<T
>(round(
coeff[n] * a)), max[n]);
2555 a =
qBound(min[n], a, max[n]);
2562 for (
int i = 0; i <
nThreads * m_Statistics.channels; i++)
2563 futures[i].waitForFinished();
2567 for (
int i = 0; i < 3; i++)
2569 m_Statistics.min[i] = min[i];
2570 m_Statistics.max[i] = max[i];
2580 if (!isHistogramConstructed())
2581 constructHistogram();
2585 double coeff = 255.0 / (height * width);
2589 for (
int i = 0; i < m_Statistics.channels; i++)
2591 uint32_t offset = i * m_Statistics.samples_per_channel;
2592 for (uint32_t
j = 0;
j < height;
j++)
2594 row = offset +
j * width;
2595 for (uint32_t
k = 0;
k < width;
k++)
2598 bufferVal = (image[index] - min[i]) / m_HistogramBinWidth[i];
2600 if (
bufferVal >= m_CumulativeFrequency[i].size())
2601 bufferVal = m_CumulativeFrequency[i].size() - 1;
2603 image[index] =
qBound(min[i],
static_cast<T
>(round(
coeff * m_CumulativeFrequency[i][
bufferVal])), max[i]);
2610 calculateStats(
true,
false);
2616 uint8_t
BBP = m_Statistics.bytesPerPixel;
2617 auto * extension =
new T[(width + 2) * (height + 2)];
2622 for (uint32_t
ch = 0;
ch < m_Statistics.channels;
ch++)
2624 uint32_t offset =
ch * m_Statistics.samples_per_channel;
2625 uint32_t N = width, M = height;
2627 for (uint32_t i = 0; i < M; ++i)
2629 memcpy(extension + (N + 2) * (i + 1) + 1, image + (N * i) + offset, N *
BBP);
2630 extension[(N + 2) * (i + 1)] = image[N * i + offset];
2631 extension[(N + 2) * (i + 2) - 1] = image[N * (i + 1) - 1 + offset];
2634 memcpy(extension, extension + N + 2, (N + 2) *
BBP);
2636 memcpy(extension + (N + 2) * (M + 1), extension + (N + 2) * M, (N + 2) *
BBP);
2643 for (uint32_t
m = 1;
m < M - 1; ++
m)
2644 for (uint32_t n = 1; n < N - 1; ++n)
2650 memset(&window[0], 0, 9 *
sizeof(
float));
2651 for (uint32_t
j =
m - 1;
j <
m + 2; ++
j)
2652 for (uint32_t i = n - 1; i < n + 2; ++i)
2653 window[
k++] = extension[
j * N + i];
2655 for (uint32_t
j = 0;
j < 5; ++
j)
2659 for (uint32_t l =
j + 1; l < 9; ++l)
2660 if (window[l] < window[
mine])
2668 image[(
m - 1) * (N - 2) + n - 1 + offset] =
window[4];
2681 gaussianBlur<T>(Options::focusGaussianKernelSize(), Options::focusGaussianSigma());
2683 calculateStats(
true,
false);
2686 case FITS_ROTATE_CW:
2691 case FITS_ROTATE_CCW:
2696 case FITS_MOUNT_FLIP_H:
2701 case FITS_MOUNT_FLIP_V:
2714 for (
int i = 0; i < starCenters.
count(); i++)
2716 int x =
static_cast<int>(starCenters[i]->x);
2717 int y =
static_cast<int>(starCenters[i]->y);
2726bool FITSData::loadWCS()
2728#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
2730 if (m_WCSState == Success)
2732 qCWarning(
KSTARS_FITS) <<
"WCS data already loaded";
2736 if (m_WCSHandle !=
nullptr)
2740 m_WCSHandle =
nullptr;
2743 qCDebug(
KSTARS_FITS) <<
"Started WCS Data Processing...";
2750 char *header =
nullptr;
2756 m_WCSState = Failure;
2782 m_WCSHandle =
nullptr;
2785 m_WCSState = Failure;
2789 if (m_WCSHandle ==
nullptr)
2791 m_LastError =
i18n(
"No world coordinate systems found.");
2792 m_WCSState = Failure;
2797 if (m_WCSHandle->crpix[0] == 0)
2800 m_WCSHandle =
nullptr;
2802 m_LastError =
i18n(
"No world coordinate systems found.");
2803 m_WCSState = Failure;
2808 if ((status =
wcsset(m_WCSHandle)) != 0)
2811 m_WCSHandle =
nullptr;
2814 m_WCSState = Failure;
2818 m_ObjectsSearched =
false;
2819 m_WCSState = Success;
2822 qCDebug(
KSTARS_FITS) <<
"Finished WCS Data processing...";
2832#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
2836 if (m_WCSHandle ==
nullptr)
2838 m_LastError =
i18n(
"No world coordinate systems found.");
2868#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
2872 if (m_WCSHandle ==
nullptr)
2874 m_LastError =
i18n(
"No world coordinate systems found.");
2900#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
2901bool FITSData::searchObjects()
2903 if (m_ObjectsSearched)
2906 m_ObjectsSearched =
true;
2911 pixelToWCS(
QPointF(0, 0), startPoint);
2912 pixelToWCS(
QPointF(width() - 1, height() - 1), endPoint);
2919 if (m_WCSHandle ==
nullptr)
2921 m_LastError =
i18n(
"No world coordinate systems found.");
2948 for (
int y = 0; y < height(); y++)
2954 for (
int x = 1; x < width() - 1; x++)
2982#if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
2985 if (KStarsData::Instance() ==
nullptr)
2993 if (getRecordValue(
"DATE-OBS", date))
3008 num =
new KSNumbers(KStarsData::Instance()->ut().djd());
3013 m_SkyObjects.
clear();
3015 QList<SkyObject *> list = KStarsData::Instance()->skyComposite()->findObjectsInArea(startPoint, endPoint);
3018 int type = oneObject->type();
3019 return (type == SkyObject::STAR || type == SkyObject::PLANET || type == SkyObject::ASTEROID ||
3020 type == SkyObject::COMET || type == SkyObject::SUPERNOVA || type == SkyObject::MOON ||
3021 type == SkyObject::SATELLITE);
3026 for (
auto &
object :
list)
3028 world[0] =
object->ra0().Degrees();
3029 world[1] =
object->dec0().Degrees();
3036 if (x > 0 && y > 0 && x < w && y < h)
3037 m_SkyObjects.
append(
new FITSSkyObject(
object, x, y));
3046int FITSData::getFlipVCounter()
const
3048 return flipVCounter;
3051void FITSData::setFlipVCounter(
int value)
3053 flipVCounter = value;
3056int FITSData::getFlipHCounter()
const
3058 return flipHCounter;
3061void FITSData::setFlipHCounter(
int value)
3063 flipHCounter = value;
3066int FITSData::getRotCounter()
const
3071void FITSData::setRotCounter(
int value)
3081template <
typename T>
3082bool FITSData::rotFITS(
int rotate,
int mirror)
3091 else if (rotate == 2)
3093 else if (rotate == 3)
3095 else if (rotate < 0)
3096 rotate = rotate + 360;
3098 nx = m_Statistics.width;
3099 ny = m_Statistics.height;
3101 int BBP = m_Statistics.bytesPerPixel;
3104 rotimage =
new uint8_t[m_Statistics.samples_per_channel * m_Statistics.channels *
BBP];
3108 qWarning() <<
"Unable to allocate memory for rotated image buffer!";
3113 auto * buffer =
reinterpret_cast<T *
>(m_ImageBuffer);
3120 for (
int i = 0; i < m_Statistics.channels; i++)
3122 offset = m_Statistics.samples_per_channel * i;
3123 for (x1 = 0; x1 < nx; x1++)
3126 for (y1 = 0; y1 < ny; y1++)
3127 rotBuffer[(y1 * nx) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3131 else if (mirror == 2)
3133 for (
int i = 0; i < m_Statistics.channels; i++)
3135 offset = m_Statistics.samples_per_channel * i;
3136 for (y1 = 0; y1 < ny; y1++)
3139 for (x1 = 0; x1 < nx; x1++)
3140 rotBuffer[(y2 * nx) + x1 + offset] = buffer[(y1 * nx) + x1 + offset];
3146 for (
int i = 0; i < m_Statistics.channels; i++)
3148 offset = m_Statistics.samples_per_channel * i;
3149 for (y1 = 0; y1 < ny; y1++)
3151 for (x1 = 0; x1 < nx; x1++)
3152 rotBuffer[(y1 * nx) + x1 + offset] = buffer[(y1 * nx) + x1 + offset];
3159 else if (rotate >= 45 && rotate < 135)
3163 for (
int i = 0; i < m_Statistics.channels; i++)
3165 offset = m_Statistics.samples_per_channel * i;
3166 for (y1 = 0; y1 < ny; y1++)
3169 for (x1 = 0; x1 < nx; x1++)
3172 rotBuffer[(y2 * ny) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3177 else if (mirror == 2)
3179 for (
int i = 0; i < m_Statistics.channels; i++)
3181 offset = m_Statistics.samples_per_channel * i;
3182 for (y1 = 0; y1 < ny; y1++)
3184 for (x1 = 0; x1 < nx; x1++)
3185 rotBuffer[(x1 * ny) + y1 + offset] = buffer[(y1 * nx) + x1 + offset];
3191 for (
int i = 0; i < m_Statistics.channels; i++)
3193 offset = m_Statistics.samples_per_channel * i;
3194 for (y1 = 0; y1 < ny; y1++)
3197 for (x1 = 0; x1 < nx; x1++)
3200 rotBuffer[(y2 * ny) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3206 m_Statistics.width = ny;
3207 m_Statistics.height = nx;
3211 else if (rotate >= 135 && rotate < 225)
3215 for (
int i = 0; i < m_Statistics.channels; i++)
3217 offset = m_Statistics.samples_per_channel * i;
3218 for (y1 = 0; y1 < ny; y1++)
3221 for (x1 = 0; x1 < nx; x1++)
3222 rotBuffer[(y2 * nx) + x1 + offset] = buffer[(y1 * nx) + x1 + offset];
3226 else if (mirror == 2)
3228 for (
int i = 0; i < m_Statistics.channels; i++)
3230 offset = m_Statistics.samples_per_channel * i;
3231 for (x1 = 0; x1 < nx; x1++)
3234 for (y1 = 0; y1 < ny; y1++)
3235 rotBuffer[(y1 * nx) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3241 for (
int i = 0; i < m_Statistics.channels; i++)
3243 offset = m_Statistics.samples_per_channel * i;
3244 for (y1 = 0; y1 < ny; y1++)
3247 for (x1 = 0; x1 < nx; x1++)
3250 rotBuffer[(y2 * nx) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3258 else if (rotate >= 225 && rotate < 315)
3262 for (
int i = 0; i < m_Statistics.channels; i++)
3264 offset = m_Statistics.samples_per_channel * i;
3265 for (y1 = 0; y1 < ny; y1++)
3267 for (x1 = 0; x1 < nx; x1++)
3268 rotBuffer[(x1 * ny) + y1 + offset] = buffer[(y1 * nx) + x1 + offset];
3272 else if (mirror == 2)
3274 for (
int i = 0; i < m_Statistics.channels; i++)
3276 offset = m_Statistics.samples_per_channel * i;
3277 for (y1 = 0; y1 < ny; y1++)
3280 for (x1 = 0; x1 < nx; x1++)
3283 rotBuffer[(y2 * ny) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3290 for (
int i = 0; i < m_Statistics.channels; i++)
3292 offset = m_Statistics.samples_per_channel * i;
3293 for (y1 = 0; y1 < ny; y1++)
3296 for (x1 = 0; x1 < nx; x1++)
3299 rotBuffer[(y2 * ny) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3305 m_Statistics.width = ny;
3306 m_Statistics.height = nx;
3310 else if (rotate >= 315 && mirror)
3312 for (
int i = 0; i < m_Statistics.channels; i++)
3314 offset = m_Statistics.samples_per_channel * i;
3315 for (y1 = 0; y1 < ny; y1++)
3317 for (x1 = 0; x1 < nx; x1++)
3321 rotBuffer[(y2 * ny) + x2 + offset] = buffer[(y1 * nx) + x1 + offset];
3327 delete[] m_ImageBuffer;
3333void FITSData::rotWCSFITS(
int angle,
int mirror)
3340 naxis1 = m_Statistics.width;
3341 naxis2 = m_Statistics.height;
3445 else if (angle == 90)
3450 else if (angle == 180)
3455 else if (angle == 270)
3468 else if (angle == 180)
3473 else if (angle == 270)
3491 else if (angle == 90)
3496 else if (angle == 180)
3501 else if (angle == 270)
3514 else if (angle == 180)
3519 else if (angle == 270)
3546 else if (angle == 90)
3553 else if (angle == 180)
3560 else if (angle == 270)
3577 else if (angle == 180)
3584 else if (angle == 270)
3602 for (i = 1; i < 13; i++)
3607 for (i = 1; i < 13; i++)
3616uint8_t * FITSData::getWritableImageBuffer()
3618 return m_ImageBuffer;
3621uint8_t
const * FITSData::getImageBuffer()
const
3623 return m_ImageBuffer;
3626void FITSData::setImageBuffer(uint8_t * buffer)
3628 delete[] m_ImageBuffer;
3629 m_ImageBuffer = buffer;
3632bool FITSData::checkDebayer()
3641 if (m_Statistics.dataType !=
TUSHORT && m_Statistics.dataType !=
TBYTE)
3643 m_LastError =
i18n(
"Only 8 and 16 bits bayered images supported.");
3647 pattern = pattern.remove(
'\'').trimmed();
3650 order = order.remove(
'\'').trimmed();
3652 if (order ==
"BOTTOM-UP" && !(m_Statistics.height % 2))
3654 if (pattern ==
"RGGB")
3656 else if (pattern ==
"GBRG")
3658 else if (pattern ==
"GRBG")
3660 else if (pattern ==
"BGGR")
3665 if (pattern ==
"RGGB")
3666 debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
3667 else if (pattern ==
"GBRG")
3668 debayerParams.filter = DC1394_COLOR_FILTER_GBRG;
3669 else if (pattern ==
"GRBG")
3670 debayerParams.filter = DC1394_COLOR_FILTER_GRBG;
3671 else if (pattern ==
"BGGR")
3672 debayerParams.filter = DC1394_COLOR_FILTER_BGGR;
3676 m_LastError =
i18n(
"Unsupported bayer pattern %1.", pattern);
3680 fits_read_key(fptr,
TINT,
"XBAYROFF", &debayerParams.offsetX,
nullptr, &status);
3681 fits_read_key(fptr,
TINT,
"YBAYROFF", &debayerParams.offsetY,
nullptr, &status);
3683 if (debayerParams.offsetX == 1)
3688 switch (debayerParams.filter)
3690 case DC1394_COLOR_FILTER_RGGB:
3691 debayerParams.filter = DC1394_COLOR_FILTER_GRBG;
3693 case DC1394_COLOR_FILTER_GBRG:
3694 debayerParams.filter = DC1394_COLOR_FILTER_BGGR;
3696 case DC1394_COLOR_FILTER_GRBG:
3697 debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
3699 case DC1394_COLOR_FILTER_BGGR:
3700 debayerParams.filter = DC1394_COLOR_FILTER_GBRG;
3703 debayerParams.offsetX = 0;
3705 if (debayerParams.offsetX != 0 || debayerParams.offsetY > 1 || debayerParams.offsetY < 0)
3707 m_LastError =
i18n(
"Unsupported bayer offsets %1 %2.", debayerParams.offsetX, debayerParams.offsetY);
3716void FITSData::getBayerParams(BayerParams *
param)
3718 param->method = debayerParams.method;
3719 param->filter = debayerParams.filter;
3720 param->offsetX = debayerParams.offsetX;
3721 param->offsetY = debayerParams.offsetY;
3724void FITSData::setBayerParams(BayerParams *
param)
3726 debayerParams.method =
param->method;
3727 debayerParams.filter =
param->filter;
3728 debayerParams.offsetX =
param->offsetX;
3729 debayerParams.offsetY =
param->offsetY;
3732bool FITSData::debayer(
bool reload)
3738 if (
fits_read_img(fptr, m_Statistics.dataType, 1, m_Statistics.samples_per_channel,
nullptr, m_ImageBuffer,
3748 switch (m_Statistics.dataType)
3751 return debayer_8bit();
3754 return debayer_16bit();
3761bool FITSData::debayer_8bit()
3765 uint32_t
rgb_size = m_Statistics.samples_per_channel * 3 * m_Statistics.bytesPerPixel;
3772 catch (
const std::bad_alloc &e)
3775 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer: %1", e.what());
3785 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer.");
3792 if (debayerParams.offsetY == 1)
3800 debayerParams.filter,
3801 debayerParams.method);
3806 m_Statistics.channels = 1;
3813 delete[] m_ImageBuffer;
3816 m_ImageBuffer =
new uint8_t[
rgb_size];
3818 catch (
const std::bad_alloc &e)
3822 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer: %1", e.what());
3829 auto bayered_buffer =
reinterpret_cast<uint8_t *
>(m_ImageBuffer);
3837 int imax = m_Statistics.samples_per_channel * 3 - 3;
3838 for (
int i = 0; i <=
imax; i += 3)
3848 m_Statistics.channels = (m_Mode == FITS_NORMAL || m_Mode == FITS_CALIBRATE) ? 3 : 1;
3849 m_Statistics.dataType =
TBYTE;
3854bool FITSData::debayer_16bit()
3858 uint32_t
rgb_size = m_Statistics.samples_per_channel * 3 * m_Statistics.bytesPerPixel;
3864 catch (
const std::bad_alloc &e)
3867 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer: %1", e.what());
3877 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer.");
3884 if (debayerParams.offsetY == 1)
3892 debayerParams.filter,
3893 debayerParams.method, 16);
3897 m_LastError =
i18n(
"Debayer failed (%1)");
3898 m_Statistics.channels = 1;
3905 delete[] m_ImageBuffer;
3908 m_ImageBuffer =
new uint8_t[
rgb_size];
3910 catch (
const std::bad_alloc &e)
3914 m_LastError =
i18n(
"Unable to allocate memory for temporary bayer buffer: %1", e.what());
3921 auto bayered_buffer =
reinterpret_cast<uint16_t *
>(m_ImageBuffer);
3929 int imax = m_Statistics.samples_per_channel * 3 - 3;
3930 for (
int i = 0; i <=
imax; i += 3)
3937 m_Statistics.channels = (m_Mode == FITS_NORMAL || m_Mode == FITS_CALIBRATE) ? 3 : 1;
3938 m_Statistics.dataType =
TUSHORT;
3946 <<
"Available system memory:" << KSUtils::getAvailableRAM();
3949double FITSData::getADU()
const
3952 for (
int i = 0; i < m_Statistics.channels; i++)
3953 adu += m_Statistics.mean[i];
3955 return (
adu /
static_cast<double>(m_Statistics.channels));
3958QString FITSData::getLastError()
const
3963template <
typename T>
3964void FITSData::convertToQImage(
double dataMin,
double dataMax,
double scale,
double zero,
QImage &image)
3966 const auto * buffer =
reinterpret_cast<const T *
>(getImageBuffer());
3967 const T limit = std::numeric_limits<T>::max();
3970 uint16_t w = width();
3971 uint16_t h = height();
3972 uint32_t size = w * h;
3975 if (channels() == 1)
3978 for (
int j = 0;
j < h;
j++)
3980 unsigned char * scanLine = image.
scanLine(
j);
3982 for (
int i = 0; i < w; i++)
3985 val = val * scale + zero;
3995 for (
int j = 0;
j < h;
j++)
3997 auto * scanLine =
reinterpret_cast<QRgb *
>((image.
scanLine(
j)));
3999 for (
int i = 0; i < w; i++)
4005 value = qRgb(
rval * scale + zero,
gval * scale + zero,
bval * scale + zero);
4007 scanLine[i] = value;
4023 future.waitForFinished();
4024 if (future.result() ==
false)
4027 data.getMinMax(&min, &max);
4035 if (data.channels() == 1)
4040 for (
int i = 0; i < 256; i++)
4048 double dataMin = data.m_Statistics.mean[0] - data.m_Statistics.stddev[0];
4049 double dataMax = data.m_Statistics.mean[0] + data.m_Statistics.stddev[0] * 3;
4055 switch (data.m_Statistics.dataType)
4100 qCCritical(
KSTARS_FITS) <<
"Failed to convert" << filename <<
"to FITS since format" << format <<
"is not supported in Qt";
4108 qCCritical(
KSTARS_FITS) <<
"Failed to open image" << filename;
4112 output =
QDir(getTemporaryPath()).
filePath(filename +
".fits");
4166 qCWarning(
KSTARS_FITS) <<
"Not enough memory for RGB buffer";
4175 for (uint32_t i = 0; i <
srcBytes; i += 4)
4215void FITSData::injectWCS(
double orientation,
double ra,
double dec,
double pixscale,
bool eastToTheRight)
4219 if (fptr ==
nullptr)
4233 char ctype1[16] =
"RA---TAN";
4234 char ctype2[16] =
"DEC--TAN";
4240 double crpix1 = width() / 2.0;
4241 double crpix2 = height() / 2.0;
4260 double rotation = 360 - orientation;
4270bool FITSData::contains(
const QPointF &point)
const
4272 return (point.x() >= 0 && point.y() >= 0 && point.x() <= m_Statistics.width && point.y() <= m_Statistics.height);
4275void FITSData::saveStatistics(FITSImage::Statistic &
other)
4277 other = m_Statistics;
4280void FITSData::restoreStatistics(FITSImage::Statistic &
other)
4282 m_Statistics =
other;
4287void FITSData::constructHistogram()
4289 switch (m_Statistics.dataType)
4328template <
typename T> int32_t FITSData::histogramBinInternal(T value,
int channel)
const
4330 return qMax(
static_cast<T
>(0),
qMin(
static_cast<T
>(m_HistogramBinCount),
4331 static_cast<T
>(
rint((value - m_Statistics.min[channel]) / m_HistogramBinWidth[channel]))));
4334template <
typename T> int32_t FITSData::histogramBinInternal(
int x,
int y,
int channel)
const
4336 if (!m_ImageBuffer || !isHistogramConstructed())
4338 uint32_t samples = m_Statistics.width * m_Statistics.height;
4339 uint32_t offset = channel * samples;
4340 auto *
const buffer =
reinterpret_cast<T
const *
>(m_ImageBuffer);
4341 int index = y * m_Statistics.width + x;
4342 const T &
sample = buffer[index + offset];
4343 return histogramBinInternal(
sample, channel);
4346int32_t FITSData::histogramBin(
int x,
int y,
int channel)
const
4348 switch (m_Statistics.dataType)
4388template <
typename T>
void FITSData::constructHistogramInternal()
4390 auto *
const buffer =
reinterpret_cast<T
const *
>(m_ImageBuffer);
4391 uint32_t samples = m_Statistics.width * m_Statistics.height;
4392 const uint32_t
sampleBy = samples > 500000 ? samples / 500000 : 1;
4394 m_HistogramBinCount =
qMax(0.,
qMin(m_Statistics.max[0] - m_Statistics.min[0], 256.0));
4395 if (m_HistogramBinCount <= 0)
4396 m_HistogramBinCount = 256;
4398 for (
int n = 0; n < m_Statistics.channels; n++)
4400 m_HistogramIntensity[n].fill(0, m_HistogramBinCount + 1);
4401 m_HistogramFrequency[n].fill(0, m_HistogramBinCount + 1);
4402 m_CumulativeFrequency[n].fill(0, m_HistogramBinCount + 1);
4404 const double minBinSize = (m_Statistics.max[n] > 1.1) ? 1.0 : .0001;
4405 m_HistogramBinWidth[n] =
qMax(
minBinSize, (m_Statistics.max[n] - m_Statistics.min[n]) / m_HistogramBinCount);
4410 for (
int n = 0; n < m_Statistics.channels; n++)
4414 for (
int i = 0; i < m_HistogramBinCount; i++)
4415 m_HistogramIntensity[n][i] = m_Statistics.min[n] + (m_HistogramBinWidth[n] * i);
4419 for (
int n = 0; n < m_Statistics.channels; n++)
4423 uint32_t offset = n * samples;
4425 for (uint32_t i = 0; i < samples; i +=
sampleBy)
4428 m_HistogramFrequency[n][id] +=
sampleBy;
4434 future.waitForFinished();
4438 for (
int n = 0; n < m_Statistics.channels; n++)
4443 for (
int i = 0; i < m_HistogramBinCount; i++)
4452 future.waitForFinished();
4457 if (m_CumulativeFrequency[RED_CHANNEL][m_HistogramBinCount / 4] > 0)
4458 m_JMIndex = m_CumulativeFrequency[RED_CHANNEL][m_HistogramBinCount / 8] /
static_cast<double>
4459 (m_CumulativeFrequency[RED_CHANNEL][m_HistogramBinCount /
4464 qCDebug(
KSTARS_FITS) <<
"FITHistogram: JMIndex " << m_JMIndex;
4466 m_HistogramConstructed =
true;
4467 emit histogramReady();
4470void FITSData::recordLastError(
int errorCode)
4477double FITSData::getAverageMean(
bool roi)
const
4479 const FITSImage::Statistic* ptr = (
roi ? &m_ROIStatistics : &m_Statistics);
4480 if (ptr->channels == 1)
4481 return ptr->mean[0];
4483 return (ptr->mean[0] + ptr->mean[1] + ptr->mean[2]) / 3.0;
4486double FITSData::getAverageMedian(
bool roi)
const
4488 const FITSImage::Statistic* ptr = (
roi ? &m_ROIStatistics : &m_Statistics);
4489 if (ptr->channels == 1)
4490 return ptr->median[0];
4492 return (ptr->median[0] + ptr->median[1] + ptr->median[2]) / 3.0;
4495double FITSData::getAverageStdDev(
bool roi)
const
4497 const FITSImage::Statistic* ptr = (
roi ? &m_ROIStatistics : &m_Statistics);
4498 if (ptr->channels == 1)
4499 return ptr->stddev[0];
4501 return (ptr->stddev[0] + ptr->stddev[1] + ptr->stddev[2]) / 3.0;
There are several time-dependent values used in position calculations, that are not specific to an ob...
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
Provides all necessary information about an object in the sky: its coordinates, name(s),...
The sky coordinates of a point in the sky.
virtual void updateCoordsNow(const KSNumbers *num)
updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr,...
An angle, stored as degrees, but expressible in many ways.
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
char * toString(const EngineQuery &query)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & end()
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
const char * constData() const const
bool isEmpty() const const
void push_back(QByteArrayView str)
qsizetype size() const const
QDateTime currentDateTime()
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
QString filePath(const QString &fileName) const const
QString path() const const
virtual qint64 size() const const override
virtual void close() override
qint64 size() const const
QString suffix() const const
bool isRunning() const const
bool allGray() const const
bool load(QIODevice *device, const char *format)
qint64 write(const QByteArray &data)
void append(QList< T > &&value)
bool contains(const AT &value) const const
qsizetype count() const const
iterator erase(const_iterator begin, const_iterator end)
qsizetype size() const const
bool contains(const QPoint &point, bool proper) const const
bool isNull() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QByteArray toLocal8Bit() const const
QTextStream & center(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< T > run(Function function,...)
virtual QString fileName() const const override
void setFileTemplate(const QString &name)
bool isValid() const const
QDateTime toDateTime() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const