Compare commits

...

4 Commits

Author SHA1 Message Date
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
15 changed files with 2134 additions and 333 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)
+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
+18 -1
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)
{ {
+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
+11 -28
View File
@@ -118,9 +118,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
navigationToolbar->setObjectName("navigationtoolbar"); navigationToolbar->setObjectName("navigationtoolbar");
navigationToolbar->hide(); navigationToolbar->hide();
QAction *prevAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowLeft), tr("Previous image")); 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")); QAction *nextAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowRight), tr("Next image"));
QAction *prevSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowUp), tr("Prev sub image")); nextAction->setShortcuts({Qt::Key_Right, Qt::Key_Down});
QAction *nextSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowDown), tr("Next sub image")); 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(prevAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::decrement));
connect(nextAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment)); 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(prevSubAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::prevSubImage));
@@ -195,6 +197,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);
@@ -359,32 +368,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__
-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;
+15
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;
+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);
+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;
} }
+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