Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1e98d818b | |||
| f3f194bcef | |||
| efd36f96c3 | |||
| 37923b37b3 | |||
| 900453577e | |||
| 34d466c3e0 | |||
| 9e98127084 | |||
| ba6062b925 | |||
| 6411b7cd15 | |||
| 223f7cd0ea | |||
| f8f9ee08b3 | |||
| af5aed7ef8 | |||
| 8f5249b142 | |||
| a7dc942c62 | |||
| 1a1399434b | |||
| be567841bf | |||
| 62d2671112 | |||
| 1f8923512e | |||
| 455c3b2d64 | |||
| 4fe546f0e5 | |||
| 95c6fc5343 | |||
| 2bc54ea0cc | |||
| be6e472081 | |||
| 9746f8f653 | |||
| b51a305c63 | |||
| 39775b5e98 | |||
| 93b56e2966 | |||
| 2e41464ff4 | |||
| b6ae7d4cdb | |||
| 1bd48e8fb4 | |||
| 1d65eda490 | |||
| 97346df596 | |||
| a157b274a2 | |||
| 6466702819 | |||
| b4ea65b42a | |||
| 1682de4e1b | |||
| 00872b31df | |||
| 2884787916 | |||
| 86ea9fc137 | |||
| 67199a033d | |||
| 0f182900c2 | |||
| 19ed5ae1a4 | |||
| 46215c7a7d | |||
| c346487504 | |||
| 5b6fead6f1 | |||
| 3e94aa0eda | |||
| 08e70cdb52 | |||
| 04e587b51c | |||
| 42dd55244a | |||
| 6b9ea5e4b9 | |||
| 701a425cc7 | |||
| 9cd2ae14b3 | |||
| f7e4e1874f | |||
| e6749fc487 | |||
| dc6aa6baa8 | |||
| c8a70d22f8 | |||
| dbb533176c | |||
| 032f5b0577 | |||
| eb417010c3 | |||
| 5b44d2ac69 | |||
| d1df789691 | |||
| ab7d04b625 |
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+35
-13
@@ -19,6 +19,7 @@ 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)
|
||||||
|
find_library(WCS_LIB wcs wcslib PATHS REQUIRED)
|
||||||
|
|
||||||
set(TENMON_SRC
|
set(TENMON_SRC
|
||||||
about.cpp
|
about.cpp
|
||||||
@@ -34,53 +35,74 @@ set(TENMON_SRC
|
|||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
markedfiles.cpp
|
markedfiles.cpp
|
||||||
rawimage.cpp
|
rawimage.cpp
|
||||||
|
settingsdialog.cpp
|
||||||
starfit.cpp
|
starfit.cpp
|
||||||
|
statusbar.cpp
|
||||||
stfslider.cpp
|
stfslider.cpp
|
||||||
stretchtoolbar.cpp
|
stretchtoolbar.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
|
||||||
|
if(${Qt5Core_VERSION_STRING} VERSION_LESS "5.14")
|
||||||
|
set(COLOR_MANAGMENT OFF)
|
||||||
|
endif(${Qt5Core_VERSION_STRING} VERSION_LESS "5.14")
|
||||||
|
|
||||||
|
if(COLOR_MANAGMENT)
|
||||||
|
add_compile_definitions("COLOR_MANAGMENT")
|
||||||
|
endif(COLOR_MANAGMENT)
|
||||||
|
|
||||||
qt5_add_resources(TENMON_SRC resources.qrc)
|
qt5_add_resources(TENMON_SRC resources.qrc)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND TENMON_SRC icon.rc)
|
list(APPEND TENMON_SRC icon.rc)
|
||||||
add_compile_definitions("__PCL_WINDOWS")
|
add_compile_definitions("__PCL_WINDOWS")
|
||||||
|
set(tenmon_ICON "")
|
||||||
|
elseif(APPLE)
|
||||||
|
add_compile_definitions("__PCL_MACOS")
|
||||||
|
set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/tenmon.icns)
|
||||||
|
set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||||
else()
|
else()
|
||||||
add_compile_definitions("__PCL_LINUX")
|
add_compile_definitions("__PCL_LINUX")
|
||||||
|
set(tenmon_ICON "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(tenmon WIN32 ${TENMON_SRC})
|
add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
|
||||||
|
|
||||||
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
||||||
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include ${CMAKE_BINARY_DIR})
|
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_directories(tenmon PRIVATE 3rdparty/lib/Windows)
|
target_link_directories(tenmon PRIVATE 3rdparty/lib/Windows)
|
||||||
|
elseif(APPLE)
|
||||||
|
target_link_directories(tenmon PRIVATE 3rdparty/lib/MacOS)
|
||||||
else()
|
else()
|
||||||
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
|
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB})
|
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB})
|
||||||
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib)
|
if(APPLE)
|
||||||
|
target_link_libraries(tenmon PCL-pxi lcms-pxi lz4-pxi RFC6234-pxi zlib-pxi "-framework CoreFoundation")
|
||||||
|
else()
|
||||||
|
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib)
|
||||||
|
endif(APPLE)
|
||||||
|
|
||||||
if(LIBRAW_STATIC)
|
if(LIBRAW_STATIC)
|
||||||
add_compile_definitions("LIBRAW_NODLL")
|
add_compile_definitions("LIBRAW_NODLL")
|
||||||
target_link_libraries(tenmon jasper)
|
target_link_libraries(tenmon jasper)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS tenmon)
|
install(TARGETS tenmon BUNDLE DESTINATION .)
|
||||||
if(UNIX)
|
if(UNIX AND NOT APPLE)
|
||||||
|
include(GNUInstallDirs)
|
||||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
||||||
if(XDG-DESKTOP-MENU_EXECUTABLE)
|
if(XDG-DESKTOP-MENU_EXECUTABLE)
|
||||||
install(SCRIPT install.cmake)
|
install(SCRIPT install.cmake)
|
||||||
else()
|
else()
|
||||||
if(DEFINED ENV{FLATPAK_DEST})
|
install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
||||||
install(FILES org.nou.tenmon.desktop DESTINATION "$ENV{FLATPAK_DEST}/share/applications")
|
install(FILES space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps")
|
||||||
install(FILES org.nou.tenmon.png DESTINATION "$ENV{FLATPAK_DEST}/share/icons/hicolor/32x32/apps")
|
install(FILES space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
|
||||||
else()
|
|
||||||
install(FILES org.nou.tenmon.desktop DESTINATION "/usr/share/applications")
|
|
||||||
install(FILES org.nou.tenmon.png DESTINATION "/usr/share/icons/hicolor/32x32/apps")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif(UNIX)
|
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||||
|
endif(UNIX AND NOT APPLE)
|
||||||
|
|
||||||
option(RELEASE_BUILD "Release build" OFF)
|
option(RELEASE_BUILD "Release build" OFF)
|
||||||
if(RELEASE_BUILD)
|
if(RELEASE_BUILD)
|
||||||
|
|||||||
@@ -2,10 +2,23 @@ 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 qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev cmake
|
sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev libopencv-dev cmake
|
||||||
|
|
||||||
|
on OpenSUSE
|
||||||
|
|
||||||
|
sudo zypper install opencv-devel gsl-devel exif-devel libraw-devel wcslib-devel libqt5-qtbase-devel
|
||||||
|
|
||||||
|
MacOS X
|
||||||
|
|
||||||
|
To compile on MacOS install XCode first. Then install homebrew in x86_64 mode
|
||||||
|
with "arch -i x86_64". Building on native ARM is not supported.
|
||||||
|
|
||||||
|
homebrew install qt5 libraw cfitsio libexif libgsl wcslib opencv
|
||||||
|
|
||||||
|
You may need to set CMAKE_PREFIX_PATH for Qt5 and OpenCV so CMake can find them.
|
||||||
|
|
||||||
Then to build run standard cmake
|
Then to build run standard cmake
|
||||||
|
|
||||||
cmake -B build -S .
|
cmake -B build -S .
|
||||||
make
|
cmake --build build
|
||||||
./tenmon
|
./build/tenmon
|
||||||
|
|||||||
+23
-1
@@ -14,6 +14,7 @@ img { margin: 5px; }
|
|||||||
<li>FITS 8, 16 bit integer and 32 bit float</li>
|
<li>FITS 8, 16 bit integer and 32 bit float</li>
|
||||||
<li>XISF 8, 16 bit integer and 32 bit float</li>
|
<li>XISF 8, 16 bit integer and 32 bit float</li>
|
||||||
<li>JPEG and PNG images</li>
|
<li>JPEG and PNG images</li>
|
||||||
|
<li>CR2, NEF, DNG raw images</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ The <i>File system</i> panel shows other images in the same directory as the loa
|
|||||||
<i>Stretch panel</i> containing various options for auto-stretching linear image data.</p>
|
<i>Stretch panel</i> containing various options for auto-stretching linear image data.</p>
|
||||||
<p>All panels in the interface can be moved around and/or closed. Any closed or non-visible panel can be
|
<p>All panels in the interface can be moved around and/or closed. Any closed or non-visible panel can be
|
||||||
re-opened through the <i>Docks</i> menu at the top.</p>
|
re-opened through the <i>Docks</i> menu at the top.</p>
|
||||||
|
<p>At bottom there is status bar that show current lightness or red, green, blue pixel value under mouse cursor then X and Y coordinates and
|
||||||
|
if image contain World Coordinate System metadata it show celestial coordinates.</p>
|
||||||
|
|
||||||
<h3>Opening and saving images</h3>
|
<h3>Opening and saving images</h3>
|
||||||
<p>To load an image select <i>File->Open</i> and choose the file. After a file is loaded, it becomes visible in the main image panel, and the
|
<p>To load an image select <i>File->Open</i> and choose the file. After a file is loaded, it becomes visible in the main image panel, and the
|
||||||
@@ -66,9 +69,20 @@ This dialog can be useful to clear marks from images. Marked images show a <b>*<
|
|||||||
Marked images can be copied or moved to a selected directory with <i>File->Copy/Move marked files</i>.
|
Marked images can be copied or moved to a selected directory with <i>File->Copy/Move marked files</i>.
|
||||||
After copying or moving, the list of marked files is cleared. The list of marked files will be remembered after quitting the program.</p>
|
After copying or moving, the list of marked files is cleared. The list of marked files will be remembered after quitting the program.</p>
|
||||||
<p>Another way to mark images is in database view where you can select rows and then select mark or unmark action in context menu. Marked
|
<p>Another way to mark images is in database view where you can select rows and then select mark or unmark action in context menu. Marked
|
||||||
files will be shown with bold text. Third way to mark files is from thumnails view where you can press press <i>Shift</i> and click with left
|
files will be shown with bold text. Third way to mark files is from thumbnails view where you can press press <i>Shift</i> and click with left
|
||||||
mouse button and drag across thumbnails to mark them. Holding <i>Ctrl</i> will unmark files.</p>
|
mouse button and drag across thumbnails to mark them. Holding <i>Ctrl</i> will unmark files.</p>
|
||||||
|
|
||||||
|
<h3>File system and tree</h3>
|
||||||
|
<p>File system panel contain list of images in current opened directory. You can select file from this list and it will be displayed. It is also possible to
|
||||||
|
use arrow keys to go back (left and up) and forth (right and down) between images.</p>
|
||||||
|
<p>File tree show file system structure. You can right click to show context menu to perform various actions from <i>File</i> menu. There are also few others
|
||||||
|
<ul>
|
||||||
|
<li><i>Set as root directory</i> show only this directory and subdirectories</li>
|
||||||
|
<li><i>Reset root directory</i> show whole file system</li>
|
||||||
|
<li><i>Go up</i> show directory that is one level above current root directory</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Database of FITS/XISF files</h3>
|
<h3>Database of FITS/XISF files</h3>
|
||||||
<p>Tenmon can scan a directory of FITS/XISF files and index metadata from FITS headers into it's internal database. This allows searching and sorting images based on that metadata.</p>
|
<p>Tenmon can scan a directory of FITS/XISF files and index metadata from FITS headers into it's internal database. This allows searching and sorting images based on that metadata.</p>
|
||||||
<p>To populate the database, select a directory of FITS/XISF files with <i>File->Index directory</i>.
|
<p>To populate the database, select a directory of FITS/XISF files with <i>File->Index directory</i>.
|
||||||
@@ -81,6 +95,14 @@ Below the table is button to select which columns/properties are displayed.</p>
|
|||||||
|
|
||||||
<p>Also at the bottom of the database panel are three combo boxes and text inputs used for filtering.
|
<p>Also at the bottom of the database panel are three combo boxes and text inputs used for filtering.
|
||||||
Select the property to filter on with the combo box and in the adjacent text box enter a string to search for in that property.
|
Select the property to filter on with the combo box and in the adjacent text box enter a string to search for in that property.
|
||||||
|
These three combo box contain list of all properties that are found during indexing except first five. First one set searching in file name.
|
||||||
|
Next two "RA pos" and "DEC pos" allow to filter out indexed images that contain point with entered RA/DEC coordinate. Expected format is three
|
||||||
|
number separated by space. In case of "DEC pos" it also accept +- sign. Omitting one or two last number is also valid. Some examples "02 12 32" "-12 43 12" "+45 32" "13".
|
||||||
|
So for RA it means hour, minutes and seconds while for DEC it is degrees, minutes and seconds.
|
||||||
|
Setting both "RA pos" and "DEC pos" can return images that doesn't contain entered point as it search against minimum and maximum RA/DEC coordinates that images contain.
|
||||||
|
"RA range" and "DEC range" filter out images which center coordinate is within entered range.
|
||||||
|
Pressing Enter or clicking on <i>Filter</i> button will filter out database record according to search parameter.
|
||||||
|
|
||||||
<p>Wildcards:
|
<p>Wildcards:
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>%</b> (percent) is a wildcard representing zero or more of any characters.</li>
|
<li><b>%</b> (percent) is a wildcard representing zero or more of any characters.</li>
|
||||||
|
|||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
h1, h2, h3, h4 { padding:0px; margin:10px; }
|
||||||
|
p { padding:0px; margin:5px; }
|
||||||
|
img { margin: 5px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Aide de Tenmon</h2>
|
||||||
|
|
||||||
|
<p>Tenmon est destiné principalement à la visualisation de photos et d'images astronomique. Il prend en charge les formats suivants :
|
||||||
|
<ul>
|
||||||
|
<li>FITS 8, 16 bit entier et 32 bit point flottant</li>
|
||||||
|
<li>XISF 8, 16 bit entier et 32 bit point flottant</li>
|
||||||
|
<li>images JPEG et PNG</li>
|
||||||
|
<li>images RAW CR2, NEF, DNG</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Fenêtre principale</h3>
|
||||||
|
<p>La fenêtre principale affiche l'image actuellement chargée. Sur la gauche se trouve le panneau <i>Informations de l'image</i> qui affiche des détails sur l'image chargée.
|
||||||
|
Le panneau <i>Système de fichier</i> affiche d'autres images dans le même répertoire que l'image chargée.
|
||||||
|
En haut se trouve le menu principal et en dessous se trouve le panneau <i>Réglage de la luminosité</i> contenant diverses options pour l'étirement automatique des images linéaires.</p>
|
||||||
|
<p>Tous les panneaux de l'interface peuvent être déplacés et/ou fermés. Tout panneau fermé ou non visible peut être rouvert via le menu <i>Fenêtres encrables</i> en haut.</p>
|
||||||
|
<p>En bas, il y a une barre d'état qui affiche l'intensité ou la valeur du pixel rouge, vert, bleu sous le curseur de la souris, puis les coordonnées X et Y et,
|
||||||
|
si l'image contient des métadonnées du World Coordinate System, elle affiche les coordonnées célestes.</p>
|
||||||
|
|
||||||
|
<h3>Ouvrir et enregistrer des images</h3>
|
||||||
|
<p>Pour charger une image, sélectionnez <i>Fichier->Ouvrir</i> et choisissez le fichier. Une fois qu'un fichier est chargé,
|
||||||
|
il devient visible dans le panneau d'image et le panneau du <i>système de fichiers</i> affiche les autres images dans le même répertoire.</p>
|
||||||
|
<p>L'image chargée peut être exportée dans un format différent avec <i>Fichier->Enregistrer sous</i>. Tous les formats JPEG, PNG FITS et XISF
|
||||||
|
peuvent être sélectionnés. Dans le cas d'un enregistrement JPEG ou PNG, la fonction d'étirement de la luminosité est appliquée à l'image enregistrée.
|
||||||
|
FITS et XISF sont enregistrés/convertis sans appliquer l'étirement.
|
||||||
|
Pour ouvrir une image, vous pouvez également la faire glisser et la déposer dans la fenêtre principale.</p>
|
||||||
|
|
||||||
|
<h3>Voir</h3>
|
||||||
|
<p>Le menu <i>Voir</i> propose des options pour contrôler la taille et l'échelle des images affichées :
|
||||||
|
<ul>
|
||||||
|
<li><i>Zoom avant</i> et <i>Zoom arrière</i> agrandissent et rétrécissent l'image. La molette de la souris peut également être utilisée pour zoomer librement.</li>
|
||||||
|
<li><i>Meilleur ajustement</i>, zoom automatiquement l'image pour l'adapter à la taille actuelle de la fenêtre.</li>
|
||||||
|
<li><i>100 %</i>, zoom à l'échelle 1:1.</li>
|
||||||
|
<li><i>Plein écran</i> agrandit la fenêtre principale sur tout l'écran.</li>
|
||||||
|
<li><i>Vignettes</i>, affiche de petites vignettes pour toutes les images du répertoire actuel.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<h3>Barre d'outils du réglage de la luminosité</h3>
|
||||||
|
<p>Ces outils modifient la luminosité des images affichées.
|
||||||
|
<br><img src=":/about/stretch-panel.png"></p>
|
||||||
|
<p>À partir de la gauche, il y a une échelle de luminosité avec trois points réglables pour contrôler manuellement l'étirement.
|
||||||
|
<ul>
|
||||||
|
<li>point noir - tous les pixels avec une valeur inférieure (plus sombre) que ce paramètre seront écrêtés en noir</li>
|
||||||
|
<li>point médian - définit la valeur à étirer à 50 % d'intensité</li>
|
||||||
|
<li>point blanc - tous les pixels avec une valeur supérieure (plus lumineuse) que celle-ci seront écrêtés en blanc</li>
|
||||||
|
</ul>
|
||||||
|
Après le curseur se trouvent 5 boutons pour la luminosité automatique :
|
||||||
|
<ul>
|
||||||
|
<li><i>Luminosité automatique</i>, applique automatiquement les points noirs et moyens pour rendre l'image avec une luminosité optimale.</li>
|
||||||
|
<li><i>Réinitialiser</i>, réinitialise les trois valeurs pour le point noir, moyen et blanc par défaut.</li>
|
||||||
|
<li><i>Inverser</i>, inverse les couleurs pour afficher l'image en négatif.</li>
|
||||||
|
<li><i>Super pixel CFA </i>, moyenne 2x2 pixels en un (adapté aux images d'une caméra couleur).</li>
|
||||||
|
<li><i>Appliquer la luminosité automatique au chargement</i>, applique la luminosité automatique pour chaque image lors du chargement.</p>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Marquer les images</h3>
|
||||||
|
<p>Les images peuvent être marquées dans le menu <i>Sélectionner</i>. Pour afficher une liste des seules images marquées, utilisez <i>Sélectionner->Afficher marqué</i>. Cette boîte de dialogue peut être utile pour effacer les marques des images. Les images marquées affichent un caractère <b>*</b> dans la barre de titre de la fenêtre principale. Les images marquées peuvent être copiées ou déplacées vers un répertoire sélectionné avec <i>Fichier->Copier/Déplacer les fichiers marqués</i>. Après la copie ou le déplacement, la liste des fichiers marqués est effacée. La liste des fichiers marqués sera mémorisée après avoir quitté le programme.</p>
|
||||||
|
<p>Une autre façon de marquer des images est dans la vue de la base de données où vous pouvez sélectionner des lignes, puis sélectionner l'action marquer ou décocher dans le menu contextuel. Les fichiers marqués seront affichés en texte gras. La troisième façon de marquer les fichiers est de voir les vignettes où vous pouvez appuyer sur <i>Maj</i> et cliquer avec le bouton gauche de la souris et faire glisser sur les vignettes pour les marquer. Maintenir <i>Ctrl</i> décochera les fichiers.</p>
|
||||||
|
|
||||||
|
<h3>Système de fichier et arborescence</h3>
|
||||||
|
<p>Le panneau du système de fichiers contient la liste des images du répertoire ouvert. Vous pouvez sélectionner un fichier dans cette liste et il sera affiché. Il est également possible
|
||||||
|
d'utiliser les touches fléchées pour revenir en arrière (gauche et haut) et avancer (droite et bas) entre les images.</p>
|
||||||
|
<p>L'arborescence des fichiers montre la structure du système de fichiers. Vous pouvez cliquer avec le bouton droit de la souris pour afficher un menu contextuel permettant d'effectuer diverses actions du menu <i>Fichier</i>. Il y a aussi les actions suivantes
|
||||||
|
<ul>
|
||||||
|
<li><i>Définir comme répertoire racine</i> afficher uniquement ce répertoire et ses sous-répertoires</li>
|
||||||
|
<li><i>Réinitialiser la racine</i> afficher tout le système de fichiers</li>
|
||||||
|
<li><i>Monter</i> afficher le répertoire qui est un niveau au-dessus du répertoire racine actuel</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Base de donnée de fichiers FITS/XISF</h3>
|
||||||
|
<p>Tenmon peut analyser un répertoire de fichiers FITS/XISF et indexer les métadonnées des en-têtes FITS dans sa base de données interne. Cela permet de rechercher et de trier des images en fonction de ces métadonnées.</p>
|
||||||
|
<p>Pour remplir la base de données, sélectionnez un répertoire de fichiers FITS/XISF avec <i>Fichier->Indexer le répertoire</i>. Une fois le répertoire parcouru, les métadonnées analysées à partir des images seront stockées dans la base de données. Pour actualiser la base de données, exécutez <i>Fichier-> Ré-indexer les fichiers</i>. Cela mettra à jour toutes les métadonnées modifiées et supprimera tout enregistrement de fichiers supprimés. Pour indexer de nouveaux fichiers, exécutez simplement à nouveau <i>Fichier->Indexer le répertoire</i>.</p>
|
||||||
|
<p>La base de données est visualisée via un panneau qui n'est pas visible dans la mise en page par défaut. Pour ajouter le panneau de base de données à la vue, basculez <i>Fenêtres encrables->Base de données FITS/XISF</i>. Une fois visible, le panneau de la base de données affiche la base de données sous forme de tableau avec une colonne pour chaque propriété. Sous le tableau se trouve un bouton pour sélectionner les colonnes/propriétés à afficher.</p>
|
||||||
|
|
||||||
|
<p>Au bas du panneau de la base de données se trouvent également trois boîtes de liste déroulante et des entrées de texte utilisées pour le filtrage. Sélectionnez la propriété à filtrer dans une liste déroulante et dans la zone de texte adjacente, entrez un texte à rechercher pour cette propriété.
|
||||||
|
Ces trois boîtes contiennent la liste de toutes les propriétés qui sont trouvées pendant l'indexation, sauf les trois premières. La première définit la recherche dans le nom du fichier.
|
||||||
|
Les deux suivantes "RA pos" et "DEC pos" permettent de filtrer les images indexées qui contiennent un point avec les coordonnées RA/DEC entrée. Le format attendu est trois nombres séparés par un espace.
|
||||||
|
Dans le cas de "DEC pos", il accepte également le signe +-. L'omission d'un ou deux derniers chiffres est également valable. Quelques exemples "02 12 32" "-12 43 12" "+45 32" "13".
|
||||||
|
Le fait de définir à la fois "RA pos" et "DEC pos" peut renvoyer des images qui ne contiennent pas le point saisi, car la recherche est faite sur les coordonnées RA/DEC minimum et maximum que les images contiennent.
|
||||||
|
"RA range" et "DEC range" filtrent les images dont les coordonnées centrale se trouvent dans la plage saisie.
|
||||||
|
En appuyant sur la touche Enter ou en cliquant sur le bouton <i>Filtre</i>, les enregistrements de la base de données seront filtrés en fonction des paramètres de recherche.
|
||||||
|
|
||||||
|
<p>Caractères génériques :
|
||||||
|
<ul>
|
||||||
|
<li><b>%</b> (pourcentage) est un caractère générique représentant zéro ou plusieurs caractères.</li>
|
||||||
|
<li><b>_</b> (trait de soulignement) est un caractère générique pour exactement un caractère quelconque.</li>
|
||||||
|
<li>En l'absence de caractères génériques, le texte exacte doit correspondre.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<br><img src=":/about/filter.png"><br>
|
||||||
|
Cet exemple filtre les fichiers où : "Bias" figure dans le nom de fichier, la propriété OBJECT est "M_42" (où le trait de soulignement peut être n'importe quel caractère) et la propriété DATE commence par "2022".
|
||||||
|
</p>
|
||||||
|
<p><small>PS: Le Kanji de icône (tenmon) signifie astronomie en japonais</small></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -13,6 +13,7 @@ p { padding:0px; margin:5px 5px 10px 5px; }
|
|||||||
<li>FITS 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
|
<li>FITS 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
|
||||||
<li>XISF 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
|
<li>XISF 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
|
||||||
<li>JPEG a PNG obrázky</li>
|
<li>JPEG a PNG obrázky</li>
|
||||||
|
<li>CR2, NEF, DNG raw obrázky</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<table><tr>
|
<table><tr>
|
||||||
<td style="padding-right:10px"><img src=":/org.nou.tenmon.png"></td>
|
<td style="padding-right:10px"><img src=":/space.nouspiro.tenmon.png"></td>
|
||||||
<td><h3>Tenmon</h3>
|
<td><h3>Tenmon</h3>
|
||||||
Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br>
|
Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br>
|
||||||
v@GITVERSION@ Copyright © 2022 Dušan Poizl<br><br>
|
v@GITVERSION@ Copyright © 2022 Dušan Poizl<br><br>
|
||||||
|
|||||||
+73
-16
@@ -22,16 +22,32 @@ bool Database::init()
|
|||||||
|
|
||||||
if(m_database.isValid())
|
if(m_database.isValid())
|
||||||
{
|
{
|
||||||
m_database.setDatabaseName(dir.absoluteFilePath("database.db"));
|
m_database.setDatabaseName(dir.absoluteFilePath("database2.db"));
|
||||||
if(m_database.open())
|
if(m_database.open())
|
||||||
{
|
{
|
||||||
m_database.exec("PRAGMA foreign_keys = ON");
|
m_database.exec("PRAGMA foreign_keys = ON");
|
||||||
m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
|
int version = checkVersion();
|
||||||
m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME)");
|
if(version == 0)
|
||||||
m_database.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)");
|
m_database.exec("PRAGMA user_version = 1");
|
||||||
m_database.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)");
|
m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
|
||||||
m_database.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)");
|
m_database.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)");
|
||||||
|
m_database.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)");
|
||||||
|
m_database.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)");
|
||||||
|
m_database.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)");
|
||||||
|
m_database.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)");
|
||||||
|
}
|
||||||
|
else if(version > 1)
|
||||||
|
{
|
||||||
|
qDebug() << "Database version is too new";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QSqlError error = m_database.lastError();
|
QSqlError error = m_database.lastError();
|
||||||
|
|
||||||
if(error.type() == QSqlError::NoError)
|
if(error.type() == QSqlError::NoError)
|
||||||
@@ -45,6 +61,8 @@ bool Database::init()
|
|||||||
|
|
||||||
m_insertFile = QSqlQuery(m_database);
|
m_insertFile = QSqlQuery(m_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.prepare("INSERT INTO fits_files (file, mtime, minRa, maxRa, minDec, maxDec, crVal1, crVal2) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
m_insertFitsHeader = QSqlQuery(m_database);
|
m_insertFitsHeader = QSqlQuery(m_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(m_database);
|
||||||
@@ -55,7 +73,6 @@ bool Database::init()
|
|||||||
m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?");
|
m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << error.text();
|
qDebug() << error.text();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,6 +146,15 @@ bool Database::checkError(QSqlQuery &query)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Database::checkVersion()
|
||||||
|
{
|
||||||
|
QSqlDatabase db = QSqlDatabase::database();
|
||||||
|
QSqlQuery query = db.exec("PRAGMA user_version");
|
||||||
|
if(query.next())
|
||||||
|
return query.value(0).toInt();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static QStringList nameFilters = {"*.fit", "*.fits", "*.xisf"};
|
static QStringList nameFilters = {"*.fit", "*.fits", "*.xisf"};
|
||||||
|
|
||||||
static int countFiles(const QDir &dir, int count = 0)
|
static int countFiles(const QDir &dir, int count = 0)
|
||||||
@@ -148,9 +174,14 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
|||||||
QSqlDatabase database = QSqlDatabase::database();
|
QSqlDatabase database = QSqlDatabase::database();
|
||||||
database.transaction();
|
database.transaction();
|
||||||
if(indexDir2(dir, progress))
|
if(indexDir2(dir, progress))
|
||||||
|
{
|
||||||
database.commit();
|
database.commit();
|
||||||
|
emit databaseChanged();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
database.rollback();
|
database.rollback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::reindex(QProgressDialog *progress)
|
void Database::reindex(QProgressDialog *progress)
|
||||||
@@ -158,8 +189,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");
|
||||||
|
size.next();
|
||||||
|
progress->setMaximum(size.value(0).toInt());
|
||||||
QSqlQuery files = database.exec("SELECT id,file,mtime FROM fits_files");
|
QSqlQuery files = database.exec("SELECT id,file,mtime FROM fits_files");
|
||||||
progress->setMaximum(files.size());
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(files.next())
|
while(files.next())
|
||||||
{
|
{
|
||||||
@@ -240,16 +273,40 @@ bool Database::indexFile(const QFileInfo &file)
|
|||||||
qlonglong last_id = -1;
|
qlonglong last_id = -1;
|
||||||
if(ok)
|
if(ok)
|
||||||
{
|
{
|
||||||
m_insertFile.bindValue(0, filePath);
|
if(info.wcs)
|
||||||
m_insertFile.bindValue(1, mtime);
|
|
||||||
if(!m_insertFile.exec())
|
|
||||||
{
|
{
|
||||||
qDebug() << m_insertFile.lastError();
|
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
|
||||||
return false;
|
info.wcs->calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
|
||||||
|
qDebug() << "bounds" << minRa << maxRa << minDec << maxDec;
|
||||||
|
m_insertFileWcs.bindValue(0, filePath);
|
||||||
|
m_insertFileWcs.bindValue(1, mtime);
|
||||||
|
m_insertFileWcs.bindValue(2, minRa);
|
||||||
|
m_insertFileWcs.bindValue(3, maxRa);
|
||||||
|
m_insertFileWcs.bindValue(4, minDec);
|
||||||
|
m_insertFileWcs.bindValue(5, maxDec);
|
||||||
|
m_insertFileWcs.bindValue(6, crVal1);
|
||||||
|
m_insertFileWcs.bindValue(7, crVal2);
|
||||||
|
if(!m_insertFileWcs.exec())
|
||||||
|
{
|
||||||
|
qDebug() << m_insertFileWcs.lastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
last_id = m_insertFileWcs.lastInsertId().toLongLong();
|
||||||
}
|
}
|
||||||
last_id = m_insertFile.lastInsertId().toLongLong();
|
else
|
||||||
|
{
|
||||||
|
m_insertFile.bindValue(0, filePath);
|
||||||
|
m_insertFile.bindValue(1, mtime);
|
||||||
|
if(!m_insertFile.exec())
|
||||||
|
{
|
||||||
|
qDebug() << m_insertFile.lastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
last_id = m_insertFile.lastInsertId().toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
QVariantList file_id, keys, values, comments;
|
QVariantList file_id, keys, values, comments;
|
||||||
for(auto &record : info.fitsHeader)
|
for(const auto &record : info.fitsHeader)
|
||||||
{
|
{
|
||||||
file_id << last_id;
|
file_id << last_id;
|
||||||
keys << QString(record.key);
|
keys << QString(record.key);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Database : public QObject
|
|||||||
QSqlQuery m_isMarkedQuery;
|
QSqlQuery m_isMarkedQuery;
|
||||||
|
|
||||||
QSqlQuery m_insertFile;
|
QSqlQuery m_insertFile;
|
||||||
|
QSqlQuery m_insertFileWcs;
|
||||||
QSqlQuery m_insertFitsHeader;
|
QSqlQuery m_insertFitsHeader;
|
||||||
QSqlQuery m_checkFile;
|
QSqlQuery m_checkFile;
|
||||||
QSqlQuery m_headerKeywords;
|
QSqlQuery m_headerKeywords;
|
||||||
@@ -39,6 +40,9 @@ protected:
|
|||||||
bool indexDir2(const QDir &dir, QProgressDialog *progress);
|
bool indexDir2(const QDir &dir, QProgressDialog *progress);
|
||||||
bool indexFile(const QFileInfo &file);
|
bool indexFile(const QFileInfo &file);
|
||||||
bool checkError(QSqlQuery &query);
|
bool checkError(QSqlQuery &query);
|
||||||
|
int checkVersion();
|
||||||
|
signals:
|
||||||
|
void databaseChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DATABASE_H
|
#endif // DATABASE_H
|
||||||
|
|||||||
+74
-7
@@ -8,10 +8,34 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
|
#include <QRegExp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
|
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
|
||||||
|
|
||||||
|
double RA(const QString &ra)
|
||||||
|
{
|
||||||
|
QRegularExpression reg("(\\d+)\\s*(\\d+)?\\s*(\\d+)?");
|
||||||
|
QRegularExpressionMatch match = reg.match(ra);
|
||||||
|
double h = match.captured(1).toDouble();
|
||||||
|
double m = match.captured(2).toDouble();
|
||||||
|
double s = match.captured(3).toDouble();
|
||||||
|
qDebug() << match.capturedTexts() << h << m << s;
|
||||||
|
return h*15 + m*0.25 + s*15/3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
double DEC(const QString &dec)
|
||||||
|
{
|
||||||
|
QRegularExpression reg("([\\+\\-])?(\\d+)\\s*(\\d+)?\\s*(\\d+)?");
|
||||||
|
QRegularExpressionMatch match = reg.match(dec);
|
||||||
|
double sign = match.captured(1) == "-" ? -1 : 1;
|
||||||
|
double d = match.captured(2).toDouble();
|
||||||
|
double m = match.captured(3).toDouble();
|
||||||
|
double s = match.captured(4).toDouble();
|
||||||
|
qDebug() << match.capturedTexts() << sign << d << m << s;
|
||||||
|
return sign * (d + m/60 + s/3600);
|
||||||
|
}
|
||||||
|
|
||||||
SelectColumnsDialog::SelectColumnsDialog(QWidget *parent) : QDialog(parent)
|
SelectColumnsDialog::SelectColumnsDialog(QWidget *parent) : QDialog(parent)
|
||||||
{
|
{
|
||||||
m_listWidget = new QListWidget(this);
|
m_listWidget = new QListWidget(this);
|
||||||
@@ -73,17 +97,19 @@ void FITSFileModel::setColumns(const QStringList &columns)
|
|||||||
prepareQuery();
|
prepareQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FITSFileModel::setFilter(const QStringList &key, const QStringList &value)
|
void FITSFileModel::setFilter(const QStringList &key, const QStringList &value, const QStringList &limit)
|
||||||
{
|
{
|
||||||
if(value.isEmpty())
|
if(value.isEmpty())
|
||||||
{
|
{
|
||||||
m_key.clear();
|
m_key.clear();
|
||||||
m_value.clear();
|
m_value.clear();
|
||||||
|
m_limit.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_key = key;
|
m_key = key;
|
||||||
m_value = value;
|
m_value = value;
|
||||||
|
m_limit = limit;
|
||||||
}
|
}
|
||||||
prepareQuery();
|
prepareQuery();
|
||||||
}
|
}
|
||||||
@@ -130,12 +156,20 @@ void FITSFileModel::prepareQuery()
|
|||||||
{
|
{
|
||||||
QString cols;
|
QString cols;
|
||||||
QString join;
|
QString join;
|
||||||
QString where;
|
QStringList where;
|
||||||
QString sql = "SELECT f.file,";
|
QString sql = "SELECT f.file,";
|
||||||
for(int i=0; i<m_value.size(); i++)
|
for(int i=0; i<m_value.size(); i++)
|
||||||
{
|
{
|
||||||
if(m_key[i] == "file")
|
if(m_key[i] == "file")
|
||||||
where = QString(" WHERE f.file LIKE '%1'").arg(m_value[i]);
|
where.append(QString(" f.file LIKE '%1' ").arg(m_value[i]));
|
||||||
|
else if(m_key[i] == "RA pos")
|
||||||
|
where.append(QString(" %1 BETWEEN f.minRa AND f.maxRa ").arg(RA(m_value[i])));
|
||||||
|
else if(m_key[i] == "DEC pos")
|
||||||
|
where.append(QString(" %1 BETWEEN f.minDec AND f.maxDec ").arg(DEC(m_value[i])));
|
||||||
|
else if(m_key[i] == "RA range")
|
||||||
|
where.append(QString(" crVal1 BETWEEN %1 AND %2 ").arg(RA(m_value[i])).arg(RA(m_limit[i])));
|
||||||
|
else if(m_key[i] == "DEC range")
|
||||||
|
where.append(QString(" crVal2 BETWEEN %1 AND %2 ").arg(DEC(m_value[i])).arg(DEC(m_limit[i])));
|
||||||
else
|
else
|
||||||
join += QString(" JOIN fits_headers AS s%1 ON f.id=s%1.id_file AND s%1.key='%2' AND s%1.value LIKE '%3'").arg(i).arg(m_key[i]).arg(m_value[i]);
|
join += QString(" JOIN fits_headers AS s%1 ON f.id=s%1.id_file AND s%1.key='%2' AND s%1.value LIKE '%3'").arg(i).arg(m_key[i]).arg(m_value[i]);
|
||||||
}
|
}
|
||||||
@@ -150,7 +184,7 @@ void FITSFileModel::prepareQuery()
|
|||||||
sql += cols;
|
sql += cols;
|
||||||
sql += " FROM fits_files AS f";
|
sql += " FROM fits_files AS f";
|
||||||
sql += join;
|
sql += join;
|
||||||
sql += where;
|
if(!where.isEmpty())sql += " WHERE " + where.join("AND");
|
||||||
sql += " GROUP BY f.id" + m_sort;
|
sql += " GROUP BY f.id" + m_sort;
|
||||||
setQuery(sql);
|
setQuery(sql);
|
||||||
setHeaderData(0, Qt::Horizontal, tr("File name"));
|
setHeaderData(0, Qt::Horizontal, tr("File name"));
|
||||||
@@ -232,21 +266,52 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
|
|||||||
m_model->filesUnmarked(indexes);
|
m_model->filesUnmarked(indexes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto addFilterItems = [](QComboBox *combobox, const QStringList &fitsKeywords)
|
||||||
|
{
|
||||||
|
combobox->clear();
|
||||||
|
combobox->addItem("file");
|
||||||
|
combobox->addItem("RA pos");
|
||||||
|
combobox->addItem("DEC pos");
|
||||||
|
combobox->addItem("RA range");
|
||||||
|
combobox->addItem("DEC range");
|
||||||
|
combobox->addItems(fitsKeywords);
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringList fitsKeywords = m_database->getFitsKeywords();
|
||||||
for(int i=0; i<3; i++)
|
for(int i=0; i<3; i++)
|
||||||
{
|
{
|
||||||
m_filterKeyword[i] = new QComboBox(this);
|
m_filterKeyword[i] = new QComboBox(this);
|
||||||
m_filterKeyword[i]->addItem("file");
|
addFilterItems(m_filterKeyword[i], fitsKeywords);
|
||||||
m_filterKeyword[i]->addItems(m_database->getFitsKeywords());
|
|
||||||
|
|
||||||
m_search[i] = new QLineEdit(this);
|
m_search[i] = new QLineEdit(this);
|
||||||
m_search[i]->setPlaceholderText(tr("Text to search, you can % as wildcard"));
|
m_search[i]->setPlaceholderText(tr("Text to search, you can % as wildcard"));
|
||||||
|
|
||||||
|
m_limit[i] = new QLineEdit(this);
|
||||||
|
m_limit[i]->hide();
|
||||||
|
connect(m_filterKeyword[i], static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this, i](int index){
|
||||||
|
if(index == 3 || index == 4)m_limit[i]->show();
|
||||||
|
else m_limit[i]->hide();
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_search[i], &QLineEdit::returnPressed, this, &DataBaseView::applyFilter);
|
connect(m_search[i], &QLineEdit::returnPressed, this, &DataBaseView::applyFilter);
|
||||||
|
connect(m_limit[i], &QLineEdit::returnPressed, this, &DataBaseView::applyFilter);
|
||||||
hlayout->addWidget(m_filterKeyword[i]);
|
hlayout->addWidget(m_filterKeyword[i]);
|
||||||
hlayout->addWidget(m_search[i]);
|
hlayout->addWidget(m_search[i]);
|
||||||
|
hlayout->addWidget(m_limit[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton *filterButton = new QPushButton(tr("Filter"), this);
|
QPushButton *filterButton = new QPushButton(tr("Filter"), this);
|
||||||
connect(filterButton, SIGNAL(pressed()), this, SLOT(applyFilter()));
|
connect(filterButton, SIGNAL(pressed()), this, SLOT(applyFilter()));
|
||||||
hlayout->addWidget(filterButton);
|
hlayout->addWidget(filterButton);
|
||||||
|
|
||||||
|
connect(m_database, &Database::databaseChanged, [this, &addFilterItems](){
|
||||||
|
QStringList fitsKeywords = m_database->getFitsKeywords();
|
||||||
|
for(int i=0; i<3; i++)
|
||||||
|
addFilterItems(m_filterKeyword[i], fitsKeywords);
|
||||||
|
|
||||||
|
applyFilter();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBaseView::~DataBaseView()
|
DataBaseView::~DataBaseView()
|
||||||
@@ -284,6 +349,7 @@ void DataBaseView::applyFilter()
|
|||||||
{
|
{
|
||||||
QStringList keys;
|
QStringList keys;
|
||||||
QStringList values;
|
QStringList values;
|
||||||
|
QStringList limits;
|
||||||
for(int i=0; i<3; i++)
|
for(int i=0; i<3; i++)
|
||||||
{
|
{
|
||||||
QString key = m_filterKeyword[i]->currentText();
|
QString key = m_filterKeyword[i]->currentText();
|
||||||
@@ -291,7 +357,8 @@ void DataBaseView::applyFilter()
|
|||||||
{
|
{
|
||||||
keys.append(key);
|
keys.append(key);
|
||||||
values.append(m_search[i]->text());
|
values.append(m_search[i]->text());
|
||||||
|
limits.append(m_limit[i]->text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_model->setFilter(keys, values);
|
m_model->setFilter(keys, values, limits);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -27,13 +27,14 @@ class FITSFileModel : public QSqlQueryModel
|
|||||||
QString m_sort;
|
QString m_sort;
|
||||||
QStringList m_key;
|
QStringList m_key;
|
||||||
QStringList m_value;
|
QStringList m_value;
|
||||||
|
QStringList m_limit;
|
||||||
QSet<QString> m_markedFiles;
|
QSet<QString> m_markedFiles;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
public:
|
public:
|
||||||
explicit FITSFileModel(Database *database, QObject *parent = nullptr);
|
explicit FITSFileModel(Database *database, QObject *parent = nullptr);
|
||||||
void sort(int column, Qt::SortOrder order) override;
|
void sort(int column, Qt::SortOrder order) override;
|
||||||
void setColumns(const QStringList &columns);
|
void setColumns(const QStringList &columns);
|
||||||
void setFilter(const QStringList &key, const QStringList &value);
|
void setFilter(const QStringList &key, const QStringList &value, const QStringList &limit);
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
void filesMarked(const QModelIndexList &indexes);
|
void filesMarked(const QModelIndexList &indexes);
|
||||||
void filesUnmarked(const QModelIndexList &indexes);
|
void filesUnmarked(const QModelIndexList &indexes);
|
||||||
@@ -61,6 +62,7 @@ class DataBaseView : public QWidget
|
|||||||
FITSFileModel *m_model;
|
FITSFileModel *m_model;
|
||||||
QComboBox *m_filterKeyword[3];
|
QComboBox *m_filterKeyword[3];
|
||||||
QLineEdit *m_search[3];
|
QLineEdit *m_search[3];
|
||||||
|
QLineEdit *m_limit[3];
|
||||||
public:
|
public:
|
||||||
explicit DataBaseView(Database *database, QWidget *parent = nullptr);
|
explicit DataBaseView(Database *database, QWidget *parent = nullptr);
|
||||||
~DataBaseView() override;
|
~DataBaseView() override;
|
||||||
|
|||||||
+18
-11
@@ -17,26 +17,20 @@ FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) :
|
|||||||
|
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
connect(m_listView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(fileClicked(QModelIndex)));
|
connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FilesystemWidget::fileClicked);
|
||||||
}
|
|
||||||
|
|
||||||
void FilesystemWidget::setDir(const QString &dir)
|
|
||||||
{
|
|
||||||
//m_model->setRootPath(dir);
|
|
||||||
//m_treeView->setRootIndex(m_model->index(m_model->rootPath()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilesystemWidget::selectFile(int row)
|
void FilesystemWidget::selectFile(int row)
|
||||||
{
|
{
|
||||||
QModelIndex index = m_model->index(row, 0);
|
QModelIndex index = m_model->index(row, 0);
|
||||||
m_listView->selectionModel()->clearSelection();
|
|
||||||
m_listView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
|
m_listView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
|
||||||
m_listView->scrollTo(index);
|
m_listView->scrollTo(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilesystemWidget::fileClicked(const QModelIndex &index)
|
void FilesystemWidget::fileClicked(const QModelIndex &index, const QModelIndex &)
|
||||||
{
|
{
|
||||||
emit fileSelected(index.row());
|
if(index.isValid())
|
||||||
|
emit fileSelected(index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
Filetree::Filetree(QWidget *parent) : QTreeView(parent)
|
Filetree::Filetree(QWidget *parent) : QTreeView(parent)
|
||||||
@@ -45,8 +39,11 @@ Filetree::Filetree(QWidget *parent) : QTreeView(parent)
|
|||||||
m_rootDir = settings.value("filetree/rootDir", QDir::homePath()).toString();
|
m_rootDir = settings.value("filetree/rootDir", QDir::homePath()).toString();
|
||||||
m_fileSystemModel = new QFileSystemModel(this);
|
m_fileSystemModel = new QFileSystemModel(this);
|
||||||
m_fileSystemModel->setRootPath(m_rootDir);
|
m_fileSystemModel->setRootPath(m_rootDir);
|
||||||
m_fileSystemModel->setNameFilters({"*.fits", "*.fit", "*.xisf", "*.jpg", "*.jpeg", "*.png"});
|
m_fileSystemModel->setNameFilters({"*.fits", "*.fit", "*.xisf", "*.jpg", "*.jpeg", "*.png", "*.cr2", "*.nef", "*.dng"});
|
||||||
m_fileSystemModel->setNameFilterDisables(false);
|
m_fileSystemModel->setNameFilterDisables(false);
|
||||||
|
if(settings.value("filetree/showHidden", false).toBool())
|
||||||
|
m_fileSystemModel->setFilter(m_fileSystemModel->filter() | QDir::Hidden);
|
||||||
|
|
||||||
setModel(m_fileSystemModel);
|
setModel(m_fileSystemModel);
|
||||||
setRootIndex(m_fileSystemModel->index(m_rootDir));
|
setRootIndex(m_fileSystemModel->index(m_rootDir));
|
||||||
header()->restoreState(settings.value("filetree/header").toByteArray());
|
header()->restoreState(settings.value("filetree/header").toByteArray());
|
||||||
@@ -57,6 +54,7 @@ Filetree::~Filetree()
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.setValue("filetree/rootDir", m_rootDir);
|
settings.setValue("filetree/rootDir", m_rootDir);
|
||||||
settings.setValue("filetree/header", header()->saveState());
|
settings.setValue("filetree/header", header()->saveState());
|
||||||
|
settings.setValue("filetree/showHidden", (bool)(m_fileSystemModel->filter() & QDir::Hidden));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
||||||
@@ -85,6 +83,9 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
|
|
||||||
QAction *resetRoot = menu.addAction(tr("Reset root"));
|
QAction *resetRoot = menu.addAction(tr("Reset root"));
|
||||||
QAction *goUp = menu.addAction(tr("Go up"));
|
QAction *goUp = menu.addAction(tr("Go up"));
|
||||||
|
QAction *showHidden = menu.addAction(tr("Show hidden files"));
|
||||||
|
showHidden->setCheckable(true);
|
||||||
|
showHidden->setChecked(m_fileSystemModel->filter() & QDir::Hidden);
|
||||||
|
|
||||||
QAction *a = menu.exec(event->globalPos());
|
QAction *a = menu.exec(event->globalPos());
|
||||||
if(a == nullptr)
|
if(a == nullptr)
|
||||||
@@ -121,6 +122,12 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
{
|
{
|
||||||
emit indexDirectory(m_fileSystemModel->filePath(index));
|
emit indexDirectory(m_fileSystemModel->filePath(index));
|
||||||
}
|
}
|
||||||
|
else if(a == showHidden)
|
||||||
|
{
|
||||||
|
auto filter = m_fileSystemModel->filter();
|
||||||
|
filter ^= QDir::Hidden;
|
||||||
|
m_fileSystemModel->setFilter(filter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filetree::mouseDoubleClickEvent(QMouseEvent *event)
|
void Filetree::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
|||||||
+1
-2
@@ -13,10 +13,9 @@ class FilesystemWidget : public QWidget
|
|||||||
QAbstractItemModel *m_model;
|
QAbstractItemModel *m_model;
|
||||||
public:
|
public:
|
||||||
explicit FilesystemWidget(QAbstractItemModel *model, QWidget *parent = nullptr);
|
explicit FilesystemWidget(QAbstractItemModel *model, QWidget *parent = nullptr);
|
||||||
void setDir(const QString &dir);
|
|
||||||
private slots:
|
private slots:
|
||||||
void selectFile(int row);
|
void selectFile(int row);
|
||||||
void fileClicked(const QModelIndex &index);
|
void fileClicked(const QModelIndex &index, const QModelIndex &);
|
||||||
signals:
|
signals:
|
||||||
void fileSelected(int row);
|
void fileSelected(int row);
|
||||||
};
|
};
|
||||||
|
|||||||
+229
@@ -1,6 +1,10 @@
|
|||||||
#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 "pcl/FITSHeaderKeyword.h"
|
||||||
|
|
||||||
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
|
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
|
||||||
|
|
||||||
@@ -9,6 +13,44 @@ bool FITSRecord::editable() const
|
|||||||
return noEditableKey.count(key);
|
return noEditableKey.count(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FITSRecord::FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment) :
|
||||||
|
key(key), value(value), comment(comment)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FITSRecord::FITSRecord(const pcl::FITSHeaderKeyword &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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
setColumnCount(3);
|
setColumnCount(3);
|
||||||
@@ -47,3 +89,190 @@ void ImageInfo::setInfo(const ImageInfoData &info)
|
|||||||
}
|
}
|
||||||
expandAll();
|
expandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WCSData::freeWCS()
|
||||||
|
{
|
||||||
|
wcsvfree(&nwcs, &wcs);
|
||||||
|
nwcs = 0;
|
||||||
|
wcs = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
WCSData::WCSData(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
WCSData::WCSData(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
WCSData::~WCSData()
|
||||||
|
{
|
||||||
|
if(wcs)
|
||||||
|
freeWCS();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WCSData::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 WCSData::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 WCSData::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::modf(dec, °) * 60;
|
||||||
|
sec = std::modf(min, &min) * 60;
|
||||||
|
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
|
||||||
|
}
|
||||||
|
|||||||
+41
@@ -2,6 +2,11 @@
|
|||||||
#define IMAGEINFO_H
|
#define IMAGEINFO_H
|
||||||
|
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
#include <wcslib/wcs.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace pcl { class FITSHeaderKeyword; }
|
||||||
|
|
||||||
struct FITSRecord
|
struct FITSRecord
|
||||||
{
|
{
|
||||||
@@ -9,12 +14,48 @@ struct FITSRecord
|
|||||||
QVariant value;
|
QVariant value;
|
||||||
QByteArray comment;
|
QByteArray comment;
|
||||||
bool editable() const;
|
bool editable() const;
|
||||||
|
FITSRecord(){}
|
||||||
|
FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment);
|
||||||
|
FITSRecord(const pcl::FITSHeaderKeyword &record);
|
||||||
|
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 DEC() const { return dec; }
|
||||||
|
QString toString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WCSData
|
||||||
|
{
|
||||||
|
int nwcs = 0;
|
||||||
|
struct wcsprm *wcs = nullptr;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
void freeWCS();
|
||||||
|
public:
|
||||||
|
WCSData(int width, int height, char *header, int nrec);
|
||||||
|
WCSData(int width, int height, const QVector<FITSRecord> &header);
|
||||||
|
WCSData(const WCSData &) = delete;
|
||||||
|
~WCSData();
|
||||||
|
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; };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageInfoData
|
struct ImageInfoData
|
||||||
{
|
{
|
||||||
QVector<FITSRecord> fitsHeader;
|
QVector<FITSRecord> fitsHeader;
|
||||||
QVector<QPair<QString, QString>> info;
|
QVector<QPair<QString, QString>> info;
|
||||||
|
std::shared_ptr<WCSData> wcs;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ImageInfoData);
|
Q_DECLARE_METATYPE(ImageInfoData);
|
||||||
|
|||||||
+57
-30
@@ -3,10 +3,11 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const int DEFAULT_WIDTH = 2;
|
int DEFAULT_WIDTH = 2;
|
||||||
|
|
||||||
Image::Image(const QString name, int number, ImageRingList *ringList) :
|
Image::Image(const QString name, int number, ImageRingList *ringList) :
|
||||||
m_loading(false),
|
m_loading(false),
|
||||||
@@ -50,9 +51,9 @@ QString Image::name() const
|
|||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
RawImage *Image::rawImage()
|
std::shared_ptr<RawImage> Image::rawImage()
|
||||||
{
|
{
|
||||||
return m_rawImage.get();
|
return m_rawImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RawImage *Image::thumbnail() const
|
const RawImage *Image::thumbnail() const
|
||||||
@@ -97,11 +98,14 @@ void Image::thumbnailLoadFinish(void *rawImage)
|
|||||||
emit thumbnailLoaded(this);
|
emit thumbnailLoaded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageRingList::ImageRingList(QObject *parent) : QAbstractItemModel(parent)
|
ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent) : QAbstractItemModel(parent)
|
||||||
, m_liveMode(false)
|
, m_liveMode(false)
|
||||||
, m_analyzeLevel(None)
|
, m_analyzeLevel(None)
|
||||||
|
, m_database(database)
|
||||||
|
, m_nameFilter(nameFilter)
|
||||||
{
|
{
|
||||||
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
||||||
|
m_nameFilter.replaceInStrings(QRegExp("^"), "*.");
|
||||||
m_thumbPool = new QThreadPool(this);
|
m_thumbPool = new QThreadPool(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +124,7 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
|||||||
|
|
||||||
if(dir.exists())
|
if(dir.exists())
|
||||||
{
|
{
|
||||||
QStringList nameFilter;
|
QStringList list = dir.entryList(m_nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
|
||||||
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.fit" << "*.fits" << "*.xisf";
|
|
||||||
|
|
||||||
QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
|
|
||||||
QStringList absolutePaths;
|
QStringList absolutePaths;
|
||||||
foreach(const QString &file, list)
|
foreach(const QString &file, list)
|
||||||
{
|
{
|
||||||
@@ -210,30 +211,38 @@ void ImageRingList::loadFile(int row)
|
|||||||
{
|
{
|
||||||
if(row < m_images.size())
|
if(row < m_images.size())
|
||||||
{
|
{
|
||||||
m_firstImage = m_currImage = m_lastImage = m_images.begin()+row;
|
int diff = m_currImage != m_images.end() ? row - (m_currImage - m_images.begin()) : -100;
|
||||||
if(m_images.empty())
|
if(diff == 1)
|
||||||
return;
|
increment();
|
||||||
|
else if(diff == -1)
|
||||||
(*m_currImage)->load();
|
decrement();
|
||||||
|
else
|
||||||
m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
|
|
||||||
if(m_liveMode)
|
|
||||||
m_width = 0;
|
|
||||||
|
|
||||||
for(int i=0; i<m_width; i++)
|
|
||||||
{
|
{
|
||||||
m_firstImage = decrement(m_firstImage);
|
m_firstImage = m_currImage = m_lastImage = m_images.begin()+row;
|
||||||
(*m_firstImage)->load();
|
if(m_images.empty())
|
||||||
m_lastImage = increment(m_lastImage);
|
return;
|
||||||
(*m_lastImage)->load();
|
|
||||||
}
|
(*m_currImage)->load();
|
||||||
if(m_lastImage != m_firstImage)
|
|
||||||
{
|
m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
|
||||||
QList<ImagePtr>::iterator iter = increment(m_lastImage);
|
if(m_liveMode)
|
||||||
while(m_firstImage != iter)
|
m_width = 0;
|
||||||
|
|
||||||
|
for(int i=0; i<m_width; i++)
|
||||||
{
|
{
|
||||||
(*iter)->release();
|
m_firstImage = decrement(m_firstImage);
|
||||||
iter = increment(iter);
|
(*m_firstImage)->load();
|
||||||
|
m_lastImage = increment(m_lastImage);
|
||||||
|
(*m_lastImage)->load();
|
||||||
|
}
|
||||||
|
if(m_lastImage != m_firstImage)
|
||||||
|
{
|
||||||
|
QList<ImagePtr>::iterator iter = increment(m_lastImage);
|
||||||
|
while(m_firstImage != iter)
|
||||||
|
{
|
||||||
|
(*iter)->release();
|
||||||
|
iter = increment(iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,6 +259,7 @@ void ImageRingList::loadThumbnails()
|
|||||||
void ImageRingList::stopLoading()
|
void ImageRingList::stopLoading()
|
||||||
{
|
{
|
||||||
m_thumbPool->clear();
|
m_thumbPool->clear();
|
||||||
|
m_thumbPool->waitForDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ImageRingList::imageCount() const
|
int ImageRingList::imageCount() const
|
||||||
@@ -265,6 +275,15 @@ QStringList ImageRingList::imageNames() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageRingList::updateMark()
|
||||||
|
{
|
||||||
|
if(m_images.size())
|
||||||
|
{
|
||||||
|
QModelIndex idx = index(m_currImage - m_images.begin(), 0);
|
||||||
|
emit dataChanged(idx, idx, {Qt::FontRole});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return createIndex(row, column, m_images.at(row).get());
|
return createIndex(row, column, m_images.at(row).get());
|
||||||
@@ -297,6 +316,13 @@ QVariant ImageRingList::data(const QModelIndex &index, int role) const
|
|||||||
QFileInfo info(m_images.at(index.row())->name());
|
QFileInfo info(m_images.at(index.row())->name());
|
||||||
return info.fileName();
|
return info.fileName();
|
||||||
}
|
}
|
||||||
|
case Qt::FontRole:
|
||||||
|
{
|
||||||
|
bool marked = m_database->isMarked(m_images.at(index.row())->name());
|
||||||
|
QFont font;
|
||||||
|
font.setBold(marked);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@@ -333,6 +359,7 @@ void ImageRingList::setFiles(const QStringList files, const QString ¤tFile
|
|||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
m_currImage = m_images.end();
|
||||||
loadFile(index);
|
loadFile(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -19,7 +19,7 @@ class Image : public QObject
|
|||||||
bool m_released;
|
bool m_released;
|
||||||
bool m_current;
|
bool m_current;
|
||||||
int m_number;
|
int m_number;
|
||||||
std::unique_ptr<RawImage> m_rawImage;
|
std::shared_ptr<RawImage> m_rawImage;
|
||||||
std::unique_ptr<RawImage> m_thumbnail;
|
std::unique_ptr<RawImage> m_thumbnail;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
ImageInfoData m_info;
|
ImageInfoData m_info;
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
void loadThumbnail(QThreadPool *pool);
|
void loadThumbnail(QThreadPool *pool);
|
||||||
void release();
|
void release();
|
||||||
QString name() const;
|
QString name() const;
|
||||||
RawImage* rawImage();
|
std::shared_ptr<RawImage> rawImage();
|
||||||
const RawImage* thumbnail() const;
|
const RawImage* thumbnail() const;
|
||||||
ImageInfoData info() const;
|
ImageInfoData info() const;
|
||||||
bool isCurrent() const;
|
bool isCurrent() const;
|
||||||
@@ -45,6 +45,8 @@ protected slots:
|
|||||||
|
|
||||||
typedef std::shared_ptr<Image> ImagePtr;
|
typedef std::shared_ptr<Image> ImagePtr;
|
||||||
|
|
||||||
|
class Database;
|
||||||
|
|
||||||
class ImageRingList : public QAbstractItemModel
|
class ImageRingList : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -57,8 +59,10 @@ class ImageRingList : public QAbstractItemModel
|
|||||||
bool m_liveMode;
|
bool m_liveMode;
|
||||||
AnalyzeLevel m_analyzeLevel;
|
AnalyzeLevel m_analyzeLevel;
|
||||||
QThreadPool *m_thumbPool;
|
QThreadPool *m_thumbPool;
|
||||||
|
Database *m_database;
|
||||||
|
QStringList m_nameFilter;
|
||||||
public:
|
public:
|
||||||
explicit ImageRingList(QObject *parent = 0);
|
explicit ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent = 0);
|
||||||
~ImageRingList() override;
|
~ImageRingList() override;
|
||||||
bool setDir(const QString path, const QString ¤tFile = QString());
|
bool setDir(const QString path, const QString ¤tFile = QString());
|
||||||
void setFile(const QString &file);
|
void setFile(const QString &file);
|
||||||
@@ -75,6 +79,7 @@ public:
|
|||||||
void stopLoading();
|
void stopLoading();
|
||||||
int imageCount() const;
|
int imageCount() const;
|
||||||
QStringList imageNames() const;
|
QStringList imageNames() const;
|
||||||
|
void updateMark();
|
||||||
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QModelIndex parent(const QModelIndex &child) const override;
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
|||||||
+125
-24
@@ -11,6 +11,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
struct RawImageType
|
struct RawImageType
|
||||||
{
|
{
|
||||||
@@ -24,12 +25,20 @@ const RawImageType rawImageTypes[] = {
|
|||||||
{QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true},
|
{QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true},
|
||||||
{QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true},
|
{QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true},
|
||||||
{QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true},
|
{QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true},
|
||||||
|
#ifdef COLOR_MANAGMENT
|
||||||
|
{QOpenGLTexture::RGB, QOpenGLTexture::SRGB8, QOpenGLTexture::UInt8, false},
|
||||||
|
{QOpenGLTexture::RGBA,QOpenGLTexture::SRGB8_Alpha8, QOpenGLTexture::UInt8, false},
|
||||||
|
#else
|
||||||
{QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
|
||||||
{QOpenGLTexture::BGRA,QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
|
{QOpenGLTexture::RGBA,QOpenGLTexture::RGBA8_UNorm, QOpenGLTexture::UInt8, false},
|
||||||
|
#endif
|
||||||
{QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
|
||||||
|
{QOpenGLTexture::RGBA, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
|
||||||
{QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false}
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool MANUAL_MIPMAP_GEN = false;
|
||||||
|
|
||||||
void setScrollRange(QScrollBar *scrollBar, int newRange)
|
void setScrollRange(QScrollBar *scrollBar, int newRange)
|
||||||
{
|
{
|
||||||
int page = scrollBar->pageStep();
|
int page = scrollBar->pageStep();
|
||||||
@@ -66,6 +75,7 @@ ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(pa
|
|||||||
m_updateTimer = new QTimer(this);
|
m_updateTimer = new QTimer(this);
|
||||||
m_updateTimer->setInterval(500);
|
m_updateTimer->setInterval(500);
|
||||||
m_updateTimer->setSingleShot(true);
|
m_updateTimer->setSingleShot(true);
|
||||||
|
m_sizesDirty = false;
|
||||||
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
|
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
QTimer::singleShot(1000, [this](){
|
QTimer::singleShot(1000, [this](){
|
||||||
@@ -75,6 +85,8 @@ ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(pa
|
|||||||
QCoreApplication::exit(-1);
|
QCoreApplication::exit(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWidget::~ImageWidget()
|
ImageWidget::~ImageWidget()
|
||||||
@@ -82,17 +94,22 @@ ImageWidget::~ImageWidget()
|
|||||||
makeCurrent();
|
makeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::setImage(const RawImage *image, int index)
|
void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
|
||||||
{
|
{
|
||||||
if(image == nullptr)return;
|
if(image == nullptr)return;
|
||||||
|
|
||||||
|
m_rawImage = image;
|
||||||
|
|
||||||
m_imgWidth = image->width();
|
m_imgWidth = image->width();
|
||||||
m_imgHeight = image->height();
|
m_imgHeight = image->height();
|
||||||
m_currentImg = index;
|
m_currentImg = index;
|
||||||
|
|
||||||
const RawImageType &rawImageType = rawImageTypes[image->type()];
|
const RawImageType &rawImageType = rawImageTypes[image->type()];
|
||||||
|
m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8 || rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8;
|
||||||
|
m_bwImg = rawImageType.bw;
|
||||||
|
|
||||||
m_image->destroy();
|
m_image->destroy();
|
||||||
|
m_image->setAutoMipMapGenerationEnabled(false);
|
||||||
m_image->setFormat(rawImageType.textureFormat);
|
m_image->setFormat(rawImageType.textureFormat);
|
||||||
m_image->setSize(image->width(), image->height());
|
m_image->setSize(image->width(), image->height());
|
||||||
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
|
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
|
||||||
@@ -100,13 +117,51 @@ void ImageWidget::setImage(const RawImage *image, int index)
|
|||||||
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
||||||
m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
|
m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
|
||||||
m_image->setBorderColor(0, 0, 0, 0);
|
m_image->setBorderColor(0, 0, 0, 0);
|
||||||
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, image->data(), m_transferOptions.get());
|
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data(), m_transferOptions.get());
|
||||||
|
|
||||||
|
auto sRGB_linear = [](cv::Point3f &pixel, const int *pos)
|
||||||
|
{
|
||||||
|
pixel.x = pixel.x <= 0.04045f ? pixel.x / 12.92f : std::pow((pixel.x + 0.055) / 1.055f, 2.4f);
|
||||||
|
pixel.y = pixel.y <= 0.04045f ? pixel.y / 12.92f : std::pow((pixel.y + 0.055) / 1.055f, 2.4f);
|
||||||
|
pixel.z = pixel.z <= 0.04045f ? pixel.z / 12.92f : std::pow((pixel.z + 0.055) / 1.055f, 2.4f);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto linear_sRGB = [](cv::Point3f &pixel, const int *pos)
|
||||||
|
{
|
||||||
|
pixel.x = pixel.x <= 0.0031308f ? pixel.x * 12.92f : 1.055f * std::pow(pixel.x , 1/2.4f) - 0.055f;
|
||||||
|
pixel.y = pixel.y <= 0.0031308f ? pixel.y * 12.92f : 1.055f * std::pow(pixel.y , 1/2.4f) - 0.055f;
|
||||||
|
pixel.z = pixel.z <= 0.0031308f ? pixel.z * 12.92f : 1.055f * std::pow(pixel.z , 1/2.4f) - 0.055f;
|
||||||
|
};
|
||||||
|
|
||||||
|
//AMD OpenGL driver on Windows doesn't generate mipmaps for sRGB textures correctly
|
||||||
|
if(m_srgb && MANUAL_MIPMAP_GEN)
|
||||||
|
{
|
||||||
|
cv::Mat img = image->mat();
|
||||||
|
img.convertTo(img, CV_32FC3, 1/255.0);
|
||||||
|
img.forEach<cv::Point3f>(sRGB_linear);
|
||||||
|
cv::Size size(img.cols, img.rows);
|
||||||
|
for(int i=1; i<m_image->mipLevels(); i++)
|
||||||
|
{
|
||||||
|
cv::Mat mip;
|
||||||
|
size /= 2;
|
||||||
|
cv::resize(img, mip, size);
|
||||||
|
mip.copyTo(img);
|
||||||
|
mip.forEach<cv::Point3f>(linear_sRGB);
|
||||||
|
mip.convertTo(mip, CV_8UC3, 255);
|
||||||
|
m_image->setData(i, rawImageType.pixelFormat, rawImageType.dataType, (const void*)mip.ptr(), m_transferOptions.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else m_image->generateMipMaps();
|
||||||
|
|
||||||
m_image->setLevelOfDetailRange(m_superpixel ? 1 : 0, m_image->mipMaxLevel());
|
m_image->setLevelOfDetailRange(m_superpixel ? 1 : 0, m_image->mipMaxLevel());
|
||||||
m_image->generateMipMaps();
|
|
||||||
m_bwImg = rawImageType.bw;
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageWidget::setWCS(std::shared_ptr<WCSData> wcs)
|
||||||
|
{
|
||||||
|
m_wcs = wcs;
|
||||||
|
}
|
||||||
|
|
||||||
void ImageWidget::setScale(float scale)
|
void ImageWidget::setScale(float scale)
|
||||||
{
|
{
|
||||||
m_scale = scale;
|
m_scale = scale;
|
||||||
@@ -137,11 +192,6 @@ void ImageWidget::allocateThumbnails(const QStringList &paths)
|
|||||||
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
||||||
m_thumbnailTexture->setLayers(paths.size());
|
m_thumbnailTexture->setLayers(paths.size());
|
||||||
m_thumbnailTexture->allocateStorage();
|
m_thumbnailTexture->allocateStorage();
|
||||||
m_bufferSizes->bind();
|
|
||||||
float *tmp = new float[count*3];
|
|
||||||
memset(tmp, 0, count * sizeof(float)*3);
|
|
||||||
m_bufferSizes->allocate(tmp, count * sizeof(float)*3);
|
|
||||||
delete [] tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::setMTFParams(float low, float mid, float high)
|
void ImageWidget::setMTFParams(float low, float mid, float high)
|
||||||
@@ -199,8 +249,7 @@ void ImageWidget::thumbnailLoaded(const Image *image)
|
|||||||
m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGB, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get());
|
m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGB, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get());
|
||||||
float a = raw->thumbAspect();
|
float a = raw->thumbAspect();
|
||||||
int sizes[3] = { std::max(1, a > 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE * a)), std::max(1, a < 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE / a)), image->number() };
|
int sizes[3] = { std::max(1, a > 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE * a)), std::max(1, a < 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE / a)), image->number() };
|
||||||
m_bufferSizes->bind();
|
m_sizesDirty = true;
|
||||||
m_bufferSizes->write(image->number() * sizeof(sizes), sizes, sizeof(sizes));
|
|
||||||
m_thumnails[image->number()].size = QSize(sizes[0], sizes[1]);
|
m_thumnails[image->number()].size = QSize(sizes[0], sizes[1]);
|
||||||
if(!m_updateTimer->isActive())m_updateTimer->start();
|
if(!m_updateTimer->isActive())m_updateTimer->start();
|
||||||
}
|
}
|
||||||
@@ -227,12 +276,27 @@ void ImageWidget::paintGL()
|
|||||||
{
|
{
|
||||||
m_vaoThumb->bind();
|
m_vaoThumb->bind();
|
||||||
m_thumbnailTexture->bind(1);
|
m_thumbnailTexture->bind(1);
|
||||||
|
if(m_sizesDirty)
|
||||||
|
{
|
||||||
|
m_bufferSizes->bind();
|
||||||
|
int i = 0;
|
||||||
|
std::vector<int> sizes(m_thumbnailCount*3);
|
||||||
|
for(auto &s : m_thumnails)
|
||||||
|
{
|
||||||
|
sizes[3*i] = s.size.width();
|
||||||
|
sizes[3*i+1] = s.size.height();
|
||||||
|
sizes[3*i+2] = i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
m_bufferSizes->allocate(&sizes[0], sizes.size()*sizeof(int));
|
||||||
|
m_sizesDirty = false;
|
||||||
|
}
|
||||||
m_thumbnailProgram->bind();
|
m_thumbnailProgram->bind();
|
||||||
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
|
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
|
||||||
|
f->glUniform3i(m_thumbnailProgram->uniformLocation("thumb_size"), THUMB_SIZE_BORDER/2, THUMB_SIZE_BORDER, THUMB_SIZE_BORDER_Y);
|
||||||
m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high);
|
m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high);
|
||||||
m_thumbnailProgram->setUniformValue("invert", m_invert);
|
m_thumbnailProgram->setUniformValue("invert", m_invert);
|
||||||
m_thumbnailProgram->setUniformValue("offset", 0, m_dy);
|
m_thumbnailProgram->setUniformValue("offset", 0, m_dy);
|
||||||
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
|
||||||
QMatrix4x4 mvp;
|
QMatrix4x4 mvp;
|
||||||
mvp.ortho(rect());
|
mvp.ortho(rect());
|
||||||
m_thumbnailProgram->setUniformValue("mvp", mvp);
|
m_thumbnailProgram->setUniformValue("mvp", mvp);
|
||||||
@@ -277,6 +341,9 @@ void ImageWidget::paintGL()
|
|||||||
m_program->setUniformValue("zoom", 1.0f/m_scale);
|
m_program->setUniformValue("zoom", 1.0f/m_scale);
|
||||||
m_program->setUniformValue("bw", m_bwImg);
|
m_program->setUniformValue("bw", m_bwImg);
|
||||||
m_program->setUniformValue("invert", m_invert);
|
m_program->setUniformValue("invert", m_invert);
|
||||||
|
#ifdef COLOR_MANAGMENT
|
||||||
|
m_program->setUniformValue("srgb", m_srgb);
|
||||||
|
#endif
|
||||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +383,8 @@ void ImageWidget::initializeGL()
|
|||||||
qDebug() << (char*)f->glGetString(GL_RENDERER);
|
qDebug() << (char*)f->glGetString(GL_RENDERER);
|
||||||
qDebug() << (char*)f->glGetString(GL_VERSION);
|
qDebug() << (char*)f->glGetString(GL_VERSION);
|
||||||
|
|
||||||
|
MANUAL_MIPMAP_GEN = QString((const char*)f->glGetString(GL_VENDOR)).startsWith("ATI Technologies Inc", Qt::CaseInsensitive);
|
||||||
|
|
||||||
qDebug() << context()->format();
|
qDebug() << context()->format();
|
||||||
|
|
||||||
// each vertex is x,y 2D position and s,t texture coordinates
|
// each vertex is x,y 2D position and s,t texture coordinates
|
||||||
@@ -368,10 +437,10 @@ void ImageWidget::initializeGL()
|
|||||||
m_bufferSizes->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
m_bufferSizes->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
m_bufferSizes->create();
|
m_bufferSizes->create();
|
||||||
m_bufferSizes->bind();
|
m_bufferSizes->bind();
|
||||||
|
|
||||||
m_bufferSizes->allocate(12);
|
m_bufferSizes->allocate(12);
|
||||||
|
|
||||||
m_thumbnailProgram->enableAttributeArray("imageSize_num");
|
m_thumbnailProgram->enableAttributeArray("imageSize_num");
|
||||||
m_thumbnailProgram->setAttributeBuffer("imageSize_num", GL_FLOAT, 0, 3);
|
f3->glVertexAttribIPointer(m_thumbnailProgram->attributeLocation("imageSize_num"), 3, GL_INT, 0, nullptr);
|
||||||
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
||||||
|
|
||||||
m_image = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2D));
|
m_image = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2D));
|
||||||
@@ -387,8 +456,7 @@ void ImageWidget::initializeGL()
|
|||||||
m_thumbnailTexture->setLayers(1);
|
m_thumbnailTexture->setLayers(1);
|
||||||
m_thumbnailTexture->allocateStorage();
|
m_thumbnailTexture->allocateStorage();
|
||||||
m_thumbnailTexture->bind(1);
|
m_thumbnailTexture->bind(1);
|
||||||
m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
m_thumbnailTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||||
m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
|
||||||
|
|
||||||
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
||||||
m_transferOptions->setAlignment(1);
|
m_transferOptions->setAlignment(1);
|
||||||
@@ -419,7 +487,7 @@ void ImageWidget::dropEvent(QDropEvent *event)
|
|||||||
|
|
||||||
void ImageWidget::mousePressEvent(QMouseEvent *event)
|
void ImageWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if(m_thumbnailCount && event->button() == Qt::LeftButton && event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))
|
if(m_showThumbnails && event->button() == Qt::LeftButton && event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))
|
||||||
m_selecting = true;
|
m_selecting = true;
|
||||||
|
|
||||||
if(m_selecting)
|
if(m_selecting)
|
||||||
@@ -438,6 +506,35 @@ void ImageWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
|
||||||
|
if(!m_showThumbnails && m_rawImage)
|
||||||
|
{
|
||||||
|
float dx = m_dx;
|
||||||
|
float dy = m_dy;
|
||||||
|
if(width() > m_image->width()*m_scale)
|
||||||
|
dx = -width()*0.5f + m_image->width()*m_scale*0.5f;
|
||||||
|
if(height() > m_image->height()*m_scale)
|
||||||
|
dy = -height()*0.5f + m_image->height()*m_scale*0.5f;
|
||||||
|
|
||||||
|
QVector2D offset(dx, dy);
|
||||||
|
QVector2D pos = QVector2D(event->pos());
|
||||||
|
QVector2D pix = (pos + offset) / m_scale;
|
||||||
|
QVector3D rgb;
|
||||||
|
|
||||||
|
SkyPoint sky;
|
||||||
|
if(m_wcs)
|
||||||
|
{
|
||||||
|
m_wcs->pixelToWorld(QPointF(pix.x(), pix.y()), sky);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_rawImage->pixel(pix.x(), pix.y(), rgb))
|
||||||
|
{
|
||||||
|
if(m_bwImg)
|
||||||
|
emit status(tr("L:%1").arg(rgb.x()), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString());
|
||||||
|
else
|
||||||
|
emit status(tr("R:%1 G:%2 B:%3").arg(rgb.x()).arg(rgb.y()).arg(rgb.z()), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::mouseReleaseEvent(QMouseEvent *event)
|
void ImageWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||||
@@ -525,13 +622,17 @@ ImageScrollAreaGL::~ImageScrollAreaGL()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::setImage(RawImage *image, int index)
|
void ImageScrollAreaGL::setImage(Image *image)
|
||||||
{
|
{
|
||||||
m_imageWidget->setImage(image, index);
|
if(image && image->rawImage())
|
||||||
m_imgWidth = image->width();
|
{
|
||||||
m_imgHeight = image->height();
|
m_imageWidget->setImage(image->rawImage(), image->number());
|
||||||
if(m_bestFit)bestFit();
|
m_imageWidget->setWCS(image->info().wcs);
|
||||||
updateScrollbars();
|
m_imgWidth = image->rawImage()->width();
|
||||||
|
m_imgHeight = image->rawImage()->height();
|
||||||
|
if(m_bestFit)bestFit();
|
||||||
|
updateScrollbars();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWidget *ImageScrollAreaGL::imageWidget()
|
ImageWidget *ImageScrollAreaGL::imageWidget()
|
||||||
|
|||||||
+8
-2
@@ -39,6 +39,8 @@ class ImageWidget : public QOpenGLWidget
|
|||||||
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
|
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
|
||||||
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
|
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
|
||||||
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
|
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
|
||||||
|
std::shared_ptr<RawImage> m_rawImage;
|
||||||
|
std::shared_ptr<WCSData> m_wcs;
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
int m_imgWidth, m_imgHeight;
|
int m_imgWidth, m_imgHeight;
|
||||||
int m_currentImg;
|
int m_currentImg;
|
||||||
@@ -54,14 +56,17 @@ class ImageWidget : public QOpenGLWidget
|
|||||||
bool m_superpixel;
|
bool m_superpixel;
|
||||||
bool m_showThumbnails;
|
bool m_showThumbnails;
|
||||||
bool m_selecting;
|
bool m_selecting;
|
||||||
|
bool m_sizesDirty;
|
||||||
|
bool m_srgb;
|
||||||
int m_thumbnailCount;
|
int m_thumbnailCount;
|
||||||
QVector<ImageThumb> m_thumnails;
|
QVector<ImageThumb> m_thumnails;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
public:
|
public:
|
||||||
explicit ImageWidget(Database *database, QWidget *parent = nullptr);
|
explicit ImageWidget(Database *database, QWidget *parent = nullptr);
|
||||||
~ImageWidget() override;
|
~ImageWidget() override;
|
||||||
void setImage(const RawImage *image, int index);
|
void setImage(std::shared_ptr<RawImage> image, int index);
|
||||||
void setImage(const QPixmap &pixmap);
|
void setImage(const QPixmap &pixmap);
|
||||||
|
void setWCS(std::shared_ptr<WCSData> wcs);
|
||||||
void setScale(float scale);
|
void setScale(float scale);
|
||||||
void blockRepaint(bool block);
|
void blockRepaint(bool block);
|
||||||
void allocateThumbnails(const QStringList &paths);
|
void allocateThumbnails(const QStringList &paths);
|
||||||
@@ -85,6 +90,7 @@ protected:
|
|||||||
void thumbSelect(QMouseEvent *event);
|
void thumbSelect(QMouseEvent *event);
|
||||||
signals:
|
signals:
|
||||||
void fileDropped(const QString &path);
|
void fileDropped(const QString &path);
|
||||||
|
void status(const QString &value, const QString &pixelCoords, const QString &celestialCoords);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageScrollAreaGL : public QWidget
|
class ImageScrollAreaGL : public QWidget
|
||||||
@@ -101,7 +107,7 @@ class ImageScrollAreaGL : public QWidget
|
|||||||
public:
|
public:
|
||||||
explicit ImageScrollAreaGL(Database *database, QWidget *parent = nullptr);
|
explicit ImageScrollAreaGL(Database *database, QWidget *parent = nullptr);
|
||||||
~ImageScrollAreaGL() override;
|
~ImageScrollAreaGL() override;
|
||||||
void setImage(RawImage *image, int index);
|
void setImage(Image *image);
|
||||||
ImageWidget* imageWidget();
|
ImageWidget* imageWidget();
|
||||||
void setThumbnails(int count);
|
void setThumbnails(int count);
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,5 @@
|
|||||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
||||||
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
|
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
|
||||||
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor org.nou.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 32 org.nou.tenmon.png org.nou.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 space.nouspiro.tenmon.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|||||||
+70
-22
@@ -12,6 +12,12 @@
|
|||||||
#include <pcl/XISF.h>
|
#include <pcl/XISF.h>
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "starfit.h"
|
#include "starfit.h"
|
||||||
|
#include "wcslib/wcshdr.h"
|
||||||
|
|
||||||
|
#ifdef COLOR_MANAGMENT
|
||||||
|
#include <QColorSpace>
|
||||||
|
static pcl::ICCProfile sRgbIccProfile((void*)QColorSpace(QColorSpace::SRgb).iccProfile().data());
|
||||||
|
#endif
|
||||||
|
|
||||||
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(file),
|
||||||
@@ -82,17 +88,17 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image)
|
|||||||
if(!image)
|
if(!image)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LibRaw raw;
|
std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
|
||||||
raw.open_file(path.toLocal8Bit().data());
|
raw->open_file(path.toLocal8Bit().data());
|
||||||
raw.imgdata.params.half_size = true;
|
raw->imgdata.params.half_size = true;
|
||||||
raw.imgdata.params.use_camera_wb = true;
|
raw->imgdata.params.use_camera_wb = true;
|
||||||
raw.imgdata.params.user_flip = 0;
|
raw->imgdata.params.user_flip = 0;
|
||||||
if(raw.unpack())
|
if(raw->unpack())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(image)
|
if(image)
|
||||||
{
|
{
|
||||||
libraw_rawdata_t rawdata = raw.imgdata.rawdata;
|
libraw_rawdata_t rawdata = raw->imgdata.rawdata;
|
||||||
size_t size = rawdata.sizes.width*rawdata.sizes.height;
|
size_t size = rawdata.sizes.width*rawdata.sizes.height;
|
||||||
|
|
||||||
std::vector<uint16_t> out;
|
std::vector<uint16_t> out;
|
||||||
@@ -114,14 +120,14 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image)
|
|||||||
memcpy((*image)->data(), &out[0], sizeof(uint16_t)*d);
|
memcpy((*image)->data(), &out[0], sizeof(uint16_t)*d);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString shutterSpeed = QString::number(raw.imgdata.other.shutter);
|
QString shutterSpeed = QString::number(raw->imgdata.other.shutter);
|
||||||
if(raw.imgdata.other.shutter < 1)
|
if(raw->imgdata.other.shutter < 1)
|
||||||
{
|
{
|
||||||
shutterSpeed = QString("1/%1s").arg(1.0f/raw.imgdata.other.shutter);
|
shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter);
|
||||||
}
|
}
|
||||||
//info.append(StringPair(QObject::tr("Width"), QString::number(rawImg->width)));
|
//info.append(StringPair(QObject::tr("Width"), QString::number(rawImg->width)));
|
||||||
//info.append(StringPair(QObject::tr("Height"), QString::number(rawImg->height)));
|
//info.append(StringPair(QObject::tr("Height"), QString::number(rawImg->height)));
|
||||||
info.info.append({QObject::tr("ISO"), QString::number(raw.imgdata.other.iso_speed)});
|
info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)});
|
||||||
info.info.append({QObject::tr("Shutter speed"), shutterSpeed});
|
info.info.append({QObject::tr("Shutter speed"), shutterSpeed});
|
||||||
#if LIBRAW_MINOR_VERSION>=19
|
#if LIBRAW_MINOR_VERSION>=19
|
||||||
// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature)));
|
// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature)));
|
||||||
@@ -131,6 +137,9 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image)
|
|||||||
|
|
||||||
int loadFITSHeader(fitsfile *file, ImageInfoData &info)
|
int loadFITSHeader(fitsfile *file, ImageInfoData &info)
|
||||||
{
|
{
|
||||||
|
int imgtype;
|
||||||
|
int naxis;
|
||||||
|
long naxes[3] = {0};
|
||||||
int nexist;
|
int nexist;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
char key[FLEN_KEYWORD];
|
char key[FLEN_KEYWORD];
|
||||||
@@ -138,6 +147,7 @@ int loadFITSHeader(fitsfile *file, ImageInfoData &info)
|
|||||||
char comm[FLEN_COMMENT];
|
char comm[FLEN_COMMENT];
|
||||||
char strval[FLEN_VALUE];
|
char strval[FLEN_VALUE];
|
||||||
QVariant var;
|
QVariant var;
|
||||||
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
|
||||||
fits_get_hdrspace(file, &nexist, nullptr, &status);
|
fits_get_hdrspace(file, &nexist, nullptr, &status);
|
||||||
for(int i=1; i<=nexist; i++)
|
for(int i=1; i<=nexist; i++)
|
||||||
{
|
{
|
||||||
@@ -156,16 +166,29 @@ int loadFITSHeader(fitsfile *file, ImageInfoData &info)
|
|||||||
var = vald;
|
var = vald;
|
||||||
else if(status == VALUE_UNDEFINED)
|
else if(status == VALUE_UNDEFINED)
|
||||||
var = QVariant();
|
var = QVariant();
|
||||||
|
else if(string == "T" || string == "F")
|
||||||
|
var = string == "T";
|
||||||
else
|
else
|
||||||
var = strval;
|
var = string;
|
||||||
status = 0;
|
status = 0;
|
||||||
info.fitsHeader.append({key, var, comm});
|
info.fitsHeader.append(FITSRecord(key, var, comm));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return status;
|
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<WCSData>(naxes[0], naxes[1], header, nrec);
|
||||||
|
if(!info.wcs->valid())info.wcs.reset();
|
||||||
|
}
|
||||||
|
fits_free_memory(header, &status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,11 +286,26 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "pcl/ICCProfileTransformation.h"
|
||||||
|
|
||||||
template<typename T, typename PCLtype, int CVtype>
|
template<typename T, typename PCLtype, int CVtype>
|
||||||
bool loadPCLImage(pcl::XISFReader &xisf, RawImage **image)
|
bool loadPCLImage(pcl::XISFReader &xisf, RawImage **image)
|
||||||
{
|
{
|
||||||
PCLtype pclImage;
|
PCLtype pclImage;
|
||||||
xisf.ReadImage(pclImage);
|
xisf.ReadImage(pclImage);
|
||||||
|
pclImage.Status().DisableInitialization();
|
||||||
|
|
||||||
|
#ifdef COLOR_MANAGMENT
|
||||||
|
pcl::ICCProfile iccProfile = xisf.ReadICCProfile();
|
||||||
|
if(iccProfile.IsProfile())
|
||||||
|
{
|
||||||
|
pcl::ICCProfileTransformation iccTran;
|
||||||
|
iccTran.DisableParallelProcessing();
|
||||||
|
iccTran.Add(iccProfile);
|
||||||
|
iccTran.Add(sRgbIccProfile);
|
||||||
|
iccTran >> pclImage;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int numChannels = pclImage.NumberOfChannels();
|
int numChannels = pclImage.NumberOfChannels();
|
||||||
cv::Mat cvImg[numChannels];
|
cv::Mat cvImg[numChannels];
|
||||||
@@ -297,6 +335,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
pcl::XISF::EnsurePTLUTInitialized();
|
||||||
pcl::String pclPath = path.utf16();
|
pcl::String pclPath = path.utf16();
|
||||||
pcl::XISFReader xisf;
|
pcl::XISFReader xisf;
|
||||||
xisf.Open(pclPath);
|
xisf.Open(pclPath);
|
||||||
@@ -308,8 +347,10 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
|
|||||||
auto fitskeywords = xisf.ReadFITSKeywords();
|
auto fitskeywords = xisf.ReadFITSKeywords();
|
||||||
for(auto fits : fitskeywords)
|
for(auto fits : fitskeywords)
|
||||||
{
|
{
|
||||||
info.fitsHeader.append({fits.name.c_str(), fits.value.c_str(), fits.comment.c_str()});
|
info.fitsHeader.append(fits);
|
||||||
}
|
}
|
||||||
|
info.wcs = std::make_shared<WCSData>(xisf.ImageInfo().width, xisf.ImageInfo().height, info.fitsHeader);
|
||||||
|
if(!info.wcs->valid())info.wcs.reset();
|
||||||
|
|
||||||
if(floatType && bps == 32)
|
if(floatType && bps == 32)
|
||||||
return loadPCLImage<float, pcl::FImage, CV_32F>(xisf, image);
|
return loadPCLImage<float, pcl::FImage, CV_32F>(xisf, image);
|
||||||
@@ -322,7 +363,6 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
|
|||||||
case 16:
|
case 16:
|
||||||
return loadPCLImage<uint16_t, pcl::UInt16Image, CV_16U>(xisf, image);
|
return loadPCLImage<uint16_t, pcl::UInt16Image, CV_16U>(xisf, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (pcl::Error err)
|
catch (pcl::Error err)
|
||||||
@@ -347,7 +387,7 @@ void LoadRunable::run()
|
|||||||
|
|
||||||
RawImage *rawImage = nullptr;
|
RawImage *rawImage = nullptr;
|
||||||
bool raw = false;
|
bool raw = false;
|
||||||
if(m_file.endsWith(".CR2", Qt::CaseInsensitive))
|
if(m_file.endsWith(".CR2", Qt::CaseInsensitive) || m_file.endsWith(".NEF", Qt::CaseInsensitive) || m_file.endsWith(".DNG", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
timer.start();
|
timer.start();
|
||||||
loadRAW(m_file, info, &rawImage);
|
loadRAW(m_file, info, &rawImage);
|
||||||
@@ -365,6 +405,11 @@ void LoadRunable::run()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
QImage img(m_file);
|
QImage img(m_file);
|
||||||
|
#ifdef COLOR_MANAGMENT
|
||||||
|
if(img.colorSpace().isValid() && img.colorSpace() != QColorSpace::SRgb)
|
||||||
|
img.convertToColorSpace(QColorSpace::SRgb);
|
||||||
|
#endif
|
||||||
|
|
||||||
ExifData *exif = exif_data_new_from_file(m_file.toLocal8Bit().constData());
|
ExifData *exif = exif_data_new_from_file(m_file.toLocal8Bit().constData());
|
||||||
info.info.append({QObject::tr("Width"), QString::number(img.width())});
|
info.info.append({QObject::tr("Width"), QString::number(img.width())});
|
||||||
info.info.append({QObject::tr("Height"), QString::number(img.height())});
|
info.info.append({QObject::tr("Height"), QString::number(img.height())});
|
||||||
@@ -483,8 +528,10 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
|
|||||||
auto fitskeywords = xisf.ReadFITSKeywords();
|
auto fitskeywords = xisf.ReadFITSKeywords();
|
||||||
for(auto fits : fitskeywords)
|
for(auto fits : fitskeywords)
|
||||||
{
|
{
|
||||||
info.fitsHeader.append({fits.name.c_str(), fits.value.c_str(), fits.comment.c_str()});
|
info.fitsHeader.append(fits);
|
||||||
}
|
}
|
||||||
|
info.wcs = std::make_shared<WCSData>(xisf.ImageInfo().width, xisf.ImageInfo().height, info.fitsHeader);
|
||||||
|
if(!info.wcs->valid())info.wcs.reset();
|
||||||
}
|
}
|
||||||
catch (pcl::Error err)
|
catch (pcl::Error err)
|
||||||
{
|
{
|
||||||
@@ -494,9 +541,10 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertRunable::ConvertRunable(const QString &in, const QString &out) :
|
ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format) :
|
||||||
m_infile(in),
|
m_infile(in),
|
||||||
m_outfile(out)
|
m_outfile(out),
|
||||||
|
m_format(format)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,13 +649,13 @@ void ConvertRunable::run()
|
|||||||
|
|
||||||
if(rawimage)
|
if(rawimage)
|
||||||
{
|
{
|
||||||
if(m_outfile.endsWith(".XISF", Qt::CaseInsensitive))
|
if(m_format == "XISF")
|
||||||
{
|
{
|
||||||
pcl::XISFOptions options;
|
pcl::XISFOptions options;
|
||||||
pcl::FITSKeywordArray fitskeywords;
|
pcl::FITSKeywordArray fitskeywords;
|
||||||
for(auto &record : imageinfo.fitsHeader)
|
for(auto &record : imageinfo.fitsHeader)
|
||||||
{
|
{
|
||||||
pcl::FITSHeaderKeyword key(pcl::IsoString(record.key.data()), pcl::IsoString(record.value.toString().toLatin1().data()), pcl::IsoString(record.comment.data()));
|
pcl::FITSHeaderKeyword key(pcl::IsoString(record.key.data()), pcl::IsoString(record.valueToByteArray().data()), pcl::IsoString(record.comment.data()));
|
||||||
fitskeywords.Append(key);
|
fitskeywords.Append(key);
|
||||||
}
|
}
|
||||||
pcl::XISFWriter xisf;
|
pcl::XISFWriter xisf;
|
||||||
@@ -628,7 +676,7 @@ void ConvertRunable::run()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_outfile.endsWith(".FITS", Qt::CaseInsensitive) || m_outfile.endsWith(".FIT", Qt::CaseInsensitive))
|
if(m_format == "FITS")
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
fitsfile *fw;
|
fitsfile *fw;
|
||||||
|
|||||||
+2
-1
@@ -25,8 +25,9 @@ class ConvertRunable : public QRunnable
|
|||||||
{
|
{
|
||||||
QString m_infile;
|
QString m_infile;
|
||||||
QString m_outfile;
|
QString m_outfile;
|
||||||
|
QString m_format;
|
||||||
public:
|
public:
|
||||||
ConvertRunable(const QString &in, const QString &out);
|
ConvertRunable(const QString &in, const QString &out, const QString &format);
|
||||||
void run() override;
|
void run() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,14 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
setenv("LC_NUMERIC", "C", 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setMajorVersion(3);
|
format.setMajorVersion(3);
|
||||||
format.setMinorVersion(3);
|
format.setMinorVersion(3);
|
||||||
@@ -15,7 +20,7 @@ int main(int argc, char *argv[])
|
|||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
a.setOrganizationName("nou");
|
a.setOrganizationName("nou");
|
||||||
a.setApplicationName("Tenmon");
|
a.setApplicationName("Tenmon");
|
||||||
a.setWindowIcon(QIcon(":/org.nou.tenmon.png"));
|
a.setWindowIcon(QIcon(":/space.nouspiro.tenmon.png"));
|
||||||
|
|
||||||
QTranslator translator;
|
QTranslator translator;
|
||||||
QTranslator translator2;
|
QTranslator translator2;
|
||||||
|
|||||||
+112
-33
@@ -13,11 +13,16 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QCoreApplication>
|
#include <QGuiApplication>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
|
#include <QStatusBar>
|
||||||
|
#include <QImageReader>
|
||||||
|
#include <QMimeDatabase>
|
||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "markedfiles.h"
|
#include "markedfiles.h"
|
||||||
#include "about.h"
|
#include "about.h"
|
||||||
|
#include "statusbar.h"
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -32,15 +37,33 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
qRegisterMetaType<ImageInfoData>("ImageInfoData");
|
qRegisterMetaType<ImageInfoData>("ImageInfoData");
|
||||||
qRegisterMetaType<RawImage*>("RawImage");
|
qRegisterMetaType<RawImage*>("RawImage");
|
||||||
|
|
||||||
|
SettingsDialog::loadSettings();
|
||||||
|
|
||||||
|
QStringList nameFilter;
|
||||||
|
_saveFilter = tr("FITS (*.fits *.fit);;XISF (*.xisf);;");
|
||||||
|
_openFilter = tr("Images (");
|
||||||
|
QMimeDatabase db;
|
||||||
|
auto supportedFormats = QImageReader::supportedMimeTypes();
|
||||||
|
QStringList filters;
|
||||||
|
for(auto format : supportedFormats)
|
||||||
|
{
|
||||||
|
QMimeType mimeType = db.mimeTypeForName(format);
|
||||||
|
_saveFilter.append(mimeType.filterString() + ";;");
|
||||||
|
_openFilter.append("*.");
|
||||||
|
_openFilter.append(mimeType.suffixes().join(" *."));
|
||||||
|
_openFilter.append(" ");
|
||||||
|
nameFilter.append(mimeType.suffixes());
|
||||||
|
}
|
||||||
|
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.nef *.dng)");
|
||||||
|
nameFilter.append({"fit", "fits", "xisf", "cr2", "nef", "dng"});
|
||||||
|
|
||||||
m_info = new ImageInfo(this);
|
m_info = new ImageInfo(this);
|
||||||
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
||||||
infoDock->setWidget(m_info);
|
infoDock->setWidget(m_info);
|
||||||
infoDock->setObjectName("infoDock");
|
infoDock->setObjectName("infoDock");
|
||||||
addDockWidget(Qt::LeftDockWidgetArea, infoDock);
|
addDockWidget(Qt::LeftDockWidgetArea, infoDock);
|
||||||
//m_image = new ImageScrollArea(this);
|
|
||||||
//m_image->resize(0,0);
|
|
||||||
//setCentralWidget(m_image);
|
|
||||||
resize(800, 600);
|
resize(800, 600);
|
||||||
|
setStatusBar(new QStatusBar(this));
|
||||||
|
|
||||||
m_database = new Database(this);
|
m_database = new Database(this);
|
||||||
if(!m_database->init())
|
if(!m_database->init())
|
||||||
@@ -49,13 +72,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
m_imageGL = new ImageScrollAreaGL(m_database, this);
|
m_imageGL = new ImageScrollAreaGL(m_database, this);
|
||||||
setCentralWidget(m_imageGL);
|
setCentralWidget(m_imageGL);
|
||||||
|
|
||||||
|
StatusBar *statusBar = new StatusBar(this);
|
||||||
|
setStatusBar(statusBar);
|
||||||
|
connect(m_imageGL->imageWidget(), &ImageWidget::status, statusBar, &StatusBar::newStatus);
|
||||||
|
|
||||||
m_stretchPanel = new StretchToolbar(this);
|
m_stretchPanel = new StretchToolbar(this);
|
||||||
connect(m_stretchPanel, SIGNAL(paramChanged(float,float,float)), m_imageGL->imageWidget(), SLOT(setMTFParams(float,float,float)));
|
connect(m_stretchPanel, SIGNAL(paramChanged(float,float,float)), m_imageGL->imageWidget(), SLOT(setMTFParams(float,float,float)));
|
||||||
connect(m_stretchPanel, &StretchToolbar::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
connect(m_stretchPanel, &StretchToolbar::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
||||||
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
||||||
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
||||||
|
|
||||||
m_ringList = new ImageRingList(this);
|
m_ringList = new ImageRingList(m_database, nameFilter, this);
|
||||||
m_filesystem = new FilesystemWidget(m_ringList, this);
|
m_filesystem = new FilesystemWidget(m_ringList, this);
|
||||||
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
|
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
|
||||||
|
|
||||||
@@ -113,6 +140,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
exitAction->setShortcut(QKeySequence::Quit);
|
exitAction->setShortcut(QKeySequence::Quit);
|
||||||
menuBar()->addMenu(fileMenu);
|
menuBar()->addMenu(fileMenu);
|
||||||
|
|
||||||
|
QMenu *editMenu = new QMenu(tr("Edit"), this);
|
||||||
|
editMenu->addAction(tr("Settings"), this, &MainWindow::showSettingsDialog);
|
||||||
|
menuBar()->addMenu(editMenu);
|
||||||
|
|
||||||
QMenu *viewMenu = new QMenu(tr("View"), this);
|
QMenu *viewMenu = new QMenu(tr("View"), this);
|
||||||
viewMenu->addAction(tr("Zoom In"), m_imageGL, SLOT(zoomIn()), QKeySequence::ZoomIn);
|
viewMenu->addAction(tr("Zoom In"), m_imageGL, SLOT(zoomIn()), QKeySequence::ZoomIn);
|
||||||
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
||||||
@@ -186,7 +217,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
_lastDir = standardLocations.first();
|
_lastDir = standardLocations.first();
|
||||||
|
|
||||||
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
|
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
|
||||||
m_filesystem->setDir(_lastDir);
|
|
||||||
|
|
||||||
QStringList args = QCoreApplication::arguments();
|
QStringList args = QCoreApplication::arguments();
|
||||||
args.removeFirst();
|
args.removeFirst();
|
||||||
@@ -204,6 +234,16 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_imageGL->setFocus();
|
m_imageGL->setFocus();
|
||||||
|
|
||||||
|
// workaround for nasty wayland backend bug https://bugreports.qt.io/browse/QTBUG-87332
|
||||||
|
if(static_cast<QGuiApplication*>(QCoreApplication::instance())->platformName() == "wayland")
|
||||||
|
{
|
||||||
|
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
m_stretchPanel->setFloatable(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@@ -216,9 +256,11 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
|
|||||||
switch (event->key())
|
switch (event->key())
|
||||||
{
|
{
|
||||||
case Qt::Key_Left:
|
case Qt::Key_Left:
|
||||||
|
case Qt::Key_Up:
|
||||||
m_ringList->decrement();
|
m_ringList->decrement();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Right:
|
case Qt::Key_Right:
|
||||||
|
case Qt::Key_Down:
|
||||||
m_ringList->increment();
|
m_ringList->increment();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -271,7 +313,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
|||||||
|
|
||||||
void MainWindow::copyOrMove(bool copy)
|
void MainWindow::copyOrMove(bool copy)
|
||||||
{
|
{
|
||||||
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"), _lastDir);
|
QString dest = QFileDialog::getExistingDirectory(this,
|
||||||
|
tr("Select destination"),
|
||||||
|
_lastDir,
|
||||||
|
QFileDialog::ShowDirsOnly);
|
||||||
copyOrMove(copy, dest);
|
copyOrMove(copy, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +332,7 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
|
|||||||
progress.show();
|
progress.show();
|
||||||
foreach(const QString &file, files)
|
foreach(const QString &file, files)
|
||||||
{
|
{
|
||||||
|
bool result = false;
|
||||||
QFileInfo info(file);
|
QFileInfo info(file);
|
||||||
QFile srcFile(file);
|
QFile srcFile(file);
|
||||||
QFile dstFile(dir.absoluteFilePath(info.fileName()));
|
QFile dstFile(dir.absoluteFilePath(info.fileName()));
|
||||||
@@ -295,7 +341,7 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(progress.wasCanceled())
|
if(progress.wasCanceled())
|
||||||
break;
|
return;
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if(copy)
|
if(copy)
|
||||||
{
|
{
|
||||||
@@ -305,20 +351,30 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
|
|||||||
{
|
{
|
||||||
dstFile.remove();
|
dstFile.remove();
|
||||||
dstFile.close();
|
dstFile.close();
|
||||||
qDebug() << dstFile.fileName();
|
result = srcFile.copy(dstFile.fileName());
|
||||||
srcFile.copy(dstFile.fileName());
|
|
||||||
}
|
}
|
||||||
|
else result = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
srcFile.rename(dstFile.fileName());
|
result = srcFile.rename(dstFile.fileName());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if(copy)
|
if(copy)
|
||||||
srcFile.copy(dstFile.fileName());
|
result = srcFile.copy(dstFile.fileName());
|
||||||
else
|
else
|
||||||
srcFile.rename(dstFile.fileName());
|
result = srcFile.rename(dstFile.fileName());
|
||||||
#endif
|
#endif
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
QString t = copy ? tr("Failed to copy") : tr("Failed to move");
|
||||||
|
QString m = copy ? tr("Failed to copy from %1 to %2") : tr("Failed to move from %1 to %2");
|
||||||
|
QMessageBox::StandardButton button = QMessageBox::warning(this, t,
|
||||||
|
m.arg(srcFile.fileName()).arg(dir.absolutePath()),
|
||||||
|
QMessageBox::Ignore | QMessageBox::Abort);
|
||||||
|
qDebug() << button;
|
||||||
|
if(button == QMessageBox::Abort)return;
|
||||||
|
}
|
||||||
progress.setValue(i++);
|
progress.setValue(i++);
|
||||||
}
|
}
|
||||||
m_database->clearMarkedFiles();
|
m_database->clearMarkedFiles();
|
||||||
@@ -336,16 +392,18 @@ void MainWindow::socketNotify()
|
|||||||
|
|
||||||
void MainWindow::pixmapLoaded(Image *image)
|
void MainWindow::pixmapLoaded(Image *image)
|
||||||
{
|
{
|
||||||
//m_image->setImage(image->pixmap());
|
|
||||||
if(image->rawImage())
|
if(image->rawImage())
|
||||||
{
|
{
|
||||||
m_imageGL->setImage(image->rawImage(), image->number());
|
m_imageGL->setImage(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::loadFile()
|
void MainWindow::loadFile()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getOpenFileName(this, tr("Open file"), _lastDir, tr("Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)"));
|
QString file = QFileDialog::getOpenFileName(this,
|
||||||
|
tr("Open file"),
|
||||||
|
_lastDir,
|
||||||
|
_openFilter);
|
||||||
loadFile(file);
|
loadFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +420,6 @@ void MainWindow::loadFile(const QString &path)
|
|||||||
_lastDir = info.canonicalPath();
|
_lastDir = info.canonicalPath();
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.setValue("mainwindow/lastdir", _lastDir);
|
settings.setValue("mainwindow/lastdir", _lastDir);
|
||||||
m_filesystem->setDir(_lastDir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +430,7 @@ void MainWindow::loadFile(int row)
|
|||||||
|
|
||||||
void MainWindow::indexDir()
|
void MainWindow::indexDir()
|
||||||
{
|
{
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir);
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir, QFileDialog::ShowDirsOnly);
|
||||||
indexDir(dir);
|
indexDir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,35 +454,45 @@ void MainWindow::reindex()
|
|||||||
void MainWindow::saveAs()
|
void MainWindow::saveAs()
|
||||||
{
|
{
|
||||||
QString selectedFilter;
|
QString selectedFilter;
|
||||||
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)"), &selectedFilter);
|
QString file = QFileDialog::getSaveFileName(this,
|
||||||
|
tr("Save as"),
|
||||||
|
_lastDir,
|
||||||
|
_saveFilter,
|
||||||
|
&selectedFilter);
|
||||||
|
auto filterToFormat = [](const QString &file, const QString &filter) -> const char*
|
||||||
|
{
|
||||||
|
QString suffix = QFileInfo(file).suffix();
|
||||||
|
if(!suffix.compare("jpg", Qt::CaseInsensitive) || !suffix.compare("jpeg", Qt::CaseInsensitive))return "JPEG";
|
||||||
|
if(!suffix.compare("png", Qt::CaseInsensitive))return "PNG";
|
||||||
|
if(!suffix.compare("fits", Qt::CaseInsensitive) || !suffix.compare("fit", Qt::CaseInsensitive))return "FITS";
|
||||||
|
if(!suffix.compare("xisf", Qt::CaseInsensitive))return "XISF";
|
||||||
|
if(filter.contains("png"))return "PNG";
|
||||||
|
if(filter.contains("fits"))return "FITS";
|
||||||
|
if(filter.contains("xisf"))return "XISF";
|
||||||
|
return "JPEG";
|
||||||
|
};
|
||||||
|
|
||||||
if(!file.isEmpty())
|
if(!file.isEmpty())
|
||||||
{
|
{
|
||||||
QFileInfo info(file);
|
QString format = filterToFormat(file, selectedFilter);
|
||||||
if(info.suffix().isEmpty())
|
|
||||||
{
|
|
||||||
if(selectedFilter.contains("jpg"))file += ".jpg";
|
|
||||||
if(selectedFilter.contains("png"))file += ".png";
|
|
||||||
if(selectedFilter.contains("fits"))file += ".fits";
|
|
||||||
if(selectedFilter.contains("xisf"))file += ".xisf";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(file.endsWith(".fits") || file.endsWith(".xisf"))
|
if(format == "FITS" || format == "XISF")
|
||||||
{
|
{
|
||||||
convert(file);
|
convert(file, format);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QImage img = m_imageGL->imageWidget()->renderToImage();
|
QImage img = m_imageGL->imageWidget()->renderToImage();
|
||||||
if(!img.isNull())
|
if(!img.isNull())
|
||||||
img.save(file);
|
img.save(file, filterToFormat(file, selectedFilter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::convert(const QString &outfile)
|
void MainWindow::convert(const QString &outfile, const QString &format)
|
||||||
{
|
{
|
||||||
QString file = m_ringList->currentImage()->name();
|
QString file = m_ringList->currentImage()->name();
|
||||||
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile));
|
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile, format));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::markImage()
|
void MainWindow::markImage()
|
||||||
@@ -435,7 +502,10 @@ void MainWindow::markImage()
|
|||||||
{
|
{
|
||||||
QString file = ptr->name();
|
QString file = ptr->name();
|
||||||
if(!file.isEmpty())
|
if(!file.isEmpty())
|
||||||
|
{
|
||||||
m_database->mark(file);
|
m_database->mark(file);
|
||||||
|
m_ringList->updateMark();
|
||||||
|
}
|
||||||
|
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
}
|
}
|
||||||
@@ -448,7 +518,10 @@ void MainWindow::unmarkImage()
|
|||||||
{
|
{
|
||||||
QString file = ptr->name();
|
QString file = ptr->name();
|
||||||
if(!file.isEmpty())
|
if(!file.isEmpty())
|
||||||
|
{
|
||||||
m_database->unmark(file);
|
m_database->unmark(file);
|
||||||
|
m_ringList->updateMark();
|
||||||
|
}
|
||||||
|
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
}
|
}
|
||||||
@@ -514,10 +587,16 @@ void MainWindow::starFinder(bool findStars)
|
|||||||
|
|
||||||
void MainWindow::showMarkFilesDialog()
|
void MainWindow::showMarkFilesDialog()
|
||||||
{
|
{
|
||||||
MarkedFiles markedFiles;
|
MarkedFiles markedFiles(this);
|
||||||
markedFiles.exec();
|
markedFiles.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::showSettingsDialog()
|
||||||
|
{
|
||||||
|
SettingsDialog settingsDialog(this);
|
||||||
|
settingsDialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::updateWindowTitle()
|
void MainWindow::updateWindowTitle()
|
||||||
{
|
{
|
||||||
ImagePtr ptr = m_ringList->currentImage();
|
ImagePtr ptr = m_ringList->currentImage();
|
||||||
|
|||||||
+4
-1
@@ -28,6 +28,8 @@ class MainWindow : public QMainWindow
|
|||||||
QSocketNotifier *socketNotifier;
|
QSocketNotifier *socketNotifier;
|
||||||
QString _lastDir;
|
QString _lastDir;
|
||||||
bool _maximized;
|
bool _maximized;
|
||||||
|
QString _openFilter;
|
||||||
|
QString _saveFilter;
|
||||||
public:
|
public:
|
||||||
MainWindow(QWidget *parent = 0);
|
MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow() override;
|
~MainWindow() override;
|
||||||
@@ -50,7 +52,7 @@ protected slots:
|
|||||||
void indexDir(const QString &dir);
|
void indexDir(const QString &dir);
|
||||||
void reindex();
|
void reindex();
|
||||||
void saveAs();
|
void saveAs();
|
||||||
void convert(const QString &outfile);
|
void convert(const QString &outfile, const QString &format);
|
||||||
void markImage();
|
void markImage();
|
||||||
void unmarkImage();
|
void unmarkImage();
|
||||||
void markAndNext();
|
void markAndNext();
|
||||||
@@ -63,6 +65,7 @@ protected slots:
|
|||||||
void peakFinder(bool findPeaks);
|
void peakFinder(bool findPeaks);
|
||||||
void starFinder(bool findStars);
|
void starFinder(bool findStars);
|
||||||
void showMarkFilesDialog();
|
void showMarkFilesDialog();
|
||||||
|
void showSettingsDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
+101
-1
@@ -1,5 +1,9 @@
|
|||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
|
||||||
|
int THUMB_SIZE = 128;
|
||||||
|
int THUMB_SIZE_BORDER = 138;
|
||||||
|
int THUMB_SIZE_BORDER_Y = 158;
|
||||||
|
|
||||||
RawImage::ImgType CV2Type(int cvtype)
|
RawImage::ImgType CV2Type(int cvtype)
|
||||||
{
|
{
|
||||||
switch (cvtype)
|
switch (cvtype)
|
||||||
@@ -16,6 +20,8 @@ RawImage::ImgType CV2Type(int cvtype)
|
|||||||
return RawImage::UINT8C4;
|
return RawImage::UINT8C4;
|
||||||
case CV_16UC3:
|
case CV_16UC3:
|
||||||
return RawImage::UINT16C3;
|
return RawImage::UINT16C3;
|
||||||
|
case CV_16UC4:
|
||||||
|
return RawImage::UINT16C4;
|
||||||
case CV_32FC3:
|
case CV_32FC3:
|
||||||
return RawImage::FLOAT32C3;
|
return RawImage::FLOAT32C3;
|
||||||
default:
|
default:
|
||||||
@@ -39,6 +45,8 @@ int Type2CV(RawImage::ImgType type)
|
|||||||
return CV_8UC4;
|
return CV_8UC4;
|
||||||
case RawImage::UINT16C3:
|
case RawImage::UINT16C3:
|
||||||
return CV_16UC3;
|
return CV_16UC3;
|
||||||
|
case RawImage::UINT16C4:
|
||||||
|
return CV_16UC4;
|
||||||
case RawImage::FLOAT32C3:
|
case RawImage::FLOAT32C3:
|
||||||
return CV_32FC3;
|
return CV_32FC3;
|
||||||
case RawImage::UNKNOWN:
|
case RawImage::UNKNOWN:
|
||||||
@@ -63,6 +71,7 @@ RawImage::RawImage(cv::Mat &img)
|
|||||||
{
|
{
|
||||||
m_img = img;
|
m_img = img;
|
||||||
m_stats = false;
|
m_stats = false;
|
||||||
|
scaleToUnit();
|
||||||
}
|
}
|
||||||
|
|
||||||
RawImage::RawImage(const RawImage &d)
|
RawImage::RawImage(const RawImage &d)
|
||||||
@@ -84,6 +93,27 @@ RawImage::RawImage(const QImage &img)
|
|||||||
m_img.create(img.height(), img.width(), CV_8UC4);
|
m_img.create(img.height(), img.width(), CV_8UC4);
|
||||||
for(int i=0; i<img.height(); i++)
|
for(int i=0; i<img.height(); i++)
|
||||||
std::memcpy(m_img.ptr(i), img.scanLine(i), img.width()*4);
|
std::memcpy(m_img.ptr(i), img.scanLine(i), img.width()*4);
|
||||||
|
cv::cvtColor(m_img, m_img, cv::COLOR_BGRA2RGB);
|
||||||
|
}
|
||||||
|
else if(img.format() == QImage::Format_ARGB32)
|
||||||
|
{
|
||||||
|
m_img.create(img.height(), img.width(), CV_8UC4);
|
||||||
|
for(int i=0; i<img.height(); i++)
|
||||||
|
std::memcpy(m_img.ptr(i), img.scanLine(i), img.width()*4);
|
||||||
|
cv::cvtColor(m_img, m_img, cv::COLOR_BGRA2RGBA);
|
||||||
|
}
|
||||||
|
else if(img.format() == QImage::Format_RGBX64)
|
||||||
|
{
|
||||||
|
m_img.create(img.height(), img.width(), CV_16UC4);
|
||||||
|
for(int i=0; i<img.height(); i++)
|
||||||
|
std::memcpy(m_img.ptr(i), img.scanLine(i), img.width()*8);
|
||||||
|
cv::cvtColor(m_img, m_img, cv::COLOR_RGBA2RGB);
|
||||||
|
}
|
||||||
|
else if(img.format() == QImage::Format_RGBA64)
|
||||||
|
{
|
||||||
|
m_img.create(img.height(), img.width(), CV_16UC4);
|
||||||
|
for(int i=0; i<img.height(); i++)
|
||||||
|
std::memcpy(m_img.ptr(i), img.scanLine(i), img.width()*8);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -284,7 +314,7 @@ void RawImage::convertToThumbnail()
|
|||||||
if(m_img.channels() == 1)
|
if(m_img.channels() == 1)
|
||||||
cv::cvtColor(m_img, m_img, cv::COLOR_GRAY2RGB);
|
cv::cvtColor(m_img, m_img, cv::COLOR_GRAY2RGB);
|
||||||
if(m_img.channels() == 4)
|
if(m_img.channels() == 4)
|
||||||
cv::cvtColor(m_img, m_img, cv::COLOR_BGRA2RGB);
|
cv::cvtColor(m_img, m_img, cv::COLOR_RGBA2RGB);
|
||||||
cv::Size dsize(THUMB_SIZE, THUMB_SIZE);
|
cv::Size dsize(THUMB_SIZE, THUMB_SIZE);
|
||||||
cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_NEAREST);
|
cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_NEAREST);
|
||||||
}
|
}
|
||||||
@@ -298,3 +328,73 @@ const cv::Mat& RawImage::mat() const
|
|||||||
{
|
{
|
||||||
return m_img;
|
return m_img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RawImage::pixel(int x, int y, QVector3D &rgb) const
|
||||||
|
{
|
||||||
|
if(x < 0 || y < 0 || x >= (int)width() || y >= (int)height())return false;
|
||||||
|
|
||||||
|
switch(m_img.type())
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
{
|
||||||
|
uint8_t v = m_img.at<uint8_t>(y, x);
|
||||||
|
rgb = QVector3D(v, v, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_16U:
|
||||||
|
{
|
||||||
|
uint16_t v = m_img.at<uint16_t>(y, x);
|
||||||
|
rgb = QVector3D(v, v, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_32F:
|
||||||
|
{
|
||||||
|
float v = m_img.at<float>(y, x);
|
||||||
|
rgb = QVector3D(v, v, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_8UC3:
|
||||||
|
{
|
||||||
|
cv::Vec3b v = m_img.at<cv::Vec3b>(y, x);
|
||||||
|
rgb = QVector3D(v[0], v[1], v[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_8UC4:
|
||||||
|
{
|
||||||
|
cv::Vec4b v = m_img.at<cv::Vec4b>(y, x);
|
||||||
|
rgb = QVector3D(v[0], v[1], v[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_16UC3:
|
||||||
|
{
|
||||||
|
cv::Vec3w v = m_img.at<cv::Vec3w>(y, x);
|
||||||
|
rgb = QVector3D(v[0], v[1], v[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CV_32FC3:
|
||||||
|
{
|
||||||
|
cv::Vec3f v = m_img.at<cv::Vec3f>(y, x);
|
||||||
|
rgb = QVector3D(v[0], v[1], v[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
rgb = QVector3D(0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawImage::scaleToUnit()
|
||||||
|
{
|
||||||
|
if(CV_MAT_DEPTH(m_img.type()) == CV_32F)
|
||||||
|
{
|
||||||
|
double min, max;
|
||||||
|
cv::minMaxIdx(m_img, &min, &max);
|
||||||
|
if(min < 0 || max > 1)
|
||||||
|
{
|
||||||
|
float scale = 1.0 / (max - min);
|
||||||
|
float zero = min * scale;
|
||||||
|
m_img = m_img * scale - zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+7
-3
@@ -8,10 +8,11 @@
|
|||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <opencv2/imgproc.hpp>
|
#include <opencv2/imgproc.hpp>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
const int THUMB_SIZE = 128;
|
extern int THUMB_SIZE;
|
||||||
const int THUMB_SIZE_BORDER = 138;
|
extern int THUMB_SIZE_BORDER;
|
||||||
const int THUMB_SIZE_BORDER_Y = 158;
|
extern int THUMB_SIZE_BORDER_Y;
|
||||||
|
|
||||||
class Peak
|
class Peak
|
||||||
{
|
{
|
||||||
@@ -56,6 +57,7 @@ public:
|
|||||||
UINT8C3,
|
UINT8C3,
|
||||||
UINT8C4,
|
UINT8C4,
|
||||||
UINT16C3,
|
UINT16C3,
|
||||||
|
UINT16C4,
|
||||||
FLOAT32C3,
|
FLOAT32C3,
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
};
|
};
|
||||||
@@ -81,6 +83,8 @@ public:
|
|||||||
void convertToThumbnail();
|
void convertToThumbnail();
|
||||||
float thumbAspect() const;
|
float thumbAspect() const;
|
||||||
const cv::Mat& mat() const;
|
const cv::Mat& mat() const;
|
||||||
|
bool pixel(int x, int y, QVector3D &rgb) const;
|
||||||
|
void scaleToUnit();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RAWIMAGE_H
|
#endif // RAWIMAGE_H
|
||||||
|
|||||||
+10
-11
@@ -1,15 +1,9 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/shaders">
|
|
||||||
<file>image.frag</file>
|
|
||||||
<file>image.vert</file>
|
|
||||||
<file>thumb.frag</file>
|
|
||||||
<file>thumb.vert</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>invert.png</file>
|
<file>invert.png</file>
|
||||||
<file>nuke.png</file>
|
<file>nuke.png</file>
|
||||||
<file>bayer.png</file>
|
<file>bayer.png</file>
|
||||||
<file>org.nou.tenmon.png</file>
|
<file>space.nouspiro.tenmon.png</file>
|
||||||
<file>nuke_a.png</file>
|
<file>nuke_a.png</file>
|
||||||
<file>about/tenmon</file>
|
<file>about/tenmon</file>
|
||||||
<file>about/pcl</file>
|
<file>about/pcl</file>
|
||||||
@@ -18,13 +12,18 @@
|
|||||||
<file>about/filter.png</file>
|
<file>about/filter.png</file>
|
||||||
<file>about/stretch-panel.png</file>
|
<file>about/stretch-panel.png</file>
|
||||||
<file>translations/tenmon_fr.qm</file>
|
<file>translations/tenmon_fr.qm</file>
|
||||||
|
<file>shaders/image.frag</file>
|
||||||
|
<file>shaders/image.vert</file>
|
||||||
|
<file>shaders/thumb.frag</file>
|
||||||
|
<file>shaders/thumb.vert</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" lang="en">
|
<qresource lang="en" prefix="/">
|
||||||
<file alias="help">about/help_en</file>
|
<file alias="help">about/help_en</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" lang="sk">
|
<qresource lang="sk" prefix="/">
|
||||||
<file alias="help">about/help_sk</file>
|
<file alias="help">about/help_sk</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/help"/>
|
<qresource lang="fr" prefix="/">
|
||||||
<qresource prefix="/helpimg"/>
|
<file alias="help">about/help_fr</file>
|
||||||
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include "settingsdialog.h"
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSettings>
|
||||||
|
#include "rawimage.h"
|
||||||
|
|
||||||
|
extern int DEFAULT_WIDTH;
|
||||||
|
|
||||||
|
class EvenNumber : public QSpinBox
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit EvenNumber(QWidget *parent) : QSpinBox(parent){}
|
||||||
|
protected:
|
||||||
|
QValidator::State validate(QString &text, int &) const
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
int val = text.toInt(&ok);
|
||||||
|
if(ok && (val & 1) == 0)return QValidator::Acceptable;
|
||||||
|
if(ok)return QValidator::Intermediate;
|
||||||
|
return QValidator::Invalid;
|
||||||
|
}
|
||||||
|
void fixup(QString &input) const
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
int val = input.toInt(&ok);
|
||||||
|
val -= val & 1;
|
||||||
|
input = QString::number(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
QFormLayout *layout = new QFormLayout(this);
|
||||||
|
setWindowTitle(tr("Settings"));
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
|
||||||
|
m_preloadImages = new QSpinBox(this);
|
||||||
|
m_preloadImages->setRange(0, 8);
|
||||||
|
m_preloadImages->setValue(settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt());
|
||||||
|
m_preloadImages->setToolTip(tr("How many images are preloaded before and after current image."));
|
||||||
|
|
||||||
|
m_thumSize = new EvenNumber(this);
|
||||||
|
m_thumSize->setRange(64, 512);
|
||||||
|
m_thumSize->setSingleStep(2);
|
||||||
|
m_thumSize->setValue(settings.value("settings/thumnailsize", THUMB_SIZE).toInt());
|
||||||
|
m_thumSize->setToolTip(tr("Thumbnail size in pixels"));
|
||||||
|
|
||||||
|
layout->addRow(tr("Image preload count"), m_preloadImages);
|
||||||
|
layout->addRow(tr("Thumbnails size"), m_thumSize);
|
||||||
|
layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart.")));
|
||||||
|
|
||||||
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
connect(this, &QDialog::accepted, this, &SettingsDialog::saveSettings);
|
||||||
|
|
||||||
|
layout->addRow(buttonBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::loadSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
THUMB_SIZE = settings.value("settings/thumbnailsize", THUMB_SIZE).toInt();
|
||||||
|
THUMB_SIZE_BORDER = THUMB_SIZE + 10;
|
||||||
|
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
|
||||||
|
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::saveSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("settings/thumbnailsize", m_thumSize->value());
|
||||||
|
settings.setValue("settings/preloadimagecount", m_preloadImages->value());
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef SETTINGSDIALOG_H
|
||||||
|
#define SETTINGSDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QSpinBox>
|
||||||
|
|
||||||
|
class SettingsDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||||
|
static void loadSettings();
|
||||||
|
private:
|
||||||
|
void saveSettings();
|
||||||
|
|
||||||
|
QSpinBox *m_preloadImages;
|
||||||
|
QSpinBox *m_thumSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSDIALOG_H
|
||||||
@@ -4,9 +4,17 @@ uniform sampler2D qt_Texture0;
|
|||||||
uniform vec3 mtf_param;
|
uniform vec3 mtf_param;
|
||||||
uniform bool bw;
|
uniform bool bw;
|
||||||
uniform bool invert;
|
uniform bool invert;
|
||||||
|
uniform bool srgb;
|
||||||
in vec2 qt_TexCoord0;
|
in vec2 qt_TexCoord0;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
|
vec3 Linear2sRGB(vec3 color)
|
||||||
|
{
|
||||||
|
return mix(12.92 * color.rgb,
|
||||||
|
1.055 * pow(color, vec3(1.0 / 2.4)) - 0.055,
|
||||||
|
greaterThan(color, vec3(0.0031308)));
|
||||||
|
}
|
||||||
|
|
||||||
vec4 MTF(vec4 x, vec3 m)
|
vec4 MTF(vec4 x, vec3 m)
|
||||||
{
|
{
|
||||||
x = (x - m.x) / (m.z - m.x);
|
x = (x - m.x) / (m.z - m.x);
|
||||||
@@ -14,6 +22,12 @@ vec4 MTF(vec4 x, vec3 m)
|
|||||||
return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y);
|
return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 checker()
|
||||||
|
{
|
||||||
|
vec2 pattern = fract(gl_FragCoord.xy * 0.0625) - 0.5;
|
||||||
|
return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
color = texture(qt_Texture0, qt_TexCoord0);
|
color = texture(qt_Texture0, qt_TexCoord0);
|
||||||
@@ -22,6 +36,12 @@ void main(void)
|
|||||||
|
|
||||||
if(invert)color = vec4(1.0) - color;
|
if(invert)color = vec4(1.0) - color;
|
||||||
|
|
||||||
|
color.rgb = mix(checker(), color.rgb, color.a);
|
||||||
|
|
||||||
|
if(srgb)color.rgb = Linear2sRGB(color.rgb);
|
||||||
|
|
||||||
if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0))))
|
if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0))))
|
||||||
color = vec4(0.0);
|
color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
color.a = 1.0;
|
||||||
}
|
}
|
||||||
@@ -18,4 +18,5 @@ void main(void)
|
|||||||
color = texture(qt_Texture0, qt_TexCoord0);
|
color = texture(qt_Texture0, qt_TexCoord0);
|
||||||
color = MTF(color, mtf_param);
|
color = MTF(color, mtf_param);
|
||||||
if(invert)color = vec4(1.0) - color;
|
if(invert)color = vec4(1.0) - color;
|
||||||
|
color.a = 1.0;
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,14 @@ out vec3 qt_TexCoord0;
|
|||||||
uniform ivec3 viewport_row;
|
uniform ivec3 viewport_row;
|
||||||
uniform mat4 mvp;
|
uniform mat4 mvp;
|
||||||
uniform vec2 offset;
|
uniform vec2 offset;
|
||||||
|
uniform ivec3 thumb_size;
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
vec2 pos = qt_Vertex * 0.5;
|
vec2 pos = qt_Vertex * 0.5;
|
||||||
pos.y *= -1.0;
|
pos.y *= -1.0;
|
||||||
pos = pos * imageSize_num.xy + 69;
|
pos = pos * imageSize_num.xy + thumb_size.x;
|
||||||
ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * ivec2(138, 158);
|
ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * thumb_size.yz;
|
||||||
|
|
||||||
gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0);
|
gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0);
|
||||||
qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1);
|
qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1);
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Exec=tenmon %U
|
Exec=tenmon %U
|
||||||
Icon=org.nou.tenmon
|
Icon=space.nouspiro.tenmon
|
||||||
Comment=FITS Image viewer
|
Comment=FITS Image viewer
|
||||||
Name=Tenmon
|
Name=Tenmon
|
||||||
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
|
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;Science;Astronomy
|
||||||
MimeType=image/fits;image/x-xisf;
|
MimeType=image/fits;image/x-xisf;
|
||||||
Terminal=false
|
Terminal=false
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop">
|
||||||
|
<id>space.nouspiro.tenmon</id>
|
||||||
|
<launchable type="desktop-id">space.nouspiro.tenmon.desktop</launchable>
|
||||||
|
<name>Tenmon</name>
|
||||||
|
<summary>FITS/XISF image viewer, converter, index and search</summary>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0</project_license>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
It is intended primarily for viewing astro photos and images. It supports the following formats:
|
||||||
|
<ul>
|
||||||
|
<li>FITS 8, 16 bit integer and 32 bit float</li>
|
||||||
|
<li>XISF 8, 16 bit integer and 32 bit float</li>
|
||||||
|
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Features:
|
||||||
|
<ul>
|
||||||
|
<li>Using same stretch function as PixInsight</li>
|
||||||
|
<li>OpenGL accelerated drawing</li>
|
||||||
|
<li>Index and search FITS XISF header data</li>
|
||||||
|
<li>Quick mark images and then copy/move marked files</li>
|
||||||
|
<li>Convert FITS <-> XISF</li>
|
||||||
|
<li>Convert FITS/XISF -> JPEG/PNG</li>
|
||||||
|
<li>Image statistics mean, media, min, max</li>
|
||||||
|
<li>Support for WCS</li>
|
||||||
|
<li>Thumbnails</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
<url type="homepage">https://nouspiro.space/?page_id=206</url>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>https://nouspiro.space/wp-content/uploads/2022/04/tenmon-1024x579.png</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<content_rating type="oars-1.1"/>
|
||||||
|
<releases>
|
||||||
|
<release version="20221209" date="2022-12-09"/>
|
||||||
|
<release version="20221126" date="2022-11-26"/>
|
||||||
|
<release version="20221121" date="2022-11-11"/>
|
||||||
|
<release version="20221023" date="2022-10-23"/>
|
||||||
|
</releases>
|
||||||
|
</component>
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
viewBox="0 0 33.866666 33.866668"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="space.nouspiro.tenmon.svg"
|
||||||
|
inkscape:export-filename="/home/nou/c++/tenmon/space.nouspiro.tenmon_128.png"
|
||||||
|
inkscape:export-xdpi="96.000008"
|
||||||
|
inkscape:export-ydpi="96.000008"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="128px"
|
||||||
|
inkscape:zoom="2.6547419"
|
||||||
|
inkscape:cx="54.430903"
|
||||||
|
inkscape:cy="78.162024"
|
||||||
|
inkscape:window-width="1862"
|
||||||
|
inkscape:window-height="1136"
|
||||||
|
inkscape:window-x="58"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Vrstva 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;stroke:#ffffff;stroke-width:0;stroke-linejoin:round"
|
||||||
|
id="rect1196"
|
||||||
|
width="33.866665"
|
||||||
|
height="33.866665"
|
||||||
|
x="5e-07"
|
||||||
|
y="5e-07" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:17.276px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.359917"
|
||||||
|
x="-0.41414261"
|
||||||
|
y="23.331123"
|
||||||
|
id="text8592"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan8590"
|
||||||
|
style="fill:#ffffff;stroke-width:0.359917"
|
||||||
|
x="-0.41414261"
|
||||||
|
y="23.331123">天文</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
#include "statusbar.h"
|
||||||
|
#include <QFontMetrics>
|
||||||
|
|
||||||
|
StatusBar::StatusBar(QWidget *parent) : QStatusBar(parent)
|
||||||
|
{
|
||||||
|
m_value = new QLabel(this);
|
||||||
|
m_pixelCoords = new QLabel(this);
|
||||||
|
m_celestianCoords = new QLabel(this);
|
||||||
|
|
||||||
|
m_value->setMinimumWidth(m_value->fontMetrics().horizontalAdvance("R:65536 G:65536 B:65536 "));
|
||||||
|
m_pixelCoords->setMinimumWidth(m_pixelCoords->fontMetrics().horizontalAdvance("X:65536 Y:65536 "));
|
||||||
|
m_celestianCoords->setMinimumWidth(m_celestianCoords->fontMetrics().horizontalAdvance("RA: 00h00m00s DEC: 00° 00' 00\" "));
|
||||||
|
addPermanentWidget(m_value);
|
||||||
|
addPermanentWidget(m_pixelCoords);
|
||||||
|
addPermanentWidget(m_celestianCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatusBar::newStatus(const QString &value, const QString &pixelCoords, const QString &celestialCoords)
|
||||||
|
{
|
||||||
|
m_value->setText(value);
|
||||||
|
m_pixelCoords->setText(pixelCoords);
|
||||||
|
m_celestianCoords->setText(celestialCoords);
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef STATUSBAR_H
|
||||||
|
#define STATUSBAR_H
|
||||||
|
|
||||||
|
#include <QStatusBar>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
class StatusBar : public QStatusBar
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QLabel *m_value;
|
||||||
|
QLabel *m_pixelCoords;
|
||||||
|
QLabel *m_celestianCoords;
|
||||||
|
public:
|
||||||
|
explicit StatusBar(QWidget *parent = nullptr);
|
||||||
|
public slots:
|
||||||
|
void newStatus(const QString &value, const QString &pixelCoords, const QString &celestialCoords);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATUSBAR_H
|
||||||
+6
-4
@@ -48,14 +48,16 @@ void StretchToolbar::stretchImage(Image *img)
|
|||||||
{
|
{
|
||||||
if(img->rawImage())
|
if(img->rawImage())
|
||||||
{
|
{
|
||||||
double median, mad;
|
double median, mad, max;
|
||||||
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, nullptr, &mad);
|
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, &max, &mad);
|
||||||
median /= img->rawImage()->norm();
|
median /= img->rawImage()->norm();
|
||||||
mad /= img->rawImage()->norm();
|
mad /= img->rawImage()->norm();
|
||||||
|
max /= img->rawImage()->norm();
|
||||||
|
if(max>1.0f)max = 1.0f;
|
||||||
float bp = median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA;
|
float bp = median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA;
|
||||||
float mid = MTF(median - bp, TARGET_BACKGROUND);
|
float mid = MTF(median - bp, TARGET_BACKGROUND);
|
||||||
m_stfSlider->setMTFParams(bp, mid, 1.0f);
|
m_stfSlider->setMTFParams(bp, mid, max);
|
||||||
emit paramChanged(m_stfSlider->blackPoint(), m_stfSlider->midPoint(), 1.0f);
|
emit paramChanged(m_stfSlider->blackPoint(), m_stfSlider->midPoint(), max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Exec=Tenmon %U
|
|
||||||
Icon=org.nou.tenmon
|
|
||||||
Comment=FITS Image viewer
|
|
||||||
Name=Tenmon
|
|
||||||
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
|
|
||||||
MimeType=image/fits;image/x-xisf;
|
|
||||||
Terminal=false
|
|
||||||
BIN
Binary file not shown.
Binary file not shown.
+104
-2
@@ -23,6 +23,17 @@
|
|||||||
<translation>Filter</translation>
|
<translation>Filter</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DatabaseTableView</name>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Mark</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Unmark</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>FITSFileModel</name>
|
<name>FITSFileModel</name>
|
||||||
<message>
|
<message>
|
||||||
@@ -60,6 +71,10 @@
|
|||||||
<source>Go up</source>
|
<source>Go up</source>
|
||||||
<translation>Go up</translation>
|
<translation>Go up</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show hidden files</source>
|
||||||
|
<translation>Show hidden files</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>HelpDialog</name>
|
<name>HelpDialog</name>
|
||||||
@@ -108,6 +123,18 @@
|
|||||||
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
<translation>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</translation>
|
<translation>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>L:%1</source>
|
||||||
|
<translation>L:%1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>X:%3 Y:%4</source>
|
||||||
|
<translation>X:%3 Y:%4</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>R:%1 G:%2 B:%3</source>
|
||||||
|
<translation>R:%1 G:%2 B:%3</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
@@ -269,7 +296,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Indexing FITS files</source>
|
<source>Indexing FITS files</source>
|
||||||
@@ -277,7 +304,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
<translation type="vanished">JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reindex files</source>
|
<source>Reindex files</source>
|
||||||
@@ -295,6 +322,54 @@
|
|||||||
<source>Star finder</source>
|
<source>Star finder</source>
|
||||||
<translation>Star finder</translation>
|
<translation>Star finder</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit</source>
|
||||||
|
<translation>Edit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS header editor</source>
|
||||||
|
<translation type="vanished">FITS header editor</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Settings</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (</source>
|
||||||
|
<translation>Images (</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
|
||||||
|
<translation>FITS image (*.fits *.fit);;XISF image (*.xisf);;</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy</source>
|
||||||
|
<translation>Failed to copy</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move</source>
|
||||||
|
<translation>Failed to move</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy %1 from to %2</source>
|
||||||
|
<translation type="vanished">Failed to copy from %1 to %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2</source>
|
||||||
|
<translation>Failed to move from %1 to %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy from %1 to %2</source>
|
||||||
|
<translation>Failed to copy from %1 to %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2ˇ</source>
|
||||||
|
<translation type="obsolete">Failed to move from %1 to %2ˇ {1 ?}</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MarkedFiles</name>
|
<name>MarkedFiles</name>
|
||||||
@@ -393,6 +468,33 @@
|
|||||||
<translation>Select columns</translation>
|
<translation>Select columns</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Settings</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>How many images are preloaded before and after current image.</source>
|
||||||
|
<translation>How many images are preloaded before and after current image.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnail size in pixels</source>
|
||||||
|
<translation>Thumbnail size in pixels</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image preload count</source>
|
||||||
|
<translation>Image preload count</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails size</source>
|
||||||
|
<translation>Thumbnails size</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Changes in settings will take effect after program restart.</source>
|
||||||
|
<translation>Changes in settings will take effect after program restart.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StretchToolbar</name>
|
<name>StretchToolbar</name>
|
||||||
<message>
|
<message>
|
||||||
|
|||||||
Binary file not shown.
+120
-18
@@ -23,6 +23,17 @@
|
|||||||
<translation>Filtre</translation>
|
<translation>Filtre</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DatabaseTableView</name>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Marquer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Décocher</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>FITSFileModel</name>
|
<name>FITSFileModel</name>
|
||||||
<message>
|
<message>
|
||||||
@@ -34,31 +45,35 @@
|
|||||||
<name>Filetree</name>
|
<name>Filetree</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Open</source>
|
<source>Open</source>
|
||||||
<translation type="unfinished">Ouvrir</translation>
|
<translation>Ouvrir</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy marked files</source>
|
<source>Copy marked files</source>
|
||||||
<translation type="unfinished">Copier les fichiers marqués</translation>
|
<translation>Copier les fichiers marqués</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Move marked files</source>
|
<source>Move marked files</source>
|
||||||
<translation type="unfinished">Déplace les fichiers marqués</translation>
|
<translation>Déplacer les fichiers marqués</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Index directory</source>
|
<source>Index directory</source>
|
||||||
<translation type="unfinished">Indexation du répertoire</translation>
|
<translation>Indexer le répertoire</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Set as root</source>
|
<source>Set as root</source>
|
||||||
<translation type="unfinished">Définir comme répertoire racine</translation>
|
<translation>Définir comme répertoire racine</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reset root</source>
|
<source>Reset root</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Réinitialiser la racine</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Go up</source>
|
<source>Go up</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Monter</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show hidden files</source>
|
||||||
|
<translation>Afficher les fichiers cachés</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -108,12 +123,24 @@
|
|||||||
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
<translation>Impossible d'initialiser le contexte OpenGL 3.3. Assurez-vous que le pilote GPU approprié est installé.</translation>
|
<translation>Impossible d'initialiser le contexte OpenGL 3.3. Assurez-vous que le pilote GPU approprié est installé.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>L:%1</source>
|
||||||
|
<translation>L:%1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>X:%3 Y:%4</source>
|
||||||
|
<translation>X:%3 Y:%4</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>R:%1 G:%2 B:%3</source>
|
||||||
|
<translation>R:%1 G:%2 B:%3</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Image info</source>
|
<source>Image info</source>
|
||||||
<translation>Information de l'image</translation>
|
<translation>Information sur l'image</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Can't open DB</source>
|
<source>Can't open DB</source>
|
||||||
@@ -185,7 +212,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select</source>
|
<source>Select</source>
|
||||||
<translation>Choisir</translation>
|
<translation>Sélectionner</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Mark</source>
|
<source>Mark</source>
|
||||||
@@ -217,7 +244,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Docks</source>
|
<source>Docks</source>
|
||||||
<translation>Encrage</translation>
|
<translation>Fenêtres encrables</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Open file</source>
|
<source>Open file</source>
|
||||||
@@ -237,11 +264,11 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Move marked files</source>
|
<source>Move marked files</source>
|
||||||
<translation>Déplace les fichiers marqués</translation>
|
<translation>Déplacer les fichiers marqués</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Index directory</source>
|
<source>Index directory</source>
|
||||||
<translation>Indexation du répertoire</translation>
|
<translation>Indexer le répertoire</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Thumbnails</source>
|
<source>Thumbnails</source>
|
||||||
@@ -269,7 +296,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Indexing FITS files</source>
|
<source>Indexing FITS files</source>
|
||||||
@@ -277,7 +304,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
<translation type="vanished">JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reindex files</source>
|
<source>Reindex files</source>
|
||||||
@@ -293,7 +320,55 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Star finder</source>
|
<source>Star finder</source>
|
||||||
<translation>Chercheur d'étoiles</translation>
|
<translation>Détecteur d'étoiles</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit</source>
|
||||||
|
<translation>Éditer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS header editor</source>
|
||||||
|
<translation type="vanished">Éditeur d'en-tête FITS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Réglages</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (</source>
|
||||||
|
<translation>Images (</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
|
||||||
|
<translation>FITS image (*.fits *.fit);;XISF image (*.xisf);;</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy</source>
|
||||||
|
<translation type="unfinished">Échec de la copie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move</source>
|
||||||
|
<translation type="unfinished">Échec du déplacement</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy %1 from to %2</source>
|
||||||
|
<translation type="obsolete">Échec de la copie de %1 vers %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2</source>
|
||||||
|
<translation type="unfinished">Échec du déplacement de %1 vers %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy from %1 to %2</source>
|
||||||
|
<translation type="unfinished">Échec de la copie de %1 vers %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2ˇ</source>
|
||||||
|
<translation type="obsolete">Échec du déplacement de %1 vers %2ˇ {1 ?}</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -393,15 +468,42 @@
|
|||||||
<translation>Choix des colonnes</translation>
|
<translation>Choix des colonnes</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Réglages</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>How many images are preloaded before and after current image.</source>
|
||||||
|
<translation>Combien d'images sont préchargées avant et après l'image courante.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnail size in pixels</source>
|
||||||
|
<translation>Taille des vignettes en pixels</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image preload count</source>
|
||||||
|
<translation>Nombre d'images préchargées</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails size</source>
|
||||||
|
<translation>Taille des vignette</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Changes in settings will take effect after program restart.</source>
|
||||||
|
<translation>Les changements de paramètres prendront effet après le redémarrage du programme.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StretchToolbar</name>
|
<name>StretchToolbar</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Stretch toolbar</source>
|
<source>Stretch toolbar</source>
|
||||||
<translation>Barre d'outils étirer</translation>
|
<translation>Réglage de la luminosité</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto Stretch F12</source>
|
<source>Auto Stretch F12</source>
|
||||||
<translation>Étirement automatique F12</translation>
|
<translation>Luminosité automatique F12</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reset Screen Transfer Function F11</source>
|
<source>Reset Screen Transfer Function F11</source>
|
||||||
@@ -417,7 +519,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Apply auto stretch on load</source>
|
<source>Apply auto stretch on load</source>
|
||||||
<translation>Appliquer l'étirement automatiquement au chargement</translation>
|
<translation>Appliquer la luminosité automatiquement au chargement</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
Binary file not shown.
+104
-2
@@ -24,6 +24,17 @@
|
|||||||
<translation>Filter</translation>
|
<translation>Filter</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DatabaseTableView</name>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Označiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Odznačiť</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>FITSFileModel</name>
|
<name>FITSFileModel</name>
|
||||||
<message>
|
<message>
|
||||||
@@ -61,6 +72,10 @@
|
|||||||
<source>Go up</source>
|
<source>Go up</source>
|
||||||
<translation>O úroveň vyššie</translation>
|
<translation>O úroveň vyššie</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show hidden files</source>
|
||||||
|
<translation>Zobraz skryté súbory</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>HelpDialog</name>
|
<name>HelpDialog</name>
|
||||||
@@ -109,6 +124,18 @@
|
|||||||
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
<translation>Nepodarilo sa incializovať OpenGL 3.3 context. Ubezpečte sa že sú nainštalované ovládače pre GPU.</translation>
|
<translation>Nepodarilo sa incializovať OpenGL 3.3 context. Ubezpečte sa že sú nainštalované ovládače pre GPU.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>L:%1</source>
|
||||||
|
<translation>L:%1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>X:%3 Y:%4</source>
|
||||||
|
<translation>X:%3 Y:%4</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>R:%1 G:%2 B:%3</source>
|
||||||
|
<translation>R:%1 G:%2 B:%3</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
@@ -282,7 +309,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
<translation>Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Indexing FITS files</source>
|
<source>Indexing FITS files</source>
|
||||||
@@ -290,7 +317,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
<translation type="vanished">JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reindex files</source>
|
<source>Reindex files</source>
|
||||||
@@ -308,6 +335,54 @@
|
|||||||
<source>Star finder</source>
|
<source>Star finder</source>
|
||||||
<translation>Vyhľadávač hviezd</translation>
|
<translation>Vyhľadávač hviezd</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit</source>
|
||||||
|
<translation>Upraviť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS header editor</source>
|
||||||
|
<translation type="vanished">Editor FITS hlavičky</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Nastavenia</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (</source>
|
||||||
|
<translation>Obrázky (</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
|
||||||
|
<translation>Obrázok FITS (*.fits *.fit);;Obrázok XISF (*.xisf);;</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy</source>
|
||||||
|
<translation>Zlyhalo kopírovanie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move</source>
|
||||||
|
<translation>Zlyhalo presúvanie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy %1 from to %2</source>
|
||||||
|
<translation type="vanished">Zlyhalo kopírovanie z %1 do %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2</source>
|
||||||
|
<translation>Zlyhalo presúvanie z %1 do %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to copy from %1 to %2</source>
|
||||||
|
<translation>Zlyhalo kopírovanie z %1 do %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to move from %1 to %2ˇ</source>
|
||||||
|
<translation type="obsolete">Zlyhalo presúvanie z %1 do %2ˇ {1 ?}</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MarkedFiles</name>
|
<name>MarkedFiles</name>
|
||||||
@@ -406,6 +481,33 @@
|
|||||||
<translation>Výber stĺpcov</translation>
|
<translation>Výber stĺpcov</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Nastavenia</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>How many images are preloaded before and after current image.</source>
|
||||||
|
<translation>Koľko obrázkov sa prednačíta pred a za aktuálnym obrázkom.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnail size in pixels</source>
|
||||||
|
<translation>Veľkosť náhľadu v pixeloch</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image preload count</source>
|
||||||
|
<translation>Počet prednačítaných obrázkov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails size</source>
|
||||||
|
<translation>Veľkosť náhľadu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Changes in settings will take effect after program restart.</source>
|
||||||
|
<translation>Zmeny v nastaveniach sa prejavia po reštarte programu.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StretchToolbar</name>
|
<name>StretchToolbar</name>
|
||||||
<message>
|
<message>
|
||||||
|
|||||||
Reference in New Issue
Block a user