Compare commits

...

31 Commits

Author SHA1 Message Date
nou 5249b277ec Add integer resample 2025-02-25 17:41:29 +01:00
nou e4b9fefa5a Move MTFParam 2025-02-25 17:35:55 +01:00
nou d069ce3302 Add shortcut to 100% zoom 2025-02-23 09:45:29 +01:00
nou 58c182adc0 Add thumbnailer 2025-02-16 23:36:25 +01:00
nou c36068aaf4 Fix mxe build 2025-02-16 16:19:39 +01:00
nou fcb3aec81f Remove startfit 2025-02-16 15:22:38 +01:00
nou 7510dac82b Reorginize code 2025-02-16 15:19:20 +01:00
nou 55439be04c Add Portuguese translation thanks to John Peter Sá 2025-02-07 15:10:19 +01:00
nou 0ff2001797 Handle MAX_PATH every where 2025-01-26 16:00:00 +01:00
nou fc36024eee Remove deprecated QSqlQuery usage 2025-01-19 15:49:31 +01:00
nou 3cda53f26c Do not attempt open not existing file 2025-01-19 15:11:32 +01:00
nou 58d18cc28a Remove unused function 2025-01-19 15:11:14 +01:00
nou 2b96da60de Do not use global thread pool 2025-01-12 10:59:12 +01:00
nou 236f66ed2f Add solver profile to script engine 2024-12-27 23:20:51 +01:00
nou a86c100e69 Fix issue with Qt 6.8 2024-12-22 12:19:34 +01:00
nou 45ee9b7258 Fix half pixel offset and add filtering in sw rendering
Signed-off-by: Dušan Poizl <nou.spiro@gmail.com>
2024-12-05 16:02:46 +01:00
nou be1e65251d Support really big images 50000px 2024-11-30 22:03:58 +01:00
nou 9b7837e9fb SW rendering when image is too big for texture 2024-11-27 20:21:57 +01:00
nou 4afa940886 Update metainfo 2024-11-16 22:58:34 +01:00
nou d1344d2dc8 Add support for uint32 and double in boxResample 2024-11-15 23:29:22 +01:00
nou 24eea573e6 Handle all data types when converting to QImage 2024-10-29 19:43:16 +01:00
nou 8f7f527732 Add RawImage::convertToType() 2024-10-29 19:42:36 +01:00
nou 3635ac00cb Fix alpha channel in fromPlanarSSE 2024-10-29 19:39:37 +01:00
nou eba9110933 Remove dead code 2024-10-29 15:32:06 +01:00
nou 464207beb1 Apply index folder directory change immediatly 2024-10-24 20:15:06 +02:00
nou 4aeff61c44 Add TIFF as valid format for script convert 2024-10-13 19:58:13 +02:00
nou 790c836bbd MXE build 2024-10-13 19:58:12 +02:00
nou 62616898ed Forgot to add fts and fz to one place 2024-10-13 19:57:21 +02:00
nou e216af6a6d Better handling of missing and overwrite files 2024-10-13 19:57:21 +02:00
nou e0d6f417a0 fixup! Fix compiling without stellarsolver 2024-10-04 20:51:53 +02:00
nou 3c5fef988e Fix compiling without stellarsolver 2024-10-02 15:25:32 +02:00
45 changed files with 3188 additions and 1576 deletions
+25 -16
View File
@@ -18,8 +18,6 @@ if(SANITIZE_ADDRESS_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 REQUIRED)
find_library(GSL_LIB gsl REQUIRED)
find_library(GSLCBLAS_LIB gslcblas 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)
@@ -39,18 +37,20 @@ set(TENMON_SRC
histogram.cpp histogram.h histogram.cpp histogram.h
httpdownloader.h httpdownloader.cpp httpdownloader.h httpdownloader.cpp
imageinfo.cpp imageinfo.h imageinfo.cpp imageinfo.h
imageinfodata.cpp imageinfodata.h
imageringlist.cpp imageringlist.h imageringlist.cpp imageringlist.h
imagescrollarea.cpp imagescrollarea.h imagescrollarea.cpp imagescrollarea.h
imagewidget.h imagewidget.cpp imagewidget.h imagewidget.cpp
loadimage.h loadimage.cpp
loadrunable.cpp loadrunable.h loadrunable.cpp loadrunable.h
main.cpp main.cpp
mainwindow.cpp mainwindow.h mainwindow.cpp mainwindow.h
markedfiles.cpp markedfiles.h markedfiles.cpp markedfiles.h
mtfparam.h
rawimage.cpp rawimage.h rawimage.cpp rawimage.h
rawimage_sse.cpp rawimage_sse.cpp
scriptengine.cpp scriptengine.h scriptengine.cpp scriptengine.h
settingsdialog.cpp settingsdialog.h settingsdialog.cpp settingsdialog.h
starfit.cpp starfit.h
statusbar.cpp statusbar.h statusbar.cpp statusbar.h
stfslider.cpp stfslider.h stfslider.cpp stfslider.h
stretchtoolbar.cpp stretchtoolbar.h stretchtoolbar.cpp stretchtoolbar.h
@@ -81,7 +81,26 @@ if(COLOR_MANAGMENT)
target_compile_definitions(tenmon PRIVATE "COLOR_MANAGMENT") target_compile_definitions(tenmon PRIVATE "COLOR_MANAGMENT")
endif(COLOR_MANAGMENT) endif(COLOR_MANAGMENT)
target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF) find_path(STELLARSOLVER_INCLUDE stellarsolver.h PATH_SUFFIXES libstellarsolver)
if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
target_include_directories(tenmon PRIVATE ${STELLARSOLVER_INCLUDE})
if(MXE)
find_library(GSL_LIB gsl REQUIRED)
find_library(GSLCBLAS_LIB gslcblas REQUIRED)
target_link_libraries(tenmon PRIVATE ${STELLARSOLVER_LIB} ${GSL_LIB} ${GSLCBLAS_LIB} boost_regex-mt-x64)
else(MXE)
target_link_libraries(tenmon PRIVATE ${STELLARSOLVER_LIB})
endif(MXE)
target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
target_sources(tenmon PRIVATE
solver.cpp solver.h
platesolving.cpp platesolving.h platesolving.ui
platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui
)
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${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)
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)
@@ -93,18 +112,6 @@ if(LIBRAW_STATIC)
target_link_libraries(tenmon PRIVATE jasper) target_link_libraries(tenmon PRIVATE jasper)
endif() endif()
find_path(STELLARSOLVER_INCLUDE stellarsolver.h PATH_SUFFIXES libstellarsolver)
if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
target_include_directories(tenmon PRIVATE ${STELLARSOLVER_INCLUDE})
target_link_libraries(tenmon PRIVATE ${STELLARSOLVER_LIB})
target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
target_sources(tenmon PRIVATE
solver.cpp solver.h
platesolving.cpp platesolving.h platesolving.ui
platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui
)
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
option(FLATPAK "Flatpak build" OFF) option(FLATPAK "Flatpak build" OFF)
if(FLATPAK) if(FLATPAK)
target_compile_definitions(tenmon PRIVATE FLATPAK) target_compile_definitions(tenmon PRIVATE FLATPAK)
@@ -133,3 +140,5 @@ else()
execute_process(COMMAND ${CMAKE_COMMAND} -Dlocal_dir=${CMAKE_CURRENT_SOURCE_DIR} -Doutput_dir=${CMAKE_CURRENT_BINARY_DIR} execute_process(COMMAND ${CMAKE_COMMAND} -Dlocal_dir=${CMAKE_CURRENT_SOURCE_DIR} -Doutput_dir=${CMAKE_CURRENT_BINARY_DIR}
-P "${CMAKE_CURRENT_SOURCE_DIR}/gitversion.cmake") -P "${CMAKE_CURRENT_SOURCE_DIR}/gitversion.cmake")
endif() endif()
add_subdirectory(thumbnailer)
+2 -2
View File
@@ -2,11 +2,11 @@ FITS/XISF image viewer with multithreaded image loading
To get all dependencies install these packages To get all dependencies install these packages
sudo apt install qt6-base-dev qt6-declarative-dev libqt6opengl6-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev cmake libzstd-dev libqt6sql6-sqlite sudo apt install qt6-base-dev qt6-declarative-dev libqt6opengl6-dev libraw-dev libexif-dev libcfitsio-dev wcslib-dev cmake libzstd-dev libqt6sql6-sqlite
on OpenSUSE on OpenSUSE
sudo zypper install gsl-devel libexif-devel libraw-devel wcslib-devel qt6-base-devel qt6-qml-devel libzstd-devel sudo zypper install libexif-devel libraw-devel wcslib-devel qt6-base-devel qt6-qml-devel libzstd-devel
MacOS X MacOS X
-3
View File
@@ -33,9 +33,6 @@ HelpDialog::HelpDialog(QWidget *parent) : QDialog(parent)
setWindowTitle(tr("Help")); setWindowTitle(tr("Help"));
resize(800, 600); resize(800, 600);
QLocale locale;
QString l = QLocale::languageToString(locale.language());
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
QTextEdit *helpText = new QTextEdit(this); QTextEdit *helpText = new QTextEdit(this);
helpText->setReadOnly(true); helpText->setReadOnly(true);
+1 -1
View File
@@ -202,7 +202,7 @@ In <b>files</b> array there are instances of type <b>File</b> objects that have
This path can be relative or absolute. In case that <i>newpath</i> parameter is relative path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can be absolute path. This path can be relative or absolute. In case that <i>newpath</i> parameter is relative path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can be absolute path.
File is then moved to this path.</li> File is then moved to this path.</li>
<li><b>convert(outpath, format, params)</b> convert image file from any format that program is able to open into FITS, XISF, JPEG, PNG, BMP. <li><b>convert(outpath, format, params)</b> convert image file from any format that program is able to open into FITS, XISF, JPEG, PNG, BMP.
Parameters are: <i>outputpath</i> path where converted image will be saved. It automatically replace suffix according to format. <i>format</i> one of "FITS" "XISF", "JPG", "PNG" or "BMP". <i>params</i> object with attributes "compressionType" and "compressionLevel". Parameters are: <i>outputpath</i> path where converted image will be saved. It automatically replace suffix according to format. <i>format</i> one of "FITS" "XISF", "JPG", "PNG", "TIFF" or "BMP". <i>params</i> object with attributes "compressionType" and "compressionLevel".
Valid values for compressionType are be "gzip" or "rice" when converting to FITS. When converting to XISF compressionType can be "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh". Valid values for compressionType are be "gzip" or "rice" when converting to FITS. When converting to XISF compressionType can be "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh".
It is recommended to use "+sh" variants of compression. It is recommended to use "+sh" variants of compression.
XISF format also accept "compressionLevel" in range 0-100 where zero is fastest compression and 100 slowest. If you omit this attribute or set it to -1 then default compression level will be used. XISF format also accept "compressionLevel" in range 0-100 where zero is fastest compression and 100 slowest. If you omit this attribute or set it to -1 then default compression level will be used.
+1 -1
View File
@@ -161,7 +161,7 @@ le fichier <i>C:/images/lights/red/M42_001.fits</i>, alors cette méthode renver
Ce chemin peut être relatif ou absolu. Dans le cas où le paramètre <i>newpath</i> est un chemin relatif, le "répertoire de sortie" des fenêtres de l'interface graphique est utilisé comme répertoire de base. Le paramètre <i>newpath</i> peut être un chemin absolu. Ce chemin peut être relatif ou absolu. Dans le cas où le paramètre <i>newpath</i> est un chemin relatif, le "répertoire de sortie" des fenêtres de l'interface graphique est utilisé comme répertoire de base. Le paramètre <i>newpath</i> peut être un chemin absolu.
Le fichier est ensuite déplacé vers ce chemin.</li> Le fichier est ensuite déplacé vers ce chemin.</li>
<li><b>convert(outpath, format, params)</b> Convertir un fichier image à partir de n'importe quel format que le programme peut ouvrir en FITS, XISF, JPEG, PNG, BMP. <li><b>convert(outpath, format, params)</b> Convertir un fichier image à partir de n'importe quel format que le programme peut ouvrir en FITS, XISF, JPEG, PNG, BMP.
Les paramètres sont : <i>outputpath</i> chemin où l'image convertie sera enregistrée. Il remplace automatiquement le suffixe en fonction du format. <i>format</i> l'un des éléments suivants : "FITS", "XISF", "JPG", "PNG" ou "BMP". <i>params</i> objet avec les attributs "compressionType" et "compressionLevel". Les paramètres sont : <i>outputpath</i> chemin où l'image convertie sera enregistrée. Il remplace automatiquement le suffixe en fonction du format. <i>format</i> l'un des éléments suivants : "FITS", "XISF", "JPG", "PNG", "TIFF" ou "BMP". <i>params</i> objet avec les attributs "compressionType" et "compressionLevel".
Les valeurs valides pour compressionType sont "gzip" ou "rice" lors de la conversion en FITS. Lors de la conversion en XISF, compressionType peut être "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh". Les valeurs valides pour compressionType sont "gzip" ou "rice" lors de la conversion en FITS. Lors de la conversion en XISF, compressionType peut être "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh".
Il est recommandé d'utiliser les variantes de compression "+sh". Il est recommandé d'utiliser les variantes de compression "+sh".
Le format XISF accepte également les "compressionLevel" dans la plage 0-100, où zéro est la compression la plus rapide et 100 la plus lente. Si vous omettez cet attribut ou le définissez sur -1, le niveau de compression par défaut sera utilisé. Le format XISF accepte également les "compressionLevel" dans la plage 0-100, où zéro est la compression la plus rapide et 100 la plus lente. Si vous omettez cet attribut ou le définissez sur -1, le niveau de compression par défaut sera utilisé.
+1 -1
View File
@@ -151,7 +151,7 @@ V poli <b>files</b> sú inštancie objektu typu <b>File</b> ktorý ma nasledovn
This path can be relative or absolute. In case that <i>newpath</i> parameter is relative path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can be absolute path. This path can be relative or absolute. In case that <i>newpath</i> parameter is relative path then it "Output directory" from GUI windows is used as base directory. Parameter <i>newpath</i> can be absolute path.
File is then moved to this path.</li> File is then moved to this path.</li>
<li><b>convert(outpath, format, params)</b> convert image file from any format that program is able to open into FITS, XISF, JPEG, PNG, BMP. <li><b>convert(outpath, format, params)</b> convert image file from any format that program is able to open into FITS, XISF, JPEG, PNG, BMP.
Parameters are: <i>outputpath</i> path where converted image will be saved. It automatically replace suffix according to format. <i>format</i> one of "FITS" "XISF", "JPG", "PNG" or "BMP". <i>params</i> object with attributes "compressionType" and "compressionLevel". Parameters are: <i>outputpath</i> path where converted image will be saved. It automatically replace suffix according to format. <i>format</i> one of "FITS" "XISF", "JPG", "PNG", "TIFF" or "BMP". <i>params</i> object with attributes "compressionType" and "compressionLevel".
Valid values for compressionType are be "gzip" or "rice" when converting to FITS. When converting to XISF compressionType can be "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh". Valid values for compressionType are be "gzip" or "rice" when converting to FITS. When converting to XISF compressionType can be "zlib", "lz4", "lz4hc", "zstd", "zlib+sh", "lz4+sh", "lz4hc+sh", "zstd+sh".
It is recommended to use "+sh" variants of compression. It is recommended to use "+sh" variants of compression.
XISF format also accept "compressionLevel" in range 0-100 where zero is fastest compression and 100 slowest. If you omit this attribute or set it to -1 then default compression level will be used. XISF format also accept "compressionLevel" in range 0-100 where zero is fastest compression and 100 slowest. If you omit this attribute or set it to -1 then default compression level will be used.
+31 -30
View File
@@ -4,7 +4,7 @@
#include <QSqlError> #include <QSqlError>
#include <QDebug> #include <QDebug>
#include <QDateTime> #include <QDateTime>
#include "loadrunable.h" #include "loadimage.h"
Database::Database(QObject *parent) : QObject(parent) Database::Database(QObject *parent) : QObject(parent)
{ {
@@ -15,32 +15,33 @@ bool Database::init(const QLatin1String &connectionName)
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(path); QDir dir(path);
QSqlDatabase m_database = QSqlDatabase::addDatabase("QSQLITE", connectionName); QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
if(!dir.mkpath(".")) if(!dir.mkpath("."))
return false; return false;
if(m_database.isValid()) if(database.isValid())
{ {
m_database.setDatabaseName(dir.absoluteFilePath("database2.db")); database.setDatabaseName(dir.absoluteFilePath("database2.db"));
if(m_database.open()) if(database.open())
{ {
m_database.exec("PRAGMA foreign_keys = ON"); QSqlQuery query(database);
query.exec("PRAGMA foreign_keys = ON");
int version = checkVersion(); int version = checkVersion();
if(version == 0) if(version == 0)
{ {
m_database.exec("PRAGMA user_version = 1"); query.exec("PRAGMA user_version = 1");
m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)"); query.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME," query.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME,"
" minRa REAL, maxRa REAL, minDec REAL, maxDec REAL, crVal1 REAL, crVal2 REAL)"); " minRa REAL, maxRa REAL, minDec REAL, maxDec REAL, crVal1 REAL, crVal2 REAL)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER," query.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER,"
"key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)"); "key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)");
m_database.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)"); query.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)");
m_database.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)"); query.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)");
m_database.exec("CREATE INDEX IF NOT EXISTS minRa_idx ON fits_files(minRa)"); query.exec("CREATE INDEX IF NOT EXISTS minRa_idx ON fits_files(minRa)");
m_database.exec("CREATE INDEX IF NOT EXISTS maxRa_idx ON fits_files(maxRa)"); query.exec("CREATE INDEX IF NOT EXISTS maxRa_idx ON fits_files(maxRa)");
m_database.exec("CREATE INDEX IF NOT EXISTS minDec_idx ON fits_files(minDec)"); query.exec("CREATE INDEX IF NOT EXISTS minDec_idx ON fits_files(minDec)");
m_database.exec("CREATE INDEX IF NOT EXISTS maxDec_idx ON fits_files(maxDec)"); query.exec("CREATE INDEX IF NOT EXISTS maxDec_idx ON fits_files(maxDec)");
} }
else if(version > 1) else if(version > 1)
{ {
@@ -48,28 +49,28 @@ bool Database::init(const QLatin1String &connectionName)
return false; return false;
} }
QSqlError error = m_database.lastError(); QSqlError error = database.lastError();
if(error.type() == QSqlError::NoError) if(error.type() == QSqlError::NoError)
{ {
m_markQuery = QSqlQuery(m_database); m_markQuery = QSqlQuery(database);
m_markQuery.prepare("INSERT INTO files (file) VALUES (?)"); m_markQuery.prepare("INSERT INTO files (file) VALUES (?)");
m_unmarkQuery = QSqlQuery(m_database); m_unmarkQuery = QSqlQuery(database);
m_unmarkQuery.prepare("DELETE FROM files WHERE file = (?)"); m_unmarkQuery.prepare("DELETE FROM files WHERE file = (?)");
m_isMarkedQuery = QSqlQuery(m_database); m_isMarkedQuery = QSqlQuery(database);
m_isMarkedQuery.prepare("SELECT * FROM files WHERE file = (:name)"); m_isMarkedQuery.prepare("SELECT * FROM files WHERE file = (:name)");
m_insertFile = QSqlQuery(m_database); m_insertFile = QSqlQuery(database);
m_insertFile.prepare("INSERT INTO fits_files (file, mtime) VALUES (?, ?)"); m_insertFile.prepare("INSERT INTO fits_files (file, mtime) VALUES (?, ?)");
m_insertFileWcs = QSqlQuery(m_database); m_insertFileWcs = QSqlQuery(database);
m_insertFileWcs.prepare("INSERT INTO fits_files (file, mtime, minRa, maxRa, minDec, maxDec, crVal1, crVal2) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); m_insertFileWcs.prepare("INSERT INTO fits_files (file, mtime, minRa, maxRa, minDec, maxDec, crVal1, crVal2) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
m_insertFitsHeader = QSqlQuery(m_database); m_insertFitsHeader = QSqlQuery(database);
m_insertFitsHeader.prepare("INSERT INTO fits_headers (id_file, key, value, comment) VALUES (?, ?, ?, ?)"); m_insertFitsHeader.prepare("INSERT INTO fits_headers (id_file, key, value, comment) VALUES (?, ?, ?, ?)");
m_checkFile = QSqlQuery(m_database); m_checkFile = QSqlQuery(database);
m_checkFile.prepare("SELECT id,mtime FROM fits_files WHERE file=?"); m_checkFile.prepare("SELECT id,mtime FROM fits_files WHERE file=?");
m_headerKeywords = QSqlQuery(m_database); m_headerKeywords = QSqlQuery(database);
m_headerKeywords.prepare("SELECT DISTINCT key FROM fits_headers ORDER BY key"); m_headerKeywords.prepare("SELECT DISTINCT key FROM fits_headers ORDER BY key");
m_deleteFile = QSqlQuery(m_database); m_deleteFile = QSqlQuery(database);
m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?"); m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?");
return true; return true;
} }
@@ -130,7 +131,7 @@ QStringList Database::getMarkedFiles()
void Database::clearMarkedFiles() void Database::clearMarkedFiles()
{ {
QSqlDatabase::database().exec("DELETE FROM files"); QSqlQuery query("DELETE FROM files");
} }
bool Database::checkError(QSqlQuery &query) bool Database::checkError(QSqlQuery &query)
@@ -148,7 +149,7 @@ bool Database::checkError(QSqlQuery &query)
int Database::checkVersion() int Database::checkVersion()
{ {
QSqlDatabase db = QSqlDatabase::database(); QSqlDatabase db = QSqlDatabase::database();
QSqlQuery query = db.exec("PRAGMA user_version"); QSqlQuery query("PRAGMA user_version");
if(query.next()) if(query.next())
return query.value(0).toInt(); return query.value(0).toInt();
return -1; return -1;
@@ -194,10 +195,10 @@ void Database::reindex(QProgressDialog *progress)
QVariantList deleteids; QVariantList deleteids;
QSqlDatabase database = QSqlDatabase::database(); QSqlDatabase database = QSqlDatabase::database();
database.transaction(); database.transaction();
QSqlQuery size = database.exec("SELECT COUNT(*) FROM fits_files"); QSqlQuery size("SELECT COUNT(*) FROM fits_files", database);
size.next(); size.next();
progress->setMaximum(size.value(0).toInt()); progress->setMaximum(size.value(0).toInt());
QSqlQuery files = database.exec("SELECT id,file,mtime FROM fits_files"); QSqlQuery files("SELECT id,file,mtime FROM fits_files", database);
int i = 0; int i = 0;
while(files.next()) while(files.next())
{ {
+1 -1
View File
@@ -375,7 +375,7 @@ bool DataBaseView::exportCSV(const QString &path)
if(!csv.open(QIODevice::WriteOnly | QIODevice::Text)) if(!csv.open(QIODevice::WriteOnly | QIODevice::Text))
return false; return false;
QSqlQuery sql = m_model->query(); QSqlQuery sql(m_model->query().lastQuery());
int colCount = m_model->columnCount(); int colCount = m_model->columnCount();
QStringList header; QStringList header;
for(int i=0; i<colCount; i++) for(int i=0; i<colCount; i++)
+1
View File
@@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include <QPainter> #include <QPainter>
#include <QDebug> #include <QDebug>
#include <QStyleOption>
Histogram::Histogram(QWidget *parent) : QWidget(parent) Histogram::Histogram(QWidget *parent) : QWidget(parent)
{ {
-410
View File
@@ -1,63 +1,6 @@
#include "imageinfo.h" #include "imageinfo.h"
#include <QSettings> #include <QSettings>
#include <QTime>
#include <QHeaderView> #include <QHeaderView>
#include <wcslib/wcshdr.h>
#include <wcslib/wcsfix.h>
#include <libxisf.h>
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
bool FITSRecord::editable() const
{
return noEditableKey.count(key);
}
FITSRecord::FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment) :
key(key), value(value), comment(comment)
{
}
FITSRecord::FITSRecord(const LibXISF::FITSKeyword &record)
{
key = record.name.c_str();
comment = record.comment.c_str();
QString string = record.value.c_str();
if(string.startsWith('\'') && string.endsWith('\''))
{
string.chop(1);
string.remove(0, 1);
}
bool isint;
bool isdouble;
double vald = string.toDouble(&isdouble);
long long vall = string.toLongLong(&isint);
if(isint)
value = vall;
else if(isdouble)
value = vald;
else if(string == "T" || string == "F")
value = string == "T";
else
value = string;
}
FITSRecord::FITSRecord(const LibXISF::Property &property)
{
key = property.id.c_str();
value = QString::fromStdString(property.value.toString());
comment = property.comment.c_str();
xisf = true;
}
QByteArray FITSRecord::valueToByteArray() const
{
if(value.type() == QVariant::Bool)
return value.toBool() ? "T" : "F";
else
return value.toString().toLatin1();
}
ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent) ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent)
{ {
@@ -97,356 +40,3 @@ void ImageInfo::setInfo(const ImageInfoData &info)
} }
expandAll(); expandAll();
} }
void WCSDataT::freeWCS()
{
wcsvfree(&nwcs, &wcs);
nwcs = 0;
wcs = nullptr;
}
WCSDataT::WCSDataT(int width, int height, char *header, int nrec) :
width(width),
height(height)
{
int nreject = 0;
int status = wcspih(header, nrec, 1, 0, &nreject, &nwcs, &wcs);
if(status != 0)
{
freeWCS();
return;
}
status = cdfix(wcs);
if(status > 0 || wcs->crpix[0] == 0)
freeWCS();
}
WCSDataT::WCSDataT(int width, int height, const QVector<FITSRecord> &header) :
width(width),
height(height)
{
int status = 0;
QByteArray str;
int nrec = 1;
for(const FITSRecord &record : header)
{
if(record.key.startsWith("PV"))continue;
QByteArray rec;
rec.append(record.key.leftJustified(8, ' '));
rec.append("= ");
rec.append(record.value.toString().toLatin1());
rec.append(" / ");
rec.append(record.comment);
str.append(rec.leftJustified(80, ' ', true));
nrec++;
}
str.append(QByteArray("END").leftJustified(80));
int nreject = 0;
status = wcspih(str.data(), nrec, 1, 0, &nreject, &nwcs, &wcs);
if(status != 0)
{
freeWCS();
return;
}
status = cdfix(wcs);
if(status > 0 || wcs->crpix[0] == 0)
freeWCS();
}
WCSDataT::~WCSDataT()
{
if(wcs)
freeWCS();
}
bool WCSDataT::pixelToWorld(const QPointF &pixel, SkyPoint &point) const
{
if(!valid())return false;
double pixcrd[2] = {pixel.x(), pixel.y()};
double imgcrd[8] = {0};
double phi = 0;
double theta = 0;
double world[8] = {0};
int stat[NWCSFIX] = {0};
int status = wcsp2s(wcs, 1, 2, pixcrd, imgcrd, &phi, &theta, world, stat);
if(status == 0)
{
point = SkyPoint(world[0], world[1]);
return true;
}
return false;
}
bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const
{
if(!valid())return false;
double world[2] = {point.RA(), point.DEC()};
double phi = 0;
double theta = 0;
double imgcrd[8] = {0};
double pixcrd[8] = {0};
int stat[NWCSFIX] = {0};
int status = wcss2p(wcs, 1, 2, world, &phi, &theta, imgcrd, pixcrd, stat);
if(status == 0)
{
pixel = QPointF(pixcrd[0], pixcrd[1]);
return true;
}
return false;
}
void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const
{
if(wcs == nullptr)return;
minRa = 1000;
maxRa = -1000;
minDec = 1000;
maxDec = -1000;
if(wcs->crval)
{
crVal1 = wcs->crval[0];
crVal2 = wcs->crval[1];
}
else
{
crVal1 = crVal2 = NAN;
}
auto update = [&](const QPointF &pixel)
{
SkyPoint point;
pixelToWorld(pixel, point);
minRa = std::min(minRa, point.RA());
maxRa = std::max(maxRa, point.RA());
minDec = std::min(minDec, point.DEC());
maxDec = std::max(maxDec, point.DEC());
};
for(int x=0; x<width; x++)
{
update(QPointF(x, 0));
update(QPointF(x, height - 1));
}
for(int y=0; y<height; y++)
{
update(QPointF(0, y));
update(QPointF(width - 1, y));
}
QPointF ncp;
QPointF scp;
QRectF s(0, 0, width - 1, height - 1);
if(worldToPixel(SkyPoint(0, 90), ncp))
{
if(s.contains(ncp))
maxDec = 90;
}
if(worldToPixel(SkyPoint(0, -90), scp))
{
if(s.contains(scp))
minDec = -90;
}
}
double hav(double x)
{
return (1.0 - std::cos(x)) * 0.5;
}
double haverSine(const SkyPoint &a, SkyPoint &b)
{
const double ToRAD = M_PI / 180.0;
double d = hav((a.DEC() - b.DEC()) * ToRAD) + std::cos(a.DEC() * ToRAD) * std::cos(b.DEC() * ToRAD) * hav((a.RA() - b.RA()) * ToRAD);
return std::acos(1.0 - 2.0 * d) * (180.0 / M_PI);
}
SkyPointScale WCSDataT::getRaDecScale() const
{
SkyPointScale ret;
pixelToWorld(QPointF(width/2.0, height/2.0), ret.point);
SkyPoint pointX;
SkyPoint pointY;
pixelToWorld(QPointF(width/2.0+1, height/2.0), pointX);
pixelToWorld(QPointF(width/2.0, height/2.0+1), pointY);
double scaleX = haverSine(ret.point, pointX) * 3600.0;
double scaleY = haverSine(ret.point, pointY) * 3600.0;
ret.scaleLow = std::min(scaleX, scaleY);
ret.scaleHigh = std::max(scaleX, scaleY);
ret.scaleValid = true;
return ret;
}
SkyPoint::SkyPoint() : ra(NAN), dec(NAN)
{
}
SkyPoint::SkyPoint(double ra, double dec) : ra(ra), dec(dec)
{
}
void SkyPoint::set(double ra, double dec)
{
this->ra = ra;
this->dec = dec;
}
QString SkyPoint::toString() const
{
if(std::isnan(ra) || std::isnan(dec))
return QString();
QTime t(0, 0);
t = t.addSecs(ra * 240);
double deg, min, sec;
min = std::abs(std::modf(dec, &deg) * 60);
sec = std::modf(min, &min) * 60;
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
}
double SkyPoint::fromHMS(const QString &hms)
{
double deg = fromDMS(hms);
if(std::isnan(deg))return deg;
return deg * 15.0;
}
double SkyPoint::fromDMS(const QString &dms)
{
double deg = 0.0;
QString str = dms.trimmed();
str.remove(QRegularExpression("[hdms°'\"]"));
str.replace(':', ' ');
str.replace(QRegularExpression("\\s+"), " ");
QStringList fields = str.split(' ');
double sign = 1.0;
bool ok = false;
if(fields.size() >= 1)
deg = fields.at(0).toDouble(&ok);
if(!ok)return NAN;
if(deg < 0.0)
sign = -1.0;
if(fields.size() >= 2)
deg += sign * fields.at(1).toDouble() / 60.0;
if(fields.size() >= 3)
deg += sign * fields.at(2).toDouble() / 3600.0;
return deg;
}
QString SkyPoint::toHMS(double decHour)
{
double h,m,s,md;
md = std::modf(decHour, &h) * 60.0;
s = std::modf(md, &m) * 60.0;
return QString("%1h %2m %3s").arg((int)h, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
}
QString SkyPoint::toDMS(double deg)
{
double d,m,s,md;
md = std::modf(deg, &d) * 60.0;
s = std::modf(md, &m) * 60.0;
return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
}
SkyPointScale ImageInfoData::getCenterRaDec() const
{
SkyPointScale ret;
if(wcs && wcs->valid())
{
ret = wcs->getRaDecScale();
}
else
{
double ra,dec,focalLen,scale,pixSizeX,pixSizeY;
int binX = 1;
int binY = 1;
ra = dec = focalLen = scale = pixSizeX = pixSizeY = NAN;
bool ok;
for(const FITSRecord &header : fitsHeader)
{
if(header.key == "OBJCTRA")
{
double tmp = SkyPoint::fromHMS(header.value.toString());
if(!std::isnan(tmp))ra = tmp;
}
else if(header.key == "RA" && std::isnan(ra))
{
double tmp = header.value.toDouble(&ok);
if(ok)ra = tmp;
}
else if(header.key == "OBJCTDEC")
{
double tmp = SkyPoint::fromDMS(header.value.toString());
if(!std::isnan(tmp))dec = tmp;
}
else if(header.key == "DEC" && std::isnan(dec))
{
double tmp = SkyPoint::fromDMS(header.value.toString());
if(!std::isnan(tmp))dec = tmp;
}
else if(header.key == "SCALE")
{
double tmp = header.value.toDouble(&ok);
if(ok)scale = tmp;
}
else if(header.key == "FOCALLEN")
{
double tmp = header.value.toDouble(&ok);
if(ok)focalLen = tmp;
}
else if(header.key == "PIXSIZE1" || header.key == "XPIXSZ")
{
pixSizeX = header.value.toDouble();
}
else if(header.key == "PIXSIZE2" || header.key == "YPIXSZ")
{
pixSizeY = header.value.toDouble();
}
else if(header.key == "XBINNING")
{
int tmp = header.value.toInt(&ok);
if(ok)binX = tmp;
}
else if(header.key == "YBINNING")
{
int tmp = header.value.toInt(&ok);
if(ok)binY = tmp;
}
}
ret.point.set(ra, dec);
if(!std::isnan(scale))
{
ret.scaleLow = ret.scaleHigh = scale;
ret.scaleValid = true;
}
else if(!(std::isnan(focalLen) || std::isnan(pixSizeX) || std::isnan(pixSizeY)))
{
const double r = 206.2648097656; // (180 * 3600) / (1000 * pi) magic number to convert pixel size to focal length ratio to arcsec.
ret.scaleLow = std::min(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r);
ret.scaleHigh = std::max(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r);
ret.scaleValid = true;
}
}
if(ret.scaleValid)
{
ret.scaleLow *= 0.8;
ret.scaleHigh *= 1.2;
}
return ret;
}
+1 -83
View File
@@ -2,89 +2,7 @@
#define IMAGEINFO_H #define IMAGEINFO_H
#include <QTreeWidget> #include <QTreeWidget>
#include <wcslib/wcs.h> #include "imageinfodata.h"
#include <cmath>
#include <memory>
namespace LibXISF { struct FITSKeyword; struct Property; }
struct FITSRecord
{
QByteArray key;
QVariant value;
QByteArray comment;
bool xisf = false;
bool editable() const;
FITSRecord(){}
FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment);
FITSRecord(const LibXISF::FITSKeyword &record);
FITSRecord(const LibXISF::Property &property);
QByteArray valueToByteArray() const;
};
class SkyPoint
{
double ra = NAN;
double dec = NAN;
public:
SkyPoint();
SkyPoint(double ra, double dec);
void set(double ra, double dec);
double RA() const { return ra; }
double RAHour() const { return ra / 15.0; }
double DEC() const { return dec; }
QString toString() const;
static double fromHMS(const QString &hms);
static double fromDMS(const QString &dms);
static QString toHMS(double decHour);
static QString toDMS(double deg);
};
struct SkyPointScale
{
SkyPoint point;
//arcsec per pixel
bool scaleValid = false;
double scaleLow = 0.0;
double scaleHigh = 10000.0;
};
class WCSDataT
{
int nwcs = 0;
struct wcsprm *wcs = nullptr;
int width;
int height;
void freeWCS();
public:
WCSDataT(int width, int height, char *header, int nrec);
WCSDataT(int width, int height, const QVector<FITSRecord> &header);
WCSDataT(const WCSDataT &) = delete;
~WCSDataT();
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const;
bool worldToPixel(const SkyPoint &point, QPointF &pixel) const;
void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const;
bool valid() const { return wcs; };
SkyPointScale getRaDecScale() const;
};
struct ImageInfoData
{
QVector<FITSRecord> fitsHeader;
QVector<QPair<QString, QString>> info;
std::shared_ptr<WCSDataT> wcs;
SkyPointScale getCenterRaDec() const;
};
Q_DECLARE_METATYPE(ImageInfoData);
typedef enum
{
None,
Statistics,
Peaks,
Stars,
}AnalyzeLevel;
class ImageInfo : public QTreeWidget class ImageInfo : public QTreeWidget
{ {
+405
View File
@@ -0,0 +1,405 @@
#include "imageinfodata.h"
#include <QTime>
#include <QRectF>
#include <QRegularExpression>
#include <wcslib/wcshdr.h>
#include <wcslib/wcsfix.h>
#include "libxisf.h"
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
bool FITSRecord::editable() const
{
return noEditableKey.count(key);
}
FITSRecord::FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment) :
key(key), value(value), comment(comment)
{
}
FITSRecord::FITSRecord(const LibXISF::FITSKeyword &record)
{
key = record.name.c_str();
comment = record.comment.c_str();
QString string = record.value.c_str();
if(string.startsWith('\'') && string.endsWith('\''))
{
string.chop(1);
string.remove(0, 1);
}
bool isint;
bool isdouble;
double vald = string.toDouble(&isdouble);
long long vall = string.toLongLong(&isint);
if(isint)
value = vall;
else if(isdouble)
value = vald;
else if(string == "T" || string == "F")
value = string == "T";
else
value = string;
}
FITSRecord::FITSRecord(const LibXISF::Property &property)
{
key = property.id.c_str();
value = QString::fromStdString(property.value.toString());
comment = property.comment.c_str();
xisf = true;
}
void WCSDataT::freeWCS()
{
wcsvfree(&nwcs, &wcs);
nwcs = 0;
wcs = nullptr;
}
WCSDataT::WCSDataT(int width, int height, char *header, int nrec) :
width(width),
height(height)
{
int nreject = 0;
int status = wcspih(header, nrec, 1, 0, &nreject, &nwcs, &wcs);
if(status != 0)
{
freeWCS();
return;
}
status = cdfix(wcs);
if(status > 0 || wcs->crpix[0] == 0)
freeWCS();
}
WCSDataT::WCSDataT(int width, int height, const QVector<FITSRecord> &header) :
width(width),
height(height)
{
int status = 0;
QByteArray str;
int nrec = 1;
for(const FITSRecord &record : header)
{
if(record.key.startsWith("PV"))continue;
QByteArray rec;
rec.append(record.key.leftJustified(8, ' '));
rec.append("= ");
rec.append(record.value.toString().toLatin1());
rec.append(" / ");
rec.append(record.comment);
str.append(rec.leftJustified(80, ' ', true));
nrec++;
}
str.append(QByteArray("END").leftJustified(80));
int nreject = 0;
status = wcspih(str.data(), nrec, 1, 0, &nreject, &nwcs, &wcs);
if(status != 0)
{
freeWCS();
return;
}
status = cdfix(wcs);
if(status > 0 || wcs->crpix[0] == 0)
freeWCS();
}
WCSDataT::~WCSDataT()
{
if(wcs)
freeWCS();
}
bool WCSDataT::pixelToWorld(const QPointF &pixel, SkyPoint &point) const
{
if(!valid())return false;
double pixcrd[2] = {pixel.x(), pixel.y()};
double imgcrd[8] = {0};
double phi = 0;
double theta = 0;
double world[8] = {0};
int stat[NWCSFIX] = {0};
int status = wcsp2s(wcs, 1, 2, pixcrd, imgcrd, &phi, &theta, world, stat);
if(status == 0)
{
point = SkyPoint(world[0], world[1]);
return true;
}
return false;
}
bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const
{
if(!valid())return false;
double world[2] = {point.RA(), point.DEC()};
double phi = 0;
double theta = 0;
double imgcrd[8] = {0};
double pixcrd[8] = {0};
int stat[NWCSFIX] = {0};
int status = wcss2p(wcs, 1, 2, world, &phi, &theta, imgcrd, pixcrd, stat);
if(status == 0)
{
pixel = QPointF(pixcrd[0], pixcrd[1]);
return true;
}
return false;
}
void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const
{
if(wcs == nullptr)return;
minRa = 1000;
maxRa = -1000;
minDec = 1000;
maxDec = -1000;
if(wcs->crval)
{
crVal1 = wcs->crval[0];
crVal2 = wcs->crval[1];
}
else
{
crVal1 = crVal2 = NAN;
}
auto update = [&](const QPointF &pixel)
{
SkyPoint point;
pixelToWorld(pixel, point);
minRa = std::min(minRa, point.RA());
maxRa = std::max(maxRa, point.RA());
minDec = std::min(minDec, point.DEC());
maxDec = std::max(maxDec, point.DEC());
};
for(int x=0; x<width; x++)
{
update(QPointF(x, 0));
update(QPointF(x, height - 1));
}
for(int y=0; y<height; y++)
{
update(QPointF(0, y));
update(QPointF(width - 1, y));
}
QPointF ncp;
QPointF scp;
QRectF s(0, 0, width - 1, height - 1);
if(worldToPixel(SkyPoint(0, 90), ncp))
{
if(s.contains(ncp))
maxDec = 90;
}
if(worldToPixel(SkyPoint(0, -90), scp))
{
if(s.contains(scp))
minDec = -90;
}
}
double hav(double x)
{
return (1.0 - std::cos(x)) * 0.5;
}
double haverSine(const SkyPoint &a, SkyPoint &b)
{
const double ToRAD = M_PI / 180.0;
double d = hav((a.DEC() - b.DEC()) * ToRAD) + std::cos(a.DEC() * ToRAD) * std::cos(b.DEC() * ToRAD) * hav((a.RA() - b.RA()) * ToRAD);
return std::acos(1.0 - 2.0 * d) * (180.0 / M_PI);
}
SkyPointScale WCSDataT::getRaDecScale() const
{
SkyPointScale ret;
pixelToWorld(QPointF(width/2.0, height/2.0), ret.point);
SkyPoint pointX;
SkyPoint pointY;
pixelToWorld(QPointF(width/2.0+1, height/2.0), pointX);
pixelToWorld(QPointF(width/2.0, height/2.0+1), pointY);
double scaleX = haverSine(ret.point, pointX) * 3600.0;
double scaleY = haverSine(ret.point, pointY) * 3600.0;
ret.scaleLow = std::min(scaleX, scaleY);
ret.scaleHigh = std::max(scaleX, scaleY);
ret.scaleValid = true;
return ret;
}
SkyPoint::SkyPoint() : ra(NAN), dec(NAN)
{
}
SkyPoint::SkyPoint(double ra, double dec) : ra(ra), dec(dec)
{
}
void SkyPoint::set(double ra, double dec)
{
this->ra = ra;
this->dec = dec;
}
QString SkyPoint::toString() const
{
if(std::isnan(ra) || std::isnan(dec))
return QString();
QTime t(0, 0);
t = t.addSecs(ra * 240);
double deg, min, sec;
min = std::abs(std::modf(dec, &deg) * 60);
sec = std::modf(min, &min) * 60;
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
}
double SkyPoint::fromHMS(const QString &hms)
{
double deg = fromDMS(hms);
if(std::isnan(deg))return deg;
return deg * 15.0;
}
double SkyPoint::fromDMS(const QString &dms)
{
double deg = 0.0;
QString str = dms.trimmed();
str.remove(QRegularExpression("[hdms°'\"]"));
str.replace(':', ' ');
str.replace(QRegularExpression("\\s+"), " ");
QStringList fields = str.split(' ');
double sign = 1.0;
bool ok = false;
if(fields.size() >= 1)
deg = fields.at(0).toDouble(&ok);
if(!ok)return NAN;
if(deg < 0.0)
sign = -1.0;
if(fields.size() >= 2)
deg += sign * fields.at(1).toDouble() / 60.0;
if(fields.size() >= 3)
deg += sign * fields.at(2).toDouble() / 3600.0;
return deg;
}
QString SkyPoint::toHMS(double decHour)
{
double h,m,s,md;
md = std::modf(decHour, &h) * 60.0;
s = std::modf(md, &m) * 60.0;
return QString("%1h %2m %3s").arg((int)h, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
}
QString SkyPoint::toDMS(double deg)
{
double d,m,s,md;
md = std::modf(deg, &d) * 60.0;
s = std::modf(md, &m) * 60.0;
return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
}
SkyPointScale ImageInfoData::getCenterRaDec() const
{
SkyPointScale ret;
if(wcs && wcs->valid())
{
ret = wcs->getRaDecScale();
}
else
{
double ra,dec,focalLen,scale,pixSizeX,pixSizeY;
int binX = 1;
int binY = 1;
ra = dec = focalLen = scale = pixSizeX = pixSizeY = NAN;
bool ok;
for(const FITSRecord &header : fitsHeader)
{
if(header.key == "OBJCTRA")
{
double tmp = SkyPoint::fromHMS(header.value.toString());
if(!std::isnan(tmp))ra = tmp;
}
else if(header.key == "RA" && std::isnan(ra))
{
double tmp = header.value.toDouble(&ok);
if(ok)ra = tmp;
}
else if(header.key == "OBJCTDEC")
{
double tmp = SkyPoint::fromDMS(header.value.toString());
if(!std::isnan(tmp))dec = tmp;
}
else if(header.key == "DEC" && std::isnan(dec))
{
double tmp = SkyPoint::fromDMS(header.value.toString());
if(!std::isnan(tmp))dec = tmp;
}
else if(header.key == "SCALE")
{
double tmp = header.value.toDouble(&ok);
if(ok)scale = tmp;
}
else if(header.key == "FOCALLEN")
{
double tmp = header.value.toDouble(&ok);
if(ok)focalLen = tmp;
}
else if(header.key == "PIXSIZE1" || header.key == "XPIXSZ")
{
pixSizeX = header.value.toDouble();
}
else if(header.key == "PIXSIZE2" || header.key == "YPIXSZ")
{
pixSizeY = header.value.toDouble();
}
else if(header.key == "XBINNING")
{
int tmp = header.value.toInt(&ok);
if(ok)binX = tmp;
}
else if(header.key == "YBINNING")
{
int tmp = header.value.toInt(&ok);
if(ok)binY = tmp;
}
}
ret.point.set(ra, dec);
if(!std::isnan(scale))
{
ret.scaleLow = ret.scaleHigh = scale;
ret.scaleValid = true;
}
else if(!(std::isnan(focalLen) || std::isnan(pixSizeX) || std::isnan(pixSizeY)))
{
const double r = 206.2648097656; // (180 * 3600) / (1000 * pi) magic number to convert pixel size to focal length ratio to arcsec.
ret.scaleLow = std::min(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r);
ret.scaleHigh = std::max(pixSizeX * binX / focalLen * r, pixSizeY * binY / focalLen * r);
ret.scaleValid = true;
}
}
if(ret.scaleValid)
{
ret.scaleLow *= 0.8;
ret.scaleHigh *= 1.2;
}
return ret;
}
+91
View File
@@ -0,0 +1,91 @@
#ifndef IMAGEINFODATA_H
#define IMAGEINFODATA_H
#include <QString>
#include <QPointF>
#include <QVector>
#include <QVariant>
#include <wcslib/wcs.h>
#include <cmath>
#include <memory>
namespace LibXISF { struct FITSKeyword; struct Property; }
struct FITSRecord
{
QByteArray key;
QVariant value;
QByteArray comment;
bool xisf = false;
bool editable() const;
FITSRecord(){}
FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment);
FITSRecord(const LibXISF::FITSKeyword &record);
FITSRecord(const LibXISF::Property &property);
};
class SkyPoint
{
double ra = NAN;
double dec = NAN;
public:
SkyPoint();
SkyPoint(double ra, double dec);
void set(double ra, double dec);
double RA() const { return ra; }
double RAHour() const { return ra / 15.0; }
double DEC() const { return dec; }
QString toString() const;
static double fromHMS(const QString &hms);
static double fromDMS(const QString &dms);
static QString toHMS(double decHour);
static QString toDMS(double deg);
};
struct SkyPointScale
{
SkyPoint point;
//arcsec per pixel
bool scaleValid = false;
double scaleLow = 0.0;
double scaleHigh = 10000.0;
};
class WCSDataT
{
int nwcs = 0;
struct wcsprm *wcs = nullptr;
int width;
int height;
void freeWCS();
public:
WCSDataT(int width, int height, char *header, int nrec);
WCSDataT(int width, int height, const QVector<FITSRecord> &header);
WCSDataT(const WCSDataT &) = delete;
~WCSDataT();
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const;
bool worldToPixel(const SkyPoint &point, QPointF &pixel) const;
void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const;
bool valid() const { return wcs; };
SkyPointScale getRaDecScale() const;
};
struct ImageInfoData
{
QVector<FITSRecord> fitsHeader;
QVector<QPair<QString, QString>> info;
std::shared_ptr<WCSDataT> wcs;
SkyPointScale getCenterRaDec() const;
};
typedef enum
{
None,
Statistics,
Peaks,
Stars,
}AnalyzeLevel;
Q_DECLARE_METATYPE(ImageInfoData);
#endif // IMAGEINFODATA_H
+27 -20
View File
@@ -4,6 +4,7 @@
#include <QDir> #include <QDir>
#include <QSettings> #include <QSettings>
#include <QTimer> #include <QTimer>
#include <QRegularExpression>
#include "loadrunable.h" #include "loadrunable.h"
#include "rawimage.h" #include "rawimage.h"
#include "database.h" #include "database.h"
@@ -22,13 +23,13 @@ Image::Image(const QString name, int number, ImageRingList *ringList) :
{ {
} }
void Image::load() void Image::load(QThreadPool *pool)
{ {
if(!m_rawImage && !m_loading) if(!m_rawImage && !m_loading)
{ {
m_loading = true; m_loading = true;
m_released = false; m_released = false;
QThreadPool::globalInstance()->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel())); pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel()));
} }
if(!m_loading && m_rawImage) if(!m_loading && m_rawImage)
emit pixmapLoaded(this); emit pixmapLoaded(this);
@@ -110,7 +111,10 @@ ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter,
{ {
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*."); m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
m_loadPool = new QThreadPool(this);
m_loadPool->setThreadPriority(QThread::LowPriority);
m_thumbPool = new QThreadPool(this); m_thumbPool = new QThreadPool(this);
m_thumbPool->setThreadPriority(QThread::LowPriority);
m_slideShowTimer = new QTimer(this); m_slideShowTimer = new QTimer(this);
connect(m_slideShowTimer, &QTimer::timeout, this, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment)); connect(m_slideShowTimer, &QTimer::timeout, this, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment));
@@ -123,10 +127,10 @@ ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter,
ImageRingList::~ImageRingList() ImageRingList::~ImageRingList()
{ {
QThreadPool::globalInstance()->clear(); m_loadPool->clear();
m_thumbPool->clear(); m_thumbPool->clear();
QThreadPool::globalInstance()->waitForDone(); m_loadPool->waitForDone();
m_thumbPool->waitForDone(); m_thumbPool->waitForDone();
} }
@@ -173,11 +177,14 @@ bool ImageRingList::setDir(const QString path, const QString &currentFile, bool
void ImageRingList::setFile(const QString &file) void ImageRingList::setFile(const QString &file)
{ {
QFileInfo info(file); if(!file.isEmpty())
if(info.isDir()) {
setDir(file, QString(), true); QFileInfo info(file);
else if(info.isDir())
setDir(info.absolutePath(), file); setDir(file, QString(), true);
else
setDir(info.absolutePath(), file);
}
} }
ImagePtr ImageRingList::currentImage() ImagePtr ImageRingList::currentImage()
@@ -199,9 +206,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_currImage)->load(m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(); (*m_lastImage)->load(m_loadPool);
} }
} }
@@ -212,9 +219,9 @@ 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_currImage)->load(m_loadPool);
m_lastImage = decrement(m_lastImage); m_lastImage = decrement(m_lastImage);
(*m_firstImage)->load(); (*m_firstImage)->load(m_loadPool);
} }
} }
@@ -268,7 +275,7 @@ void ImageRingList::loadFile(int row)
if(m_images.empty()) if(m_images.empty())
return; return;
(*m_currImage)->load(); (*m_currImage)->load(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)
@@ -277,9 +284,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_firstImage)->load(m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(); (*m_lastImage)->load(m_loadPool);
} }
if(m_lastImage != m_firstImage) if(m_lastImage != m_firstImage)
{ {
@@ -403,9 +410,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_firstImage)->load(m_loadPool);
m_lastImage = increment(m_lastImage); m_lastImage = increment(m_lastImage);
(*m_lastImage)->load(); (*m_lastImage)->load(m_loadPool);
} }
} }
if(newWidth < m_width) if(newWidth < m_width)
@@ -460,9 +467,9 @@ void ImageRingList::toggleSlideshow(bool start)
void ImageRingList::setFiles(const QStringList files, const QString &currentFile) void ImageRingList::setFiles(const QStringList files, const QString &currentFile)
{ {
QThreadPool::globalInstance()->clear(); m_loadPool->clear();
m_thumbPool->clear(); m_thumbPool->clear();
QThreadPool::globalInstance()->waitForDone(); m_loadPool->waitForDone();
m_thumbPool->waitForDone(); m_thumbPool->waitForDone();
beginResetModel(); beginResetModel();
m_images.clear(); m_images.clear();
+4 -2
View File
@@ -7,8 +7,9 @@
#include <QPixmap> #include <QPixmap>
#include <QDir> #include <QDir>
#include <memory> #include <memory>
#include "imageinfo.h" #include "imageinfodata.h"
#include "rawimage.h" #include "rawimage.h"
#include <QAbstractItemModel>
class ImageRingList; class ImageRingList;
class QThreadPool; class QThreadPool;
@@ -27,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(); void load(QThreadPool *pool);
void loadThumbnail(QThreadPool *pool); void loadThumbnail(QThreadPool *pool);
void release(); void release();
QString name() const; QString name() const;
@@ -62,6 +63,7 @@ class ImageRingList : public QAbstractItemModel
QDir::SortFlag m_sort = QDir::Name; QDir::SortFlag m_sort = QDir::Name;
bool m_reversed = false; bool m_reversed = false;
AnalyzeLevel m_analyzeLevel; AnalyzeLevel m_analyzeLevel;
QThreadPool *m_loadPool;
QThreadPool *m_thumbPool; QThreadPool *m_thumbPool;
Database *m_database; Database *m_database;
QStringList m_nameFilter; QStringList m_nameFilter;
+208 -55
View File
@@ -10,6 +10,7 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QPainter> #include <QPainter>
#include "imageringlist.h" #include "imageringlist.h"
#include <QFloat16>
int FILTERING = 1; int FILTERING = 1;
bool OpenGLES = false; bool OpenGLES = false;
@@ -110,16 +111,15 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
m_error.clear(); m_error.clear();
makeCurrent(); makeCurrent();
m_rawImage = image; m_rawImage = image;
if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize)
{
uint32_t newW = std::min(image->width() * m_maxTextureSize / image->width(), image->width() * m_maxTextureSize / image->height());
uint32_t newH = std::min(image->height() * m_maxTextureSize / image->width(), image->height() * m_maxTextureSize / image->height());
m_rawImage->resize(newW, newH);
}
m_imgWidth = image->width(); m_imgWidth = image->width();
m_imgHeight = image->height(); m_imgHeight = image->height();
bool tooBig = false;
if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize)
{
tooBig = true;
m_swPaint = true;
}
if(!m_image)return; if(!m_image)return;
@@ -133,21 +133,26 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
m_lut->setData(0, 0, 0, LUT_SIZE, LUT_SIZE, LUT_SIZE, 0, QOpenGLTexture::RGBA, QOpenGLTexture::RGBA, QOpenGLTexture::Float16, image->getLUT().data()); m_lut->setData(0, 0, 0, LUT_SIZE, LUT_SIZE, LUT_SIZE, 0, QOpenGLTexture::RGBA, QOpenGLTexture::RGBA, QOpenGLTexture::Float16, image->getLUT().data());
} }
QElapsedTimer timer; if(!tooBig)
timer.start(); {
m_image->destroy(); while(f->glGetError() != GL_NO_ERROR);
m_image->setAutoMipMapGenerationEnabled(false); QElapsedTimer timer;
m_image->setFormat(rawImageType.textureFormat); timer.start();
m_image->setSize(image->width(), image->height()); m_image->destroy();
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }()); m_image->setAutoMipMapGenerationEnabled(false);
m_image->allocateStorage(); m_image->setFormat(rawImageType.textureFormat);
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); m_image->setSize(image->width(), image->height());
m_image->setWrapMode(QOpenGLTexture::ClampToEdge); m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data()); m_image->allocateStorage();
m_image->bind(); m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
f->glGenerateMipmap(GL_TEXTURE_2D); m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data());
qDebug() << "setImage" << timer.elapsed(); m_image->bind();
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
f->glGenerateMipmap(GL_TEXTURE_2D);
qDebug() << "setImage" << timer.elapsed();
m_swPaint = f->glGetError() != GL_NO_ERROR;
}
m_unit_scale[0] = 1.0f; m_unit_scale[0] = 1.0f;
m_unit_scale[1] = 0.0f; m_unit_scale[1] = 0.0f;
@@ -185,10 +190,10 @@ void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos)
if(!mousePos.isNull()) if(!mousePos.isNull())
focus = mousePos; focus = mousePos;
if(width() > m_image->width() * m_scale) if(width() > m_imgWidth * m_scale)
m_dx = -width() * 0.5f + m_image->width() * m_scale * 0.5f; m_dx = -width() * 0.5f + m_imgWidth * m_scale * 0.5f;
if(height() > m_image->height() * m_scale) if(height() > m_imgHeight * m_scale)
m_dy = -height() * 0.5f + m_image->height() * m_scale * 0.5f; m_dy = -height() * 0.5f + m_imgHeight * m_scale * 0.5f;
float newScale = std::sqrt(std::pow(2.0f, (float)m_scaleStop)); float newScale = std::sqrt(std::pow(2.0f, (float)m_scaleStop));
float r = newScale / m_scale; float r = newScale / m_scale;
@@ -232,10 +237,10 @@ QVector2D ImageWidgetGL::getImagePixelCoord(const QVector2D &pos)
{ {
float dx = m_dx; float dx = m_dx;
float dy = m_dy; float dy = m_dy;
if(m_width > m_image->width()*m_scale) if(m_width > m_imgWidth * m_scale)
dx = -width()*0.5f + m_image->width()*m_scale*0.5f; dx = -width()*0.5f + m_imgWidth*m_scale * 0.5f;
if(m_height > m_image->height()*m_scale) if(m_height > m_imgHeight * m_scale)
dy = -height()*0.5f + m_image->height()*m_scale*0.5f; dy = -height()*0.5f + m_imgHeight*m_scale * 0.5f;
QVector2D offset(dx, dy); QVector2D offset(dx, dy);
return (pos + offset) / m_scale; return (pos + offset) / m_scale;
@@ -341,17 +346,158 @@ void ImageWidgetGL::showThumbnail(bool enable)
setOffset(m_dx, m_dy); setOffset(m_dx, m_dy);
} }
void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scale, const MTFParam &mtfParams, QWidget *widget)
{
QPainter painter(widget);
int width = widget->width();
int height = widget->height();
QImage img(width, height, QImage::Format_RGB32);
img.fill(Qt::darkGray);
int64_t ox = dx;
int64_t oy = dy;
auto mtf = [&mtfParams](int i, float x)
{
x = (x - mtfParams.blackPoint[i]) / (mtfParams.whitePoint[i] - mtfParams.blackPoint[i]);
x = std::clamp(x, 0.0f, 1.0f);
return ((mtfParams.midPoint[i] - 1.0f) * x) / ((2.0f * mtfParams.midPoint[i] - 1.0f) * x - mtfParams.midPoint[i]);
};
int imgWidth = rawImage->width();
int imgHeight = rawImage->height();
auto convert = [&](auto *src)
{
float s = 1.0f;
if constexpr(std::numeric_limits<std::remove_reference_t<decltype(*src)>>::is_integer)
s = (float)std::numeric_limits<std::remove_reference_t<decltype(*src)>>::max();
float iscale = 1.0f / scale;
float r[4];
float g[4];
float b[4];
for(int64_t y = std::max((int64_t)0, -oy); y < height; y++)
{
uint32_t *pixels = (uint32_t*)(img.scanLine(y));
float iptr;
float fy = std::modf((y + oy) * iscale, &iptr);
int64_t py = iptr;
int64_t w = py * rawImage->widthBytes();
int64_t w2 = w;
if(py+1 < imgHeight)w2 += rawImage->widthBytes();
if(py >= imgHeight)break;
for(int64_t x = std::max((int64_t)0, -ox); x < width; x++)
{
float fx = std::modf((x + ox) * iscale, &iptr);
int px = iptr;
int px2 = px + 1 < imgWidth ? px + 1 : px;
if(px >= imgWidth)break;
if(rawImage->channels() > 1)
{
r[0] = src[w + px * 4 + 0];
g[0] = src[w + px * 4 + 1];
b[0] = src[w + px * 4 + 2];
if(FILTERING)
{
r[1] = src[w + px2 * 4 + 0];
g[1] = src[w + px2 * 4 + 1];
b[1] = src[w + px2 * 4 + 2];
r[2] = src[w2 + px * 4 + 0];
g[2] = src[w2 + px * 4 + 1];
b[2] = src[w2 + px * 4 + 2];
r[3] = src[w2 + px2 * 4 + 0];
g[3] = src[w2 + px2 * 4 + 1];
b[3] = src[w2 + px2 * 4 + 2];
}
}
else
{
r[0] = src[w + px];
if(FILTERING)
{
r[2] = src[w2 + px];
r[1] = src[w + px2];
r[3] = src[w2 + px2];
}
}
uint32_t rgb = 0xff000000;
if(FILTERING)
{
if(rawImage->channels() > 1)
{
rgb |= (uint8_t)(mtf(0, ((r[3] * fx + r[2] * (1.0f - fx)) * fy + (r[1] * fx + r[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f) << 16;
rgb |= (uint8_t)(mtf(1, ((g[3] * fx + g[2] * (1.0f - fx)) * fy + (g[1] * fx + g[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f) << 8;
rgb |= (uint8_t)(mtf(1, ((b[3] * fx + b[2] * (1.0f - fx)) * fy + (b[1] * fx + b[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f);
}
else
{
uint32_t v = (uint8_t)(mtf(0, ((r[3] * fx + r[2] * (1.0f - fx)) * fy + (r[1] * fx + r[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f);
rgb = 0xff000000 | (v << 16) | (v << 8) | v;
}
}
else
{
if(rawImage->channels() > 1)
{
rgb |= (uint8_t)(mtf(0, r[0] / s) * 255.0f) << 16;
rgb |= (uint8_t)(mtf(1, g[0] / s) * 255.0f) << 8;
rgb |= (uint8_t)(mtf(1, b[0] / s) * 255.0f);
}
else
{
uint32_t v = (uint8_t)(mtf(0, r[0] / s) * 255.0f);
rgb = 0xff000000 | (v << 16) | (v << 8) | v;
}
}
pixels[x] = rgb;
}
}
};
if(rawImage)
{
switch(rawImage->type())
{
case RawImage::UINT8:
convert(static_cast<uint8_t*>(rawImage->data()));
break;
case RawImage::UINT16:
convert(static_cast<uint16_t*>(rawImage->data()));
break;
case RawImage::UINT32:
convert(static_cast<uint32_t*>(rawImage->data()));
break;
case RawImage::FLOAT16:
convert(static_cast<qfloat16*>(rawImage->data()));
break;
case RawImage::FLOAT32:
convert(static_cast<float*>(rawImage->data()));
break;
case RawImage::FLOAT64:
convert(static_cast<double*>(rawImage->data()));
break;
}
}
painter.drawImage(0, 0, img);
}
void ImageWidgetGL::paintGL() void ImageWidgetGL::paintGL()
{ {
float dx = m_dx; float dx = m_dx;
float dy = m_dy; float dy = m_dy;
if(m_width > m_image->width() * m_scale) if(m_width > m_imgWidth * m_scale)
dx = -width() * 0.5f + m_image->width() * m_scale * 0.5f; dx = -width() * 0.5f + m_imgWidth * m_scale * 0.5f;
if(m_height > m_image->height() * m_scale) if(m_height > m_imgHeight * m_scale)
dy = -height() * 0.5f + m_image->height() * m_scale * 0.5f; dy = -height() * 0.5f + m_imgHeight * m_scale * 0.5f;
QBrush highlight = style()->standardPalette().highlight(); QBrush highlight = style()->standardPalette().highlight();
f->glClear(GL_COLOR_BUFFER_BIT); f->glClear(GL_COLOR_BUFFER_BIT);
f->glBlendFunc(GL_ONE, GL_ZERO);
if(m_showThumbnails) if(m_showThumbnails)
{ {
m_vaoThumb->bind(); m_vaoThumb->bind();
@@ -425,31 +571,38 @@ void ImageWidgetGL::paintGL()
} }
else else
{ {
m_vao->bind(); if(m_swPaint)
debayer();
if(m_superpixel && m_debayerTex)
{ {
f->glActiveTexture(GL_TEXTURE0); swPaint(m_rawImage, dx, dy, m_scale, m_mtfParams, this);
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
} }
else else
m_image->bind(0); {
m_vao->bind();
debayer();
m_program->bind(); if(m_superpixel && m_debayerTex)
m_program->setUniformValue("viewport", (float)width(), (float)height()); {
m_program->setUniformValue("offset", std::floor(dx), std::floor(dy)); f->glActiveTexture(GL_TEXTURE0);
m_program->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3); f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
m_program->setUniformValue("unit_scale", m_unit_scale[0], m_unit_scale[1]); }
m_program->setUniformValue("zoom", 1.0f/m_scale); else
m_program->setUniformValue("bw", m_bwImg && !m_superpixel); m_image->bind(0);
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
m_program->setUniformValue("invert", m_invert); m_program->bind();
m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1); m_program->setUniformValue("viewport", (float)width(), (float)height());
m_program->setUniformValue("lut_table", 2); m_program->setUniformValue("offset", std::floor(dx), std::floor(dy));
m_program->setUniformValue("srgb", m_srgb); m_program->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); m_program->setUniformValue("unit_scale", m_unit_scale[0], m_unit_scale[1]);
m_vao->release(); m_program->setUniformValue("zoom", 1.0f/m_scale);
m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
m_program->setUniformValue("invert", m_invert);
m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1);
m_program->setUniformValue("lut_table", 2);
m_program->setUniformValue("srgb", m_srgb);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_vao->release();
}
} }
} }
+1
View File
@@ -91,6 +91,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
Database *m_database = nullptr; Database *m_database = nullptr;
QPointF m_lastPos; QPointF m_lastPos;
QString m_error; QString m_error;
bool m_swPaint = false;
public: public:
explicit ImageWidgetGL(Database *database, QWidget *parent = nullptr); explicit ImageWidgetGL(Database *database, QWidget *parent = nullptr);
~ImageWidgetGL() override; ~ImageWidgetGL() override;
+409
View File
@@ -0,0 +1,409 @@
#include "loadimage.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
#include <libraw/libraw.h>
#include <fitsio2.h>
#include "libxisf.h"
#include <libexif/exif-data.h>
#include "rawimage.h"
QString makeMaxPath(QString path)
{
#ifdef Q_OS_WIN64
if(!path.startsWith("\\\\?\\"))
{
QFileInfo info(path);
path = info.absoluteFilePath();
path = QDir::toNativeSeparators(path);
path.prepend("\\\\?\\");
qDebug() << path;
}
#endif
return path;
}
int loadFITSHeader(fitsfile *file, ImageInfoData &info)
{
int imgtype;
int naxis;
long naxes[3] = {0};
int nexist;
int status = 0;
char key[FLEN_KEYWORD];
char val[FLEN_VALUE];
char comm[FLEN_COMMENT];
char strval[FLEN_VALUE];
QVariant var;
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
fits_get_hdrspace(file, &nexist, nullptr, &status);
for(int i=1; i<=nexist; i++)
{
fits_read_keyn(file, i, key, val, comm, &status);
fits_read_key(file, TSTRING, key, strval, nullptr, &status);
if(status == 0 || status == VALUE_UNDEFINED)
{
QString string(strval);
bool isint;
bool isdouble;
double vald = string.toDouble(&isdouble);
long long vall = string.toLongLong(&isint);
if(isint)
var = vall;
else if(isdouble)
var = vald;
else if(status == VALUE_UNDEFINED)
var = QVariant();
else if(string == "T" || string == "F")
var = string == "T";
else
var = string;
status = 0;
info.fitsHeader.append(FITSRecord(key, var, comm));
}
else
{
return status;
}
}
char *header = nullptr;
int nrec = 0;
const char *exclist[] = {"PV1_1", "PV1_2"};
fits_hdr2str(file, TRUE, (char**)exclist, 2, &header, &nrec, &status);
if(status == 0)
{
info.wcs = std::make_shared<WCSDataT>(naxes[0], naxes[1], header, nrec);
if(!info.wcs->valid())info.wcs.reset();
}
fits_free_memory(header, &status);
return status;
}
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
{
fitsfile *file;
int status = 0;
int type = -1;
fits_open_diskfile(&file, path.toLocal8Bit().data(), READONLY, &status);
int num = 0;
fits_get_num_hdus(file, &num, &status);
int imgtype;
int naxis;
long naxes[3] = {0};
for(int i=1; i <= num; i++)
{
fits_movabs_hdu(file, i, IMAGE_HDU, &status);
fits_get_hdu_type(file, &type, &status);
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
fits_get_img_equivtype(file, &imgtype, &status);
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
{
RawImage::DataType type;
int fitstype;
long fpixel[3] = {1,1,1};
switch(imgtype)
{
case BYTE_IMG:
type = RawImage::UINT8;
fitstype = TBYTE;
break;
case SHORT_IMG:
type = RawImage::UINT16;
fitstype = TSHORT;
break;
case USHORT_IMG:
type = RawImage::UINT16;
fitstype = TUSHORT;
break;
case ULONG_IMG:
type = RawImage::UINT32;
fitstype = TUINT;
break;
case FLOAT_IMG:
type = RawImage::FLOAT32;
fitstype = TFLOAT;
break;
case DOUBLE_IMG:
type = RawImage::FLOAT64;
fitstype = TDOUBLE;
break;
default:
info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")});
goto noload;
break;
}
size_t size = naxes[0]*naxes[1];
size_t w = naxes[0];
size_t h = naxes[1];
info.info.append({QObject::tr("Width"), QString::number(naxes[0])});
info.info.append({QObject::tr("Height"), QString::number(naxes[1])});
RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type);
uint8_t *data = static_cast<uint8_t*>(img.data());
for (int i=1; i==1 || i<=naxes[2]; i++)
{
fpixel[2] = i;
fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status);
}
if(fitstype == TSHORT)
{
uint16_t *s = static_cast<uint16_t*>(img.data());
size_t size = img.size() * img.channels();
for(size_t i=0; i<size; i++)
s[i] -= INT16_MIN;
}
if(img.channels() == 1 || planar)
image = std::make_shared<RawImage>(std::move(img));
else
image = RawImage::fromPlanar(img);
break;
}
}
noload:
if(file)
loadFITSHeader(file, info);
if(image)
{
for(auto fits : info.fitsHeader)
{
if(fits.key == "ROWORDER" && fits.value == "BOTTOM-UP")
image->flip();
}
}
fits_close_file(file, &status);
if(status)
{
char err[100];
fits_get_errstatus(status, err);
info.info.append({QObject::tr("Error"), QString(err)});
qDebug() << "Failed to load FITS file" << err;
}
return true;
}
bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
{
try
{
LibXISF::XISFReader xisf;
xisf.open(path.toLocal8Bit().data());
const LibXISF::Image &xisfImage = xisf.getImage(0);
auto fitskeywords = xisfImage.fitsKeywords();
for(auto fits : fitskeywords)
{
info.fitsHeader.append(fits);
}
auto imageproperties = xisfImage.imageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
if(!info.wcs->valid())info.wcs.reset();
RawImage::DataType type;
switch(xisfImage.sampleFormat())
{
case LibXISF::Image::UInt8: type = RawImage::UINT8; break;
case LibXISF::Image::UInt16: type = RawImage::UINT16; break;
case LibXISF::Image::UInt32: type = RawImage::UINT32; break;
case LibXISF::Image::Float32: type = RawImage::FLOAT32; break;
case LibXISF::Image::Float64: type = RawImage::FLOAT64; break;
default: break;
}
LibXISF::Image tmpImage = xisfImage;
tmpImage.convertPixelStorageTo(LibXISF::Image::Planar);
if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
{
image = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
image->setICCProfile(tmpImage.iccProfile());
return true;
}
else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
{
if(planar)
{
image = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize());
}
else
{
image = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
}
image->setICCProfile(tmpImage.iccProfile());
return true;
}
return false;
}
catch (LibXISF::Error &err)
{
info.info.append(QPair<QString, QString>("Error", err.what()));
qDebug() << "Failed to load XISF" << err.what();
return false;
}
info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")});
return false;
}
bool readFITSHeader(const QString &path, ImageInfoData &info)
{
fitsfile *fr;
int status = 0;
QString path2 = makeMaxPath(path);
fits_open_diskfile(&fr, path2.toLocal8Bit().data(), READONLY, &status);
if(fr && status == 0)
{
status = loadFITSHeader(fr, info);
fits_close_file(fr, &status);
}
return status == 0;
}
bool readXISFHeader(const QString &path, ImageInfoData &info)
{
QString path2 = makeMaxPath(path);
try
{
LibXISF::XISFReader xisf;
xisf.open(path2.toLocal8Bit().data());
const LibXISF::Image &image = xisf.getImage(0, false);
auto fitskeywords = image.fitsKeywords();
for(auto fits : fitskeywords)
{
info.fitsHeader.append(fits);
}
auto imageproperties = image.imageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSDataT>(image.width(), image.height(), info.fitsHeader);
if(!info.wcs->valid())info.wcs.reset();
}
catch (LibXISF::Error &err)
{
qDebug() << err.what();
return false;
}
return true;
}
void loadExifEntry(ImageInfoData &info, ExifContent *content, ExifTag tag)
{
char val[1024];
ExifEntry *entry = exif_content_get_entry(content, tag);
if(entry)
{
exif_entry_get_value(entry, val, sizeof(val));
info.info.append({exif_tag_get_title(tag), val});
}
}
bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image)
{
std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
raw->open_file(path.toLocal8Bit().data());
raw->imgdata.params.half_size = true;
raw->imgdata.params.use_camera_wb = true;
raw->imgdata.params.user_flip = 0;
if(raw->unpack())
return false;
libraw_rawdata_t rawdata = raw->imgdata.rawdata;
size_t size = rawdata.sizes.width*rawdata.sizes.height;
std::vector<uint16_t> out;
out.resize(size);
size_t d = 0;
uint h=rawdata.sizes.top_margin+rawdata.sizes.height;
uint w=rawdata.sizes.left_margin+rawdata.sizes.width;
size_t pitch = rawdata.sizes.raw_pitch/sizeof(uint16_t);
for(size_t i=rawdata.sizes.top_margin;i<h;i++)
{
for(size_t o=rawdata.sizes.left_margin;o<w;o++)
{
uint16_t p = rawdata.raw_image[i*pitch+o];
out[d++] = p;
}
}
image = std::make_shared<RawImage>(rawdata.sizes.width, rawdata.sizes.height, 1, RawImage::UINT16);
memcpy(image->data(), &out[0], sizeof(uint16_t)*d);
QString shutterSpeed = QString::number(raw->imgdata.other.shutter);
if(raw->imgdata.other.shutter < 1)
{
shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter);
}
info.info.append({QObject::tr("Width"), QString::number(raw->imgdata.sizes.width)});
info.info.append({QObject::tr("Height"), QString::number(raw->imgdata.sizes.height)});
info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)});
info.info.append({QObject::tr("Shutter speed"), shutterSpeed});
#if LIBRAW_MINOR_VERSION>=19
// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature)));
#endif
return true;
}
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar)
{
bool ret = false;
QElapsedTimer timer;
timer.start();
if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive))
{
ret = loadRAW(path, info, rawImage);
qDebug() << "LoadRAW" << timer.elapsed();
}
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive))
{
ret = loadFITS(path, info, rawImage, planar);
qDebug() << "LoadFITS" << timer.elapsed();
}
else if(path.endsWith(".XISF", Qt::CaseInsensitive))
{
ret = loadXISF(path, info, rawImage, planar);
qDebug() << "LoadXISF" << timer.elapsed();
}
else
{
QImage img(path);
ExifData *exif = exif_data_new_from_file(path.toLocal8Bit().constData());
info.info.append({QObject::tr("Width"), QString::number(img.width())});
info.info.append({QObject::tr("Height"), QString::number(img.height())});
if(exif)
{
loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_ISO_SPEED_RATINGS);
loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_SHUTTER_SPEED_VALUE);
exif_data_free(exif);
}
rawImage = std::make_shared<RawImage>(img);
qDebug() << "LoadQImage" << timer.elapsed();
ret = !img.isNull();
}
return ret;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef LOADIMAGE_H
#define LOADIMAGE_H
#include <QString>
#include "imageinfodata.h"
class RawImage;
QString makeMaxPath(QString path);
bool readFITSHeader(const QString &path, ImageInfoData &info);
bool readXISFHeader(const QString &path, ImageInfoData &info);
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar = false);
#endif // LOADIMAGE_H
+56 -438
View File
@@ -1,371 +1,23 @@
#include "loadrunable.h" #include "loadrunable.h"
#include "imageringlist.h" #include "imageringlist.h"
#include <libraw/libraw.h>
#include "imageinfo.h"
#include <QFileInfo> #include <QFileInfo>
#include <QPainter> #include <QPainter>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QDebug> #include <QDebug>
#include <iostream>
#include <algorithm> #include <algorithm>
#include <libexif/exif-data.h>
#include <fitsio2.h> #include <fitsio2.h>
#include <libxisf.h>
#include "rawimage.h" #include "rawimage.h"
#include "starfit.h" #include "loadimage.h"
#include <lcms2.h> #include <lcms2.h>
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) : LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
m_file(file), m_file(makeMaxPath(file)),
m_receiver(receiver), m_receiver(receiver),
m_analyzeLevel(level), m_analyzeLevel(level),
m_thumbnail(thumbnail) m_thumbnail(thumbnail)
{ {
} }
void loadExifEntry(ImageInfoData &info, ExifContent *content, ExifTag tag)
{
char val[1024];
ExifEntry *entry = exif_content_get_entry(content, tag);
if(entry)
{
exif_entry_get_value(entry, val, sizeof(val));
info.info.append({exif_tag_get_title(tag), val});
}
}
void drawPeaks(QImage &img, const std::vector<Peak> &peaks)
{
QPixmap pix = QPixmap::fromImage(img);
QPainter painter(&pix);
painter.setPen(Qt::red);
for(auto peak : peaks)
{
painter.drawEllipse(QPoint(peak.x(), peak.y()), 5, 5);
}
img = pix.toImage();
}
void drawStars(QImage &img, const std::vector<Star> &stars)
{
QPixmap pix = QPixmap::fromImage(img);
QPainter painter(&pix);
painter.setPen(Qt::red);
for(auto star : stars)
{
painter.drawEllipse(QPointF(star.m_x, star.m_y), star.hw20X(), star.hw20Y());
}
img = pix.toImage();
}
void printStarModel(int radius, const std::vector<double> &data, const Star &star)
{
QString d = "d=[";
QString m = "m=[";
for(int y=0; y<radius; y++)
{
for(int x=0; x<radius; x++)
{
d += QString::number(data[y*radius+x]) + ",";
m += QString::number(gauss_model(star.m_am, star.m_x, star.m_y, star.m_sx, star.m_sy, x, y)) + ",";
}
d += ";";
m += ";";
}
d += "];";
m += "];";
//std::cout << star.m_am << " " << star.m_sx << star.m_sy << std::endl;
std::cout << d.toStdString() << std::endl;
std::cout << m.toStdString() << std::endl << std::endl;
}
bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image)
{
std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
raw->open_file(path.toLocal8Bit().data());
raw->imgdata.params.half_size = true;
raw->imgdata.params.use_camera_wb = true;
raw->imgdata.params.user_flip = 0;
if(raw->unpack())
return false;
libraw_rawdata_t rawdata = raw->imgdata.rawdata;
size_t size = rawdata.sizes.width*rawdata.sizes.height;
std::vector<uint16_t> out;
out.resize(size);
size_t d = 0;
uint h=rawdata.sizes.top_margin+rawdata.sizes.height;
uint w=rawdata.sizes.left_margin+rawdata.sizes.width;
size_t pitch = rawdata.sizes.raw_pitch/sizeof(uint16_t);
for(size_t i=rawdata.sizes.top_margin;i<h;i++)
{
for(size_t o=rawdata.sizes.left_margin;o<w;o++)
{
uint16_t p = rawdata.raw_image[i*pitch+o];
out[d++] = p;
}
}
image = std::make_shared<RawImage>(rawdata.sizes.width, rawdata.sizes.height, 1, RawImage::UINT16);
memcpy(image->data(), &out[0], sizeof(uint16_t)*d);
QString shutterSpeed = QString::number(raw->imgdata.other.shutter);
if(raw->imgdata.other.shutter < 1)
{
shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter);
}
info.info.append({QObject::tr("Width"), QString::number(raw->imgdata.sizes.width)});
info.info.append({QObject::tr("Height"), QString::number(raw->imgdata.sizes.height)});
info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)});
info.info.append({QObject::tr("Shutter speed"), shutterSpeed});
#if LIBRAW_MINOR_VERSION>=19
// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature)));
#endif
return true;
}
int loadFITSHeader(fitsfile *file, ImageInfoData &info)
{
int imgtype;
int naxis;
long naxes[3] = {0};
int nexist;
int status = 0;
char key[FLEN_KEYWORD];
char val[FLEN_VALUE];
char comm[FLEN_COMMENT];
char strval[FLEN_VALUE];
QVariant var;
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
fits_get_hdrspace(file, &nexist, nullptr, &status);
for(int i=1; i<=nexist; i++)
{
fits_read_keyn(file, i, key, val, comm, &status);
fits_read_key(file, TSTRING, key, strval, nullptr, &status);
if(status == 0 || status == VALUE_UNDEFINED)
{
QString string(strval);
bool isint;
bool isdouble;
double vald = string.toDouble(&isdouble);
long long vall = string.toLongLong(&isint);
if(isint)
var = vall;
else if(isdouble)
var = vald;
else if(status == VALUE_UNDEFINED)
var = QVariant();
else if(string == "T" || string == "F")
var = string == "T";
else
var = string;
status = 0;
info.fitsHeader.append(FITSRecord(key, var, comm));
}
else
{
return status;
}
}
char *header = nullptr;
int nrec = 0;
const char *exclist[] = {"PV1_1", "PV1_2"};
fits_hdr2str(file, TRUE, (char**)exclist, 2, &header, &nrec, &status);
if(status == 0)
{
info.wcs = std::make_shared<WCSDataT>(naxes[0], naxes[1], header, nrec);
if(!info.wcs->valid())info.wcs.reset();
}
fits_free_memory(header, &status);
return status;
}
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
{
fitsfile *file;
int status = 0;
int type = -1;
fits_open_diskfile(&file, path.toLocal8Bit().data(), READONLY, &status);
int num = 0;
fits_get_num_hdus(file, &num, &status);
int imgtype;
int naxis;
long naxes[3] = {0};
for(int i=1; i <= num; i++)
{
fits_movabs_hdu(file, i, IMAGE_HDU, &status);
fits_get_hdu_type(file, &type, &status);
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
fits_get_img_equivtype(file, &imgtype, &status);
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
{
RawImage::DataType type;
int fitstype;
long fpixel[3] = {1,1,1};
switch(imgtype)
{
case BYTE_IMG:
type = RawImage::UINT8;
fitstype = TBYTE;
break;
case SHORT_IMG:
type = RawImage::UINT16;
fitstype = TSHORT;
break;
case USHORT_IMG:
type = RawImage::UINT16;
fitstype = TUSHORT;
break;
case ULONG_IMG:
type = RawImage::UINT32;
fitstype = TUINT;
break;
case FLOAT_IMG:
type = RawImage::FLOAT32;
fitstype = TFLOAT;
break;
case DOUBLE_IMG:
type = RawImage::FLOAT64;
fitstype = TDOUBLE;
break;
default:
info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")});
goto noload;
break;
}
size_t size = naxes[0]*naxes[1];
size_t w = naxes[0];
size_t h = naxes[1];
info.info.append({QObject::tr("Width"), QString::number(naxes[0])});
info.info.append({QObject::tr("Height"), QString::number(naxes[1])});
RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type);
uint8_t *data = static_cast<uint8_t*>(img.data());
for (int i=1; i==1 || i<=naxes[2]; i++)
{
fpixel[2] = i;
fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status);
}
if(fitstype == TSHORT)
{
uint16_t *s = static_cast<uint16_t*>(img.data());
size_t size = img.size() * img.channels();
for(size_t i=0; i<size; i++)
s[i] -= INT16_MIN;
}
if(img.channels() == 1 || planar)
image = std::make_shared<RawImage>(std::move(img));
else
image = RawImage::fromPlanar(img);
break;
}
}
noload:
if(file)
loadFITSHeader(file, info);
if(image)
{
for(auto fits : info.fitsHeader)
{
if(fits.key == "ROWORDER" && fits.value == "BOTTOM-UP")
image->flip();
}
}
fits_close_file(file, &status);
if(status)
{
char err[100];
fits_get_errstatus(status, err);
info.info.append({QObject::tr("Error"), QString(err)});
qDebug() << "Failed to load FITS file" << err;
}
return true;
}
bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
{
try
{
LibXISF::XISFReader xisf;
xisf.open(path.toLocal8Bit().data());
const LibXISF::Image &xisfImage = xisf.getImage(0);
auto fitskeywords = xisfImage.fitsKeywords();
for(auto fits : fitskeywords)
{
info.fitsHeader.append(fits);
}
auto imageproperties = xisfImage.imageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
if(!info.wcs->valid())info.wcs.reset();
RawImage::DataType type;
switch(xisfImage.sampleFormat())
{
case LibXISF::Image::UInt8: type = RawImage::UINT8; break;
case LibXISF::Image::UInt16: type = RawImage::UINT16; break;
case LibXISF::Image::UInt32: type = RawImage::UINT32; break;
case LibXISF::Image::Float32: type = RawImage::FLOAT32; break;
case LibXISF::Image::Float64: type = RawImage::FLOAT64; break;
default: break;
}
LibXISF::Image tmpImage = xisfImage;
tmpImage.convertPixelStorageTo(LibXISF::Image::Planar);
if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
{
image = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
image->setICCProfile(tmpImage.iccProfile());
return true;
}
else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
{
if(planar)
{
image = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
std::memcpy(image->data(), tmpImage.imageData(), tmpImage.imageDataSize());
}
else
{
image = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
}
image->setICCProfile(tmpImage.iccProfile());
return true;
}
return false;
}
catch (LibXISF::Error &err)
{
info.info.append(QPair<QString, QString>("Error", err.what()));
qDebug() << "Failed to load XISF" << err.what();
return false;
}
info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")});
return false;
}
void LoadRunable::run() void LoadRunable::run()
{ {
try try
@@ -447,93 +99,9 @@ void LoadRunable::run()
} }
} }
bool readFITSHeader(const QString &path, ImageInfoData &info)
{
fitsfile *fr;
int status = 0;
fits_open_diskfile(&fr, path.toLocal8Bit().data(), READONLY, &status);
if(fr && status == 0)
{
status = loadFITSHeader(fr, info);
fits_close_file(fr, &status);
}
return status == 0;
}
bool readXISFHeader(const QString &path, ImageInfoData &info)
{
try
{
LibXISF::XISFReader xisf;
xisf.open(path.toLocal8Bit().data());
const LibXISF::Image &image = xisf.getImage(0, false);
auto fitskeywords = image.fitsKeywords();
for(auto fits : fitskeywords)
{
info.fitsHeader.append(fits);
}
auto imageproperties = image.imageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSDataT>(image.width(), image.height(), info.fitsHeader);
if(!info.wcs->valid())info.wcs.reset();
}
catch (LibXISF::Error &err)
{
qDebug() << err.what();
return false;
}
return true;
}
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar)
{
bool ret = false;
QElapsedTimer timer;
timer.start();
if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive))
{
ret = loadRAW(path, info, rawImage);
qDebug() << "LoadRAW" << timer.elapsed();
}
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive))
{
ret = loadFITS(path, info, rawImage, planar);
qDebug() << "LoadFITS" << timer.elapsed();
}
else if(path.endsWith(".XISF", Qt::CaseInsensitive))
{
ret = loadXISF(path, info, rawImage, planar);
qDebug() << "LoadXISF" << timer.elapsed();
}
else
{
QImage img(path);
ExifData *exif = exif_data_new_from_file(path.toLocal8Bit().constData());
info.info.append({QObject::tr("Width"), QString::number(img.width())});
info.info.append({QObject::tr("Height"), QString::number(img.height())});
if(exif)
{
loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_ISO_SPEED_RATINGS);
loadExifEntry(info, exif->ifd[EXIF_IFD_EXIF], EXIF_TAG_SHUTTER_SPEED_VALUE);
exif_data_free(exif);
}
rawImage = std::make_shared<RawImage>(img);
qDebug() << "LoadQImage" << timer.elapsed();
ret = !img.isNull();
}
return ret;
}
ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format, const ConvertParams &params, QSemaphore *semaphore) : ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format, const ConvertParams &params, QSemaphore *semaphore) :
m_infile(in), m_infile(makeMaxPath(in)),
m_outfile(out), m_outfile(makeMaxPath(out)),
m_format(format), m_format(format),
m_params(params), m_params(params),
m_semaphore(semaphore) m_semaphore(semaphore)
@@ -623,6 +191,17 @@ void ConvertRunable::run()
QFileInfo info(m_outfile); QFileInfo info(m_outfile);
info.dir().mkpath("."); info.dir().mkpath(".");
if(m_params.binning > 1)
{
rawimage->resizeInt(m_params.binning, m_params.average);
}
else if(m_params.resize.isValid() && !m_params.resize.isEmpty())
{
QSize imgSize(rawimage->width(), rawimage->height());
imgSize = imgSize.scaled(m_params.resize, m_params.aspect);
rawimage->resize(imgSize.width(), imgSize.height());
}
if(rawimage) if(rawimage)
{ {
if(m_format == "xisf") if(m_format == "xisf")
@@ -709,6 +288,7 @@ void ConvertRunable::run()
{ {
QImage::Format format = QImage::Format_Invalid; QImage::Format format = QImage::Format_Invalid;
int width = rawimage->widthBytes(); int width = rawimage->widthBytes();
switch(rawimage->type()) switch(rawimage->type())
{ {
case RawImage::UINT8: case RawImage::UINT8:
@@ -722,9 +302,18 @@ void ConvertRunable::run()
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64; else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
width *= 2; width *= 2;
break; break;
default: case RawImage::FLOAT16:
return; case RawImage::FLOAT32:
case RawImage::FLOAT64:
case RawImage::UINT32:
rawimage->convertToType(RawImage::UINT16);
if(rawimage->channels() == 1)format = QImage::Format_Grayscale16;
else if(rawimage->channels() == 3)format = QImage::Format_RGBX64;
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
width *= 2;
break;
} }
if(format == QImage::Format_Invalid)return; if(format == QImage::Format_Invalid)return;
QImage qimage(rawimage->width(), rawimage->height(), format); QImage qimage(rawimage->width(), rawimage->height(), format);
@@ -745,4 +334,33 @@ ConvertRunable::ConvertParams::ConvertParams(const QVariantMap &map)
if(map.contains("compressionType")) if(map.contains("compressionType"))
compressionType = map["compressionType"].toString(); compressionType = map["compressionType"].toString();
if(map.contains("binning"))
binning = map["binning"].toInt();
if(map.contains("average"))
average = map["average"].toBool();
if(map.contains("resize"))
{
QVariantMap size = map["resize"].toMap();
if(size.contains("width") && size.contains("height"))
{
int w = size["width"].toInt();
int h = size["height"].toInt();
resize = QSize(w, h);
}
}
if(map.contains("aspect"))
{
QString aspectStr = map["aspect"].toString();
if(aspectStr == "keep")
aspect = Qt::KeepAspectRatio;
else if(aspectStr == "expand")
aspect = Qt::KeepAspectRatioByExpanding;
else if(aspectStr == "ignore")
aspect = Qt::IgnoreAspectRatio;
}
} }
+6 -8
View File
@@ -4,13 +4,8 @@
#include <QRunnable> #include <QRunnable>
#include <QString> #include <QString>
#include <QSemaphore> #include <QSemaphore>
#include "imageinfo.h" #include <QSize>
#include "imageinfodata.h"
class RawImage;
bool readFITSHeader(const QString &path, ImageInfoData &info);
bool readXISFHeader(const QString &path, ImageInfoData &info);
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar = false);
class Image; class Image;
@@ -25,7 +20,6 @@ public:
void run() override; void run() override;
}; };
class ConvertRunable : public QRunnable class ConvertRunable : public QRunnable
{ {
public: public:
@@ -33,6 +27,10 @@ public:
{ {
int compressionLevel = -1; int compressionLevel = -1;
QString compressionType; QString compressionType;
int binning = 0;
bool average = true;
QSize resize;
Qt::AspectRatioMode aspect = Qt::KeepAspectRatio;
ConvertParams(){} ConvertParams(){}
ConvertParams(const QVariantMap &map); ConvertParams(const QVariantMap &map);
}; };
+42 -3
View File
@@ -64,7 +64,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
} }
_openFilter.append("*.fit *.fits *.fts *.fz *.xisf *.cr2 *.cr3 *.nef *.dng)"); _openFilter.append("*.fit *.fits *.fts *.fz *.xisf *.cr2 *.cr3 *.nef *.dng)");
_openFilter.append(tr(";;All files (*)")); _openFilter.append(tr(";;All files (*)"));
nameFilter.append({"fit", "fits", "xisf", "cr2", "cr3", "nef", "dng"}); nameFilter.append({"fit", "fits", "fts", "fz", "xisf", "cr2", "cr3", "nef", "dng"});
QImageReader::setAllocationLimit(0); QImageReader::setAllocationLimit(0);
m_info = new ImageInfo(this); m_info = new ImageInfo(this);
@@ -186,7 +186,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
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);
viewMenu->addAction(tr("Best Fit"), QKeySequence("Ctrl+1"), m_image, &ImageScrollArea::bestFit); viewMenu->addAction(tr("Best Fit"), QKeySequence("Ctrl+1"), m_image, &ImageScrollArea::bestFit);
viewMenu->addAction(tr("100%"), m_image, &ImageScrollArea::oneToOne); viewMenu->addAction(tr("100%"), QKeySequence("Ctrl+0"), m_image, &ImageScrollArea::oneToOne);
viewMenu->addSeparator(); viewMenu->addSeparator();
QMenu *bayerMenu = viewMenu->addMenu(tr("Bayer mask")); QMenu *bayerMenu = viewMenu->addMenu(tr("Bayer mask"));
QActionGroup *bayerActionGroup = new QActionGroup(this); QActionGroup *bayerActionGroup = new QActionGroup(this);
@@ -407,6 +407,9 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
if(!dest.isEmpty() && dir.exists()) if(!dest.isEmpty() && dir.exists())
{ {
int i = 0; int i = 0;
int missing = 0;
bool overwriteAll = false;
bool skipAll = false;
QStringList files = m_database->getMarkedFiles(); QStringList files = m_database->getMarkedFiles();
QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this); QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this);
progress.setWindowModality(Qt::WindowModal); progress.setWindowModality(Qt::WindowModal);
@@ -418,8 +421,42 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
QFile srcFile(file); QFile srcFile(file);
QFile dstFile(dir.absoluteFilePath(info.fileName())); QFile dstFile(dir.absoluteFilePath(info.fileName()));
if(dstFile.exists()) if(!srcFile.exists())
{
missing++;
continue; continue;
}
if(dstFile.exists())
{
if(skipAll)
{
continue;
}
else if(overwriteAll)
{
dstFile.remove();
}
else
{
QMessageBox::StandardButton button = QMessageBox::question(this, tr("Overwrite file?"), tr("Destination file %1 already exists. Overwrite?").arg(dstFile.fileName()),
QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll);
switch (button)
{
case QMessageBox::YesToAll:
overwriteAll = true;
case QMessageBox::Yes:
dstFile.remove();
break;
case QMessageBox::NoToAll:
skipAll = true;
case QMessageBox::No:
continue;
default:
break;
}
}
}
if(progress.wasCanceled()) if(progress.wasCanceled())
return; return;
@@ -458,6 +495,8 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
progress.setValue(i++); progress.setValue(i++);
} }
m_database->clearMarkedFiles(); m_database->clearMarkedFiles();
if(missing)
QMessageBox::information(this, tr("Missing marked files"), tr("%1 marked files were missing. They were skipped.").arg(missing));
} }
} }
+1 -1
View File
@@ -60,6 +60,6 @@ void MarkedFiles::clearSelected()
void MarkedFiles::clearAll() void MarkedFiles::clearAll()
{ {
QSqlDatabase db = QSqlDatabase::database(); QSqlDatabase db = QSqlDatabase::database();
db.exec("DELETE FROM files"); QSqlQuery("DELETE FROM files", db);
m_model->select(); m_model->select();
} }
+11
View File
@@ -0,0 +1,11 @@
#ifndef MTFPARAM_H
#define MTFPARAM_H
struct MTFParam
{
float blackPoint[3] = {0.0f, 0.0f, 0.0f};
float midPoint[3] = {0.5f, 0.5f, 0.5f};
float whitePoint[3] = {1.0f, 1.0f, 1.0f};
};
#endif // MTFPARAM_H
+1
View File
@@ -198,4 +198,5 @@ void PlateSolving::settings()
{ {
PlateSolvingSettings settings(this); PlateSolvingSettings settings(this);
settings.exec(); settings.exec();
_solver->setIndexFolder(settings.indexDirectory());
} }
+5
View File
@@ -123,6 +123,11 @@ void PlateSolvingSettings::checkIndexFiles()
checkScale(_ui->scale19, 19); checkScale(_ui->scale19, 19);
} }
QString PlateSolvingSettings::indexDirectory() const
{
return _ui->indexPaths->currentText();
}
void PlateSolvingSettings::progress(int percent, int files) void PlateSolvingSettings::progress(int percent, int files)
{ {
_ui->filesRemaining->setText(tr("%1 files").arg(files)); _ui->filesRemaining->setText(tr("%1 files").arg(files));
+1
View File
@@ -18,6 +18,7 @@ public:
explicit PlateSolvingSettings(QWidget *parent = nullptr); explicit PlateSolvingSettings(QWidget *parent = nullptr);
~PlateSolvingSettings(); ~PlateSolvingSettings();
void checkIndexFiles(); void checkIndexFiles();
QString indexDirectory() const;
protected slots: protected slots:
void progress(int percent, int files); void progress(int percent, int files);
private: private:
+219 -109
View File
@@ -1,12 +1,16 @@
#include "rawimage.h" #include "rawimage.h"
#include <QDebug>
#include <cstring> #include <cstring>
#include <lcms2.h>
#ifndef NO_QT
#include <QDebug>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QFloat16> #include <QFloat16>
#include <QColorSpace> #include <QColorSpace>
#include <lcms2.h>
using F16 = qfloat16; using F16 = qfloat16;
#else
#include <algorithm>
using F16 = _Float16;
#endif
int THUMB_SIZE = 128; int THUMB_SIZE = 128;
int THUMB_SIZE_BORDER = 138; int THUMB_SIZE_BORDER = 138;
@@ -45,7 +49,7 @@ void RawImage::allocate(uint32_t w, uint32_t h, uint32_t ch, DataType type)
m_channels = ch; m_channels = ch;
m_ch = ch == 3 ? 4 : ch; m_ch = ch == 3 ? 4 : ch;
m_origType = m_type = type; m_origType = m_type = type;
m_pixels = std::make_unique<PixelType[]>(m_width * m_height * m_ch * typeSize(type)); m_pixels = std::make_unique<PixelType[]>((size_t)m_width * m_height * m_ch * typeSize(type));
} }
RawImage::RawImage() RawImage::RawImage()
@@ -60,7 +64,7 @@ RawImage::RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type)
RawImage::RawImage(const RawImage &d) RawImage::RawImage(const RawImage &d)
{ {
allocate(d.m_width, d.m_height, d.m_channels, d.m_type); allocate(d.m_width, d.m_height, d.m_channels, d.m_type);
std::memcpy(m_pixels.get(), d.m_pixels.get(), m_width * m_height * m_ch * typeSize(m_type)); std::memcpy(m_pixels.get(), d.m_pixels.get(), (size_t)m_width * m_height * m_ch * typeSize(m_type));
m_stats = d.m_stats; m_stats = d.m_stats;
} }
@@ -78,6 +82,7 @@ RawImage::RawImage(RawImage &&d)
m_thumbAspect = d.m_thumbAspect; m_thumbAspect = d.m_thumbAspect;
} }
#ifndef NO_QT
RawImage::RawImage(const QImage &img) RawImage::RawImage(const QImage &img)
{ {
qDebug() << img; qDebug() << img;
@@ -118,6 +123,24 @@ RawImage::RawImage(const QImage &img)
for(int i=0; i<img.height(); i++) for(int i=0; i<img.height(); i++)
std::memcpy(data(i), img.scanLine(i), img.width()*2); std::memcpy(data(i), img.scanLine(i), img.width()*2);
} }
else if(img.format() == QImage::Format_RGB32 || img.format() == QImage::Format_ARGB32)
{
allocate(img.width(), img.height(), 4, UINT8);
for(int i=0; i<img.height(); i++)
{
uint32_t *src = (uint32_t*)img.scanLine(i);
uint32_t *dst = (uint32_t*)data(i);
for(int o=0; o<img.width(); o++)
{
uint32_t p = src[o];
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
dst[o] = (p & 0xff000000) | (p >> 16 & 0xff) | (p & 0xff00) | (p << 16 & 0xff0000);
#else
dst[o] = (p >> 24) | (p << 8 & 0xffffff00);
#endif
}
}
}
else else
{ {
QImage tmp = img.convertToFormat(QImage::Format_RGBA8888); QImage tmp = img.convertToFormat(QImage::Format_RGBA8888);
@@ -127,6 +150,7 @@ RawImage::RawImage(const QImage &img)
} }
m_stats.m_stats = false; m_stats.m_stats = false;
} }
#endif
const RawImage::Stats& RawImage::imageStats() const const RawImage::Stats& RawImage::imageStats() const
{ {
@@ -279,46 +303,6 @@ void RawImage::calcStats()
} }
} }
void RawImage::rect(int &x, int &y, int w, int h, std::vector<double> &r) const
{
/*r.resize(w*h);
x -= w/2;
y -= h/2;
if(x<0)x = 0;
if(y<0)y = 0;
if(x+w >= m_img.cols)x = m_img.cols-w;
if(y+h >= m_img.rows)y = m_img.rows-h;
cv::Mat roiImg(m_img, cv::Rect(x, y, w, h));
cv::Mat doubleMat;
roiImg.convertTo(doubleMat, CV_64F);
r = std::vector<double>(doubleMat.begin<double>(), doubleMat.end<double>());*/
}
int RawImage::findPeaks(double background, double distance, std::vector<Peak> &peaks) const
{
/*std::vector<std::vector<cv::Point>> contours;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(distance, distance));
cv::Mat img, mask, dilate, locMax, result;
if(m_img.channels() == 1)img = m_img;
else cv::cvtColor(m_img, img, cv::COLOR_RGB2GRAY);
cv::dilate(img, dilate, kernel);
cv::compare(img, dilate, locMax, cv::CMP_GE);
cv::compare(img, cv::Scalar(background), mask, cv::CMP_GT);
cv::bitwise_and(locMax, mask, result);
cv::findContours(result, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
peaks.reserve(contours.size());
for(auto contour : contours)
{
peaks.push_back(Peak(1, contour[0].x, contour[0].y));
}
return peaks.size();*/
}
uint32_t RawImage::width() const uint32_t RawImage::width() const
{ {
return m_width; return m_width;
@@ -334,9 +318,9 @@ uint32_t RawImage::channels() const
return m_channels; return m_channels;
} }
uint32_t RawImage::size() const uint64_t RawImage::size() const
{ {
return width()*height(); return (uint64_t)width()*height();
} }
RawImage::DataType RawImage::type() const RawImage::DataType RawImage::type() const
@@ -376,12 +360,12 @@ const void *RawImage::data() const
void *RawImage::data(uint32_t row, uint32_t col) void *RawImage::data(uint32_t row, uint32_t col)
{ {
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type); return m_pixels.get() + ((size_t)m_width * row * m_ch + (size_t)col * m_ch) * typeSize(m_type);
} }
const void *RawImage::data(uint32_t row, uint32_t col) const const void *RawImage::data(uint32_t row, uint32_t col) const
{ {
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type); return m_pixels.get() + ((size_t)m_width * row * m_ch + (size_t)col * m_ch) * typeSize(m_type);
} }
const void *RawImage::origData() const const void *RawImage::origData() const
@@ -396,12 +380,12 @@ const void *RawImage::origData(uint32_t row, uint32_t col) const
{ {
if(m_original) if(m_original)
{ {
col = col * m_origWidth / m_width; col = (uint64_t)col * m_origWidth / m_width;
row = row * m_origHeight / m_height; row = (uint64_t)row * m_origHeight / m_height;
return m_original.get() + (m_origWidth * row * m_ch + col * m_ch) * typeSize(m_origType); return m_original.get() + ((size_t)m_origWidth * row * m_ch + (size_t)col * m_ch) * typeSize(m_origType);
} }
else else
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type); return m_pixels.get() + ((size_t)m_width * row * m_ch + (size_t)col * m_ch) * typeSize(m_type);
} }
bool RawImage::planar() const bool RawImage::planar() const
@@ -421,12 +405,12 @@ void RawImage::convertToThumbnail()
auto loop = [&](F16 *out, auto *in, float scale) auto loop = [&](F16 *out, auto *in, float scale)
{ {
for(int i=0; i<THUMB_SIZE; i++) for(int64_t i=0; i<THUMB_SIZE; i++)
{ {
for(int o=0; o<THUMB_SIZE; o++) for(int64_t o=0; o<THUMB_SIZE; o++)
{ {
int idx = (i*THUMB_SIZE + o)*4; int64_t idx = (i*THUMB_SIZE + o)*4;
int idx2 = ((i * m_height / THUMB_SIZE * m_width) + (o * m_width / THUMB_SIZE)) * m_ch; int64_t idx2 = ((i * m_height / THUMB_SIZE * m_width) + (o * m_width / THUMB_SIZE)) * m_ch;
if(m_channels == 1) if(m_channels == 1)
{ {
@@ -461,7 +445,9 @@ void RawImage::convertToThumbnail()
loop(out, reinterpret_cast<float*>(m_pixels.get()), 1.0f); loop(out, reinterpret_cast<float*>(m_pixels.get()), 1.0f);
break; break;
default: default:
#ifndef NO_QT
qWarning() << "FLOAT64 should not happend"; qWarning() << "FLOAT64 should not happend";
#endif
return; return;
} }
@@ -475,45 +461,106 @@ void RawImage::convertToThumbnail()
void RawImage::convertToGLFormat() void RawImage::convertToGLFormat()
{ {
size_t s = size() * m_ch; if(m_type == UINT32 || m_type == FLOAT64)
if(m_type == UINT32) convertToType(FLOAT32);
{ else if(OpenGLES && m_type == UINT16)
m_origWidth = m_width; convertToType(FLOAT16);
m_origHeight = m_height; }
m_original = std::move(m_pixels);
allocate(m_width, m_height, m_channels, FLOAT32);
m_origType = UINT32;
float *dst = reinterpret_cast<float*>(m_pixels.get());
uint32_t *src = reinterpret_cast<uint32_t*>(m_original.get());
for(size_t i = 0; i < s; i++) template<typename T, typename U>
dst[i] = src[i] / (float)UINT32_MAX; void convertType2(size_t size, const T *src, U *dst)
} {
else if(m_type == FLOAT64) if constexpr((std::is_floating_point_v<T> || std::is_same_v<T, F16>) && (std::is_floating_point_v<U> || std::is_same_v<T, F16>))
{ {
m_origWidth = m_width; for(size_t i = 0; i < size; i++)
m_origHeight = m_height;
m_original = std::move(m_pixels);
allocate(m_width, m_height, m_channels, FLOAT32);
m_origType = FLOAT64;
float *dst = reinterpret_cast<float*>(m_pixels.get());
double *src = reinterpret_cast<double*>(m_original.get());
for(size_t i = 0; i < s; i++)
dst[i] = src[i]; dst[i] = src[i];
} }
else if(OpenGLES && m_type == UINT16)
{
m_origWidth = m_width;
m_origHeight = m_height;
m_original = std::move(m_pixels);
allocate(m_width, m_height, m_channels, FLOAT16);
m_origType = UINT16;
F16 *dst = reinterpret_cast<F16*>(m_pixels.get());
uint16_t *src = reinterpret_cast<uint16_t*>(m_original.get());
for(size_t i = 0; i < s; i++) if constexpr(std::is_integral_v<T> && std::is_integral_v<U>)
dst[i] = (F16)(src[i] / (float)UINT16_MAX); {
if constexpr(sizeof(T) > sizeof(U))
for(size_t i = 0; i < size; i++)
dst[i] = src[i] >> ((sizeof(T) - sizeof(U)) * 8);
else
for(size_t i = 0; i < size; i++)
dst[i] = static_cast<U>(src[i]) << ((sizeof(U) - sizeof(T)) * 8);
}
if constexpr((std::is_floating_point_v<T> || std::is_same_v<T, F16>) && std::is_integral_v<U>)
{
U max = std::numeric_limits<U>::max();
T scale = (T)(max);
for(size_t i = 0; i < size; i++)
dst[i] = src[i] * scale;
}
if constexpr(std::is_integral_v<T> && (std::is_floating_point_v<U> || std::is_same_v<U, F16>))
{
U scale = (U)(1.0 / (double)std::numeric_limits<T>::max());
for(size_t i = 0; i < size; i++)
dst[i] = (U)src[i] * scale;
}
}
template<typename T>
void convertType(size_t size, RawImage::DataType dstType, const T *src, void *dst)
{
switch(dstType)
{
case RawImage::UINT8:
convertType2(size, src, static_cast<uint8_t*>(dst));
break;
case RawImage::UINT16:
convertType2(size, src, static_cast<uint16_t*>(dst));
break;
case RawImage::UINT32:
convertType2(size, src, static_cast<uint32_t*>(dst));
break;
case RawImage::FLOAT16:
convertType2(size, src, static_cast<F16*>(dst));
break;
case RawImage::FLOAT32:
convertType2(size, src, static_cast<float*>(dst));
break;
case RawImage::FLOAT64:
convertType2(size, src, static_cast<double*>(dst));
break;
}
}
void RawImage::convertToType(DataType type)
{
if(type == m_type)
return;
m_origWidth = m_width;
m_origHeight = m_height;
m_original = std::move(m_pixels);
DataType origType = m_type;
allocate(m_width, m_height, m_channels, type);
m_origType = origType;
size_t s = size() * m_ch;
switch(m_origType)
{
case UINT8:
convertType(s, type, reinterpret_cast<uint8_t*>(m_original.get()), m_pixels.get());
break;
case UINT16:
convertType(s, type, reinterpret_cast<uint16_t*>(m_original.get()), m_pixels.get());
break;
case UINT32:
convertType(s, type, reinterpret_cast<uint32_t*>(m_original.get()), m_pixels.get());
break;
case FLOAT16:
convertType(s, type, reinterpret_cast<F16*>(m_original.get()), m_pixels.get());
break;
case FLOAT32:
convertType(s, type, reinterpret_cast<float*>(m_original.get()), m_pixels.get());
break;
case FLOAT64:
convertType(s, type, reinterpret_cast<double*>(m_original.get()), m_pixels.get());
break;
} }
} }
@@ -622,28 +669,28 @@ bool RawImage::pixel(int x, int y, double &r, double &g, double &b) const
return true; return true;
} }
template<typename T> template<typename T, typename U = float>
void boxResample(uint32_t w, uint32_t h, uint32_t ch, uint32_t oldw, uint32_t oldh, const uint8_t *in_, uint8_t *out_) void boxResample(uint32_t w, uint32_t h, uint32_t ch, uint32_t oldw, uint32_t oldh, const uint8_t *in_, uint8_t *out_)
{ {
if(oldw == 0 || oldh == 0)return; if(oldw == 0 || oldh == 0)return;
const T *in = reinterpret_cast<const T*>(in_); const T *in = reinterpret_cast<const T*>(in_);
T *out = reinterpret_cast<T*>(out_); T *out = reinterpret_cast<T*>(out_);
float max = 255.0f; U max = 255.0f;
if constexpr(std::is_same_v<T, uint16_t>) if constexpr(std::is_integral_v<T>)
max = UINT16_MAX; max = (U)std::numeric_limits<T>::max();
float sx = (float)w / oldw; float sx = (float)w / oldw;
float sy = (float)h / oldh; float sy = (float)h / oldh;
for(uint32_t y = 0; y < h; y++)//iterate over destination Y for(uint64_t y = 0; y < h; y++)//iterate over destination Y
{ {
for(uint32_t x = 0; x < w; x++)//iterate over destination X for(uint64_t x = 0; x < w; x++)//iterate over destination X
{ {
float p[4] = {0.0f}; U p[4] = {0.0f};
uint32_t xx = x * oldw / w;//calculate source rect uint64_t xx = x * oldw / w;//calculate source rect
uint32_t yy = y * oldh / h; uint64_t yy = y * oldh / h;
uint32_t xe = std::min((x + 1) * oldw / w, oldw - 1); uint64_t xe = std::min((x + 1) * oldw / w, (uint64_t)oldw - 1);
uint32_t ye = std::min((y + 1) * oldh / h, oldh - 1); uint64_t ye = std::min((y + 1) * oldh / h, (uint64_t)oldh - 1);
for(uint32_t o = yy; o <= ye; o++)//iterate over source Y for(uint32_t o = yy; o <= ye; o++)//iterate over source Y
{ {
float cy = o * sy - y; float cy = o * sy - y;
@@ -692,14 +739,75 @@ void RawImage::resize(uint32_t w, uint32_t h)
case RawImage::UINT16: case RawImage::UINT16:
boxResample<uint16_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get()); boxResample<uint16_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break; break;
case RawImage::FLOAT32: case RawImage::UINT32:
boxResample<float>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get()); boxResample<uint32_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break; break;
case RawImage::FLOAT16: case RawImage::FLOAT16:
boxResample<F16>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get()); boxResample<F16>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break; break;
case RawImage::FLOAT32:
boxResample<float>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break;
case RawImage::FLOAT64:
boxResample<double, double>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break;
}
}
template<typename T, typename U>
void integerResample(uint32_t w, uint32_t h, uint32_t ch, uint32_t oldw, uint32_t down, bool avg, const uint8_t *in_, uint8_t *out_)
{
const T *in = reinterpret_cast<const T*>(in_);
T *out = reinterpret_cast<T*>(out_);
uint32_t down2 = down * down;
U m = std::numeric_limits<T>::max();
if constexpr(std::is_floating_point_v<T>)m = down2;
for(uint64_t i = 0; i < h; i++)
{
for(uint64_t o = 0; o < w; o++)
{
for(uint64_t p = 0; p < ch; p++)
{
U pix = 0;
for(uint32_t y = 0; y < down; y++)
for(uint32_t x = 0; x < down; x++)
pix += in[((i * down) + y) * oldw * ch + ((o * down) + x) * ch + p];
if (avg)
out[(i * w + o) * ch + p] = pix / down2;
else
out[(i * w + o) * ch + p] = std::min(pix, m);
}
}
}
}
void RawImage::resizeInt(int downsample, bool avg)
{
uint32_t oldw = m_width;
std::unique_ptr<PixelType[]> old_pixels = std::move(m_pixels);
allocate(m_width / downsample, m_height / downsample, m_channels, m_type);
switch(m_type)
{
case RawImage::UINT8:
integerResample<uint8_t, uint32_t>(m_width, m_height, m_ch, oldw, downsample, avg, old_pixels.get(), m_pixels.get());
break;
case RawImage::UINT16:
integerResample<uint16_t, uint32_t>(m_width, m_height, m_ch, oldw, downsample, avg, old_pixels.get(), m_pixels.get());
break;
case RawImage::UINT32:
integerResample<uint32_t, uint64_t>(m_width, m_height, m_ch, oldw, downsample, avg, old_pixels.get(), m_pixels.get());
break;
case RawImage::FLOAT32:
integerResample<float, double>(m_width, m_height, m_ch, oldw, downsample, avg, old_pixels.get(), m_pixels.get());
break;
case RawImage::FLOAT64:
integerResample<double, double>(m_width, m_height, m_ch, oldw, downsample, avg, old_pixels.get(), m_pixels.get());
break;
default: default:
qWarning() << "Resizing format not supported";
break; break;
} }
} }
@@ -880,11 +988,13 @@ bool RawImage::valid() const
return m_width > 0 && m_height > 0; return m_width > 0 && m_height > 0;
} }
#ifndef NO_QT
void RawImage::setICCProfile(const QByteArray &icc) 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)
{ {
@@ -931,12 +1041,12 @@ void RawImage::convertTosRGB()
} }
else else
{ {
qDebug() << "Failed to create color transform"; //qDebug() << "Failed to create color transform";
} }
} }
else else
{ {
qDebug() << "Failed to open icc profile"; //qDebug() << "Failed to open icc profile";
} }
cmsCloseProfile(inProfile); cmsCloseProfile(inProfile);
@@ -980,13 +1090,13 @@ void RawImage::generateLUT()
} }
else else
{ {
qDebug() << "Failed to create color transform"; //qDebug() << "Failed to create color transform";
m_lut.clear(); m_lut.clear();
} }
} }
else else
{ {
qDebug() << "Failed to open icc profile"; //qDebug() << "Failed to open icc profile";
m_lut.clear(); m_lut.clear();
} }
+9 -3
View File
@@ -7,7 +7,9 @@
#include <stdint.h> #include <stdint.h>
#include <math.h> #include <math.h>
#include <memory.h> #include <memory.h>
#ifndef NO_QT
#include <QImage> #include <QImage>
#endif
extern int THUMB_SIZE; extern int THUMB_SIZE;
extern int THUMB_SIZE_BORDER; extern int THUMB_SIZE_BORDER;
@@ -83,15 +85,15 @@ public:
RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type); RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type);
RawImage(const RawImage &d); RawImage(const RawImage &d);
RawImage(RawImage &&d); RawImage(RawImage &&d);
#ifndef NO_QT
RawImage(const QImage &img); RawImage(const QImage &img);
#endif
const RawImage::Stats& imageStats() const; const RawImage::Stats& imageStats() const;
void calcStats(); void calcStats();
void rect(int &x, int &y, int w, int h, std::vector<double> &r) const;
int findPeaks(double background, double distance, std::vector<Peak> &peaks) const;
uint32_t width() const; uint32_t width() const;
uint32_t height() const; uint32_t height() const;
uint32_t channels() const; uint32_t channels() const;
uint32_t size() const; uint64_t size() const;
DataType type() const; DataType type() const;
uint32_t norm() const; uint32_t norm() const;
uint32_t widthBytes() const; uint32_t widthBytes() const;
@@ -105,9 +107,11 @@ public:
void setPlanar(); void setPlanar();
void convertToThumbnail(); void convertToThumbnail();
void convertToGLFormat(); void convertToGLFormat();
void convertToType(RawImage::DataType type);
float thumbAspect() const; float thumbAspect() const;
bool pixel(int x, int y, double &r, double &g, double &b) const; bool pixel(int x, int y, double &r, double &g, double &b) const;
void resize(uint32_t w, uint32_t h); void resize(uint32_t w, uint32_t h);
void resizeInt(int downsample, bool avg);
std::pair<float, float> unitScale() const; std::pair<float, float> unitScale() const;
void flip(); void flip();
@@ -117,7 +121,9 @@ public:
static size_t typeSize(DataType type); static size_t typeSize(DataType type);
std::vector<RawImage> split() const; std::vector<RawImage> split() const;
bool valid() const; bool valid() const;
#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();
+5 -5
View File
@@ -1,7 +1,7 @@
#include "rawimage.h"
#ifdef __SSE2__ #ifdef __SSE2__
#include <x86intrin.h> #include <x86intrin.h>
#include <cstdint>
#include <limits>
template<typename T, int ch> template<typename T, int ch>
void fromPlanarSSE(const void *in, void *out, size_t count) void fromPlanarSSE(const void *in, void *out, size_t count)
@@ -21,7 +21,7 @@ void fromPlanarSSE(const void *in, void *out, size_t count)
__m128i r = _mm_loadu_si128(_in[0] + i); __m128i r = _mm_loadu_si128(_in[0] + i);
__m128i g = _mm_loadu_si128(_in[1] + i); __m128i g = _mm_loadu_si128(_in[1] + i);
__m128i b = _mm_loadu_si128(_in[2] + i); __m128i b = _mm_loadu_si128(_in[2] + i);
if constexpr(ch==4)a = _mm_loadu_si128(_in[3]); if constexpr(ch==4)a = _mm_loadu_si128(_in[3] + i);
__m128i d1 = _mm_unpacklo_epi8(r, b); __m128i d1 = _mm_unpacklo_epi8(r, b);
__m128i d2 = _mm_unpacklo_epi8(g, a); __m128i d2 = _mm_unpacklo_epi8(g, a);
@@ -43,7 +43,7 @@ void fromPlanarSSE(const void *in, void *out, size_t count)
__m128i r = _mm_loadu_si128(_in[0] + i); __m128i r = _mm_loadu_si128(_in[0] + i);
__m128i g = _mm_loadu_si128(_in[1] + i); __m128i g = _mm_loadu_si128(_in[1] + i);
__m128i b = _mm_loadu_si128(_in[2] + i); __m128i b = _mm_loadu_si128(_in[2] + i);
if constexpr(ch==4)a = _mm_loadu_si128(_in[3]); if constexpr(ch==4)a = _mm_loadu_si128(_in[3] + i);
__m128i d1 = _mm_unpacklo_epi16(r, b); __m128i d1 = _mm_unpacklo_epi16(r, b);
__m128i d2 = _mm_unpacklo_epi16(g, a); __m128i d2 = _mm_unpacklo_epi16(g, a);
@@ -66,7 +66,7 @@ void fromPlanarSSE(const void *in, void *out, size_t count)
__m128i r = _mm_loadu_si128(_in[0] + i); __m128i r = _mm_loadu_si128(_in[0] + i);
__m128i g = _mm_loadu_si128(_in[1] + i); __m128i g = _mm_loadu_si128(_in[1] + i);
__m128i b = _mm_loadu_si128(_in[2] + i); __m128i b = _mm_loadu_si128(_in[2] + i);
if constexpr(ch==4)a = _mm_loadu_si128(_in[3]); if constexpr(ch==4)a = _mm_loadu_si128(_in[3] + i);
__m128i d1 = _mm_unpacklo_epi32(r, b); __m128i d1 = _mm_unpacklo_epi32(r, b);
__m128i d2 = _mm_unpacklo_epi32(g, a); __m128i d2 = _mm_unpacklo_epi32(g, a);
+6 -4
View File
@@ -16,14 +16,16 @@
<file>grbg.png</file> <file>grbg.png</file>
<file>gbrg.png</file> <file>gbrg.png</file>
<file>space.nouspiro.tenmon.png</file> <file>space.nouspiro.tenmon.png</file>
</qresource> <file>../translations/tenmon_pt_BR.qm</file>
<qresource lang="en" prefix="/">
<file alias="help">../about/help_en</file> <file alias="help">../about/help_en</file>
</qresource> </qresource>
<qresource lang="sk" prefix="/"> <qresource prefix="/" lang="en">
<file alias="help">../about/help_en</file>
</qresource>
<qresource prefix="/" lang="sk">
<file alias="help">../about/help_sk</file> <file alias="help">../about/help_sk</file>
</qresource> </qresource>
<qresource lang="fr" prefix="/"> <qresource prefix="/" lang="fr">
<file alias="help">../about/help_fr</file> <file alias="help">../about/help_fr</file>
</qresource> </qresource>
</RCC> </RCC>
+96 -30
View File
@@ -3,13 +3,16 @@
#include <QFileInfo> #include <QFileInfo>
#include <QDebug> #include <QDebug>
#include <QInputDialog> #include <QInputDialog>
#include <QJsonValue>
#include "loadrunable.h" #include "loadrunable.h"
#include "rawimage.h" #include "rawimage.h"
#include "loadrunable.h" #include "loadimage.h"
#include "batchprocessing.h" #include "batchprocessing.h"
#include <fitsio2.h> #include <fitsio2.h>
#include "libXISF/libxisf.h" #include "libxisf.h"
#ifdef PLATESOLVER
#include "solver.h" #include "solver.h"
#endif // PLATESOLVER
namespace Script namespace Script
{ {
@@ -26,8 +29,11 @@ ScriptEngine::ScriptEngine(BatchProcessing *parent)
_jsEngine->globalObject().setProperty("FITSRecordModify", fitsRecordObject); _jsEngine->globalObject().setProperty("FITSRecordModify", fitsRecordObject);
_database->init(QLatin1String("scriptengine")); _database->init(QLatin1String("scriptengine"));
_semaphore.release(_pool->maxThreadCount()); _semaphore.release(_pool->maxThreadCount());
_pool->setThreadPriority(QThread::LowPriority);
#ifdef PLATESOLVER
_solver = new Solver(this); _solver = new Solver(this);
#endif // PLATESOLVER
} }
void ScriptEngine::setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir) void ScriptEngine::setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir)
@@ -49,7 +55,9 @@ const QString &ScriptEngine::outputDir() const
void ScriptEngine::interrupt() void ScriptEngine::interrupt()
{ {
_solver->abort(); #ifdef PLATESOLVER
if(_solver)_solver->abort();
#endif
_jsEngine->setInterrupted(true); _jsEngine->setInterrupted(true);
} }
@@ -122,25 +130,6 @@ QJSValue ScriptEngine::getItem(const QStringList &items, const QString &label, i
return ret; return ret;
} }
void ScriptEngine::setStartingSolution(const QJSValue &solution)
{
if(solution.isObject())
{
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());
if(solution.hasProperty("pixscale") && solution.property("pixscale").isNumber())
{
double scale = solution.property("pixscale").toNumber();
_solver->setSearchScale(scale * 0.8, scale * 1.2, SSolver::ScaleUnits::ARCSEC_PER_PIX);
}
}
else
{
_solver->clearStartingPositionAndScale();
}
}
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;
@@ -153,9 +142,9 @@ bool ScriptEngine::convert(File *file, QString &outpath, const QString &format,
path = dir.absoluteFilePath(outpath); path = dir.absoluteFilePath(outpath);
QString f = format.toLower(); QString f = format.toLower();
if(f != "xisf" && f != "fits" && f != "png" && f != "bmp" && f != "jpg") if(f != "xisf" && f != "fits" && f != "png" && f != "bmp" && f != "jpg" && f != "tiff")
{ {
logError("Output format must be one of xisf fits jpg png bmp"); logError("Output format must be one of xisf fits jpg png bmp tiff");
return false; return false;
} }
@@ -174,6 +163,78 @@ bool ScriptEngine::convert(File *file, QString &outpath, const QString &format,
return true; return true;
} }
#ifdef PLATESOLVER
void ScriptEngine::setSolverProfile(int index)
{
if(_solver && index >= SSolver::Parameters::DEFAULT && index < SSolver::Parameters::BIG_STARS)
{
_solver->setParameters((SSolver::Parameters::ParametersProfile)index);
}
}
void ScriptEngine::setSolverProfile(const QVariantMap &profile)
{
if(_solver)
{
SSolver::Parameters params = SSolver::Parameters::convertFromMap(profile);
_solver->setParameters(params);
}
}
QJSValue ScriptEngine::getSolverProfile() const
{
if(_solver)
{
QMap<QString, QVariant> params = SSolver::Parameters::convertToMap(_solver->getProfile());
QJSValue ret = _jsEngine->newObject();
for(auto i = params.begin(); i != params.end(); i++)
{
switch(i.value().metaType().id())
{
case QMetaType::Int:
ret.setProperty(i.key(), i.value().toInt());
break;
case QMetaType::Double:
ret.setProperty(i.key(), i.value().toDouble());
break;
case QMetaType::Bool:
ret.setProperty(i.key(), i.value().toBool());
break;
case QMetaType::QString:
ret.setProperty(i.key(), i.value().toString());
break;
default:
qWarning() << "unhandled metatype" << i.key() << i.value();
break;
}
}
return ret;
}
else
{
return QJSValue();
}
}
void ScriptEngine::setStartingSolution(const QJSValue &solution)
{
if(solution.isObject())
{
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());
if(solution.hasProperty("pixscale") && solution.property("pixscale").isNumber())
{
double scale = solution.property("pixscale").toNumber();
_solver->setSearchScale(scale * 0.8, scale * 1.2, SSolver::ScaleUnits::ARCSEC_PER_PIX);
}
}
else
{
_solver->clearStartingPositionAndScale();
}
}
QJSValue ScriptEngine::solveImage(File *file, bool updateHeader) QJSValue ScriptEngine::solveImage(File *file, bool updateHeader)
{ {
QString path = file->absoluteFilePath(); QString path = file->absoluteFilePath();
@@ -253,6 +314,7 @@ QJSValue ScriptEngine::extractStars(File *file, bool hfr)
return ret; return ret;
} }
#endif // PLATESOLVER
QJSValue ScriptEngine::newObject() QJSValue ScriptEngine::newObject()
{ {
@@ -457,7 +519,8 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
{ {
fitsfile *file; fitsfile *file;
int status = 0; int status = 0;
fits_open_diskfile(&file, _path.toLocal8Bit().data(), READWRITE, &status); QString path = makeMaxPath(_path);
fits_open_diskfile(&file, path.toLocal8Bit().data(), READWRITE, &status);
int num = 0; int num = 0;
fits_get_num_hdus(file, &num, &status); fits_get_num_hdus(file, &num, &status);
if(status) if(status)
@@ -580,9 +643,10 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
try try
{ {
LibXISF::XISFModify modifyXISF; LibXISF::XISFModify modifyXISF;
modifyXISF.open(_path.toLocal8Bit().data()); QString in = makeMaxPath(absoluteFilePath());
QFileInfo in(_path); QString out = in + "~";
QFileInfo out(_path + "~"); modifyXISF.open(in.toLocal8Bit().data());
qDebug() << "modify" << in << out;
for(auto &remove : modify->_remove) for(auto &remove : modify->_remove)
modifyXISF.removeFITSKeyword(0, remove.toStdString()); modifyXISF.removeFITSKeyword(0, remove.toStdString());
@@ -593,9 +657,9 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
for(auto &record : modify->_add) for(auto &record : modify->_add)
modifyXISF.addFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}); modifyXISF.addFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()});
modifyXISF.save(out.absoluteFilePath().toLocal8Bit().toStdString()); modifyXISF.save(out.toLocal8Bit().toStdString());
modifyXISF.close(); modifyXISF.close();
std::filesystem::rename(out.filesystemAbsoluteFilePath(), in.filesystemAbsoluteFilePath()); std::filesystem::rename(out.toLocal8Bit().toStdString(), in.toLocal8Bit().toStdString());
return true; return true;
} }
catch(std::filesystem::filesystem_error &err) catch(std::filesystem::filesystem_error &err)
@@ -682,6 +746,7 @@ QJSValue File::stats()
return _stats; return _stats;
} }
#ifdef PLATESOLVER
QJSValue File::solve(bool updateHeader) QJSValue File::solve(bool updateHeader)
{ {
if(_solution.isUndefined() || updateHeader) if(_solution.isUndefined() || updateHeader)
@@ -697,6 +762,7 @@ QJSValue File::extractStars(bool hfr)
return _stars; return _stars;
} }
#endif // PLATESOLVER
ScriptEngineThread::ScriptEngineThread(BatchProcessing *parent) : QObject(parent) ScriptEngineThread::ScriptEngineThread(BatchProcessing *parent) : QObject(parent)
{ {
+9 -2
View File
@@ -29,7 +29,7 @@ class ScriptEngine : public QObject
QString _scriptPath; QString _scriptPath;
QString _outputDir; QString _outputDir;
QList<QPair<QString, QString>> _paths; QList<QPair<QString, QString>> _paths;
Solver *_solver; Solver *_solver = nullptr;
public: public:
explicit ScriptEngine(BatchProcessing *parent = nullptr); explicit ScriptEngine(BatchProcessing *parent = nullptr);
void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir); void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir);
@@ -47,10 +47,15 @@ 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 setStartingSolution(const QJSValue &solution = QJSValue());
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
Q_INVOKABLE void setSolverProfile(int index);
Q_INVOKABLE void setSolverProfile(const QVariantMap &profile);
Q_INVOKABLE QJSValue getSolverProfile() const;
Q_INVOKABLE void setStartingSolution(const QJSValue &solution = QJSValue());
QJSValue solveImage(File *file, bool updateHeader); QJSValue solveImage(File *file, bool updateHeader);
QJSValue extractStars(File *file, bool hfr); QJSValue extractStars(File *file, bool hfr);
#endif // PLATESOLVER
QJSValue newObject(); QJSValue newObject();
QJSValue newArray(uint size); QJSValue newArray(uint size);
public slots: public slots:
@@ -116,8 +121,10 @@ public:
Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap()); Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap());
Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap()); Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap());
Q_INVOKABLE QJSValue stats(); Q_INVOKABLE QJSValue stats();
#ifdef PLATESOLVER
Q_INVOKABLE QJSValue solve(bool updateHeader = false); Q_INVOKABLE QJSValue solve(bool updateHeader = false);
Q_INVOKABLE QJSValue extractStars(bool hfr); Q_INVOKABLE QJSValue extractStars(bool hfr);
#endif // PLATESOLVER
}; };
class FITSRecordModify : public QObject class FITSRecordModify : public QObject
+6 -1
View File
@@ -7,7 +7,7 @@
#include <wcslib/wcshdr.h> #include <wcslib/wcshdr.h>
#include <wcslib/wcsutil.h> #include <wcslib/wcsutil.h>
#include "rawimage.h" #include "rawimage.h"
#include "loadrunable.h" #include "loadimage.h"
#include "scriptengine.h" #include "scriptengine.h"
Solver::Solver(QObject *parent) : QObject(parent) Solver::Solver(QObject *parent) : QObject(parent)
@@ -205,6 +205,11 @@ void Solver::setParameters(const Parameters &parameters)
_solver->setParameters(profile); _solver->setParameters(profile);
} }
Parameters Solver::getProfile() const
{
return _solver->getCurrentParameters();
}
void Solver::setSearchScale(double fovLow, double fowHigh, SSolver::ScaleUnits units) void Solver::setSearchScale(double fovLow, double fowHigh, SSolver::ScaleUnits units)
{ {
_solver->setSearchScale(fovLow, fowHigh, units); _solver->setSearchScale(fovLow, fowHigh, units);
+1
View File
@@ -34,6 +34,7 @@ public:
bool updateHeader(QString &error); bool updateHeader(QString &error);
void setParameters(SSolver::Parameters::ParametersProfile profile); void setParameters(SSolver::Parameters::ParametersProfile profile);
void setParameters(const SSolver::Parameters &parameters); void setParameters(const SSolver::Parameters &parameters);
SSolver::Parameters getProfile() const;
void setSearchScale(double fovLow, double fowHigh, ScaleUnits units); void setSearchScale(double fovLow, double fowHigh, ScaleUnits units);
void setSearchPosition(double ra, double dec); void setSearchPosition(double ra, double dec);
void clearStartingPositionAndScale(); void clearStartingPositionAndScale();
+18 -2
View File
@@ -57,6 +57,22 @@
</screenshots> </screenshots>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>
<releases> <releases>
<release version="20250126" date="2025-01-26">
<description>
<ul>
<li>Support for really big images +50000px</li>
<li>Fix handling of MAX_PATH on Windows</li>
<li>Add setting solver profile in scripts</li>
</ul>
</description>
</release>
<release version="20241116" date="2024-11-16">
<description>
<ul>
<li>Extending support of data formats</li>
</ul>
</description>
</release>
<release version="20241002" date="2024-10-02"> <release version="20241002" date="2024-10-02">
<description> <description>
<ul> <ul>
@@ -81,7 +97,7 @@
</ul> </ul>
</description> </description>
</release> </release>
<release version="20240201" date="2024-02-01"> <release version="20240201" date="2024-02-01">
<description> <description>
<ul> <ul>
<li>Smooth thumbnails</li> <li>Smooth thumbnails</li>
@@ -89,7 +105,7 @@
<li>Bugfixes</li> <li>Bugfixes</li>
</ul> </ul>
</description> </description>
</release> </release>
<release version="20240108" date="2024-01-08"> <release version="20240108" date="2024-01-08">
<description> <description>
<ul> <ul>
-299
View File
@@ -1,299 +0,0 @@
#include "starfit.h"
#include <gsl/gsl_blas.h>
#include <QDebug>
#include <iostream>
const int PARAM_AM = 0;
const int PARAM_X0 = 1;
const int PARAM_Y0 = 2;
const int PARAM_SX = 3;
const int PARAM_SY = 4;
const int PARAM_TH = 5;
const int MAX_ITER = 20;
const double TOL = 1.0e-3;
struct StarData
{
size_t size;
std::vector<double> val;
};
// a * exp(-0.5*((x-x0)/sx)^2 + ((y-y0)/sy)^2)
double gauss_model(double a, double x0, double y0, double sx, double sy, double x, double y)
{
double _x = (x-x0)/sx;
double _y = (y-y0)/sy;
return a*exp(-0.5*(_x*_x + _y*_y));
}
int func_f(const gsl_vector *X, void *params, gsl_vector *f)
{
StarData *d = static_cast<StarData*>(params);
double am = gsl_vector_get(X, PARAM_AM);
double x0 = gsl_vector_get(X, PARAM_X0);
double y0 = gsl_vector_get(X, PARAM_Y0);
double sx = gsl_vector_get(X, PARAM_SX);
double sy = gsl_vector_get(X, PARAM_SY);
int i = 0;
for(size_t y=0;y<d->size;y++)
{
for(size_t x=0;x<d->size;x++)
{
double v = gauss_model(am, x0, y0, sx, sy, x, y);
gsl_vector_set(f, i, d->val[i] - v);
i++;
}
}
return GSL_SUCCESS;
}
int func_df(const gsl_vector *X, void *params, gsl_matrix *J)
{
StarData *d = static_cast<StarData*>(params);
double am = gsl_vector_get(X, PARAM_AM);
double x0 = gsl_vector_get(X, PARAM_X0);
double y0 = gsl_vector_get(X, PARAM_Y0);
double sx = gsl_vector_get(X, PARAM_SX);
double sy = gsl_vector_get(X, PARAM_SY);
int i = 0;
for(size_t y=0;y<d->size;y++)
{
for(size_t x=0;x<d->size;x++)
{
double tx = x-x0;
double ty = y-y0;
double e = gauss_model(am, x0, y0, sx, sy, x, y);
gsl_matrix_set(J, i, PARAM_AM, -e/am);
gsl_matrix_set(J, i, PARAM_X0, -e*(tx/(sx*sx)));
gsl_matrix_set(J, i, PARAM_Y0, -e*(ty/(sy*sy)));
gsl_matrix_set(J, i, PARAM_SX, -e*(tx*tx/(sx*sx*sx)));
gsl_matrix_set(J, i, PARAM_SY, -e*(ty*ty/(sy*sy*sy)));
i++;
}
}
return GSL_SUCCESS;
}
int func_f_an(const gsl_vector *X, void *params, gsl_vector *f)
{
StarData *d = static_cast<StarData*>(params);
double am = gsl_vector_get(X, PARAM_AM);
double x0 = gsl_vector_get(X, PARAM_X0);
double y0 = gsl_vector_get(X, PARAM_Y0);
double sx = gsl_vector_get(X, PARAM_SX);
double sy = gsl_vector_get(X, PARAM_SY);
double th = gsl_vector_get(X, PARAM_TH);
int i = 0;
double a = sin(th);
double b = cos(th);
for(size_t y=0;y<d->size;y++)
{
for(size_t x=0;x<d->size;x++)
{
double v = gauss_model(am, x0, y0, sx, sy, x*b-y*a, x*a+y*b);
gsl_vector_set(f, i, d->val[i] - v);
i++;
}
}
return GSL_SUCCESS;
}
int func_df_af(const gsl_vector *X, void *params, gsl_matrix *J)
{
StarData *d = static_cast<StarData*>(params);
double am = gsl_vector_get(X, PARAM_AM);
double x0 = gsl_vector_get(X, PARAM_X0);
double y0 = gsl_vector_get(X, PARAM_Y0);
double sx = gsl_vector_get(X, PARAM_SX);
double sy = gsl_vector_get(X, PARAM_SY);
int i = 0;
for(size_t y=0;y<d->size;y++)
{
for(size_t x=0;x<d->size;x++)
{
double tx = x-x0;
double ty = y-y0;
double e = gauss_model(am, x0, y0, sx, sy, x, y);
gsl_matrix_set(J, i, PARAM_AM, -e/am);
gsl_matrix_set(J, i, PARAM_X0, -e*(tx/(sx*sx)));
gsl_matrix_set(J, i, PARAM_Y0, -e*(ty/(sy*sy)));
gsl_matrix_set(J, i, PARAM_SX, -e*(tx*tx/(sx*sx*sx)));
gsl_matrix_set(J, i, PARAM_SY, -e*(ty*ty/(sy*sy*sy)));
i++;
}
}
return GSL_SUCCESS;
}
//int func_fvv(const gsl_vector *x, const gsl_vector * v, void *params, gsl_vector *fvv)
//{
// return GSL_SUCCESS;
//}
void callback(const size_t iter, void *, const gsl_multifit_nlinear_workspace *w)
{
double rcond;
gsl_vector *x = gsl_multifit_nlinear_position(w);
gsl_multifit_nlinear_rcond(&rcond, w);
QString r = "Iter: " + QString::number(iter)
+ " Am: " + QString::number(gsl_vector_get(x, PARAM_AM))
+ " X0: " + QString::number(gsl_vector_get(x, PARAM_X0))
+ " Y0: " + QString::number(gsl_vector_get(x, PARAM_Y0))
+ " SX: " + QString::number(gsl_vector_get(x, PARAM_SX))
+ " SY: " + QString::number(gsl_vector_get(x, PARAM_SY))
+ " J(X) :" + QString::number(1.0/rcond)
+ " av: " + QString::number(gsl_multifit_nlinear_avratio(w));
std::cout << r.toStdString() << std::endl;
}
void callback_an(const size_t iter, void *, const gsl_multifit_nlinear_workspace *w)
{
double rcond;
gsl_vector *x = gsl_multifit_nlinear_position(w);
gsl_multifit_nlinear_rcond(&rcond, w);
qDebug() << "Iter:" << iter << "Am:" << gsl_vector_get(x, PARAM_AM)
<< "X0:" << gsl_vector_get(x, PARAM_X0)
<< "Y0:" << gsl_vector_get(x, PARAM_Y0)
<< "SX:" << gsl_vector_get(x, PARAM_SX)
<< "SY:" << gsl_vector_get(x, PARAM_SY)
<< "TH:" << gsl_vector_get(x, PARAM_TH)
<< "J(X):" << 1.0/rcond
<< "av:" << gsl_multifit_nlinear_avratio(w);
}
Star::Star()
{
m_am = m_x = m_y = m_sx = m_sy = NAN;
}
bool Star::valid() const
{
return !isnan(m_am);
}
//half width at half maximum = sqrt(2*ln(2))
double Star::hwhmX() const
{
return 1.177410023*m_sx;
}
double Star::hwhmY() const
{
return 1.177410023*m_sy;
}
// half width at 1/20 maximum
double Star::hw20X() const
{
return 2.447746831*m_sx;
}
double Star::hw20Y() const
{
return 2.447746831*m_sy;
}
// full width at half maximum
double Star::fwhmX() const
{
return 2.354820045*m_sx;
}
double Star::fwhmY() const
{
return 2.354820045*m_sy;
}
bool Star::operator<(const Star &d) const
{
return m_am < d.m_am;
}
StarFit::StarFit(int size)
{
m_size = size;
m_fdf_params = gsl_multifit_nlinear_default_parameters();
m_fdf_params.trs = gsl_multifit_nlinear_trs_lmaccel;
m_fdf.f = func_f;
m_fdf.df = func_df;
m_fdf.fvv = nullptr;
m_fdf.n = size*size;
m_fdf.p = 5;//number of model parameters amplitude, x, y, fwhm_x, fwhm_y
m_fdf_an.f = func_f_an;
m_fdf_an.df = nullptr;
m_fdf_an.fvv = nullptr;
m_fdf_an.n = size*size;
m_fdf_an.p = 6;//number of model parameters amplitude, x, y, sigma_x, sigma_y, angle
gsl_set_error_handler_off();
}
StarFit::~StarFit()
{
}
Star StarFit::fitStar(const std::vector<double> &data, bool angle)
{
gsl_multifit_nlinear_fdf *fdf = angle ? &m_fdf_an : &m_fdf;
Star star;
StarData d;
d.val = data;
d.size = m_size;
d.val = data;
fdf->params = &d;
int info;
double min = *std::min_element(data.begin(), data.end());
double max = *std::max_element(data.begin(), data.end()) - min;
for(double &v : d.val)
{
v -= min;
}
gsl_vector *start = gsl_vector_alloc(fdf->p);
gsl_vector_set(start, PARAM_AM, max);
gsl_vector_set(start, PARAM_X0, m_size/2);
gsl_vector_set(start, PARAM_Y0, m_size/2);
gsl_vector_set(start, PARAM_SX, 1.0);
gsl_vector_set(start, PARAM_SY, 1.0);
if(angle)
gsl_vector_set(start, PARAM_TH, 0.0);
gsl_multifit_nlinear_workspace *workspace = gsl_multifit_nlinear_alloc(gsl_multifit_nlinear_trust, &m_fdf_params, fdf->n, fdf->p);
int ret = gsl_multifit_nlinear_init(start, fdf, workspace);
if(ret)return star;
ret = gsl_multifit_nlinear_driver(MAX_ITER, TOL, TOL, TOL, nullptr, nullptr, &info, workspace);
if(ret==0)
{
gsl_vector *y = gsl_multifit_nlinear_position(workspace);
star.m_am = gsl_vector_get(y, PARAM_AM);
star.m_x = gsl_vector_get(y, PARAM_X0);
star.m_y = gsl_vector_get(y, PARAM_Y0);
star.m_sx = gsl_vector_get(y, PARAM_SX);
star.m_sy = gsl_vector_get(y, PARAM_SY);
if(angle)
star.m_theta = gsl_vector_get(y, PARAM_TH);
//qDebug() << "finished" << star.m_am << star.m_sx << star.m_sy;
}
gsl_vector_free(start);
gsl_multifit_nlinear_free(workspace);
return star;
}
-39
View File
@@ -1,39 +0,0 @@
#ifndef STARFIT_H
#define STARFIT_H
#include "rawimage.h"
#include <gsl/gsl_multifit_nlinear.h>
double gauss_model(double a, double x0, double y0, double sx, double sy, double x, double y);
struct Star
{
double m_am;
double m_x,m_y;
double m_sx,m_sy;
double m_theta;
Star();
bool valid() const;
double hwhmX() const;
double hwhmY() const;
double hw20X() const;
double hw20Y() const;
double fwhmX() const;
double fwhmY() const;
bool operator<(const Star &d) const;
};
class StarFit
{
int m_size;
gsl_multifit_nlinear_fdf m_fdf;
gsl_multifit_nlinear_fdf m_fdf_an;
gsl_multifit_nlinear_parameters m_fdf_params;
gsl_vector *m_vector;
public:
StarFit(int size);
~StarFit();
Star fitStar(const std::vector<double> &data, bool angle);
};
#endif // STARFIT_H
+1
View File
@@ -3,6 +3,7 @@
#include <QDebug> #include <QDebug>
#include <QToolButton> #include <QToolButton>
#include <QSettings> #include <QSettings>
#include <QStyle>
#include "imageringlist.h" #include "imageringlist.h"
const float BLACK_POINT_SIGMA = -2.8f; const float BLACK_POINT_SIGMA = -2.8f;
+1 -7
View File
@@ -4,16 +4,10 @@
#include <QToolBar> #include <QToolBar>
#include <QStackedWidget> #include <QStackedWidget>
#include "stfslider.h" #include "stfslider.h"
#include "mtfparam.h"
class Image; class Image;
struct MTFParam
{
float blackPoint[3] = {0.0f, 0.0f, 0.0f};
float midPoint[3] = {0.5f, 0.5f, 0.5f};
float whitePoint[3] = {1.0f, 1.0f, 1.0f};
};
class StretchToolbar : public QToolBar class StretchToolbar : public QToolBar
{ {
Q_OBJECT Q_OBJECT
+26
View File
@@ -0,0 +1,26 @@
option(BUILD_THUMBNAILER "Build generator of thumbnails" OFF)
if(BUILD_THUMBNAILER)
if(WIN32)
add_library(tenmonthumbnailer SHARED
winmain.cpp
../rawimage.cpp
../rawimage_sse.cpp)
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
target_link_libraries(tenmonthumbnailer PRIVATE ${LCMS2_LIB} XISF)
else(WIN32)
qt_add_executable(tenmonthumbnailer
main.cpp
../rawimage.cpp
../rawimage_sse.cpp
../loadimage.cpp
../imageinfodata.cpp)
target_link_libraries(tenmonthumbnailer PRIVATE Qt6::Core Qt6::Gui ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF)
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
endif(WIN32)
endif(BUILD_THUMBNAILER)
+48
View File
@@ -0,0 +1,48 @@
#include <QCoreApplication>
#include <QCommandLineParser>
#include "../rawimage.h"
#include "../loadimage.h"
bool OpenGLES = false;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QCommandLineParser parser;
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;
QString input = args[0];
QString output = args[1];
ImageInfoData info;
std::shared_ptr<RawImage> rawImage;
if(!loadImage(input, info, rawImage))
return 1;
if(!rawImage)
return 2;
rawImage->convertToType(RawImage::UINT8);
QImage img((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), QImage::Format_RGBA8888);
bool ok = false;
int size = parser.value("s").toInt(&ok);
if(!ok)size = 128;
img = img.scaled(size, size, Qt::KeepAspectRatio);
img.save(output, "png");
//rawImage->convertTosRGB();
return 0;
}
+1
View File
@@ -0,0 +1 @@
bool OpenGLES = false;
Binary file not shown.
File diff suppressed because it is too large Load Diff