Compare commits

..

14 Commits

Author SHA1 Message Date
nou 2415717ce0 Add STFSlider ability to be vertical 2025-04-10 23:09:59 +02:00
nou e7acbca01e Color highlight FITS header 2025-04-10 19:58:29 +02:00
nou 7c4118b0b6 Fix bug in script solver 2025-04-10 00:34:14 +02:00
nou 8178efdafd Reload image when header is updated 2025-04-09 14:58:23 +02:00
nou 90026f931f Add open file and open file location to DB view 2025-04-02 20:27:59 +02:00
nou eee4613b25 Add plot() script method 2025-04-02 20:27:19 +02:00
nou 24a9e96bbf Streamline standalone thumbnailer 2025-04-02 15:24:41 +02:00
nou 5af5f4f068 Navigation menu 2025-04-02 12:24:17 +02:00
nou 85f9822b96 Fix prevSubImage 2025-03-25 18:26:08 +01:00
nou 7fc6c64fd7 Make help windows non modal 2025-03-24 22:09:18 +01:00
nou 4488c2e6af Always make 4 channels 2025-03-24 22:08:54 +01:00
nou 0047607c1d Add loading sub images 2025-03-23 13:33:34 +01:00
nou 45c368bbbb Remove usage of SLOT() and SIGNAL() 2025-03-19 13:50:39 +01:00
nou c96cb86a29 Show relative path in title bar for when browsing dir recursive 2025-03-19 13:14:04 +01:00
38 changed files with 2477 additions and 432 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ if(SANITIZE_ADDRESS_LEAK)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak")
endif(SANITIZE_ADDRESS_LEAK) endif(SANITIZE_ADDRESS_LEAK)
find_package(Qt6 COMPONENTS Widgets Sql OpenGLWidgets Qml REQUIRED) find_package(Qt6 COMPONENTS Widgets Sql OpenGLWidgets Qml Charts REQUIRED)
find_library(EXIF_LIB exif REQUIRED) find_library(EXIF_LIB exif REQUIRED)
find_library(FITS_LIB cfitsio REQUIRED) find_library(FITS_LIB cfitsio REQUIRED)
find_library(RAW_LIB NAMES raw_r REQUIRED) find_library(RAW_LIB NAMES raw_r REQUIRED)
@@ -102,7 +102,7 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}") message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF) target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml Qt6::Charts ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF)
if(APPLE) if(APPLE)
target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation") target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation")
elseif(UNIX) elseif(UNIX)
+2
View File
@@ -32,6 +32,8 @@ HelpDialog::HelpDialog(QWidget *parent) : QDialog(parent)
{ {
setWindowTitle(tr("Help")); setWindowTitle(tr("Help"));
resize(1000, 600); resize(1000, 600);
setModal(false);
setAttribute(Qt::WA_DeleteOnClose, true);
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
QTextEdit *helpText = new QTextEdit(this); QTextEdit *helpText = new QTextEdit(this);
+48 -13
View File
@@ -10,6 +10,9 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDesktopServices> #include <QDesktopServices>
#include <QInputDialog> #include <QInputDialog>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include "scriptengine.h" #include "scriptengine.h"
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@@ -180,19 +183,7 @@ void BatchProcessing::browse()
void BatchProcessing::openScriptDir() void BatchProcessing::openScriptDir()
{ {
#ifdef Q_OS_LINUX openDir(_scriptBasePath);
QDBusConnection con = QDBusConnection::sessionBus();
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", "ShowFolders");
QList<QVariant> args = {QStringList(QUrl::fromLocalFile(_scriptBasePath).toString()), QString()};
message.setArguments(args);
con.call(message);
#endif
#ifdef Q_OS_WINDOWS
QProcess::startDetached("explorer.exe", {QDir::toNativeSeparators(_scriptBasePath)});
#endif
#ifdef Q_OS_MACOS
QDesktopServices::openUrl(QUrl::fromLocalFile(_scriptBasePath));
#endif
} }
void BatchProcessing::runScript() void BatchProcessing::runScript()
@@ -279,3 +270,47 @@ QJSValue BatchProcessing::getItem(const QStringList &items, const QString &label
QString ret = QInputDialog::getItem(this, tr("Select item"), label, items, current, false, &ok); QString ret = QInputDialog::getItem(this, tr("Select item"), label, items, current, false, &ok);
return ok ? ret : QJSValue(); return ok ? ret : QJSValue();
} }
void BatchProcessing::plot(const QVector<QPointF> &points)
{
QDialog *diag = new QDialog(this);
diag->setAttribute(Qt::WA_DeleteOnClose);
diag->setModal(false);
diag->setWindowTitle(tr("Chart"));
QChartView *chartView = new QChartView(diag);
diag->setLayout(new QVBoxLayout);
diag->layout()->addWidget(chartView);
QChart *chart = new QChart;
chart->setParent(chartView);
auto series = new QLineSeries(chartView);
series->append(points);
chart->addSeries(series);
chart->createDefaultAxes();
chart->setTitle("Simple line graph");
chart->legend()->hide();
chartView->setChart(chart);
chartView->setRenderHint(QPainter::Antialiasing);
diag->resize(640, 480);
diag->show();
}
void openDir(const QString &path)
{
#ifdef Q_OS_LINUX
QDBusConnection con = QDBusConnection::sessionBus();
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", "ShowFolders");
QList<QVariant> args = {QStringList(QUrl::fromLocalFile(path).toString()), QString()};
message.setArguments(args);
con.call(message);
#endif
#ifdef Q_OS_WINDOWS
QProcess::startDetached("explorer.exe", {QDir::toNativeSeparators(path)});
#endif
#ifdef Q_OS_MACOS
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
#endif
}
+4
View File
@@ -41,6 +41,10 @@ public slots:
QJSValue getInt(const QString &label, int value); QJSValue getInt(const QString &label, int value);
QJSValue getFloat(const QString &label, double value, int decimals); QJSValue getFloat(const QString &label, double value, int decimals);
QJSValue getItem(const QStringList &items, const QString &label, int current); QJSValue getItem(const QStringList &items, const QString &label, int current);
void plot(const QVector<QPointF> &points);
}; };
void openDir(const QString &path);
#endif // BATCHPROCESSING_H #endif // BATCHPROCESSING_H
+19 -2
View File
@@ -10,6 +10,7 @@
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QRegularExpression> #include <QRegularExpression>
#include <iostream> #include <iostream>
#include "batchprocessing.h"
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"}; const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
@@ -214,6 +215,8 @@ void DatabaseTableView::contextMenuEvent(QContextMenuEvent *event)
QMenu menu; QMenu menu;
QAction *mark = menu.addAction(tr("Mark")); QAction *mark = menu.addAction(tr("Mark"));
QAction *unmark = menu.addAction(tr("Unmark")); QAction *unmark = menu.addAction(tr("Unmark"));
QAction *open = menu.addAction(tr("Open"));
QAction *openDirAction = menu.addAction(tr("Open file location"));
QAction *a = menu.exec(event->globalPos()); QAction *a = menu.exec(event->globalPos());
if(a == nullptr) if(a == nullptr)
@@ -225,7 +228,10 @@ void DatabaseTableView::contextMenuEvent(QContextMenuEvent *event)
emit filesMarked(indexes); emit filesMarked(indexes);
else if(a == unmark) else if(a == unmark)
emit filesUnmarked(indexes); emit filesUnmarked(indexes);
else if(a == open)
emit openFile(indexes);
else if(a == openDirAction)
emit openDir(indexes);
} }
DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent) DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent)
@@ -270,6 +276,17 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
m_database->unmark(files); m_database->unmark(files);
m_model->filesUnmarked(indexes); m_model->filesUnmarked(indexes);
}); });
connect(m_tableView, &DatabaseTableView::openFile, [this](QModelIndexList indexes){
if(indexes.size())
emit loadFile(m_model->data(indexes.front().siblingAtColumn(0)).toString());
});
connect(m_tableView, &DatabaseTableView::openDir, [this](QModelIndexList indexes){
if(indexes.size())
{
QFileInfo info(m_model->data(indexes.front().siblingAtColumn(0)).toString());
openDir(info.absolutePath());
}
});
auto addFilterItems = [](QComboBox *combobox, const QStringList &fitsKeywords) auto addFilterItems = [](QComboBox *combobox, const QStringList &fitsKeywords)
{ {
@@ -308,7 +325,7 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
} }
QPushButton *filterButton = new QPushButton(tr("Filter"), this); QPushButton *filterButton = new QPushButton(tr("Filter"), this);
connect(filterButton, SIGNAL(pressed()), this, SLOT(applyFilter())); connect(filterButton, &QPushButton::pressed, this, &DataBaseView::applyFilter);
hlayout->addWidget(filterButton); hlayout->addWidget(filterButton);
connect(m_database, &Database::databaseChanged, [this, &addFilterItems](){ connect(m_database, &Database::databaseChanged, [this, &addFilterItems](){
+2
View File
@@ -52,6 +52,8 @@ protected:
signals: signals:
void filesMarked(QModelIndexList indexes); void filesMarked(QModelIndexList indexes);
void filesUnmarked(QModelIndexList indexes); void filesUnmarked(QModelIndexList indexes);
void openFile(QModelIndexList indexes);
void openDir(QModelIndexList indexes);
}; };
class DataBaseView : public QWidget class DataBaseView : public QWidget
+10 -1
View File
@@ -2,6 +2,8 @@
#include <QSettings> #include <QSettings>
#include <QHeaderView> #include <QHeaderView>
QMap<QString, QColor> headerHighlight;
ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent) ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent)
{ {
setColumnCount(3); setColumnCount(3);
@@ -25,7 +27,14 @@ void ImageInfo::setInfo(const ImageInfoData &info)
QTreeWidgetItem *fitsHeader = new QTreeWidgetItem({tr("FITS Header")}); QTreeWidgetItem *fitsHeader = new QTreeWidgetItem({tr("FITS Header")});
for(const FITSRecord &record : info.fitsHeader) for(const FITSRecord &record : info.fitsHeader)
{ {
new QTreeWidgetItem(fitsHeader, {record.key, record.value.toString().left(1024), record.comment}); QTreeWidgetItem *item = new QTreeWidgetItem(fitsHeader, {record.key, record.value.toString().left(1024), record.comment});
if(headerHighlight.contains(record.key))
{
QColor color = headerHighlight[record.key];
item->setBackground(0, color);
item->setBackground(1, color);
item->setBackground(2, color);
}
} }
addTopLevelItem(fitsHeader); addTopLevelItem(fitsHeader);
} }
+2
View File
@@ -76,6 +76,8 @@ struct ImageInfoData
QVector<QPair<QString, QString>> info; QVector<QPair<QString, QString>> info;
std::shared_ptr<WCSDataT> wcs; std::shared_ptr<WCSDataT> wcs;
SkyPointScale getCenterRaDec() const; SkyPointScale getCenterRaDec() const;
int index = 0;
int num = 1;
}; };
typedef enum typedef enum
+65 -18
View File
@@ -23,13 +23,16 @@ Image::Image(const QString name, int number, ImageRingList *ringList) :
{ {
} }
void Image::load(QThreadPool *pool) void Image::load(int index, QThreadPool *pool)
{ {
if(index != m_info.index && !m_loading)
m_rawImage.reset();
if(!m_rawImage && !m_loading) if(!m_rawImage && !m_loading)
{ {
m_loading = true; m_loading = true;
m_released = false; m_released = false;
pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel())); pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel(), index));
} }
if(!m_loading && m_rawImage) if(!m_loading && m_rawImage)
emit pixmapLoaded(this); emit pixmapLoaded(this);
@@ -38,7 +41,7 @@ void Image::load(QThreadPool *pool)
void Image::loadThumbnail(QThreadPool *pool) void Image::loadThumbnail(QThreadPool *pool)
{ {
if(!m_thumbnail) if(!m_thumbnail)
pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, true)); pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, 0, true));
else else
emit thumbnailLoaded(this); emit thumbnailLoaded(this);
} }
@@ -115,7 +118,7 @@ ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter,
, m_nameFilter(nameFilter) , m_nameFilter(nameFilter)
, m_fileSuffix(nameFilter) , m_fileSuffix(nameFilter)
{ {
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &ImageRingList::dirChanged);
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*."); m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
m_loadPool = new QThreadPool(this); m_loadPool = new QThreadPool(this);
m_loadPool->setThreadPriority(QThread::LowPriority); m_loadPool->setThreadPriority(QThread::LowPriority);
@@ -146,6 +149,7 @@ bool ImageRingList::setDir(const QString path, const QString &currentFile, bool
if(dir.exists()) if(dir.exists())
{ {
m_currentDir = path;
QStringList scannedDirs; QStringList scannedDirs;
QStringList absolutePaths; QStringList absolutePaths;
std::function<void(const QString&)> scanDir = [&](const QString &path) std::function<void(const QString&)> scanDir = [&](const QString &path)
@@ -174,7 +178,8 @@ bool ImageRingList::setDir(const QString path, const QString &currentFile, bool
//qDebug() << absolutePaths.size(); //qDebug() << absolutePaths.size();
setFilesPrivate(absolutePaths, m_liveMode ? absolutePaths.first() : currentFile); setFilesPrivate(absolutePaths, m_liveMode ? absolutePaths.first() : currentFile);
m_fileSystemWatcher.removePaths(m_fileSystemWatcher.directories()); if(m_fileSystemWatcher.directories().size())
m_fileSystemWatcher.removePaths(m_fileSystemWatcher.directories());
m_fileSystemWatcher.addPath(path); m_fileSystemWatcher.addPath(path);
return true; return true;
} }
@@ -212,6 +217,11 @@ ImagePtr ImageRingList::currentImage()
return 0; return 0;
} }
QString ImageRingList::currentDir() const
{
return m_currentDir;
}
void ImageRingList::increment() void ImageRingList::increment()
{ {
if(m_images.size()) if(m_images.size())
@@ -223,9 +233,9 @@ void ImageRingList::increment()
(*m_firstImage)->release(); (*m_firstImage)->release();
m_firstImage = increment(m_firstImage); m_firstImage = increment(m_firstImage);
m_currImage = increment(m_currImage); m_currImage = increment(m_currImage);
(*m_currImage)->load(m_loadPool); (*m_currImage)->load(0, m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(m_loadPool); (*m_lastImage)->load(0, m_loadPool);
} }
} }
@@ -240,9 +250,37 @@ void ImageRingList::decrement()
(*m_lastImage)->release(); (*m_lastImage)->release();
m_firstImage = decrement(m_firstImage); m_firstImage = decrement(m_firstImage);
m_currImage = decrement(m_currImage); m_currImage = decrement(m_currImage);
(*m_currImage)->load(m_loadPool); (*m_currImage)->load(0, m_loadPool);
m_lastImage = decrement(m_lastImage); m_lastImage = decrement(m_lastImage);
(*m_firstImage)->load(m_loadPool); (*m_firstImage)->load(0, m_loadPool);
}
}
void ImageRingList::prevSubImage()
{
if(m_images.size())
{
if((*m_currImage)->isLoading())
return;
int index = (*m_currImage)->info().index;
int num = (*m_currImage)->info().num;
if(num > 1)
(*m_currImage)->load(index == 0 ? num - 1 : index - 1, m_loadPool);
}
}
void ImageRingList::nextSubImage()
{
if(m_images.size())
{
if((*m_currImage)->isLoading())
return;
int index = (*m_currImage)->info().index;
int num = (*m_currImage)->info().num;
if(num > 1)
(*m_currImage)->load((index + 1) % num, m_loadPool);
} }
} }
@@ -256,6 +294,16 @@ void ImageRingList::setMarked()
setFilesPrivate(files); setFilesPrivate(files);
} }
void ImageRingList::reloadImage()
{
if(*m_currImage)
{
int index = (*m_currImage)->info().index;
(*m_currImage)->release();
(*m_currImage)->load(index, m_loadPool);
}
}
void ImageRingList::setLiveMode(bool live) void ImageRingList::setLiveMode(bool live)
{ {
m_liveMode = live; m_liveMode = live;
@@ -296,7 +344,7 @@ void ImageRingList::loadFile(int row)
if(m_images.empty()) if(m_images.empty())
return; return;
(*m_currImage)->load(m_loadPool); (*m_currImage)->load(0, m_loadPool);
m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2; m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
if(m_liveMode) if(m_liveMode)
@@ -305,9 +353,9 @@ void ImageRingList::loadFile(int row)
for(int i=0; i<m_width; i++) for(int i=0; i<m_width; i++)
{ {
m_firstImage = decrement(m_firstImage); m_firstImage = decrement(m_firstImage);
(*m_firstImage)->load(m_loadPool); (*m_firstImage)->load(0, m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(m_loadPool); (*m_lastImage)->load(0, m_loadPool);
} }
if(m_lastImage != m_firstImage) if(m_lastImage != m_firstImage)
{ {
@@ -431,9 +479,9 @@ void ImageRingList::setPreload(int width)
for(int i = newWidth - m_width; i>0; i--) for(int i = newWidth - m_width; i>0; i--)
{ {
m_firstImage = decrement(m_firstImage); m_firstImage = decrement(m_firstImage);
(*m_firstImage)->load(m_loadPool); (*m_firstImage)->load(0, m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(m_loadPool); (*m_lastImage)->load(0, m_loadPool);
} }
} }
if(newWidth < m_width) if(newWidth < m_width)
@@ -498,8 +546,8 @@ void ImageRingList::setFilesPrivate(const QStringList files, const QString &curr
for(const QString &file : files) for(const QString &file : files)
{ {
ImagePtr ptr = make_shared<Image>(file, i++, this); ImagePtr ptr = make_shared<Image>(file, i++, this);
connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*))); connect(ptr.get(), &Image::pixmapLoaded, this, &ImageRingList::imageLoaded);
connect(ptr.get(), SIGNAL(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*))); connect(ptr.get(), &Image::thumbnailLoaded, this, &ImageRingList::thumbnailLoaded);
m_images.append(ptr); m_images.append(ptr);
} }
@@ -538,9 +586,8 @@ void ImageRingList::imageLoaded(Image *image)
} }
} }
void ImageRingList::dirChanged(QString dir) void ImageRingList::dirChanged(QString)
{ {
m_currentDir = dir;
if(m_liveMode) if(m_liveMode)
reloadDir(); reloadDir();
else else
+6 -2
View File
@@ -28,7 +28,7 @@ class Image : public QObject
ImageRingList *m_ringList; ImageRingList *m_ringList;
public: public:
explicit Image(const QString name, int number, ImageRingList *ringList); explicit Image(const QString name, int number, ImageRingList *ringList);
void load(QThreadPool *pool); void load(int index, QThreadPool *pool);
void loadThumbnail(QThreadPool *pool); void loadThumbnail(QThreadPool *pool);
void release(); void release();
QString name() const; QString name() const;
@@ -79,6 +79,7 @@ public:
void setFile(const QString &file); void setFile(const QString &file);
void setFiles(QStringList files); void setFiles(QStringList files);
ImagePtr currentImage(); ImagePtr currentImage();
QString currentDir() const;
void setLiveMode(bool live); void setLiveMode(bool live);
void setCalculateStats(bool stats); void setCalculateStats(bool stats);
void setFindPeaks(bool findPeaks); void setFindPeaks(bool findPeaks);
@@ -105,7 +106,10 @@ public slots:
void toggleSlideshow(bool start); void toggleSlideshow(bool start);
void increment(); void increment();
void decrement(); void decrement();
void prevSubImage();
void nextSubImage();
void setMarked(); void setMarked();
void reloadImage();
protected: protected:
void setFilesPrivate(const QStringList files, const QString &currentFile = QString()); void setFilesPrivate(const QStringList files, const QString &currentFile = QString());
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter); QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
@@ -117,7 +121,7 @@ signals:
void currentImageChanged(int index); void currentImageChanged(int index);
protected slots: protected slots:
void imageLoaded(Image *image); void imageLoaded(Image *image);
void dirChanged(QString dir); void dirChanged(QString);
void reloadDir(); void reloadDir();
}; };
+2 -2
View File
@@ -27,8 +27,8 @@ ImageScrollArea::ImageScrollArea(Database *database, QWidget *parent) : QWidget(
layout->addWidget(m_verticalScrollBar, 0, 1); layout->addWidget(m_verticalScrollBar, 0, 1);
layout->addWidget(m_horizontalScrollBar, 1, 0); layout->addWidget(m_horizontalScrollBar, 1, 0);
connect(m_verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent())); connect(m_verticalScrollBar, &QScrollBar::valueChanged, this, &ImageScrollArea::scrollEvent);
connect(m_horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent())); connect(m_horizontalScrollBar, &QScrollBar::valueChanged, this, &ImageScrollArea::scrollEvent);
if(imageWidgetGL) if(imageWidgetGL)
{ {
+1 -1
View File
@@ -79,7 +79,7 @@ ImageWidgetGL::ImageWidgetGL(Database *database, QWidget *parent) : QOpenGLWidge
m_updateTimer = new QTimer(this); m_updateTimer = new QTimer(this);
m_updateTimer->setInterval(500); m_updateTimer->setInterval(500);
m_updateTimer->setSingleShot(true); m_updateTimer->setSingleShot(true);
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update())); connect(m_updateTimer, &QTimer::timeout, this, static_cast<void (QOpenGLWidget::*)()>(&ImageWidgetGL::update));
setAcceptDrops(true); setAcceptDrops(true);
QTimer::singleShot(1000, [this](){ QTimer::singleShot(1000, [this](){
if(!isValid()) if(!isValid())
+40 -13
View File
@@ -83,11 +83,10 @@ int loadFITSHeader(fitsfile *file, ImageInfoData &info)
return status; return status;
} }
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar) bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index)
{ {
fitsfile *file; fitsfile *file;
int status = 0; int status = 0;
int type = -1;
int num = 0; int num = 0;
long naxes[3] = {0}; long naxes[3] = {0};
@@ -105,16 +104,32 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
fits_get_num_hdus(file, &num, &status); fits_get_num_hdus(file, &num, &status);
if(status)return checkError(); if(status)return checkError();
int hdutype;
int imgtype; int imgtype;
int naxis; int naxis;
for(int i=1; i <= num; i++) std::vector<int> imageIdxs;
for(int i = 1; i <= num; i++)
{ {
fits_movabs_hdu(file, i, IMAGE_HDU, &status);if(status)return checkError(); fits_movabs_hdu(file, i, &hdutype, &status);if(status)return checkError();
fits_get_hdu_type(file, &type, &status);if(status)return checkError(); if(hdutype == IMAGE_HDU)
{
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
if(naxis >= 2 && naxis <= 3)imageIdxs.push_back(i);
}
}
info.num = imageIdxs.size();
info.index = index;
if(index >= imageIdxs.size())return false;
fits_movabs_hdu(file, imageIdxs[index], &hdutype, &status);if(status)return checkError();
if(hdutype == IMAGE_HDU)
{
naxes[0] = naxes[1] = naxes[2] = 0;
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError(); fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError(); fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0) if(hdutype == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
{ {
RawImage::DataType type; RawImage::DataType type;
int fitstype; int fitstype;
@@ -133,6 +148,10 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
type = RawImage::UINT16; type = RawImage::UINT16;
fitstype = TUSHORT; fitstype = TUSHORT;
break; break;
case LONG_IMG:
type = RawImage::UINT32;
fitstype = TINT;
break;
case ULONG_IMG: case ULONG_IMG:
type = RawImage::UINT32; type = RawImage::UINT32;
fitstype = TUINT; fitstype = TUINT;
@@ -173,13 +192,18 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
for(size_t i=0; i<size; i++) for(size_t i=0; i<size; i++)
s[i] -= INT16_MIN; s[i] -= INT16_MIN;
} }
else if(fitstype == TINT)
{
uint32_t *s = static_cast<uint32_t*>(img.data());
size_t size = img.size() * img.channels();
for(size_t i=0; i<size; i++)
s[i] -= INT32_MIN;
}
if(img.channels() == 1 || planar) if(img.channels() == 1 || planar)
image = std::make_shared<RawImage>(std::move(img)); image = std::make_shared<RawImage>(std::move(img));
else else
image = RawImage::fromPlanar(img); image = RawImage::fromPlanar(img);
break;
} }
} }
noload: noload:
@@ -202,14 +226,15 @@ noload:
return true; return true;
} }
bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar) bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index)
{ {
try try
{ {
LibXISF::XISFReader xisf; LibXISF::XISFReader xisf;
xisf.open(path.toLocal8Bit().data()); xisf.open(path.toLocal8Bit().data());
const LibXISF::Image &xisfImage = xisf.getImage(0); if(index >= (uint32_t)xisf.imagesCount())return false;
const LibXISF::Image &xisfImage = xisf.getImage(index);
auto fitskeywords = xisfImage.fitsKeywords(); auto fitskeywords = xisfImage.fitsKeywords();
for(auto fits : fitskeywords) for(auto fits : fitskeywords)
@@ -222,6 +247,8 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
info.fitsHeader.append(prop); info.fitsHeader.append(prop);
} }
info.num = xisf.imagesCount();
info.index = index + 1;
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader); 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("Width"), QString::number(xisfImage.width())});
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())}); info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
@@ -378,7 +405,7 @@ bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
return true; return true;
} }
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar) bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar)
{ {
bool ret = false; bool ret = false;
QElapsedTimer timer; QElapsedTimer timer;
@@ -390,12 +417,12 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
} }
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive)) 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); ret = loadFITS(path, info, rawImage, planar, index);
qDebug() << "LoadFITS" << timer.elapsed(); qDebug() << "LoadFITS" << timer.elapsed();
} }
else if(path.endsWith(".XISF", Qt::CaseInsensitive)) else if(path.endsWith(".XISF", Qt::CaseInsensitive))
{ {
ret = loadXISF(path, info, rawImage, planar); ret = loadXISF(path, info, rawImage, planar, index);
qDebug() << "LoadXISF" << timer.elapsed(); qDebug() << "LoadXISF" << timer.elapsed();
} }
else else
+1 -1
View File
@@ -9,6 +9,6 @@ class RawImage;
QString makeUNCPath(const QString &path); QString makeUNCPath(const QString &path);
bool readFITSHeader(const QString &path, ImageInfoData &info); bool readFITSHeader(const QString &path, ImageInfoData &info);
bool readXISFHeader(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); bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false);
#endif // LOADIMAGE_H #endif // LOADIMAGE_H
+5 -4
View File
@@ -10,11 +10,12 @@
#include "loadimage.h" #include "loadimage.h"
#include <lcms2.h> #include <lcms2.h>
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) : LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail) :
m_file(makeUNCPath(file)), m_file(makeUNCPath(file)),
m_receiver(receiver), m_receiver(receiver),
m_analyzeLevel(level), m_analyzeLevel(level),
m_thumbnail(thumbnail) m_thumbnail(thumbnail),
m_index(index)
{ {
} }
@@ -32,7 +33,7 @@ void LoadRunable::run()
info.info.append({QObject::tr("Filename"), finfo.fileName()}); info.info.append({QObject::tr("Filename"), finfo.fileName()});
std::shared_ptr<RawImage> rawImage; std::shared_ptr<RawImage> rawImage;
if(!loadImage(m_file, info, rawImage)) if(!loadImage(m_file, info, rawImage, m_index))
info.info.append({QObject::tr("Error"), QObject::tr("Failed to load image")}); info.info.append({QObject::tr("Error"), QObject::tr("Failed to load image")});
@@ -187,7 +188,7 @@ void ConvertRunable::run()
ImageInfoData imageinfo; ImageInfoData imageinfo;
std::shared_ptr<RawImage> rawimage; std::shared_ptr<RawImage> rawimage;
loadImage(m_infile, imageinfo, rawimage); loadImage(m_infile, imageinfo, rawimage, 0);
QFileInfo info(m_outfile); QFileInfo info(m_outfile);
info.dir().mkpath("."); info.dir().mkpath(".");
+2 -1
View File
@@ -15,8 +15,9 @@ class LoadRunable : public QRunnable
Image *m_receiver; Image *m_receiver;
AnalyzeLevel m_analyzeLevel; AnalyzeLevel m_analyzeLevel;
bool m_thumbnail; bool m_thumbnail;
int m_index = 0;
public: public:
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail = false); LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail = false);
void run() override; void run() override;
}; };
+44 -44
View File
@@ -95,7 +95,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
m_ringList = new ImageRingList(m_database, nameFilter, this); m_ringList = new ImageRingList(m_database, nameFilter, this);
m_filesystem = new FilesystemWidget(m_ringList, this); m_filesystem = new FilesystemWidget(m_ringList, this);
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int))); connect(m_filesystem, &FilesystemWidget::fileSelected, this, static_cast<void (MainWindow::*)(int)>(&MainWindow::loadFile));
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort); connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort); connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
@@ -106,7 +106,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir)); connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
m_databaseView = new DataBaseView(m_database, this); m_databaseView = new DataBaseView(m_database, this);
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString))); connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
#ifdef PLATESOLVER #ifdef PLATESOLVER
_plateSolving = new PlateSolving(this); _plateSolving = new PlateSolving(this);
@@ -114,6 +114,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
_plateSolving->hide(); _plateSolving->hide();
#endif #endif
QToolBar *navigationToolbar = new QToolBar(tr("Navigation toolbar"), this);
navigationToolbar->setObjectName("navigationtoolbar");
navigationToolbar->hide();
QAction *prevAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowLeft), tr("Previous image"));
prevAction->setShortcuts({Qt::Key_Left, Qt::Key_Up});
QAction *nextAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowRight), tr("Next image"));
nextAction->setShortcuts({Qt::Key_Right, Qt::Key_Down});
QAction *prevSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowUp), tr("Prev sub image"), Qt::Key_PageUp);
QAction *nextSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowDown), tr("Next sub image"), Qt::Key_PageDown);
connect(prevAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::decrement));
connect(nextAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment));
connect(prevSubAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::prevSubImage));
connect(nextSubAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::nextSubImage));
addToolBar(Qt::TopToolBarArea, navigationToolbar);
addToolBar(Qt::TopToolBarArea, m_stretchPanel); addToolBar(Qt::TopToolBarArea, m_stretchPanel);
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this); QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
@@ -151,20 +166,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_ringList, &ImageRingList::pixmapLoaded, histogram, &Histogram::imageLoaded); connect(m_ringList, &ImageRingList::pixmapLoaded, histogram, &Histogram::imageLoaded);
#ifdef PLATESOLVER #ifdef PLATESOLVER
connect(m_ringList, &ImageRingList::pixmapLoaded, _plateSolving, &PlateSolving::imageLoaded); connect(m_ringList, &ImageRingList::pixmapLoaded, _plateSolving, &PlateSolving::imageLoaded);
connect(_plateSolving, &PlateSolving::headerUpdated, m_ringList, &ImageRingList::reloadImage);
#endif #endif
connect(m_image, &ImageScrollArea::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile)); connect(m_image, &ImageScrollArea::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
QMenu *fileMenu = new QMenu(tr("File"), this); QMenu *fileMenu = new QMenu(tr("File"), this);
fileMenu->addAction(tr("Open"), QKeySequence::Open, this, SLOT(loadFile())); fileMenu->addAction(tr("Open"), QKeySequence::Open, this, static_cast<void (MainWindow::*)()>(&MainWindow::loadFile));
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir); fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, SLOT(saveAs())); QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs);
fileMenu->addSeparator(); fileMenu->addSeparator();
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, SLOT(copyMarked())); fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, SLOT(moveMarked())); fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked); fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
fileMenu->addSeparator(); fileMenu->addSeparator();
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir())); fileMenu->addAction(tr("Index directory"), this, static_cast<void (MainWindow::*)()>(&MainWindow::indexDir));
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex())); fileMenu->addAction(tr("Reindex files"), this, &MainWindow::reindex);
fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV); fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV);
fileMenu->addAction(tr("Batch processing"), Qt::Key_B | Qt::CTRL, [this](){ fileMenu->addAction(tr("Batch processing"), Qt::Key_B | Qt::CTRL, [this](){
BatchProcessing *batchProcessing = new BatchProcessing(m_database, this); BatchProcessing *batchProcessing = new BatchProcessing(m_database, this);
@@ -172,9 +188,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
delete batchProcessing; delete batchProcessing;
}); });
fileMenu->addSeparator(); fileMenu->addSeparator();
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool))); QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode);
liveModeAction->setCheckable(true); liveModeAction->setCheckable(true);
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, SLOT(close())); QAction *exitAction = fileMenu->addAction(tr("Exit"), this, &MainWindow::close);
exitAction->setShortcut(QKeySequence::Quit); exitAction->setShortcut(QKeySequence::Quit);
menuBar()->addMenu(fileMenu); menuBar()->addMenu(fileMenu);
@@ -182,6 +198,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
editMenu->addAction(tr("Settings"), this, &MainWindow::showSettingsDialog); editMenu->addAction(tr("Settings"), this, &MainWindow::showSettingsDialog);
menuBar()->addMenu(editMenu); menuBar()->addMenu(editMenu);
QMenu *navigationMenu = new QMenu(tr("Navigation"), this);
navigationMenu->addAction(prevAction);
navigationMenu->addAction(nextAction);
navigationMenu->addAction(prevSubAction);
navigationMenu->addAction(nextSubAction);
menuBar()->addMenu(navigationMenu);
QMenu *viewMenu = new QMenu(tr("View"), this); QMenu *viewMenu = new QMenu(tr("View"), this);
viewMenu->addAction(tr("Zoom In"), QKeySequence::ZoomIn, m_image, &ImageScrollArea::zoomIn); viewMenu->addAction(tr("Zoom In"), QKeySequence::ZoomIn, m_image, &ImageScrollArea::zoomIn);
viewMenu->addAction(tr("Zoom Out"), QKeySequence::ZoomOut, m_image, &ImageScrollArea::zoomOut); viewMenu->addAction(tr("Zoom Out"), QKeySequence::ZoomOut, m_image, &ImageScrollArea::zoomOut);
@@ -243,11 +266,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
menuBar()->addMenu(viewMenu); menuBar()->addMenu(viewMenu);
QMenu *selectMenu = new QMenu(tr("Select"), this); QMenu *selectMenu = new QMenu(tr("Select"), this);
selectMenu->addAction(tr("Mark"), Qt::Key_F7, this, SLOT(markImage())); selectMenu->addAction(tr("Mark"), Qt::Key_F7, this, &MainWindow::markImage);
selectMenu->addAction(tr("Unmark"), Qt::Key_F8, this, SLOT(unmarkImage())); selectMenu->addAction(tr("Unmark"), Qt::Key_F8, this, &MainWindow::unmarkImage);
selectMenu->addSeparator(); selectMenu->addSeparator();
selectMenu->addAction(tr("Mark and next"), Qt::Key_M, this, SLOT(markAndNext())); selectMenu->addAction(tr("Mark and next"), Qt::Key_M, this, &MainWindow::markAndNext);
selectMenu->addAction(tr("Unmark and next"), Qt::Key_X, this, SLOT(unmarkAndNext())); selectMenu->addAction(tr("Unmark and next"), Qt::Key_X, this, &MainWindow::unmarkAndNext);
selectMenu->addSeparator(); selectMenu->addSeparator();
selectMenu->addAction(tr("Show marked list"), this, &MainWindow::showMarkFilesDialog); selectMenu->addAction(tr("Show marked list"), this, &MainWindow::showMarkFilesDialog);
QAction *openMarked = selectMenu->addAction(tr("Open marked"), m_ringList, &ImageRingList::setMarked); QAction *openMarked = selectMenu->addAction(tr("Open marked"), m_ringList, &ImageRingList::setMarked);
@@ -282,6 +305,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
QMenu *dockMenu = new QMenu(tr("Docks"), this); QMenu *dockMenu = new QMenu(tr("Docks"), this);
dockMenu->addAction(infoDock->toggleViewAction()); dockMenu->addAction(infoDock->toggleViewAction());
dockMenu->addAction(m_stretchPanel->toggleViewAction()); dockMenu->addAction(m_stretchPanel->toggleViewAction());
dockMenu->addAction(navigationToolbar->toggleViewAction());
dockMenu->addAction(filesystemDock->toggleViewAction()); dockMenu->addAction(filesystemDock->toggleViewAction());
dockMenu->addAction(databaseViewDock->toggleViewAction()); dockMenu->addAction(databaseViewDock->toggleViewAction());
dockMenu->addAction(filetreeDock->toggleViewAction()); dockMenu->addAction(filetreeDock->toggleViewAction());
@@ -292,7 +316,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
menuBar()->addMenu(dockMenu); menuBar()->addMenu(dockMenu);
QMenu *helpMenu = menuBar()->addMenu(tr("Help")); QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
helpMenu->addAction(tr("Help"), QKeySequence::HelpContents, [this]{ HelpDialog help(this); help.exec(); }); helpMenu->addAction(tr("Help"), QKeySequence::HelpContents, [this]{ HelpDialog *help = new HelpDialog(this); help->show(); });
helpMenu->addAction(tr("About Tenmon"), [this]{ About about(this); about.exec(); }); helpMenu->addAction(tr("About Tenmon"), [this]{ About about(this); about.exec(); });
helpMenu->addAction(tr("About Qt"), [this](){ QMessageBox::aboutQt(this); }); helpMenu->addAction(tr("About Qt"), [this](){ QMessageBox::aboutQt(this); });
helpMenu->addAction(tr("Check for update"), this, &MainWindow::checkNewVersion); helpMenu->addAction(tr("Check for update"), this, &MainWindow::checkNewVersion);
@@ -345,32 +369,6 @@ MainWindow::~MainWindow()
delete m_database; delete m_database;
} }
void MainWindow::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_Left:
case Qt::Key_Up:
m_ringList->decrement();
break;
case Qt::Key_Right:
case Qt::Key_Down:
m_ringList->increment();
break;
default:
event->ignore();
break;
}
if(event->isAccepted())
updateWindowTitle();
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
event->ignore();
}
void MainWindow::setupSigterm() void MainWindow::setupSigterm()
{ {
#ifdef __linux__ #ifdef __linux__
@@ -387,7 +385,7 @@ void MainWindow::setupSigterm()
::socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair); ::socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair);
socketNotifier = new QSocketNotifier(socketPair[1], QSocketNotifier::Read, this); socketNotifier = new QSocketNotifier(socketPair[1], QSocketNotifier::Read, this);
connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketNotify())); connect(socketNotifier, &QSocketNotifier::activated, this, &MainWindow::socketNotify);
#endif #endif
} }
@@ -811,8 +809,10 @@ void MainWindow::updateWindowTitle()
ImagePtr ptr = m_ringList->currentImage(); ImagePtr ptr = m_ringList->currentImage();
if(ptr) if(ptr)
{ {
QFileInfo info(ptr->name()); QDir dir(m_ringList->currentDir());
QString title = info.fileName(); QString title = dir.relativeFilePath(ptr->name());
if(ptr->info().num > 1)
title += QString(" [%1/%2]").arg(ptr->info().index + 1).arg(ptr->info().num);
if(m_database->isMarked(ptr->name())) if(m_database->isMarked(ptr->name()))
title += " *"; title += " *";
setWindowTitle(title); setWindowTitle(title);
-2
View File
@@ -34,8 +34,6 @@ public:
MainWindow(QWidget *parent = 0); MainWindow(QWidget *parent = 0);
~MainWindow() override; ~MainWindow() override;
protected: protected:
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void setupSigterm(); void setupSigterm();
static void signalHandler(int); static void signalHandler(int);
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
+1
View File
@@ -44,6 +44,7 @@ PlateSolving::PlateSolving(QWidget *parent)
connect(_solver, &Solver::solvingDone, this, &PlateSolving::solvingDone); connect(_solver, &Solver::solvingDone, this, &PlateSolving::solvingDone);
connect(_solver, &Solver::extractionDone, this, &PlateSolving::extractionDone); connect(_solver, &Solver::extractionDone, this, &PlateSolving::extractionDone);
connect(_solver, &Solver::logOutput, [this](const QString &log){ _ui->log->appendPlainText(log); }); connect(_solver, &Solver::logOutput, [this](const QString &log){ _ui->log->appendPlainText(log); });
connect(_solver, &Solver::headerUpdated, this, &PlateSolving::headerUpdated);
} }
PlateSolving::~PlateSolving() PlateSolving::~PlateSolving()
+2
View File
@@ -32,6 +32,8 @@ public slots:
void updateHeader(); void updateHeader();
void imageLoaded(Image *image); void imageLoaded(Image *image);
void settings(); void settings();
signals:
void headerUpdated(const QString &path);
private: private:
Ui::PlateSolving *_ui; Ui::PlateSolving *_ui;
}; };
+3 -3
View File
@@ -1,8 +1,8 @@
#include "rawimage.h" #include "rawimage.h"
#include <cstring> #include <cstring>
#include <lcms2.h>
#include <algorithm> #include <algorithm>
#ifndef NO_QT #ifndef NO_QT
#include <lcms2.h>
#include <QDebug> #include <QDebug>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QFloat16> #include <QFloat16>
@@ -55,7 +55,7 @@ void RawImage::allocate(uint32_t w, uint32_t h, uint32_t ch, DataType type)
m_width = w; m_width = w;
m_height = h; m_height = h;
m_channels = ch; m_channels = ch;
m_ch = ch == 3 ? 4 : ch; m_ch = ch > 1 ? 4 : ch;
m_origType = m_type = type; m_origType = m_type = type;
m_pixels = std::make_unique<PixelType[]>((size_t)m_width * m_height * m_ch * typeSize(type)); m_pixels = std::make_unique<PixelType[]>((size_t)m_width * m_height * m_ch * typeSize(type));
} }
@@ -1071,7 +1071,6 @@ void RawImage::setICCProfile(const QByteArray &icc)
if(icc.size()) if(icc.size())
m_iccProfile = std::vector<uint8_t>(icc.begin(), icc.end()); m_iccProfile = std::vector<uint8_t>(icc.begin(), icc.end());
} }
#endif
void RawImage::setICCProfile(const LibXISF::ByteArray &icc) void RawImage::setICCProfile(const LibXISF::ByteArray &icc)
{ {
@@ -1180,6 +1179,7 @@ void RawImage::generateLUT()
cmsCloseProfile(inProfile); cmsCloseProfile(inProfile);
cmsCloseProfile(outProfile); cmsCloseProfile(outProfile);
} }
#endif
void RawImage::applySTF(const MTFParam &mtfParams) void RawImage::applySTF(const MTFParam &mtfParams)
{ {
+1 -1
View File
@@ -125,10 +125,10 @@ public:
bool valid() const; bool valid() const;
#ifndef NO_QT #ifndef NO_QT
void setICCProfile(const QByteArray &icc); void setICCProfile(const QByteArray &icc);
#endif
void setICCProfile(const LibXISF::ByteArray &icc); void setICCProfile(const LibXISF::ByteArray &icc);
void convertTosRGB(); void convertTosRGB();
void generateLUT(); void generateLUT();
#endif
void applySTF(const MTFParam &mtfParams); void applySTF(const MTFParam &mtfParams);
MTFParam calcMTFParams(bool linked = false, bool debayer = false) const; MTFParam calcMTFParams(bool linked = false, bool debayer = false) const;
const std::vector<uint16_t>& getLUT() const; const std::vector<uint16_t>& getLUT() const;
+17 -2
View File
@@ -134,6 +134,21 @@ QJSValue ScriptEngine::getItem(const QStringList &items, const QString &label, i
return ret; return ret;
} }
void ScriptEngine::plot(const QJSValue &pointsArray)
{
if(pointsArray.isArray())
{
int len = pointsArray.property("length").toInt();
QVector<QPointF> points;
for(int i = 0; i < len; i++)
{
QJSValue point = pointsArray.property(i);
points.append(QPointF(point.property("x").toNumber(), point.property("y").toNumber()));
}
QMetaObject::invokeMethod(_parent, "plot", Qt::QueuedConnection, Q_ARG(QVector<QPointF>, points));
}
}
bool ScriptEngine::convert(File *file, QString &outpath, const QString &format, const QVariantMap &params, bool async) bool ScriptEngine::convert(File *file, QString &outpath, const QString &format, const QVariantMap &params, bool async)
{ {
QString path; QString path;
@@ -226,7 +241,7 @@ void ScriptEngine::setStartingSolution(const QJSValue &solution)
if(solution.isObject()) if(solution.isObject())
{ {
if(solution.hasProperty("ra") && solution.hasProperty("dec") && solution.property("ra").isNumber() && solution.property("dec").isNumber()) if(solution.hasProperty("ra") && solution.hasProperty("dec") && solution.property("ra").isNumber() && solution.property("dec").isNumber())
_solver->setSearchPosition(solution.property("ra").toNumber(), solution.property("dec").toNumber()); _solver->setSearchPosition(solution.property("ra").toNumber() / 15.0, solution.property("dec").toNumber());
if(solution.hasProperty("pixscale") && solution.property("pixscale").isNumber()) if(solution.hasProperty("pixscale") && solution.property("pixscale").isNumber())
{ {
@@ -737,7 +752,7 @@ QJSValue File::stats()
{ {
ImageInfoData info; ImageInfoData info;
std::shared_ptr<RawImage> rawImage; std::shared_ptr<RawImage> rawImage;
loadImage(_path, info, rawImage); loadImage(_path, info, rawImage, 0);
rawImage->calcStats(); rawImage->calcStats();
RawImage::Stats stats = rawImage->imageStats(); RawImage::Stats stats = rawImage->imageStats();
_stats = _engine->newObject(); _stats = _engine->newObject();
+1
View File
@@ -47,6 +47,7 @@ public:
Q_INVOKABLE QJSValue getInt(const QString &label = QString(), int value = 0); Q_INVOKABLE QJSValue getInt(const QString &label = QString(), int value = 0);
Q_INVOKABLE QJSValue getFloat(const QString &label = QString(), double value = 0, int decimals = 3) const; Q_INVOKABLE QJSValue getFloat(const QString &label = QString(), double value = 0, int decimals = 3) const;
Q_INVOKABLE QJSValue getItem(const QStringList &items, const QString &label = "", int current = 0) const; Q_INVOKABLE QJSValue getItem(const QStringList &items, const QString &label = "", int current = 0) const;
Q_INVOKABLE void plot(const QJSValue &pointsArray);
bool convert(File *file, QString &outpath, const QString &format, const QVariantMap &params, bool async); bool convert(File *file, QString &outpath, const QString &format, const QVariantMap &params, bool async);
#ifdef PLATESOLVER #ifdef PLATESOLVER
Q_INVOKABLE void setSolverProfile(int index); Q_INVOKABLE void setSolverProfile(int index);
+63 -3
View File
@@ -10,12 +10,15 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDir> #include <QDir>
#include <QPushButton> #include <QPushButton>
#include <QLineEdit>
#include <QColorDialog>
#include "rawimage.h" #include "rawimage.h"
extern int DEFAULT_WIDTH; extern int DEFAULT_WIDTH;
extern double SATURATION; extern double SATURATION;
extern int FILTERING; extern int FILTERING;
extern bool BESTFIT; extern bool BESTFIT;
extern QMap<QString, QColor> headerHighlight;
class EvenNumber : public QSpinBox class EvenNumber : public QSpinBox
{ {
@@ -87,6 +90,44 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
m_bestFit->setChecked(BESTFIT); m_bestFit->setChecked(BESTFIT);
m_headerHighlight = new QListWidget(this);
for(auto i = headerHighlight.begin(); i != headerHighlight.end(); i++)
{
QListWidgetItem *item = new QListWidgetItem(m_headerHighlight);
item->setText(i.key());
item->setBackground(i.value());
}
m_keyword = new QLineEdit(this);
QPushButton *color = new QPushButton(this);
QPixmap pix(16, 16);
pix.fill(m_color);
color->setIcon(pix);
connect(color, &QPushButton::clicked, [this, color](){
QColor rgb = QColorDialog::getColor(m_color, this);
if(rgb.isValid())
{
QPixmap pix(16, 16);
pix.fill(rgb);
color->setIcon(pix);
m_color = rgb;
}
});
QPushButton *add = new QPushButton(tr("Add keyword highlight"), this);
connect(add, &QPushButton::clicked, [this](){
auto list = m_headerHighlight->findItems(m_keyword->text(), Qt::MatchFixedString | Qt::MatchCaseSensitive);
if(list.size())return;
QListWidgetItem *item = new QListWidgetItem(m_headerHighlight);
item->setText(m_keyword->text());
item->setBackground(m_color);
});
QPushButton *remove = new QPushButton(tr("Remove keyword highlight"), this);
connect(remove, &QPushButton::clicked, [this](){
auto list = m_headerHighlight->selectedItems();
for(auto item : list)
delete item;
});
layout->addRow(tr("Image preload count"), m_preloadImages); layout->addRow(tr("Image preload count"), m_preloadImages);
layout->addRow(tr("Thumbnails size"), m_thumSize); layout->addRow(tr("Thumbnails size"), m_thumSize);
layout->addRow(tr("Saturation"), m_saturation); layout->addRow(tr("Saturation"), m_saturation);
@@ -95,6 +136,9 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
layout->addRow(m_qualityThumbnail); layout->addRow(m_qualityThumbnail);
layout->addRow(m_useNativeDialog); layout->addRow(m_useNativeDialog);
layout->addRow(m_bestFit); layout->addRow(m_bestFit);
layout->addRow(m_headerHighlight);
layout->addRow(m_keyword, color);
layout->addRow(add, remove);
#ifdef Q_OS_WIN64 #ifdef Q_OS_WIN64
QPushButton *installThumbnailer = new QPushButton(tr("Install"), this); QPushButton *installThumbnailer = new QPushButton(tr("Install"), this);
@@ -125,6 +169,11 @@ void SettingsDialog::loadSettings()
FILTERING = settings.value("settings/filtering", FILTERING).toInt(); FILTERING = settings.value("settings/filtering", FILTERING).toInt();
QUALITY_RESIZE = settings.value("settings/qualitythumbnail", QUALITY_RESIZE).toBool(); QUALITY_RESIZE = settings.value("settings/qualitythumbnail", QUALITY_RESIZE).toBool();
BESTFIT = settings.value("settings/bestfit", BESTFIT).toBool(); BESTFIT = settings.value("settings/bestfit", BESTFIT).toBool();
QStringList keywords = settings.value("settings/headerhighlightkeywords").toStringList();
QStringList colors = settings.value("settings/headerhighlightcolors").toStringList();
for(int i = 0; i < std::min(keywords.size(), colors.size()); i++)
headerHighlight.insert(keywords[i], QColor::fromString(colors[i]));
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool()); QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
} }
@@ -150,10 +199,10 @@ void SettingsDialog::installThumbnailer()
QProcess regsvr; QProcess regsvr;
int ret = regsvr.execute("regsvr32.exe", {"/s", path}); int ret = regsvr.execute("regsvr32.exe", {"/s", path});
if(ret) if(ret == 0)
{ QMessageBox::information(this, tr("Thumbnail support"), tr("Thumbnail generation support sucessufully installed."));
else
QMessageBox::critical(this, tr("Error"), tr("Failed to register thumbnailer. %1").arg(ret)); QMessageBox::critical(this, tr("Error"), tr("Failed to register thumbnailer. %1").arg(ret));
}
#endif #endif
} }
@@ -175,4 +224,15 @@ void SettingsDialog::saveSettings()
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked()); QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked());
if(DEFAULT_WIDTH != m_preloadImages->value()) if(DEFAULT_WIDTH != m_preloadImages->value())
emit preloadChanged(m_preloadImages->value()); emit preloadChanged(m_preloadImages->value());
headerHighlight.clear();
QStringList colors;
for(int i = 0; i < m_headerHighlight->count(); i++)
{
auto item = m_headerHighlight->item(i);
colors.push_back(item->background().color().name());
headerHighlight[item->text()] = item->background().color();
}
settings.setValue("settings/headerhighlightkeywords", headerHighlight.keys());
settings.setValue("settings/headerhighlightcolors", colors);
} }
+4
View File
@@ -5,6 +5,7 @@
#include <QSpinBox> #include <QSpinBox>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox> #include <QComboBox>
#include <QListWidget>
class SettingsDialog : public QDialog class SettingsDialog : public QDialog
{ {
@@ -28,6 +29,9 @@ private:
QCheckBox *m_qualityThumbnail; QCheckBox *m_qualityThumbnail;
QComboBox *m_filtering; QComboBox *m_filtering;
QCheckBox *m_bestFit; QCheckBox *m_bestFit;
QListWidget *m_headerHighlight;
QColor m_color = Qt::yellow;
QLineEdit *m_keyword;
}; };
#endif // SETTINGSDIALOG_H #endif // SETTINGSDIALOG_H
+2 -1
View File
@@ -41,7 +41,7 @@ bool Solver::loadImage(const QString &path)
_loaded = false; _loaded = false;
std::shared_ptr<RawImage> image; std::shared_ptr<RawImage> image;
ImageInfoData info; ImageInfoData info;
if(::loadImage(path, info, image, true)) if(::loadImage(path, info, image, 0, true))
{ {
return loadImage(image, path); return loadImage(image, path);
} }
@@ -188,6 +188,7 @@ bool Solver::updateHeader(QString &error)
modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates")); modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates"));
bool ret = file.modifyFITSRecords(&modify); bool ret = file.modifyFITSRecords(&modify);
if(!ret)error = tr("Failed to update file header"); if(!ret)error = tr("Failed to update file header");
else emit headerUpdated(_path);
return ret; return ret;
} }
+1
View File
@@ -46,6 +46,7 @@ public slots:
signals: signals:
void solvingDone(); void solvingDone();
void extractionDone(); void extractionDone();
void headerUpdated(const QString &path);
void logOutput(const QString &log); void logOutput(const QString &log);
}; };
+78 -12
View File
@@ -12,7 +12,7 @@ static float clamp(float x)
STFSlider::STFSlider(const QColor &color, QWidget *parent) : QWidget(parent) STFSlider::STFSlider(const QColor &color, QWidget *parent) : QWidget(parent)
{ {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumWidth(100); setMinimumWidth(100);
setMouseTracking(true); setMouseTracking(true);
@@ -64,12 +64,51 @@ void STFSlider::setMTFParams(float low, float mid, float high)
update(); update();
} }
void STFSlider::orientationChanged(Qt::Orientations orientation)
{
m_orientation = orientation;
if(m_orientation == Qt::Horizontal)
{
if(m_color == Qt::white)
{
setMaximumSize(QWIDGETSIZE_MAX, 16);
setMinimumSize(16, 16);
}
else
{
setMaximumSize(QWIDGETSIZE_MAX, 10);
setMinimumSize(10, 10);
}
}
else
{
if(m_color == Qt::white)
{
setMaximumSize(16, QWIDGETSIZE_MAX);
setMinimumSize(16, 16);
}
else
{
setMaximumSize(10, QWIDGETSIZE_MAX);
setMinimumSize(10, 10);
}
}
}
void STFSlider::paintEvent(QPaintEvent *event) void STFSlider::paintEvent(QPaintEvent *event)
{ {
QPainter painter(this); QPainter painter(this);
QRect rect = event->rect(); QRect rect = event->rect();
qreal w = rect.width() - 1; qreal w = rect.width() - 1;
qreal h = rect.height(); qreal h = rect.height();
if(m_orientation == Qt::Vertical)
{
rect = rect.transposed();
painter.rotate(90);
w = rect.width() - 1;
h = rect.height();
painter.translate(0, -h);
}
QLinearGradient gradient(rect.topLeft(), rect.topRight()); QLinearGradient gradient(rect.topLeft(), rect.topRight());
gradient.setColorAt(0, Qt::black); gradient.setColorAt(0, Qt::black);
for(int i=1; i<=32; i++) for(int i=1; i<=32; i++)
@@ -93,6 +132,11 @@ void STFSlider::paintEvent(QPaintEvent *event)
{ {
painter.setPen(p < m_threshold ? Qt::white : Qt::black); painter.setPen(p < m_threshold ? Qt::white : Qt::black);
painter.resetTransform(); painter.resetTransform();
if(m_orientation == Qt::Vertical)
{
painter.rotate(90);
painter.translate(0, -h);
}
painter.translate(w*p, 0); painter.translate(w*p, 0);
painter.drawPath(tick); painter.drawPath(tick);
}; };
@@ -105,15 +149,26 @@ void STFSlider::paintEvent(QPaintEvent *event)
void STFSlider::mouseMoveEvent(QMouseEvent *event) void STFSlider::mouseMoveEvent(QMouseEvent *event)
{ {
const qreal x = event->position().x(); qreal x,w;
if(std::abs(m_blackPoint*width() - x) < 5 || if(m_orientation == Qt::Horizontal)
std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - x) < 5 || {
std::abs(m_whitePoint*width() - x) < 5) x = event->position().x();
setCursor(Qt::SplitHCursor); w = width();
}
else
{
x = event->position().y();
w = height();
}
if(std::abs(m_blackPoint*w - x) < 5 ||
std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*w - x) < 5 ||
std::abs(m_whitePoint*w - x) < 5)
setCursor(m_orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
else else
unsetCursor(); unsetCursor();
qreal xw = x/width(); qreal xw = x/w;
if(event->modifiers() & Qt::ShiftModifier && !m_fineTune) if(event->modifiers() & Qt::ShiftModifier && !m_fineTune)
{ {
m_fineTune = true; m_fineTune = true;
@@ -154,18 +209,29 @@ void STFSlider::mouseMoveEvent(QMouseEvent *event)
void STFSlider::mousePressEvent(QMouseEvent *event) void STFSlider::mousePressEvent(QMouseEvent *event)
{ {
const qreal x = event->position().x(); qreal x,w;
if(m_orientation == Qt::Horizontal)
{
x = event->position().x();
w = width();
}
else
{
x = event->position().y();
w = height();
}
if(event->modifiers() & Qt::ShiftModifier) if(event->modifiers() & Qt::ShiftModifier)
{ {
m_fineTune = true; m_fineTune = true;
m_fineTuneX = x/width(); m_fineTuneX = x/w;
} }
if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - x) < 5) if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*w - x) < 5)
m_grabbed = 1; m_grabbed = 1;
else if(std::abs(m_blackPoint*width() - x) < 5) else if(std::abs(m_blackPoint*w - x) < 5)
m_grabbed = 0; m_grabbed = 0;
else if(std::abs(m_whitePoint*width() - x) < 5) else if(std::abs(m_whitePoint*w - x) < 5)
m_grabbed = 2; m_grabbed = 2;
else else
m_grabbed = -1; m_grabbed = -1;
+3
View File
@@ -15,12 +15,15 @@ class STFSlider : public QWidget
float m_fineTuneX; float m_fineTuneX;
QColor m_color; QColor m_color;
float m_threshold; float m_threshold;
Qt::Orientations m_orientation = Qt::Horizontal;
public: public:
explicit STFSlider(const QColor &color = Qt::white, QWidget *parent = nullptr); explicit STFSlider(const QColor &color = Qt::white, QWidget *parent = nullptr);
float blackPoint() const; float blackPoint() const;
float midPoint() const; float midPoint() const;
float whitePoint() const; float whitePoint() const;
void setMTFParams(float low, float mid, float high); void setMTFParams(float low, float mid, float high);
public slots:
void orientationChanged(Qt::Orientations orientation);
signals: signals:
void paramChanged(float blackPoint, float midPoint, float whitePoint); void paramChanged(float blackPoint, float midPoint, float whitePoint);
protected: protected:
+12 -16
View File
@@ -6,17 +6,6 @@
#include <QStyle> #include <QStyle>
#include "imageringlist.h" #include "imageringlist.h"
const float BLACK_POINT_SIGMA = -2.8f;
const float MAD_TO_SIGMA = 1.4826f;
const float TARGET_BACKGROUND = 0.25f;
float MTF(float x, float m)
{
if(x < 0)return 0;
if(x > 1)return 1;
return ((m - 1) * x) / ((2 * m - 1) * x - m);
}
StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar"), parent) StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar"), parent)
{ {
setObjectName("stretchtoolbar"); setObjectName("stretchtoolbar");
@@ -24,16 +13,23 @@ StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar")
QVBoxLayout *vbox1 = new QVBoxLayout(lum); QVBoxLayout *vbox1 = new QVBoxLayout(lum);
m_stfSlider = new STFSlider(Qt::white, this); m_stfSlider = new STFSlider(Qt::white, this);
vbox1->addWidget(m_stfSlider); vbox1->addWidget(m_stfSlider);
connect(this, &StretchToolbar::orientationChanged, m_stfSlider, &STFSlider::orientationChanged);
m_stfSliderR = new STFSlider(Qt::red, this); m_stfSliderR = new STFSlider(Qt::red, this);
m_stfSliderG = new STFSlider(Qt::green, this); m_stfSliderG = new STFSlider(Qt::green, this);
m_stfSliderB = new STFSlider(Qt::blue, this); m_stfSliderB = new STFSlider(Qt::blue, this);
QWidget *rgb = new QWidget(this); QWidget *rgb = new QWidget(this);
QVBoxLayout *vbox2 = new QVBoxLayout(rgb); QBoxLayout *box2 = new QBoxLayout(orientation() == Qt::Horizontal ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight, rgb);
vbox2->setSpacing(0); box2->setSpacing(0);
vbox2->addWidget(m_stfSliderR); box2->addWidget(m_stfSliderR);
vbox2->addWidget(m_stfSliderG); box2->addWidget(m_stfSliderG);
vbox2->addWidget(m_stfSliderB); box2->addWidget(m_stfSliderB);
connect(this, &StretchToolbar::orientationChanged, m_stfSliderR, &STFSlider::orientationChanged);
connect(this, &StretchToolbar::orientationChanged, m_stfSliderG, &STFSlider::orientationChanged);
connect(this, &StretchToolbar::orientationChanged, m_stfSliderB, &STFSlider::orientationChanged);
connect(this, &StretchToolbar::orientationChanged, [box2](Qt::Orientations orientation){
box2->setDirection(orientation == Qt::Horizontal ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
});
m_stack = new QStackedWidget(this); m_stack = new QStackedWidget(this);
m_stack->addWidget(lum); m_stack->addWidget(lum);
+6 -6
View File
@@ -4,7 +4,7 @@ if(BUILD_THUMBNAILER)
if(WIN32) if(WIN32)
add_library(tenmonthumbnailer SHARED add_library(tenmonthumbnailer SHARED
Dll.cpp Dll.cpp
loadxisf.cpp loadimage.cpp
TenmonThumbnailProvider.cpp TenmonThumbnailProvider.cpp
../rawimage.h ../rawimage.h
../rawimage.cpp ../rawimage.cpp
@@ -13,19 +13,19 @@ if(BUILD_THUMBNAILER)
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT) target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF) target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
target_link_libraries(tenmonthumbnailer PRIVATE shlwapi ${LCMS2_LIB} ${FITS_LIB} XISF) target_link_libraries(tenmonthumbnailer PRIVATE shlwapi ${FITS_LIB} XISF)
target_link_options(tenmonthumbnailer PRIVATE "-static") target_link_options(tenmonthumbnailer PRIVATE "-static")
else(WIN32) else(WIN32)
qt_add_executable(tenmonthumbnailer qt_add_executable(tenmonthumbnailer
main.cpp main.cpp
../loadimage.cpp loadimage.cpp
../rawimage.cpp ../rawimage.cpp
../rawimage_sse.cpp ../rawimage_sse.cpp)
../imageinfodata.cpp)
target_link_libraries(tenmonthumbnailer PRIVATE Qt6::Core Qt6::Gui ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF) target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF)
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF) target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
endif(WIN32) endif(WIN32)
endif(BUILD_THUMBNAILER) endif(BUILD_THUMBNAILER)
+87 -9
View File
@@ -2,9 +2,87 @@
#include <thumbcache.h> // For IThumbnailProvider. #include <thumbcache.h> // For IThumbnailProvider.
#include <new> #include <new>
#include "libxisf.h" #include "libxisf.h"
#include "../rawimage.h"
bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize); bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize); bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
void RawImageToHTBITMAP(std::shared_ptr<RawImage> &rawImage, HBITMAP *hbmp, UINT thumbSize)
{
rawImage->calcStats();
DWORD thre = 20;
DWORD dataSize = 4;
HRESULT hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\nou\\Tenmon\\settings", L"thumbnailstretchthreshold", RRF_RT_DWORD, NULL, &thre, &dataSize));
float thref = 0.1f;
if(hr == S_OK)
thref = thre / 100.0f;
if(rawImage->imageStats().m_median[0] < rawImage->norm() * thref)
{
//OutputDebugStringA("Stretch image");
MTFParam params = rawImage->calcMTFParams();
rawImage->applySTF(params);
}
UINT w = rawImage->width();
UINT h = rawImage->height();
UINT cw = thumbSize;
UINT ch = thumbSize;
if (w > h)
ch = h * thumbSize / w;
else
cw = w * thumbSize / h;
rawImage->resize(cw, ch);
rawImage->convertToType(RawImage::UINT8);
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = cw;
bmi.bmiHeader.biHeight = -static_cast<LONG>(ch);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
UINT lw = cw * 4;
BYTE *pBits;
HBITMAP bmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&pBits), NULL, 0);
const unsigned char *p = (const unsigned char*)rawImage->data();
const unsigned short *ps = (const unsigned short*)rawImage->data();
if(rawImage->channels() == 1)
{
for(UINT y = 0; y < ch; y++)
{
for(UINT x = 0; x < cw; x++)
{
pBits[(y * lw) + x * 4 + 0] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 1] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 2] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 3] = 255;
}
}
}
else
{
for(UINT y = 0; y < ch; y++)
{
for(UINT x = 0; x < cw; x++)
{
pBits[(y * lw) + x * 4 + 0] = p[y * cw * 4 + x * 4 + 2];
pBits[(y * lw) + x * 4 + 1] = p[y * cw * 4 + x * 4 + 1];
pBits[(y * lw) + x * 4 + 2] = p[y * cw * 4 + x * 4 + 0];
pBits[(y * lw) + x * 4 + 3] = 255;
}
}
}
*hbmp = bmp;
}
class TenmonThumbProvider : public IInitializeWithStream, class TenmonThumbProvider : public IInitializeWithStream,
public IThumbnailProvider public IThumbnailProvider
@@ -103,19 +181,19 @@ IFACEMETHODIMP TenmonThumbProvider::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_AL
*pdwAlpha = WTSAT_RGB; *pdwAlpha = WTSAT_RGB;
data.resize(readSize); data.resize(readSize);
std::shared_ptr<RawImage> rawImage;
if(data[0] == 'X' && data[1] == 'I' && data[2] == 'S' && data[3] == 'F') if(data[0] == 'X' && data[1] == 'I' && data[2] == 'S' && data[3] == 'F')
{ {
if(loadXISF(data, phbmp, cx)) if(!loadXISF(data, rawImage))
return S_OK;
else
return E_FAIL; return E_FAIL;
} }
else else
{ {
if(loadFITS(data, phbmp, cx)) if(!loadFITS(data, rawImage))
return S_OK;
else
return E_FAIL; return E_FAIL;
} }
return E_FAIL;
RawImageToHTBITMAP(rawImage, phbmp, cx);
return S_OK;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ int generateThumbnail(const QString &input, const QString &output, uint32_t size
{ {
ImageInfoData info; ImageInfoData info;
std::shared_ptr<RawImage> rawImage; std::shared_ptr<RawImage> rawImage;
if(!loadImage(input, info, rawImage)) if(!loadImage(input, info, rawImage, 0))
return 2; return 2;
if(!rawImage) if(!rawImage)
+163
View File
@@ -0,0 +1,163 @@
#include "libxisf.h"
#include "../rawimage.h"
#ifdef WIN32
#include <windows.h>
#endif
#include <fitsio2.h>
bool OpenGLES = false;
bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage)
{
try
{
LibXISF::XISFReader xisf;
xisf.open(data);
const LibXISF::Image &xisfImage = xisf.getImage(0);
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: return false;
}
LibXISF::Image tmpImage = xisfImage;
tmpImage.convertPixelStorageTo(LibXISF::Image::Planar);
if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
{
rawImage = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
std::memcpy(rawImage->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
}
else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
{
rawImage = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
}
return true;
}
catch (LibXISF::Error &err)
{
#ifdef WIN32
char text[1024];
sprintf_s(text, 1000, "Failed to open XISF image %s", err.what());
OutputDebugStringA(text);
#endif
return false;
}
return false;
}
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage)
{
fitsfile *file;
int status = 0;
int hdutype = -1;
int num = 0;
long naxes[3] = {0};
auto checkError = [&status]()
{
char err[100];
fits_get_errstatus(status, err);
#ifdef WIN32
char text[1000];
sprintf_s(text, 1000, "Failed to load FITS file %s", err);
OutputDebugStringA(text);
#endif
return false;
};
const void *dataPtr = data.data();
size_t size = data.size();
fits_open_memfile(&file, "file.fits", READONLY, (void**)&dataPtr, &size, 0, nullptr, &status);
if(status)return checkError();
fits_get_num_hdus(file, &num, &status);
if(status)return checkError();
int imgtype;
int naxis;
for(int i=1; i <= num; i++)
{
fits_movabs_hdu(file, i, &hdutype, &status);if(status)return checkError();
if(hdutype == IMAGE_HDU)
{
naxes[0] = naxes[1] = naxes[2] = 0;
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
if(hdutype == 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:
return false;
break;
}
size_t size = naxes[0]*naxes[1];
size_t w = naxes[0];
size_t h = 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(status)return checkError();
}
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)
rawImage = std::make_shared<RawImage>(std::move(img));
else
rawImage = RawImage::fromPlanar(img);
return true;
}
}
}
return false;
}
-237
View File
@@ -1,237 +0,0 @@
#include "libxisf.h"
#include <thumbcache.h>
#include "../rawimage.h"
#include <fitsio2.h>
bool OpenGLES = false;
void RawImageToHTBITMAP(std::shared_ptr<RawImage> &rawImage, HBITMAP *hbmp, UINT thumbSize)
{
rawImage->calcStats();
DWORD thre = 10;
DWORD dataSize = 4;
//HRESULT hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\nou\\Tenmon\\settings", L"thumbnailstretchthreshold", RRF_RT_DWORD, NULL, &thre, &dataSize));
float thref = 0.1f;
/*if(hr == S_OK)
thref = thre / 100.0f;*/
if(rawImage->imageStats().m_mean[0] < rawImage->norm() * thref)
{
//OutputDebugStringA("Stretch image");
MTFParam params = rawImage->calcMTFParams();
rawImage->applySTF(params);
}
UINT w = rawImage->width();
UINT h = rawImage->height();
UINT cw = thumbSize;
UINT ch = thumbSize;
if (w > h)
ch = h * thumbSize / w;
else
cw = w * thumbSize / h;
rawImage->resize(cw, ch);
rawImage->convertToType(RawImage::UINT8);
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = cw;
bmi.bmiHeader.biHeight = -static_cast<LONG>(ch);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
UINT lw = cw * 4;
BYTE *pBits;
HBITMAP bmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&pBits), NULL, 0);
const unsigned char *p = (const unsigned char*)rawImage->data();
const unsigned short *ps = (const unsigned short*)rawImage->data();
if(rawImage->channels() == 1)
{
for(UINT y = 0; y < ch; y++)
{
for(UINT x = 0; x < cw; x++)
{
pBits[(y * lw) + x * 4 + 0] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 1] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 2] = p[y * cw + x];
pBits[(y * lw) + x * 4 + 3] = 255;
}
}
}
else
{
for(UINT y = 0; y < ch; y++)
{
for(UINT x = 0; x < cw; x++)
{
pBits[(y * lw) + x * 4 + 0] = p[y * cw * 4 + x * 4 + 2];
pBits[(y * lw) + x * 4 + 1] = p[y * cw * 4 + x * 4 + 1];
pBits[(y * lw) + x * 4 + 2] = p[y * cw * 4 + x * 4 + 0];
pBits[(y * lw) + x * 4 + 3] = 255;
}
}
}
*hbmp = bmp;
}
bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize)
{
try
{
LibXISF::XISFReader xisf;
xisf.open(data);
const LibXISF::Image &xisfImage = xisf.getImage(0);
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);
std::shared_ptr<RawImage> rawImage;
if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
{
rawImage = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
std::memcpy(rawImage->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
}
else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
{
rawImage = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
}
RawImageToHTBITMAP(rawImage, hbmp, thumbSize);
return true;
}
catch (LibXISF::Error &err)
{
char text[1024];
sprintf_s(text, 1000, "Failed to open XISF image %s", err.what());
OutputDebugStringA(text);
return false;
}
return false;
}
bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize)
{
fitsfile *file;
int status = 0;
int type = -1;
int num = 0;
long naxes[3] = {0};
auto checkError = [&status]()
{
char err[100];
char text[1000];
fits_get_errstatus(status, err);
sprintf_s(text, 1000, "Failed to load FITS file %s", err);
OutputDebugStringA(text);
return false;
};
const void *dataPtr = data.data();
size_t size = data.size();
fits_open_memfile(&file, "file.fits", READONLY, (void**)&dataPtr, &size, 0, nullptr, &status);
if(status)return checkError();
fits_get_num_hdus(file, &num, &status);
if(status)return checkError();
int imgtype;
int naxis;
for(int i=1; i <= num; i++)
{
fits_movabs_hdu(file, i, IMAGE_HDU, &status);if(status)return checkError();
fits_get_hdu_type(file, &type, &status);if(status)return checkError();
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
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:
return false;
break;
}
size_t size = naxes[0]*naxes[1];
size_t w = naxes[0];
size_t h = 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(status)return checkError();
}
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;
}
std::shared_ptr<RawImage> image;
if(img.channels() == 1)
image = std::make_shared<RawImage>(std::move(img));
else
image = RawImage::fromPlanar(img);
RawImageToHTBITMAP(image, hbmp, thumbSize);
return true;
}
}
return false;
}
+53 -35
View File
@@ -1,62 +1,80 @@
#include <QCoreApplication> #include <vector>
#include <QCommandLineParser> #include <string>
#include <iostream>
#include "../rawimage.h" #include "../rawimage.h"
#include "../loadimage.h" #define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
bool OpenGLES = false; bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication a(argc, argv); std::vector<std::string> args;
for(int i=0; i<argc; i++)
args.push_back(argv[i]);
QCommandLineParser parser; if(args.size() < 3)
parser.addOption({{"s", "size"}, "Size of the thumbnail in pixels (default: 128)", "size", "128"});
parser.addPositionalArgument("input", "Input image file");
parser.addPositionalArgument("output", "Output image file");
parser.addHelpOption();
parser.process(a);
QStringList args = parser.positionalArguments();
if(args.size() < 2)
return 1; return 1;
QString input = args[0]; std::string input = args[1];
QString output = args[1]; std::string output = args[2];
ImageInfoData info;
std::shared_ptr<RawImage> rawImage; std::shared_ptr<RawImage> rawImage;
if(!loadImage(input, info, rawImage))
LibXISF::ByteArray data;
std::ifstream fr;
fr.open(input, std::ios_base::in | std::ios_base::binary);
if(!fr.is_open())
return 2; return 2;
if(!rawImage) fr.seekg(0, std::ios_base::end);
size_t len = fr.tellg();
fr.seekg(0, std::ios_base::beg);
data.resize(len);
fr.read(data.data(), len);
if(fr.bad())
return 3; return 3;
bool ok; if(input.find(".xisf") != std::string::npos)
int size = parser.value("s").toInt(&ok); {
if(!ok) if(!loadXISF(data, rawImage))
size = 128; return 4;
}
else
{
if(!loadFITS(data, rawImage))
return 4;
}
if(!rawImage)
return 5;
uint32_t thumbSize = 256;
uint32_t w = rawImage->width();
uint32_t h = rawImage->height();
uint32_t cw = thumbSize;
uint32_t ch = thumbSize;
if (w > h)
ch = h * thumbSize / w;
else
cw = w * thumbSize / h;
QSize rect(rawImage->width(), rawImage->height());
rect.scale(size, size, Qt::KeepAspectRatio);
rawImage->calcStats(); rawImage->calcStats();
rawImage->resize(rect.width(), rect.height()); rawImage->resize(cw, ch);
if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.2f) if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.1f)
{ {
MTFParam mtfParams = rawImage->calcMTFParams(true); MTFParam mtfParams = rawImage->calcMTFParams(true);
rawImage->applySTF(mtfParams); rawImage->applySTF(mtfParams);
} }
rawImage->convertToType(RawImage::UINT8); rawImage->convertToType(RawImage::UINT8);
QImage img;
if(rawImage->channels() == 1) if(rawImage->channels() == 1)
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_Grayscale8); stbi_write_png(output.c_str(), cw, ch, 1, rawImage->data(), rawImage->widthBytes());
else else
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_RGBA8888); stbi_write_png(output.c_str(), cw, ch, 4, rawImage->data(), rawImage->widthBytes());
if(!img.save(output, "png"))
return 4;
return 0; return 0;
} }
File diff suppressed because it is too large Load Diff