diff --git a/CMakeLists.txt b/CMakeLists.txt index ae8ff4f..4673dc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,9 +39,11 @@ set(TENMON_SRC histogram.cpp histogram.h httpdownloader.h httpdownloader.cpp imageinfo.cpp imageinfo.h + imageinfodata.cpp imageinfodata.h imageringlist.cpp imageringlist.h imagescrollarea.cpp imagescrollarea.h imagewidget.h imagewidget.cpp + loadimage.h loadimage.cpp loadrunable.cpp loadrunable.h main.cpp mainwindow.cpp mainwindow.h diff --git a/database.cpp b/database.cpp index 5822b99..90d6089 100644 --- a/database.cpp +++ b/database.cpp @@ -4,7 +4,7 @@ #include #include #include -#include "loadrunable.h" +#include "loadimage.h" Database::Database(QObject *parent) : QObject(parent) { diff --git a/histogram.cpp b/histogram.cpp index cc97c4c..6214582 100644 --- a/histogram.cpp +++ b/histogram.cpp @@ -3,6 +3,7 @@ #include #include #include +#include Histogram::Histogram(QWidget *parent) : QWidget(parent) { diff --git a/imageinfo.cpp b/imageinfo.cpp index 0088205..91e6a69 100644 --- a/imageinfo.cpp +++ b/imageinfo.cpp @@ -1,55 +1,6 @@ #include "imageinfo.h" #include -#include #include -#include -#include -#include - -static const QVector noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"}; - -bool FITSRecord::editable() const -{ - return noEditableKey.count(key); -} - -FITSRecord::FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment) : - key(key), value(value), comment(comment) -{ -} - -FITSRecord::FITSRecord(const LibXISF::FITSKeyword &record) -{ - key = record.name.c_str(); - comment = record.comment.c_str(); - - QString string = record.value.c_str(); - if(string.startsWith('\'') && string.endsWith('\'')) - { - string.chop(1); - string.remove(0, 1); - } - bool isint; - bool isdouble; - double vald = string.toDouble(&isdouble); - long long vall = string.toLongLong(&isint); - if(isint) - value = vall; - else if(isdouble) - value = vald; - else if(string == "T" || string == "F") - value = string == "T"; - else - value = string; -} - -FITSRecord::FITSRecord(const LibXISF::Property &property) -{ - key = property.id.c_str(); - value = QString::fromStdString(property.value.toString()); - comment = property.comment.c_str(); - xisf = true; -} ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent) { @@ -89,356 +40,3 @@ void ImageInfo::setInfo(const ImageInfoData &info) } expandAll(); } - -void WCSDataT::freeWCS() -{ - wcsvfree(&nwcs, &wcs); - nwcs = 0; - wcs = nullptr; -} - -WCSDataT::WCSDataT(int width, int height, char *header, int nrec) : - width(width), - height(height) -{ - int nreject = 0; - int status = wcspih(header, nrec, 1, 0, &nreject, &nwcs, &wcs); - if(status != 0) - { - freeWCS(); - return; - } - status = cdfix(wcs); - if(status > 0 || wcs->crpix[0] == 0) - freeWCS(); -} - -WCSDataT::WCSDataT(int width, int height, const QVector &header) : - width(width), - height(height) -{ - int status = 0; - - QByteArray str; - int nrec = 1; - for(const FITSRecord &record : header) - { - if(record.key.startsWith("PV"))continue; - - QByteArray rec; - rec.append(record.key.leftJustified(8, ' ')); - rec.append("= "); - rec.append(record.value.toString().toLatin1()); - rec.append(" / "); - rec.append(record.comment); - str.append(rec.leftJustified(80, ' ', true)); - nrec++; - } - str.append(QByteArray("END").leftJustified(80)); - - int nreject = 0; - status = wcspih(str.data(), nrec, 1, 0, &nreject, &nwcs, &wcs); - if(status != 0) - { - freeWCS(); - return; - } - status = cdfix(wcs); - if(status > 0 || wcs->crpix[0] == 0) - freeWCS(); -} - -WCSDataT::~WCSDataT() -{ - if(wcs) - freeWCS(); -} - -bool WCSDataT::pixelToWorld(const QPointF &pixel, SkyPoint &point) const -{ - if(!valid())return false; - - double pixcrd[2] = {pixel.x(), pixel.y()}; - double imgcrd[8] = {0}; - double phi = 0; - double theta = 0; - double world[8] = {0}; - int stat[NWCSFIX] = {0}; - int status = wcsp2s(wcs, 1, 2, pixcrd, imgcrd, &phi, &theta, world, stat); - if(status == 0) - { - point = SkyPoint(world[0], world[1]); - return true; - } - return false; -} - -bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const -{ - if(!valid())return false; - - double world[2] = {point.RA(), point.DEC()}; - double phi = 0; - double theta = 0; - double imgcrd[8] = {0}; - double pixcrd[8] = {0}; - int stat[NWCSFIX] = {0}; - int status = wcss2p(wcs, 1, 2, world, &phi, &theta, imgcrd, pixcrd, stat); - if(status == 0) - { - pixel = QPointF(pixcrd[0], pixcrd[1]); - return true; - } - return false; -} - -void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const -{ - if(wcs == nullptr)return; - - minRa = 1000; - maxRa = -1000; - minDec = 1000; - maxDec = -1000; - - if(wcs->crval) - { - crVal1 = wcs->crval[0]; - crVal2 = wcs->crval[1]; - } - else - { - crVal1 = crVal2 = NAN; - } - - auto update = [&](const QPointF &pixel) - { - SkyPoint point; - pixelToWorld(pixel, point); - minRa = std::min(minRa, point.RA()); - maxRa = std::max(maxRa, point.RA()); - minDec = std::min(minDec, point.DEC()); - maxDec = std::max(maxDec, point.DEC()); - }; - - for(int x=0; xra = ra; - this->dec = dec; -} - -QString SkyPoint::toString() const -{ - if(std::isnan(ra) || std::isnan(dec)) - return QString(); - - QTime t(0, 0); - t = t.addSecs(ra * 240); - - double deg, min, sec; - min = std::abs(std::modf(dec, °) * 60); - sec = std::modf(min, &min) * 60; - return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0'); -} - -double SkyPoint::fromHMS(const QString &hms) -{ - double deg = fromDMS(hms); - if(std::isnan(deg))return deg; - return deg * 15.0; -} - -double SkyPoint::fromDMS(const QString &dms) -{ - double deg = 0.0; - QString str = dms.trimmed(); - str.remove(QRegularExpression("[hdms°'\"]")); - str.replace(':', ' '); - str.replace(QRegularExpression("\\s+"), " "); - QStringList fields = str.split(' '); - double sign = 1.0; - - bool ok = false; - if(fields.size() >= 1) - deg = fields.at(0).toDouble(&ok); - if(!ok)return NAN; - if(deg < 0.0) - sign = -1.0; - if(fields.size() >= 2) - deg += sign * fields.at(1).toDouble() / 60.0; - if(fields.size() >= 3) - deg += sign * fields.at(2).toDouble() / 3600.0; - - return deg; -} - -QString SkyPoint::toHMS(double decHour) -{ - double h,m,s,md; - md = std::modf(decHour, &h) * 60.0; - s = std::modf(md, &m) * 60.0; - - return QString("%1h %2m %3s").arg((int)h, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); -} - -QString SkyPoint::toDMS(double deg) -{ - double d,m,s,md; - md = std::modf(deg, &d) * 60.0; - s = std::modf(md, &m) * 60.0; - - return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); -} - -SkyPointScale ImageInfoData::getCenterRaDec() const -{ - SkyPointScale ret; - if(wcs && wcs->valid()) - { - ret = wcs->getRaDecScale(); - } - else - { - double ra,dec,focalLen,scale,pixSizeX,pixSizeY; - int binX = 1; - int binY = 1; - ra = dec = focalLen = scale = pixSizeX = pixSizeY = NAN; - bool ok; - for(const FITSRecord &header : fitsHeader) - { - if(header.key == "OBJCTRA") - { - double tmp = SkyPoint::fromHMS(header.value.toString()); - if(!std::isnan(tmp))ra = tmp; - } - else if(header.key == "RA" && std::isnan(ra)) - { - double tmp = header.value.toDouble(&ok); - if(ok)ra = tmp; - } - else if(header.key == "OBJCTDEC") - { - double tmp = SkyPoint::fromDMS(header.value.toString()); - if(!std::isnan(tmp))dec = tmp; - } - else if(header.key == "DEC" && std::isnan(dec)) - { - double tmp = SkyPoint::fromDMS(header.value.toString()); - if(!std::isnan(tmp))dec = tmp; - } - else if(header.key == "SCALE") - { - double tmp = header.value.toDouble(&ok); - if(ok)scale = tmp; - } - else if(header.key == "FOCALLEN") - { - double tmp = header.value.toDouble(&ok); - if(ok)focalLen = tmp; - } - else if(header.key == "PIXSIZE1" || header.key == "XPIXSZ") - { - pixSizeX = header.value.toDouble(); - } - else if(header.key == "PIXSIZE2" || header.key == "YPIXSZ") - { - pixSizeY = header.value.toDouble(); - } - else if(header.key == "XBINNING") - { - int tmp = header.value.toInt(&ok); - if(ok)binX = tmp; - } - else if(header.key == "YBINNING") - { - int tmp = header.value.toInt(&ok); - if(ok)binY = tmp; - } - } - - ret.point.set(ra, dec); - if(!std::isnan(scale)) - { - ret.scaleLow = ret.scaleHigh = scale; - ret.scaleValid = true; - } - else if(!(std::isnan(focalLen) || std::isnan(pixSizeX) || std::isnan(pixSizeY))) - { - const double r = 206.2648097656; // (180 * 3600) / (1000 * pi) magic number to convert pixel size to focal length ratio to arcsec. - ret.scaleLow = std::min(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); - ret.scaleHigh = std::max(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); - ret.scaleValid = true; - } - } - - if(ret.scaleValid) - { - ret.scaleLow *= 0.8; - ret.scaleHigh *= 1.2; - } - return ret; -} diff --git a/imageinfo.h b/imageinfo.h index 7d1ac36..5db51db 100644 --- a/imageinfo.h +++ b/imageinfo.h @@ -2,88 +2,7 @@ #define IMAGEINFO_H #include -#include -#include -#include - -namespace LibXISF { struct FITSKeyword; struct Property; } - -struct FITSRecord -{ - QByteArray key; - QVariant value; - QByteArray comment; - bool xisf = false; - bool editable() const; - FITSRecord(){} - FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment); - FITSRecord(const LibXISF::FITSKeyword &record); - FITSRecord(const LibXISF::Property &property); -}; - -class SkyPoint -{ - double ra = NAN; - double dec = NAN; -public: - SkyPoint(); - SkyPoint(double ra, double dec); - void set(double ra, double dec); - double RA() const { return ra; } - double RAHour() const { return ra / 15.0; } - double DEC() const { return dec; } - QString toString() const; - static double fromHMS(const QString &hms); - static double fromDMS(const QString &dms); - static QString toHMS(double decHour); - static QString toDMS(double deg); -}; - -struct SkyPointScale -{ - SkyPoint point; - //arcsec per pixel - bool scaleValid = false; - double scaleLow = 0.0; - double scaleHigh = 10000.0; -}; - -class WCSDataT -{ - int nwcs = 0; - struct wcsprm *wcs = nullptr; - int width; - int height; - void freeWCS(); -public: - WCSDataT(int width, int height, char *header, int nrec); - WCSDataT(int width, int height, const QVector &header); - WCSDataT(const WCSDataT &) = delete; - ~WCSDataT(); - bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const; - bool worldToPixel(const SkyPoint &point, QPointF &pixel) const; - void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const; - bool valid() const { return wcs; }; - SkyPointScale getRaDecScale() const; -}; - -struct ImageInfoData -{ - QVector fitsHeader; - QVector> info; - std::shared_ptr wcs; - SkyPointScale getCenterRaDec() const; -}; - -Q_DECLARE_METATYPE(ImageInfoData); - -typedef enum -{ - None, - Statistics, - Peaks, - Stars, -}AnalyzeLevel; +#include "imageinfodata.h" class ImageInfo : public QTreeWidget { diff --git a/imageinfodata.cpp b/imageinfodata.cpp new file mode 100644 index 0000000..68a7654 --- /dev/null +++ b/imageinfodata.cpp @@ -0,0 +1,405 @@ +#include "imageinfodata.h" +#include +#include +#include +#include +#include +#include "libxisf.h" + +static const QVector noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"}; + +bool FITSRecord::editable() const +{ + return noEditableKey.count(key); +} + +FITSRecord::FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment) : + key(key), value(value), comment(comment) +{ +} + +FITSRecord::FITSRecord(const LibXISF::FITSKeyword &record) +{ + key = record.name.c_str(); + comment = record.comment.c_str(); + + QString string = record.value.c_str(); + if(string.startsWith('\'') && string.endsWith('\'')) + { + string.chop(1); + string.remove(0, 1); + } + bool isint; + bool isdouble; + double vald = string.toDouble(&isdouble); + long long vall = string.toLongLong(&isint); + if(isint) + value = vall; + else if(isdouble) + value = vald; + else if(string == "T" || string == "F") + value = string == "T"; + else + value = string; +} + +FITSRecord::FITSRecord(const LibXISF::Property &property) +{ + key = property.id.c_str(); + value = QString::fromStdString(property.value.toString()); + comment = property.comment.c_str(); + xisf = true; +} + +void WCSDataT::freeWCS() +{ + wcsvfree(&nwcs, &wcs); + nwcs = 0; + wcs = nullptr; +} + +WCSDataT::WCSDataT(int width, int height, char *header, int nrec) : + width(width), + height(height) +{ + int nreject = 0; + int status = wcspih(header, nrec, 1, 0, &nreject, &nwcs, &wcs); + if(status != 0) + { + freeWCS(); + return; + } + status = cdfix(wcs); + if(status > 0 || wcs->crpix[0] == 0) + freeWCS(); +} + +WCSDataT::WCSDataT(int width, int height, const QVector &header) : + width(width), + height(height) +{ + int status = 0; + + QByteArray str; + int nrec = 1; + for(const FITSRecord &record : header) + { + if(record.key.startsWith("PV"))continue; + + QByteArray rec; + rec.append(record.key.leftJustified(8, ' ')); + rec.append("= "); + rec.append(record.value.toString().toLatin1()); + rec.append(" / "); + rec.append(record.comment); + str.append(rec.leftJustified(80, ' ', true)); + nrec++; + } + str.append(QByteArray("END").leftJustified(80)); + + int nreject = 0; + status = wcspih(str.data(), nrec, 1, 0, &nreject, &nwcs, &wcs); + if(status != 0) + { + freeWCS(); + return; + } + status = cdfix(wcs); + if(status > 0 || wcs->crpix[0] == 0) + freeWCS(); +} + +WCSDataT::~WCSDataT() +{ + if(wcs) + freeWCS(); +} + +bool WCSDataT::pixelToWorld(const QPointF &pixel, SkyPoint &point) const +{ + if(!valid())return false; + + double pixcrd[2] = {pixel.x(), pixel.y()}; + double imgcrd[8] = {0}; + double phi = 0; + double theta = 0; + double world[8] = {0}; + int stat[NWCSFIX] = {0}; + int status = wcsp2s(wcs, 1, 2, pixcrd, imgcrd, &phi, &theta, world, stat); + if(status == 0) + { + point = SkyPoint(world[0], world[1]); + return true; + } + return false; +} + +bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const +{ + if(!valid())return false; + + double world[2] = {point.RA(), point.DEC()}; + double phi = 0; + double theta = 0; + double imgcrd[8] = {0}; + double pixcrd[8] = {0}; + int stat[NWCSFIX] = {0}; + int status = wcss2p(wcs, 1, 2, world, &phi, &theta, imgcrd, pixcrd, stat); + if(status == 0) + { + pixel = QPointF(pixcrd[0], pixcrd[1]); + return true; + } + return false; +} + +void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const +{ + if(wcs == nullptr)return; + + minRa = 1000; + maxRa = -1000; + minDec = 1000; + maxDec = -1000; + + if(wcs->crval) + { + crVal1 = wcs->crval[0]; + crVal2 = wcs->crval[1]; + } + else + { + crVal1 = crVal2 = NAN; + } + + auto update = [&](const QPointF &pixel) + { + SkyPoint point; + pixelToWorld(pixel, point); + minRa = std::min(minRa, point.RA()); + maxRa = std::max(maxRa, point.RA()); + minDec = std::min(minDec, point.DEC()); + maxDec = std::max(maxDec, point.DEC()); + }; + + for(int x=0; xra = ra; + this->dec = dec; +} + +QString SkyPoint::toString() const +{ + if(std::isnan(ra) || std::isnan(dec)) + return QString(); + + QTime t(0, 0); + t = t.addSecs(ra * 240); + + double deg, min, sec; + min = std::abs(std::modf(dec, °) * 60); + sec = std::modf(min, &min) * 60; + return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0'); +} + +double SkyPoint::fromHMS(const QString &hms) +{ + double deg = fromDMS(hms); + if(std::isnan(deg))return deg; + return deg * 15.0; +} + +double SkyPoint::fromDMS(const QString &dms) +{ + double deg = 0.0; + QString str = dms.trimmed(); + str.remove(QRegularExpression("[hdms°'\"]")); + str.replace(':', ' '); + str.replace(QRegularExpression("\\s+"), " "); + QStringList fields = str.split(' '); + double sign = 1.0; + + bool ok = false; + if(fields.size() >= 1) + deg = fields.at(0).toDouble(&ok); + if(!ok)return NAN; + if(deg < 0.0) + sign = -1.0; + if(fields.size() >= 2) + deg += sign * fields.at(1).toDouble() / 60.0; + if(fields.size() >= 3) + deg += sign * fields.at(2).toDouble() / 3600.0; + + return deg; +} + +QString SkyPoint::toHMS(double decHour) +{ + double h,m,s,md; + md = std::modf(decHour, &h) * 60.0; + s = std::modf(md, &m) * 60.0; + + return QString("%1h %2m %3s").arg((int)h, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); +} + +QString SkyPoint::toDMS(double deg) +{ + double d,m,s,md; + md = std::modf(deg, &d) * 60.0; + s = std::modf(md, &m) * 60.0; + + return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); +} + +SkyPointScale ImageInfoData::getCenterRaDec() const +{ + SkyPointScale ret; + if(wcs && wcs->valid()) + { + ret = wcs->getRaDecScale(); + } + else + { + double ra,dec,focalLen,scale,pixSizeX,pixSizeY; + int binX = 1; + int binY = 1; + ra = dec = focalLen = scale = pixSizeX = pixSizeY = NAN; + bool ok; + for(const FITSRecord &header : fitsHeader) + { + if(header.key == "OBJCTRA") + { + double tmp = SkyPoint::fromHMS(header.value.toString()); + if(!std::isnan(tmp))ra = tmp; + } + else if(header.key == "RA" && std::isnan(ra)) + { + double tmp = header.value.toDouble(&ok); + if(ok)ra = tmp; + } + else if(header.key == "OBJCTDEC") + { + double tmp = SkyPoint::fromDMS(header.value.toString()); + if(!std::isnan(tmp))dec = tmp; + } + else if(header.key == "DEC" && std::isnan(dec)) + { + double tmp = SkyPoint::fromDMS(header.value.toString()); + if(!std::isnan(tmp))dec = tmp; + } + else if(header.key == "SCALE") + { + double tmp = header.value.toDouble(&ok); + if(ok)scale = tmp; + } + else if(header.key == "FOCALLEN") + { + double tmp = header.value.toDouble(&ok); + if(ok)focalLen = tmp; + } + else if(header.key == "PIXSIZE1" || header.key == "XPIXSZ") + { + pixSizeX = header.value.toDouble(); + } + else if(header.key == "PIXSIZE2" || header.key == "YPIXSZ") + { + pixSizeY = header.value.toDouble(); + } + else if(header.key == "XBINNING") + { + int tmp = header.value.toInt(&ok); + if(ok)binX = tmp; + } + else if(header.key == "YBINNING") + { + int tmp = header.value.toInt(&ok); + if(ok)binY = tmp; + } + } + + ret.point.set(ra, dec); + if(!std::isnan(scale)) + { + ret.scaleLow = ret.scaleHigh = scale; + ret.scaleValid = true; + } + else if(!(std::isnan(focalLen) || std::isnan(pixSizeX) || std::isnan(pixSizeY))) + { + const double r = 206.2648097656; // (180 * 3600) / (1000 * pi) magic number to convert pixel size to focal length ratio to arcsec. + ret.scaleLow = std::min(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); + ret.scaleHigh = std::max(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r); + ret.scaleValid = true; + } + } + + if(ret.scaleValid) + { + ret.scaleLow *= 0.8; + ret.scaleHigh *= 1.2; + } + return ret; +} diff --git a/imageinfodata.h b/imageinfodata.h new file mode 100644 index 0000000..99d2469 --- /dev/null +++ b/imageinfodata.h @@ -0,0 +1,91 @@ +#ifndef IMAGEINFODATA_H +#define IMAGEINFODATA_H + +#include +#include +#include +#include +#include +#include +#include + +namespace LibXISF { struct FITSKeyword; struct Property; } + +struct FITSRecord +{ + QByteArray key; + QVariant value; + QByteArray comment; + bool xisf = false; + bool editable() const; + FITSRecord(){} + FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment); + FITSRecord(const LibXISF::FITSKeyword &record); + FITSRecord(const LibXISF::Property &property); +}; + +class SkyPoint +{ + double ra = NAN; + double dec = NAN; +public: + SkyPoint(); + SkyPoint(double ra, double dec); + void set(double ra, double dec); + double RA() const { return ra; } + double RAHour() const { return ra / 15.0; } + double DEC() const { return dec; } + QString toString() const; + static double fromHMS(const QString &hms); + static double fromDMS(const QString &dms); + static QString toHMS(double decHour); + static QString toDMS(double deg); +}; + +struct SkyPointScale +{ + SkyPoint point; + //arcsec per pixel + bool scaleValid = false; + double scaleLow = 0.0; + double scaleHigh = 10000.0; +}; + +class WCSDataT +{ + int nwcs = 0; + struct wcsprm *wcs = nullptr; + int width; + int height; + void freeWCS(); +public: + WCSDataT(int width, int height, char *header, int nrec); + WCSDataT(int width, int height, const QVector &header); + WCSDataT(const WCSDataT &) = delete; + ~WCSDataT(); + bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const; + bool worldToPixel(const SkyPoint &point, QPointF &pixel) const; + void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const; + bool valid() const { return wcs; }; + SkyPointScale getRaDecScale() const; +}; + +struct ImageInfoData +{ + QVector fitsHeader; + QVector> info; + std::shared_ptr wcs; + SkyPointScale getCenterRaDec() const; +}; + +typedef enum +{ + None, + Statistics, + Peaks, + Stars, +}AnalyzeLevel; + +Q_DECLARE_METATYPE(ImageInfoData); + +#endif // IMAGEINFODATA_H diff --git a/imageringlist.cpp b/imageringlist.cpp index cbc70f2..e36b193 100644 --- a/imageringlist.cpp +++ b/imageringlist.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "loadrunable.h" #include "rawimage.h" #include "database.h" diff --git a/imageringlist.h b/imageringlist.h index b835fe5..229ce6e 100644 --- a/imageringlist.h +++ b/imageringlist.h @@ -7,8 +7,9 @@ #include #include #include -#include "imageinfo.h" +#include "imageinfodata.h" #include "rawimage.h" +#include class ImageRingList; class QThreadPool; diff --git a/loadimage.cpp b/loadimage.cpp new file mode 100644 index 0000000..c844ceb --- /dev/null +++ b/loadimage.cpp @@ -0,0 +1,409 @@ +#include "loadimage.h" +#include +#include +#include +#include +#include +#include +#include "libxisf.h" +#include +#include "rawimage.h" + +QString makeMaxPath(QString path) +{ +#ifdef Q_OS_WIN64 + if(!path.startsWith("\\\\?\\")) + { + QFileInfo info(path); + path = info.absoluteFilePath(); + path = QDir::toNativeSeparators(path); + path.prepend("\\\\?\\"); + qDebug() << path; + } +#endif + return path; +} + +int loadFITSHeader(fitsfile *file, ImageInfoData &info) +{ + int imgtype; + int naxis; + long naxes[3] = {0}; + int nexist; + int status = 0; + char key[FLEN_KEYWORD]; + char val[FLEN_VALUE]; + char comm[FLEN_COMMENT]; + char strval[FLEN_VALUE]; + QVariant var; + fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status); + fits_get_hdrspace(file, &nexist, nullptr, &status); + for(int i=1; i<=nexist; i++) + { + fits_read_keyn(file, i, key, val, comm, &status); + fits_read_key(file, TSTRING, key, strval, nullptr, &status); + if(status == 0 || status == VALUE_UNDEFINED) + { + QString string(strval); + bool isint; + bool isdouble; + double vald = string.toDouble(&isdouble); + long long vall = string.toLongLong(&isint); + if(isint) + var = vall; + else if(isdouble) + var = vald; + else if(status == VALUE_UNDEFINED) + var = QVariant(); + else if(string == "T" || string == "F") + var = string == "T"; + else + var = string; + status = 0; + info.fitsHeader.append(FITSRecord(key, var, comm)); + } + else + { + return status; + } + } + + char *header = nullptr; + int nrec = 0; + const char *exclist[] = {"PV1_1", "PV1_2"}; + fits_hdr2str(file, TRUE, (char**)exclist, 2, &header, &nrec, &status); + if(status == 0) + { + info.wcs = std::make_shared(naxes[0], naxes[1], header, nrec); + if(!info.wcs->valid())info.wcs.reset(); + } + fits_free_memory(header, &status); + return status; +} + +bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr &image, bool planar) +{ + fitsfile *file; + int status = 0; + int type = -1; + fits_open_diskfile(&file, path.toLocal8Bit().data(), READONLY, &status); + int num = 0; + fits_get_num_hdus(file, &num, &status); + + int imgtype; + int naxis; + long naxes[3] = {0}; + for(int i=1; i <= num; i++) + { + fits_movabs_hdu(file, i, IMAGE_HDU, &status); + fits_get_hdu_type(file, &type, &status); + fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status); + fits_get_img_equivtype(file, &imgtype, &status); + + if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0) + { + RawImage::DataType type; + int fitstype; + long fpixel[3] = {1,1,1}; + switch(imgtype) + { + case BYTE_IMG: + type = RawImage::UINT8; + fitstype = TBYTE; + break; + case SHORT_IMG: + type = RawImage::UINT16; + fitstype = TSHORT; + break; + case USHORT_IMG: + type = RawImage::UINT16; + fitstype = TUSHORT; + break; + case ULONG_IMG: + type = RawImage::UINT32; + fitstype = TUINT; + break; + case FLOAT_IMG: + type = RawImage::FLOAT32; + fitstype = TFLOAT; + break; + case DOUBLE_IMG: + type = RawImage::FLOAT64; + fitstype = TDOUBLE; + break; + default: + info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")}); + goto noload; + break; + } + + size_t size = naxes[0]*naxes[1]; + size_t w = naxes[0]; + size_t h = naxes[1]; + + info.info.append({QObject::tr("Width"), QString::number(naxes[0])}); + info.info.append({QObject::tr("Height"), QString::number(naxes[1])}); + + RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type); + uint8_t *data = static_cast(img.data()); + for (int i=1; i==1 || i<=naxes[2]; i++) + { + fpixel[2] = i; + fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status); + } + if(fitstype == TSHORT) + { + uint16_t *s = static_cast(img.data()); + size_t size = img.size() * img.channels(); + for(size_t i=0; i(std::move(img)); + else + image = RawImage::fromPlanar(img); + + break; + } + } +noload: + if(file) + loadFITSHeader(file, info); + + if(image) + { + for(auto fits : info.fitsHeader) + { + if(fits.key == "ROWORDER" && fits.value == "BOTTOM-UP") + image->flip(); + } + } + + fits_close_file(file, &status); + if(status) + { + char err[100]; + fits_get_errstatus(status, err); + info.info.append({QObject::tr("Error"), QString(err)}); + qDebug() << "Failed to load FITS file" << err; + } + + return true; +} + +bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr &image, bool planar) +{ + try + { + LibXISF::XISFReader xisf; + xisf.open(path.toLocal8Bit().data()); + + const LibXISF::Image &xisfImage = xisf.getImage(0); + + auto fitskeywords = xisfImage.fitsKeywords(); + for(auto fits : fitskeywords) + { + info.fitsHeader.append(fits); + } + auto imageproperties = xisfImage.imageProperties(); + for(auto prop : imageproperties) + { + info.fitsHeader.append(prop); + } + + info.wcs = std::make_shared(xisfImage.width(), xisfImage.height(), info.fitsHeader); + info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())}); + info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())}); + if(!info.wcs->valid())info.wcs.reset(); + + RawImage::DataType type; + switch(xisfImage.sampleFormat()) + { + case LibXISF::Image::UInt8: type = RawImage::UINT8; break; + case LibXISF::Image::UInt16: type = RawImage::UINT16; break; + case LibXISF::Image::UInt32: type = RawImage::UINT32; break; + case LibXISF::Image::Float32: type = RawImage::FLOAT32; break; + case LibXISF::Image::Float64: type = RawImage::FLOAT64; break; + default: break; + } + + LibXISF::Image tmpImage = xisfImage; + tmpImage.convertPixelStorageTo(LibXISF::Image::Planar); + if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray) + { + image = std::make_shared(tmpImage.width(), tmpImage.height(), 1, type); + std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount()); + image->setICCProfile(tmpImage.iccProfile()); + return true; + } + else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4) + { + if(planar) + { + image = std::make_shared(tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); + std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize()); + } + else + { + image = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); + } + + image->setICCProfile(tmpImage.iccProfile()); + return true; + } + return false; + } + catch (LibXISF::Error &err) + { + info.info.append(QPair("Error", err.what())); + qDebug() << "Failed to load XISF" << err.what(); + return false; + } + info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")}); + return false; +} + +bool readFITSHeader(const QString &path, ImageInfoData &info) +{ + fitsfile *fr; + int status = 0; + QString path2 = makeMaxPath(path); + fits_open_diskfile(&fr, path2.toLocal8Bit().data(), READONLY, &status); + + if(fr && status == 0) + { + status = loadFITSHeader(fr, info); + fits_close_file(fr, &status); + } + return status == 0; +} + +bool readXISFHeader(const QString &path, ImageInfoData &info) +{ + QString path2 = makeMaxPath(path); + try + { + LibXISF::XISFReader xisf; + xisf.open(path2.toLocal8Bit().data()); + const LibXISF::Image &image = xisf.getImage(0, false); + + auto fitskeywords = image.fitsKeywords(); + for(auto fits : fitskeywords) + { + info.fitsHeader.append(fits); + } + + auto imageproperties = image.imageProperties(); + for(auto prop : imageproperties) + { + info.fitsHeader.append(prop); + } + info.wcs = std::make_shared(image.width(), image.height(), info.fitsHeader); + if(!info.wcs->valid())info.wcs.reset(); + } + catch (LibXISF::Error &err) + { + qDebug() << err.what(); + return false; + } + return true; +} + +void loadExifEntry(ImageInfoData &info, ExifContent *content, ExifTag tag) +{ + char val[1024]; + ExifEntry *entry = exif_content_get_entry(content, tag); + if(entry) + { + exif_entry_get_value(entry, val, sizeof(val)); + info.info.append({exif_tag_get_title(tag), val}); + } +} + +bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr &image) +{ + std::unique_ptr raw = std::make_unique(); + raw->open_file(path.toLocal8Bit().data()); + raw->imgdata.params.half_size = true; + raw->imgdata.params.use_camera_wb = true; + raw->imgdata.params.user_flip = 0; + if(raw->unpack()) + return false; + + + libraw_rawdata_t rawdata = raw->imgdata.rawdata; + size_t size = rawdata.sizes.width*rawdata.sizes.height; + + std::vector out; + out.resize(size); + size_t d = 0; + uint h=rawdata.sizes.top_margin+rawdata.sizes.height; + uint w=rawdata.sizes.left_margin+rawdata.sizes.width; + size_t pitch = rawdata.sizes.raw_pitch/sizeof(uint16_t); + + for(size_t i=rawdata.sizes.top_margin;i(rawdata.sizes.width, rawdata.sizes.height, 1, RawImage::UINT16); + memcpy(image->data(), &out[0], sizeof(uint16_t)*d); + + QString shutterSpeed = QString::number(raw->imgdata.other.shutter); + if(raw->imgdata.other.shutter < 1) + { + shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter); + } + info.info.append({QObject::tr("Width"), QString::number(raw->imgdata.sizes.width)}); + info.info.append({QObject::tr("Height"), QString::number(raw->imgdata.sizes.height)}); + info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)}); + info.info.append({QObject::tr("Shutter speed"), shutterSpeed}); +#if LIBRAW_MINOR_VERSION>=19 +// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature))); +#endif + return true; +} + +bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr &rawImage, bool planar) +{ + bool ret = false; + QElapsedTimer timer; + timer.start(); + if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive)) + { + ret = loadRAW(path, info, rawImage); + qDebug() << "LoadRAW" << timer.elapsed(); + } + else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive)) + { + ret = loadFITS(path, info, rawImage, planar); + qDebug() << "LoadFITS" << timer.elapsed(); + } + else if(path.endsWith(".XISF", Qt::CaseInsensitive)) + { + ret = loadXISF(path, info, rawImage, planar); + qDebug() << "LoadXISF" << timer.elapsed(); + } + else + { + QImage img(path); + + ExifData *exif = exif_data_new_from_file(path.toLocal8Bit().constData()); + info.info.append({QObject::tr("Width"), QString::number(img.width())}); + info.info.append({QObject::tr("Height"), QString::number(img.height())}); + if(exif) + { + loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_ISO_SPEED_RATINGS); + loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_SHUTTER_SPEED_VALUE); + exif_data_free(exif); + } + rawImage = std::make_shared(img); + qDebug() << "LoadQImage" << timer.elapsed(); + ret = !img.isNull(); + } + return ret; +} diff --git a/loadimage.h b/loadimage.h new file mode 100644 index 0000000..df8146e --- /dev/null +++ b/loadimage.h @@ -0,0 +1,14 @@ +#ifndef LOADIMAGE_H +#define LOADIMAGE_H + +#include +#include "imageinfodata.h" + +class RawImage; + +QString makeMaxPath(QString path); +bool readFITSHeader(const QString &path, ImageInfoData &info); +bool readXISFHeader(const QString &path, ImageInfoData &info); +bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr &rawImage, bool planar = false); + +#endif // LOADIMAGE_H diff --git a/loadrunable.cpp b/loadrunable.cpp index 50df37e..26d0122 100644 --- a/loadrunable.cpp +++ b/loadrunable.cpp @@ -1,35 +1,15 @@ #include "loadrunable.h" #include "imageringlist.h" -#include -#include "imageinfo.h" #include #include #include #include -#include #include -#include #include -#include #include "rawimage.h" -#include "starfit.h" +#include "loadimage.h" #include -QString makeMaxPath(QString path) -{ -#ifdef Q_OS_WIN64 - if(!path.startsWith("\\\\?\\")) - { - QFileInfo info(path); - path = info.absoluteFilePath(); - path = QDir::toNativeSeparators(path); - path.prepend("\\\\?\\"); - qDebug() << path; - } -#endif - return path; -} - LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) : m_file(makeMaxPath(file)), m_receiver(receiver), @@ -38,354 +18,11 @@ LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel leve { } -void loadExifEntry(ImageInfoData &info, ExifContent *content, ExifTag tag) -{ - char val[1024]; - ExifEntry *entry = exif_content_get_entry(content, tag); - if(entry) - { - exif_entry_get_value(entry, val, sizeof(val)); - info.info.append({exif_tag_get_title(tag), val}); - } -} - -void drawPeaks(QImage &img, const std::vector &peaks) -{ - QPixmap pix = QPixmap::fromImage(img); - QPainter painter(&pix); - painter.setPen(Qt::red); - for(auto peak : peaks) - { - painter.drawEllipse(QPoint(peak.x(), peak.y()), 5, 5); - } - img = pix.toImage(); -} - -void drawStars(QImage &img, const std::vector &stars) -{ - QPixmap pix = QPixmap::fromImage(img); - QPainter painter(&pix); - painter.setPen(Qt::red); - for(auto star : stars) - { - painter.drawEllipse(QPointF(star.m_x, star.m_y), star.hw20X(), star.hw20Y()); - } - img = pix.toImage(); -} - -void printStarModel(int radius, const std::vector &data, const Star &star) -{ - QString d = "d=["; - QString m = "m=["; - for(int y=0; y &image) -{ - std::unique_ptr raw = std::make_unique(); - raw->open_file(path.toLocal8Bit().data()); - raw->imgdata.params.half_size = true; - raw->imgdata.params.use_camera_wb = true; - raw->imgdata.params.user_flip = 0; - if(raw->unpack()) - return false; - - - libraw_rawdata_t rawdata = raw->imgdata.rawdata; - size_t size = rawdata.sizes.width*rawdata.sizes.height; - - std::vector out; - out.resize(size); - size_t d = 0; - uint h=rawdata.sizes.top_margin+rawdata.sizes.height; - uint w=rawdata.sizes.left_margin+rawdata.sizes.width; - size_t pitch = rawdata.sizes.raw_pitch/sizeof(uint16_t); - - for(size_t i=rawdata.sizes.top_margin;i(rawdata.sizes.width, rawdata.sizes.height, 1, RawImage::UINT16); - memcpy(image->data(), &out[0], sizeof(uint16_t)*d); - - QString shutterSpeed = QString::number(raw->imgdata.other.shutter); - if(raw->imgdata.other.shutter < 1) - { - shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter); - } - info.info.append({QObject::tr("Width"), QString::number(raw->imgdata.sizes.width)}); - info.info.append({QObject::tr("Height"), QString::number(raw->imgdata.sizes.height)}); - info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)}); - info.info.append({QObject::tr("Shutter speed"), shutterSpeed}); -#if LIBRAW_MINOR_VERSION>=19 -// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature))); -#endif - return true; -} - -int loadFITSHeader(fitsfile *file, ImageInfoData &info) -{ - int imgtype; - int naxis; - long naxes[3] = {0}; - int nexist; - int status = 0; - char key[FLEN_KEYWORD]; - char val[FLEN_VALUE]; - char comm[FLEN_COMMENT]; - char strval[FLEN_VALUE]; - QVariant var; - fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status); - fits_get_hdrspace(file, &nexist, nullptr, &status); - for(int i=1; i<=nexist; i++) - { - fits_read_keyn(file, i, key, val, comm, &status); - fits_read_key(file, TSTRING, key, strval, nullptr, &status); - if(status == 0 || status == VALUE_UNDEFINED) - { - QString string(strval); - bool isint; - bool isdouble; - double vald = string.toDouble(&isdouble); - long long vall = string.toLongLong(&isint); - if(isint) - var = vall; - else if(isdouble) - var = vald; - else if(status == VALUE_UNDEFINED) - var = QVariant(); - else if(string == "T" || string == "F") - var = string == "T"; - else - var = string; - status = 0; - info.fitsHeader.append(FITSRecord(key, var, comm)); - } - else - { - return status; - } - } - - char *header = nullptr; - int nrec = 0; - const char *exclist[] = {"PV1_1", "PV1_2"}; - fits_hdr2str(file, TRUE, (char**)exclist, 2, &header, &nrec, &status); - if(status == 0) - { - info.wcs = std::make_shared(naxes[0], naxes[1], header, nrec); - if(!info.wcs->valid())info.wcs.reset(); - } - fits_free_memory(header, &status); - return status; -} - -bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr &image, bool planar) -{ - fitsfile *file; - int status = 0; - int type = -1; - fits_open_diskfile(&file, path.toLocal8Bit().data(), READONLY, &status); - int num = 0; - fits_get_num_hdus(file, &num, &status); - - int imgtype; - int naxis; - long naxes[3] = {0}; - for(int i=1; i <= num; i++) - { - fits_movabs_hdu(file, i, IMAGE_HDU, &status); - fits_get_hdu_type(file, &type, &status); - fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status); - fits_get_img_equivtype(file, &imgtype, &status); - - if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0) - { - RawImage::DataType type; - int fitstype; - long fpixel[3] = {1,1,1}; - switch(imgtype) - { - case BYTE_IMG: - type = RawImage::UINT8; - fitstype = TBYTE; - break; - case SHORT_IMG: - type = RawImage::UINT16; - fitstype = TSHORT; - break; - case USHORT_IMG: - type = RawImage::UINT16; - fitstype = TUSHORT; - break; - case ULONG_IMG: - type = RawImage::UINT32; - fitstype = TUINT; - break; - case FLOAT_IMG: - type = RawImage::FLOAT32; - fitstype = TFLOAT; - break; - case DOUBLE_IMG: - type = RawImage::FLOAT64; - fitstype = TDOUBLE; - break; - default: - info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")}); - goto noload; - break; - } - - size_t size = naxes[0]*naxes[1]; - size_t w = naxes[0]; - size_t h = naxes[1]; - - info.info.append({QObject::tr("Width"), QString::number(naxes[0])}); - info.info.append({QObject::tr("Height"), QString::number(naxes[1])}); - - RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type); - uint8_t *data = static_cast(img.data()); - for (int i=1; i==1 || i<=naxes[2]; i++) - { - fpixel[2] = i; - fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status); - } - if(fitstype == TSHORT) - { - uint16_t *s = static_cast(img.data()); - size_t size = img.size() * img.channels(); - for(size_t i=0; i(std::move(img)); - else - image = RawImage::fromPlanar(img); - - break; - } - } - noload: - if(file) - loadFITSHeader(file, info); - - if(image) - { - for(auto fits : info.fitsHeader) - { - if(fits.key == "ROWORDER" && fits.value == "BOTTOM-UP") - image->flip(); - } - } - - fits_close_file(file, &status); - if(status) - { - char err[100]; - fits_get_errstatus(status, err); - info.info.append({QObject::tr("Error"), QString(err)}); - qDebug() << "Failed to load FITS file" << err; - } - - return true; -} - -bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr &image, bool planar) -{ - try - { - LibXISF::XISFReader xisf; - xisf.open(path.toLocal8Bit().data()); - - const LibXISF::Image &xisfImage = xisf.getImage(0); - - auto fitskeywords = xisfImage.fitsKeywords(); - for(auto fits : fitskeywords) - { - info.fitsHeader.append(fits); - } - auto imageproperties = xisfImage.imageProperties(); - for(auto prop : imageproperties) - { - info.fitsHeader.append(prop); - } - - info.wcs = std::make_shared(xisfImage.width(), xisfImage.height(), info.fitsHeader); - info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())}); - info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())}); - if(!info.wcs->valid())info.wcs.reset(); - - RawImage::DataType type; - switch(xisfImage.sampleFormat()) - { - case LibXISF::Image::UInt8: type = RawImage::UINT8; break; - case LibXISF::Image::UInt16: type = RawImage::UINT16; break; - case LibXISF::Image::UInt32: type = RawImage::UINT32; break; - case LibXISF::Image::Float32: type = RawImage::FLOAT32; break; - case LibXISF::Image::Float64: type = RawImage::FLOAT64; break; - default: break; - } - - LibXISF::Image tmpImage = xisfImage; - tmpImage.convertPixelStorageTo(LibXISF::Image::Planar); - if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray) - { - image = std::make_shared(tmpImage.width(), tmpImage.height(), 1, type); - std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount()); - image->setICCProfile(tmpImage.iccProfile()); - return true; - } - else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4) - { - if(planar) - { - image = std::make_shared(tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); - std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize()); - } - else - { - image = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); - } - - image->setICCProfile(tmpImage.iccProfile()); - return true; - } - return false; - } - catch (LibXISF::Error &err) - { - info.info.append(QPair("Error", err.what())); - qDebug() << "Failed to load XISF" << err.what(); - return false; - } - info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")}); - return false; -} - void LoadRunable::run() { try { - if(!m_thumbnail && !m_receiver->isCurrent()) + if(!m_thumbnail /*&& !m_receiver->isCurrent()*/) { return; } @@ -462,92 +99,6 @@ void LoadRunable::run() } } -bool readFITSHeader(const QString &path, ImageInfoData &info) -{ - fitsfile *fr; - int status = 0; - QString path2 = makeMaxPath(path); - fits_open_diskfile(&fr, path2.toLocal8Bit().data(), READONLY, &status); - - if(fr && status == 0) - { - status = loadFITSHeader(fr, info); - fits_close_file(fr, &status); - } - return status == 0; -} - -bool readXISFHeader(const QString &path, ImageInfoData &info) -{ - QString path2 = makeMaxPath(path); - try - { - LibXISF::XISFReader xisf; - xisf.open(path2.toLocal8Bit().data()); - const LibXISF::Image &image = xisf.getImage(0, false); - - auto fitskeywords = image.fitsKeywords(); - for(auto fits : fitskeywords) - { - info.fitsHeader.append(fits); - } - - auto imageproperties = image.imageProperties(); - for(auto prop : imageproperties) - { - info.fitsHeader.append(prop); - } - info.wcs = std::make_shared(image.width(), image.height(), info.fitsHeader); - if(!info.wcs->valid())info.wcs.reset(); - } - catch (LibXISF::Error &err) - { - qDebug() << err.what(); - return false; - } - return true; -} - -bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr &rawImage, bool planar) -{ - bool ret = false; - QElapsedTimer timer; - timer.start(); - if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive)) - { - ret = loadRAW(path, info, rawImage); - qDebug() << "LoadRAW" << timer.elapsed(); - } - else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive)) - { - ret = loadFITS(path, info, rawImage, planar); - qDebug() << "LoadFITS" << timer.elapsed(); - } - else if(path.endsWith(".XISF", Qt::CaseInsensitive)) - { - ret = loadXISF(path, info, rawImage, planar); - qDebug() << "LoadXISF" << timer.elapsed(); - } - else - { - QImage img(path); - - ExifData *exif = exif_data_new_from_file(path.toLocal8Bit().constData()); - info.info.append({QObject::tr("Width"), QString::number(img.width())}); - info.info.append({QObject::tr("Height"), QString::number(img.height())}); - if(exif) - { - loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_ISO_SPEED_RATINGS); - loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_SHUTTER_SPEED_VALUE); - exif_data_free(exif); - } - rawImage = std::make_shared(img); - qDebug() << "LoadQImage" << timer.elapsed(); - ret = !img.isNull(); - } - return ret; -} - ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format, const ConvertParams ¶ms, QSemaphore *semaphore) : m_infile(makeMaxPath(in)), m_outfile(makeMaxPath(out)), diff --git a/loadrunable.h b/loadrunable.h index 66dda51..ba8d3b0 100644 --- a/loadrunable.h +++ b/loadrunable.h @@ -4,14 +4,7 @@ #include #include #include -#include "imageinfo.h" - -class RawImage; - -QString makeMaxPath(QString path); -bool readFITSHeader(const QString &path, ImageInfoData &info); -bool readXISFHeader(const QString &path, ImageInfoData &info); -bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr &rawImage, bool planar = false); +#include "imageinfodata.h" class Image; @@ -26,7 +19,6 @@ public: void run() override; }; - class ConvertRunable : public QRunnable { public: diff --git a/rawimage_sse.cpp b/rawimage_sse.cpp index 3f0fe84..0172fc3 100644 --- a/rawimage_sse.cpp +++ b/rawimage_sse.cpp @@ -1,7 +1,7 @@ -#include "rawimage.h" - #ifdef __SSE2__ #include +#include +#include template void fromPlanarSSE(const void *in, void *out, size_t count) diff --git a/scriptengine.cpp b/scriptengine.cpp index f6f1ad8..9ae5516 100644 --- a/scriptengine.cpp +++ b/scriptengine.cpp @@ -6,10 +6,10 @@ #include #include "loadrunable.h" #include "rawimage.h" -#include "loadrunable.h" +#include "loadimage.h" #include "batchprocessing.h" #include -#include "libXISF/libxisf.h" +#include "libxisf.h" #ifdef PLATESOLVER #include "solver.h" #endif // PLATESOLVER diff --git a/solver.cpp b/solver.cpp index dba8dbe..b5125ef 100644 --- a/solver.cpp +++ b/solver.cpp @@ -7,7 +7,7 @@ #include #include #include "rawimage.h" -#include "loadrunable.h" +#include "loadimage.h" #include "scriptengine.h" Solver::Solver(QObject *parent) : QObject(parent) diff --git a/stretchtoolbar.cpp b/stretchtoolbar.cpp index 98fef5b..336e894 100644 --- a/stretchtoolbar.cpp +++ b/stretchtoolbar.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "imageringlist.h" const float BLACK_POINT_SIGMA = -2.8f;