Reorginize code
This commit is contained in:
@@ -39,9 +39,11 @@ set(TENMON_SRC
|
|||||||
histogram.cpp histogram.h
|
histogram.cpp histogram.h
|
||||||
httpdownloader.h httpdownloader.cpp
|
httpdownloader.h httpdownloader.cpp
|
||||||
imageinfo.cpp imageinfo.h
|
imageinfo.cpp imageinfo.h
|
||||||
|
imageinfodata.cpp imageinfodata.h
|
||||||
imageringlist.cpp imageringlist.h
|
imageringlist.cpp imageringlist.h
|
||||||
imagescrollarea.cpp imagescrollarea.h
|
imagescrollarea.cpp imagescrollarea.h
|
||||||
imagewidget.h imagewidget.cpp
|
imagewidget.h imagewidget.cpp
|
||||||
|
loadimage.h loadimage.cpp
|
||||||
loadrunable.cpp loadrunable.h
|
loadrunable.cpp loadrunable.h
|
||||||
main.cpp
|
main.cpp
|
||||||
mainwindow.cpp mainwindow.h
|
mainwindow.cpp mainwindow.h
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include "loadrunable.h"
|
#include "loadimage.h"
|
||||||
|
|
||||||
Database::Database(QObject *parent) : QObject(parent)
|
Database::Database(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
Histogram::Histogram(QWidget *parent) : QWidget(parent)
|
Histogram::Histogram(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
|
|||||||
-402
@@ -1,55 +1,6 @@
|
|||||||
#include "imageinfo.h"
|
#include "imageinfo.h"
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTime>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <wcslib/wcshdr.h>
|
|
||||||
#include <wcslib/wcsfix.h>
|
|
||||||
#include <libxisf.h>
|
|
||||||
|
|
||||||
static const QVector<QByteArray> 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)
|
ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent)
|
||||||
{
|
{
|
||||||
@@ -89,356 +40,3 @@ void ImageInfo::setInfo(const ImageInfoData &info)
|
|||||||
}
|
}
|
||||||
expandAll();
|
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<FITSRecord> &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; x<width; x++)
|
|
||||||
{
|
|
||||||
update(QPointF(x, 0));
|
|
||||||
update(QPointF(x, height - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int y=0; y<height; y++)
|
|
||||||
{
|
|
||||||
update(QPointF(0, y));
|
|
||||||
update(QPointF(width - 1, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF ncp;
|
|
||||||
QPointF scp;
|
|
||||||
QRectF s(0, 0, width - 1, height - 1);
|
|
||||||
if(worldToPixel(SkyPoint(0, 90), ncp))
|
|
||||||
{
|
|
||||||
if(s.contains(ncp))
|
|
||||||
maxDec = 90;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(worldToPixel(SkyPoint(0, -90), scp))
|
|
||||||
{
|
|
||||||
if(s.contains(scp))
|
|
||||||
minDec = -90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double hav(double x)
|
|
||||||
{
|
|
||||||
return (1.0 - std::cos(x)) * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
double haverSine(const SkyPoint &a, SkyPoint &b)
|
|
||||||
{
|
|
||||||
const double ToRAD = M_PI / 180.0;
|
|
||||||
double d = hav((a.DEC() - b.DEC()) * ToRAD) + std::cos(a.DEC() * ToRAD) * std::cos(b.DEC() * ToRAD) * hav((a.RA() - b.RA()) * ToRAD);
|
|
||||||
return std::acos(1.0 - 2.0 * d) * (180.0 / M_PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkyPointScale WCSDataT::getRaDecScale() const
|
|
||||||
{
|
|
||||||
SkyPointScale ret;
|
|
||||||
pixelToWorld(QPointF(width/2.0, height/2.0), ret.point);
|
|
||||||
SkyPoint pointX;
|
|
||||||
SkyPoint pointY;
|
|
||||||
pixelToWorld(QPointF(width/2.0+1, height/2.0), pointX);
|
|
||||||
pixelToWorld(QPointF(width/2.0, height/2.0+1), pointY);
|
|
||||||
double scaleX = haverSine(ret.point, pointX) * 3600.0;
|
|
||||||
double scaleY = haverSine(ret.point, pointY) * 3600.0;
|
|
||||||
ret.scaleLow = std::min(scaleX, scaleY);
|
|
||||||
ret.scaleHigh = std::max(scaleX, scaleY);
|
|
||||||
ret.scaleValid = true;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkyPoint::SkyPoint() : ra(NAN), dec(NAN)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SkyPoint::SkyPoint(double ra, double dec) : ra(ra), dec(dec)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkyPoint::set(double ra, double dec)
|
|
||||||
{
|
|
||||||
this->ra = 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;
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-82
@@ -2,88 +2,7 @@
|
|||||||
#define IMAGEINFO_H
|
#define IMAGEINFO_H
|
||||||
|
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include <wcslib/wcs.h>
|
#include "imageinfodata.h"
|
||||||
#include <cmath>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
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<FITSRecord> &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<FITSRecord> fitsHeader;
|
|
||||||
QVector<QPair<QString, QString>> info;
|
|
||||||
std::shared_ptr<WCSDataT> wcs;
|
|
||||||
SkyPointScale getCenterRaDec() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ImageInfoData);
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Statistics,
|
|
||||||
Peaks,
|
|
||||||
Stars,
|
|
||||||
}AnalyzeLevel;
|
|
||||||
|
|
||||||
class ImageInfo : public QTreeWidget
|
class ImageInfo : public QTreeWidget
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,405 @@
|
|||||||
|
#include "imageinfodata.h"
|
||||||
|
#include <QTime>
|
||||||
|
#include <QRectF>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <wcslib/wcshdr.h>
|
||||||
|
#include <wcslib/wcsfix.h>
|
||||||
|
#include "libxisf.h"
|
||||||
|
|
||||||
|
static const QVector<QByteArray> 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<FITSRecord> &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; x<width; x++)
|
||||||
|
{
|
||||||
|
update(QPointF(x, 0));
|
||||||
|
update(QPointF(x, height - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int y=0; y<height; y++)
|
||||||
|
{
|
||||||
|
update(QPointF(0, y));
|
||||||
|
update(QPointF(width - 1, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF ncp;
|
||||||
|
QPointF scp;
|
||||||
|
QRectF s(0, 0, width - 1, height - 1);
|
||||||
|
if(worldToPixel(SkyPoint(0, 90), ncp))
|
||||||
|
{
|
||||||
|
if(s.contains(ncp))
|
||||||
|
maxDec = 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(worldToPixel(SkyPoint(0, -90), scp))
|
||||||
|
{
|
||||||
|
if(s.contains(scp))
|
||||||
|
minDec = -90;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double hav(double x)
|
||||||
|
{
|
||||||
|
return (1.0 - std::cos(x)) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
double haverSine(const SkyPoint &a, SkyPoint &b)
|
||||||
|
{
|
||||||
|
const double ToRAD = M_PI / 180.0;
|
||||||
|
double d = hav((a.DEC() - b.DEC()) * ToRAD) + std::cos(a.DEC() * ToRAD) * std::cos(b.DEC() * ToRAD) * hav((a.RA() - b.RA()) * ToRAD);
|
||||||
|
return std::acos(1.0 - 2.0 * d) * (180.0 / M_PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyPointScale WCSDataT::getRaDecScale() const
|
||||||
|
{
|
||||||
|
SkyPointScale ret;
|
||||||
|
pixelToWorld(QPointF(width/2.0, height/2.0), ret.point);
|
||||||
|
SkyPoint pointX;
|
||||||
|
SkyPoint pointY;
|
||||||
|
pixelToWorld(QPointF(width/2.0+1, height/2.0), pointX);
|
||||||
|
pixelToWorld(QPointF(width/2.0, height/2.0+1), pointY);
|
||||||
|
double scaleX = haverSine(ret.point, pointX) * 3600.0;
|
||||||
|
double scaleY = haverSine(ret.point, pointY) * 3600.0;
|
||||||
|
ret.scaleLow = std::min(scaleX, scaleY);
|
||||||
|
ret.scaleHigh = std::max(scaleX, scaleY);
|
||||||
|
ret.scaleValid = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyPoint::SkyPoint() : ra(NAN), dec(NAN)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyPoint::SkyPoint(double ra, double dec) : ra(ra), dec(dec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkyPoint::set(double ra, double dec)
|
||||||
|
{
|
||||||
|
this->ra = 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#ifndef IMAGEINFODATA_H
|
||||||
|
#define IMAGEINFODATA_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <wcslib/wcs.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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<FITSRecord> &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<FITSRecord> fitsHeader;
|
||||||
|
QVector<QPair<QString, QString>> info;
|
||||||
|
std::shared_ptr<WCSDataT> wcs;
|
||||||
|
SkyPointScale getCenterRaDec() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Statistics,
|
||||||
|
Peaks,
|
||||||
|
Stars,
|
||||||
|
}AnalyzeLevel;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(ImageInfoData);
|
||||||
|
|
||||||
|
#endif // IMAGEINFODATA_H
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|||||||
+2
-1
@@ -7,8 +7,9 @@
|
|||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "imageinfo.h"
|
#include "imageinfodata.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
class ImageRingList;
|
class ImageRingList;
|
||||||
class QThreadPool;
|
class QThreadPool;
|
||||||
|
|||||||
+409
@@ -0,0 +1,409 @@
|
|||||||
|
#include "loadimage.h"
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
#include <fitsio2.h>
|
||||||
|
#include "libxisf.h"
|
||||||
|
#include <libexif/exif-data.h>
|
||||||
|
#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<WCSDataT>(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<RawImage> &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<uint8_t*>(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<uint16_t*>(img.data());
|
||||||
|
size_t size = img.size() * img.channels();
|
||||||
|
for(size_t i=0; i<size; i++)
|
||||||
|
s[i] -= INT16_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(img.channels() == 1 || planar)
|
||||||
|
image = std::make_shared<RawImage>(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<RawImage> &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<WCSDataT>(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<RawImage>(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<RawImage>(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<QString, QString>("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<WCSDataT>(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<RawImage> &image)
|
||||||
|
{
|
||||||
|
std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
|
||||||
|
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<uint16_t> 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<h;i++)
|
||||||
|
{
|
||||||
|
for(size_t o=rawdata.sizes.left_margin;o<w;o++)
|
||||||
|
{
|
||||||
|
uint16_t p = rawdata.raw_image[i*pitch+o];
|
||||||
|
out[d++] = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image = std::make_shared<RawImage>(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> &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<RawImage>(img);
|
||||||
|
qDebug() << "LoadQImage" << timer.elapsed();
|
||||||
|
ret = !img.isNull();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef LOADIMAGE_H
|
||||||
|
#define LOADIMAGE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#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> &rawImage, bool planar = false);
|
||||||
|
|
||||||
|
#endif // LOADIMAGE_H
|
||||||
+2
-451
@@ -1,35 +1,15 @@
|
|||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "imageringlist.h"
|
#include "imageringlist.h"
|
||||||
#include <libraw/libraw.h>
|
|
||||||
#include "imageinfo.h"
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <libexif/exif-data.h>
|
|
||||||
#include <fitsio2.h>
|
#include <fitsio2.h>
|
||||||
#include <libxisf.h>
|
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "starfit.h"
|
#include "loadimage.h"
|
||||||
#include <lcms2.h>
|
#include <lcms2.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
|
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
|
||||||
m_file(makeMaxPath(file)),
|
m_file(makeMaxPath(file)),
|
||||||
m_receiver(receiver),
|
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<Peak> &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<Star> &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<double> &data, const Star &star)
|
|
||||||
{
|
|
||||||
QString d = "d=[";
|
|
||||||
QString m = "m=[";
|
|
||||||
for(int y=0; y<radius; y++)
|
|
||||||
{
|
|
||||||
for(int x=0; x<radius; x++)
|
|
||||||
{
|
|
||||||
d += QString::number(data[y*radius+x]) + ",";
|
|
||||||
m += QString::number(gauss_model(star.m_am, star.m_x, star.m_y, star.m_sx, star.m_sy, x, y)) + ",";
|
|
||||||
}
|
|
||||||
d += ";";
|
|
||||||
m += ";";
|
|
||||||
}
|
|
||||||
d += "];";
|
|
||||||
m += "];";
|
|
||||||
//std::cout << star.m_am << " " << star.m_sx << star.m_sy << std::endl;
|
|
||||||
std::cout << d.toStdString() << std::endl;
|
|
||||||
std::cout << m.toStdString() << std::endl << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image)
|
|
||||||
{
|
|
||||||
std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
|
|
||||||
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<uint16_t> 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<h;i++)
|
|
||||||
{
|
|
||||||
for(size_t o=rawdata.sizes.left_margin;o<w;o++)
|
|
||||||
{
|
|
||||||
uint16_t p = rawdata.raw_image[i*pitch+o];
|
|
||||||
out[d++] = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
image = std::make_shared<RawImage>(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<WCSDataT>(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<RawImage> &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<uint8_t*>(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<uint16_t*>(img.data());
|
|
||||||
size_t size = img.size() * img.channels();
|
|
||||||
for(size_t i=0; i<size; i++)
|
|
||||||
s[i] -= INT16_MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(img.channels() == 1 || planar)
|
|
||||||
image = std::make_shared<RawImage>(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<RawImage> &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<WCSDataT>(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<RawImage>(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<RawImage>(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<QString, QString>("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()
|
void LoadRunable::run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(!m_thumbnail && !m_receiver->isCurrent())
|
if(!m_thumbnail /*&& !m_receiver->isCurrent()*/)
|
||||||
{
|
{
|
||||||
return;
|
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<WCSDataT>(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> &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<RawImage>(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) :
|
ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format, const ConvertParams ¶ms, QSemaphore *semaphore) :
|
||||||
m_infile(makeMaxPath(in)),
|
m_infile(makeMaxPath(in)),
|
||||||
m_outfile(makeMaxPath(out)),
|
m_outfile(makeMaxPath(out)),
|
||||||
|
|||||||
+1
-9
@@ -4,14 +4,7 @@
|
|||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QSemaphore>
|
#include <QSemaphore>
|
||||||
#include "imageinfo.h"
|
#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> &rawImage, bool planar = false);
|
|
||||||
|
|
||||||
class Image;
|
class Image;
|
||||||
|
|
||||||
@@ -26,7 +19,6 @@ public:
|
|||||||
void run() override;
|
void run() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ConvertRunable : public QRunnable
|
class ConvertRunable : public QRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
#include "rawimage.h"
|
|
||||||
|
|
||||||
#ifdef __SSE2__
|
#ifdef __SSE2__
|
||||||
#include <x86intrin.h>
|
#include <x86intrin.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
template<typename T, int ch>
|
template<typename T, int ch>
|
||||||
void fromPlanarSSE(const void *in, void *out, size_t count)
|
void fromPlanarSSE(const void *in, void *out, size_t count)
|
||||||
|
|||||||
+2
-2
@@ -6,10 +6,10 @@
|
|||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "loadrunable.h"
|
#include "loadimage.h"
|
||||||
#include "batchprocessing.h"
|
#include "batchprocessing.h"
|
||||||
#include <fitsio2.h>
|
#include <fitsio2.h>
|
||||||
#include "libXISF/libxisf.h"
|
#include "libxisf.h"
|
||||||
#ifdef PLATESOLVER
|
#ifdef PLATESOLVER
|
||||||
#include "solver.h"
|
#include "solver.h"
|
||||||
#endif // PLATESOLVER
|
#endif // PLATESOLVER
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#include <wcslib/wcshdr.h>
|
#include <wcslib/wcshdr.h>
|
||||||
#include <wcslib/wcsutil.h>
|
#include <wcslib/wcsutil.h>
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "loadrunable.h"
|
#include "loadimage.h"
|
||||||
#include "scriptengine.h"
|
#include "scriptengine.h"
|
||||||
|
|
||||||
Solver::Solver(QObject *parent) : QObject(parent)
|
Solver::Solver(QObject *parent) : QObject(parent)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QStyle>
|
||||||
#include "imageringlist.h"
|
#include "imageringlist.h"
|
||||||
|
|
||||||
const float BLACK_POINT_SIGMA = -2.8f;
|
const float BLACK_POINT_SIGMA = -2.8f;
|
||||||
|
|||||||
Reference in New Issue
Block a user