Compare commits

...

43 Commits

Author SHA1 Message Date
nou 1a220bc3ed Update metainfo 2022-12-28 14:59:17 +01:00
nou f67539a3a1 Update translations 2022-12-28 14:51:45 +01:00
nou 468bcb5abb Add saturation statistic 2022-12-28 14:43:47 +01:00
nou fd49ba9a44 CSV export 2022-12-28 13:24:05 +01:00
nou 22e3b06fdd Fix potentional race conditions 2022-12-28 11:33:26 +01:00
nou 01febbf421 Fix invalid query when no column is selected 2022-12-28 11:17:36 +01:00
nou 7690496cf5 Load XISF::ReadImageProperties 2022-12-26 18:07:16 +01:00
nou 743a5f50c4 Disable manual mipmap generation on ATI cards 2022-12-26 10:19:45 +01:00
nou eac534352f Tidy up some debug outputs 2022-12-26 10:19:20 +01:00
nou 57bdc74ef6 Get rid of QRegExp 2022-12-26 10:13:24 +01:00
nou fd1fd7ff08 Get rid of fullscreen mode 2022-12-25 19:34:09 +01:00
nou c1aca3ca65 Add fine tune for STF slider 2022-12-22 12:45:47 +01:00
nou 5cc8fdd83d Simple grey world white balance 2022-12-20 22:27:15 +01:00
nou 66e13529be Allow change of preload without restart 2022-12-18 10:18:01 +01:00
nou e1bed8e1cb Allow change of thumbnail size without restart 2022-12-18 00:17:28 +01:00
nou 151f521688 Set image width and height for XISF 2022-12-17 09:48:35 +01:00
nou 926647e1a7 Don't do unecessary color profile conversion 2022-12-17 09:47:38 +01:00
nou 71fc1f2bd1 Release 20221215 2022-12-15 20:54:39 +01:00
nou f1ff04382b Add option to not use native file dialogs
Thanks to Patrick Chevalley for updated french tranlation
2022-12-15 20:36:19 +01:00
nou ad91adf1d9 Implement workaround for flatpak QFile::moveToTrash 2022-12-15 17:26:16 +01:00
nou b7369c2501 Remove unused variables in debayer shader 2022-12-15 00:25:00 +01:00
nou 79ed6b2059 Fix error in slovak translation 2022-12-15 00:10:52 +01:00
nou 380974a088 Allow saving debayered image 2022-12-15 00:03:29 +01:00
nou b1ad56ca1f Fix debayer shader 2022-12-15 00:03:14 +01:00
nou 58abef5a72 Update metainfo 2022-12-14 22:00:07 +01:00
nou 5427ff57cb Change superpixel tooltip 2022-12-14 21:58:24 +01:00
nou 6a2fa3f656 Update translation 2022-12-14 21:51:19 +01:00
nou c62ec7db8c Add move to trash action 2022-12-14 21:51:05 +01:00
nou d5eb0fdce5 Change key shortcuts 2022-12-14 21:50:30 +01:00
nou 6d25919e1f Add image formats to help 2022-12-14 21:50:05 +01:00
nou 26d1af6077 Add debayer 2022-12-14 21:18:25 +01:00
nou 44d8a8b856 Fix invert button 2022-12-14 17:42:13 +01:00
nou dab6c1f79d Add All files filter to open dialog 2022-12-14 17:16:32 +01:00
nou ce6a4cc40c Refresh dir when show hidden files is toggled 2022-12-13 21:31:23 +01:00
nou 0368c1f1dc Update metainfo xml 2022-12-10 07:48:17 +01:00
nou a1e98d818b Fix copy on btrfs 2022-12-09 19:58:15 +01:00
nou f3f194bcef Update translation and metainfo 2022-12-09 19:33:26 +01:00
nou efd36f96c3 Use native dialogs 2022-12-09 18:38:07 +01:00
nou 37923b37b3 Add error message for copy/move 2022-12-09 18:36:43 +01:00
nou 900453577e Fix includes 2022-11-28 17:36:37 +01:00
nou 34d466c3e0 Show checker pattern with transparent files 2022-11-28 17:32:23 +01:00
nou 9e98127084 Do gamma conversion manualy
Requesting sRGB capable framebuffer is unreliable
2022-11-28 17:31:50 +01:00
nou ba6062b925 Enale loading all image types that Qt can load 2022-11-27 21:10:43 +01:00
38 changed files with 867 additions and 192 deletions
+5 -1
View File
@@ -25,6 +25,7 @@ set(TENMON_SRC
about.cpp
database.cpp
databaseview.cpp
delete.cpp
filesystemwidget.cpp
imageinfo.cpp
imageringlist.cpp
@@ -63,6 +64,8 @@ elseif(APPLE)
else()
add_compile_definitions("__PCL_LINUX")
set(tenmon_ICON "")
find_package(PkgConfig REQUIRED)
pkg_search_module(GIO REQUIRED gio-2.0)
endif()
add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
@@ -76,13 +79,14 @@ elseif(APPLE)
target_link_directories(tenmon PRIVATE 3rdparty/lib/MacOS)
else()
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
target_include_directories(tenmon PRIVATE ${GIO_INCLUDE_DIRS})
endif()
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB})
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)
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib ${GIO_LDFLAGS})
endif(APPLE)
if(LIBRAW_STATIC)
+1 -1
View File
@@ -13,7 +13,7 @@ img { margin: 5px; }
<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 and PNG images</li>
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
<li>CR2, NEF, DNG raw images</li>
</ul>
</p>
+1 -1
View File
@@ -13,7 +13,7 @@ img { margin: 5px; }
<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 JPEG, PNG, BMP, GIF, PBM, PGM, PPM et SVG</li>
<li>images RAW CR2, NEF, DNG</li>
</ul>
</p>
+1 -1
View File
@@ -12,7 +12,7 @@ p { padding:0px; margin:5px 5px 10px 5px; }
<ul>
<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>JPEG a PNG obrázky</li>
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM a SVG obrázky</li>
<li>CR2, NEF, DNG raw obrázky</li>
</ul>
</p>
+4 -6
View File
@@ -125,7 +125,6 @@ QStringList Database::getMarkedFiles()
files << markedFiles.value("file").toString();
}
qDebug() << files.size();
return files;
}
@@ -226,7 +225,6 @@ QStringList Database::getFitsKeywords()
return keywords;
}
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
{
QFileInfoList files = dir.entryInfoList(nameFilters, QDir::Files);
@@ -288,7 +286,7 @@ bool Database::indexFile(const QFileInfo &file)
m_insertFileWcs.bindValue(7, crVal2);
if(!m_insertFileWcs.exec())
{
qDebug() << m_insertFileWcs.lastError();
qDebug() << "Database error" << m_insertFileWcs.lastError();
return false;
}
last_id = m_insertFileWcs.lastInsertId().toLongLong();
@@ -299,7 +297,7 @@ bool Database::indexFile(const QFileInfo &file)
m_insertFile.bindValue(1, mtime);
if(!m_insertFile.exec())
{
qDebug() << m_insertFile.lastError();
qDebug() << "Database error" << m_insertFile.lastError();
return false;
}
last_id = m_insertFile.lastInsertId().toLongLong();
@@ -319,10 +317,10 @@ bool Database::indexFile(const QFileInfo &file)
m_insertFitsHeader.bindValue(3, comments);
if(!m_insertFitsHeader.execBatch())
{
qDebug() << m_insertFitsHeader.lastError();
qDebug() << "Database error" << m_insertFitsHeader.lastError();
return false;
}
}
qDebug() << filePath << last_id;
qDebug() << "Indexed" << filePath << last_id;
return true;
}
+40 -5
View File
@@ -8,7 +8,7 @@
#include <QDebug>
#include <QMenu>
#include <QContextMenuEvent>
#include <QRegExp>
#include <QRegularExpression>
#include <iostream>
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
@@ -20,7 +20,7 @@ double RA(const QString &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;
qDebug() << "RA" << match.capturedTexts() << h << m << s;
return h*15 + m*0.25 + s*15/3600;
}
@@ -32,7 +32,7 @@ double DEC(const QString &dec)
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;
qDebug() << "DEC" << match.capturedTexts() << sign << d << m << s;
return sign * (d + m/60 + s/3600);
}
@@ -157,7 +157,7 @@ void FITSFileModel::prepareQuery()
QString cols;
QString join;
QStringList where;
QString sql = "SELECT f.file,";
QString sql = m_columns.size() ? "SELECT f.file," : "SELECT f.file";
for(int i=0; i<m_value.size(); i++)
{
if(m_key[i] == "file")
@@ -195,7 +195,7 @@ void FITSFileModel::prepareQuery()
}
std::cout << sql.toStdString() << std::endl;
if(lastError().type() != QSqlError::NoError)
qDebug() << lastError();
qDebug() << "Database error" << lastError();
m_markedFiles = m_database->getMarkedFiles().toSet();
}
@@ -362,3 +362,38 @@ void DataBaseView::applyFilter()
}
m_model->setFilter(keys, values, limits);
}
bool DataBaseView::exportCSV(const QString &path)
{
QFile csv(path);
if(!csv.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QSqlQuery sql = m_model->query();
int colCount = m_model->columnCount();
QStringList header;
for(int i=0; i<colCount; i++)
header.append(m_model->headerData(i, Qt::Horizontal).toString());
csv.write(header.join(",").toUtf8());
csv.write("\n");
while(sql.next())
{
QStringList columns;
for(int i=0; i<colCount; i++)
{
QString val = sql.value(i).toString();
val.replace("\"", "\"\"");
if(val.contains('"') || val.contains(','))
{
val.prepend('"');
val.append('"');
}
columns.append(val);
}
csv.write(columns.join(",").toUtf8());
csv.write("\n");
}
return true;
}
+1
View File
@@ -71,6 +71,7 @@ public slots:
void loadDatabase();
void itemActivated(const QModelIndex &index);
void applyFilter();
bool exportCSV(const QString &path);
signals:
void loadFile(QString file);
};
+30
View File
@@ -0,0 +1,30 @@
#ifdef __linux__
#define QT_NO_KEYWORDS
#include <QString>
#include <iostream>
#include <gio/gio.h>
//flatpak bug prevent to use QFile::moveToTrash
bool moveToTrash(const QString &path)
{
GFile *gfile = g_file_new_for_path(path.toLocal8Bit().data());
GError *error = nullptr;
g_file_trash(gfile, nullptr, &error);
if(error)std::cerr << "failed to trash file " << error->code << " " << error->message << std::endl;
g_clear_error(&error);
g_object_unref(gfile);
return true;
}
#else
#include <QFile>
#include <QString>
bool moveToTrash(const QString &path)
{
return QFile::moveToTrash(path);
}
#endif
+1
View File
@@ -127,6 +127,7 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
auto filter = m_fileSystemModel->filter();
filter ^= QDir::Hidden;
m_fileSystemModel->setFilter(filter);
m_fileSystemModel->setRootPath(m_rootDir);
}
}
+33
View File
@@ -5,6 +5,7 @@
#include <wcslib/wcshdr.h>
#include <wcslib/wcsfix.h>
#include "pcl/FITSHeaderKeyword.h"
#include "pcl/Property.h"
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
@@ -43,6 +44,38 @@ FITSRecord::FITSRecord(const pcl::FITSHeaderKeyword &record)
value = string;
}
FITSRecord::FITSRecord(const pcl::Property &property)
{
key = property.Identifier().c_str();
const pcl::Variant &pclval = property.Value();
switch(pclval.Type())
{
case pcl::VariantType::Bool:
value = pclval.ToBool();
break;
case pcl::VariantType::Int8:
case pcl::VariantType::Int16:
case pcl::VariantType::Int32:
case pcl::VariantType::Int64:
value = pclval.ToInt64();
break;
case pcl::VariantType::UInt8:
case pcl::VariantType::UInt16:
case pcl::VariantType::UInt32:
case pcl::VariantType::UInt64:
value = pclval.ToUInt64();
break;
case pcl::VariantType::Float:
case pcl::VariantType::Double:
value = pclval.ToDouble();
break;
default:
value = pclval.ToIsoString().c_str();
break;
}
}
QByteArray FITSRecord::valueToByteArray() const
{
if(value.type() == QVariant::Bool)
+2 -1
View File
@@ -6,7 +6,7 @@
#include <cmath>
#include <memory>
namespace pcl { class FITSHeaderKeyword; }
namespace pcl { class FITSHeaderKeyword; class Property; }
struct FITSRecord
{
@@ -17,6 +17,7 @@ struct FITSRecord
FITSRecord(){}
FITSRecord(const QByteArray &key, const QVariant &value, const QByteArray &comment);
FITSRecord(const pcl::FITSHeaderKeyword &record);
FITSRecord(const pcl::Property &property);
QByteArray valueToByteArray() const;
};
+44 -5
View File
@@ -76,6 +76,11 @@ int Image::number() const
return m_number;
}
void Image::clearThumbnail()
{
m_thumbnail.reset();
}
void Image::imageLoaded(void *rawImage, ImageInfoData info)
{
m_loading = false;
@@ -98,12 +103,14 @@ void Image::thumbnailLoadFinish(void *rawImage)
emit thumbnailLoaded(this);
}
ImageRingList::ImageRingList(Database *database, QObject *parent) : QAbstractItemModel(parent)
ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent) : QAbstractItemModel(parent)
, m_liveMode(false)
, m_analyzeLevel(None)
, m_database(database)
, m_nameFilter(nameFilter)
{
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
m_thumbPool = new QThreadPool(this);
}
@@ -122,10 +129,7 @@ bool ImageRingList::setDir(const QString path, const QString &currentFile)
if(dir.exists())
{
QStringList nameFilter;
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.nef" << "*.dng" << "*.fit" << "*.fits" << "*.xisf";
QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
QStringList list = dir.entryList(m_nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
QStringList absolutePaths;
foreach(const QString &file, list)
{
@@ -285,6 +289,12 @@ void ImageRingList::updateMark()
}
}
void ImageRingList::clearThumbnails()
{
for(auto &img : m_images)
img->clearThumbnail();
}
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column, m_images.at(row).get());
@@ -338,6 +348,35 @@ QVariant ImageRingList::headerData(int section, Qt::Orientation orientation, int
return QVariant();
}
void ImageRingList::setPreload(int width)
{
DEFAULT_WIDTH = width;
if(m_images.size() == 0)return;
int newWidth = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
if(newWidth > m_width)
{
for(int i = newWidth - m_width; i>0; i--)
{
m_firstImage = decrement(m_firstImage);
(*m_firstImage)->load();
m_lastImage = increment(m_lastImage);
(*m_lastImage)->load();
}
}
if(newWidth < m_width)
{
for(int i = m_width - newWidth; i>0; i--)
{
(*m_firstImage)->release();
m_firstImage = increment(m_firstImage);
(*m_lastImage)->release();
m_lastImage = decrement(m_lastImage);
}
}
m_width = newWidth;
}
void ImageRingList::setFiles(const QStringList files, const QString &currentFile)
{
QThreadPool::globalInstance()->clear();
+6 -1
View File
@@ -35,6 +35,7 @@ public:
ImageInfoData info() const;
bool isCurrent() const;
int number() const;
void clearThumbnail();
signals:
void pixmapLoaded(Image *ptr);
void thumbnailLoaded(Image *ptr);
@@ -60,8 +61,9 @@ class ImageRingList : public QAbstractItemModel
AnalyzeLevel m_analyzeLevel;
QThreadPool *m_thumbPool;
Database *m_database;
QStringList m_nameFilter;
public:
explicit ImageRingList(Database *database, QObject *parent = 0);
explicit ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent = 0);
~ImageRingList() override;
bool setDir(const QString path, const QString &currentFile = QString());
void setFile(const QString &file);
@@ -79,6 +81,7 @@ public:
int imageCount() const;
QStringList imageNames() const;
void updateMark();
void clearThumbnails();
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
@@ -86,6 +89,8 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
public slots:
void setPreload(int width);
protected:
void setFiles(const QStringList files, const QString &currentFile = QString());
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
+81 -18
View File
@@ -27,10 +27,10 @@ const RawImageType rawImageTypes[] = {
{QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true},
#ifdef COLOR_MANAGMENT
{QOpenGLTexture::RGB, QOpenGLTexture::SRGB8, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::SRGB8, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::SRGB8_Alpha8, QOpenGLTexture::UInt8, false},
#else
{QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::RGBA8_UNorm, QOpenGLTexture::UInt8, false},
#endif
{QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
{QOpenGLTexture::RGBA, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
@@ -87,9 +87,6 @@ ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(pa
});
setMouseTracking(true);
#ifdef COLOR_MANAGMENT
setTextureFormat(GL_SRGB8_ALPHA8);
#endif
}
ImageWidget::~ImageWidget()
@@ -100,13 +97,15 @@ ImageWidget::~ImageWidget()
void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
{
if(image == nullptr)return;
makeCurrent();
m_rawImage = image;
m_imgWidth = image->width();
m_imgHeight = image->height();
m_currentImg = index;
if(!m_image)return;
const RawImageType &rawImageType = rawImageTypes[image->type()];
m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8 || rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8;
m_bwImg = rawImageType.bw;
@@ -156,7 +155,11 @@ void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
}
else m_image->generateMipMaps();
m_image->setLevelOfDetailRange(m_superpixel ? 1 : 0, m_image->mipMaxLevel());
if(m_debayerTex)
{
f->glDeleteTextures(1, &m_debayerTex);
m_debayerTex = 0;
}
update();
}
@@ -215,7 +218,6 @@ void ImageWidget::setOffset(int dx, int dy)
void ImageWidget::superPixel(bool enable)
{
m_superpixel = enable;
m_image->setLevelOfDetailRange(enable ? 1 : 0, m_image->mipMaxLevel());
update();
}
@@ -239,7 +241,10 @@ QImage ImageWidget::renderToImage()
m_program->setUniformValue("offset", 0.0f, 0.0f);
m_program->setUniformValue("zoom", 1.0f);
m_image->bind(0);
if(m_superpixel && m_debayerTex)
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
else
m_image->bind(0);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
fbo.bindDefault();
@@ -335,22 +340,27 @@ void ImageWidget::paintGL()
}
else
{
#ifdef COLOR_MANAGMENT
if(m_srgb)f->glEnable(GL_FRAMEBUFFER_SRGB);
#endif
debayer();
m_vao->bind();
m_image->bind(0);
if(m_superpixel && m_debayerTex)
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
else
m_image->bind(0);
m_program->bind();
m_program->setUniformValue("viewport", (float)width(), (float)height());
m_program->setUniformValue("offset", dx, dy);
m_program->setUniformValue("mtf_param", m_low, m_mid, m_high);
m_program->setUniformValue("zoom", 1.0f/m_scale);
m_program->setUniformValue("bw", m_bwImg);
m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
m_program->setUniformValue("invert", m_invert);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if(m_superpixel)m_program->setUniformValue("whiteBalance", m_whiteBalance[0], m_whiteBalance[1], m_whiteBalance[2]);
else m_program->setUniformValue("whiteBalance", 1.0f, 1.0f, 1.0f);
#ifdef COLOR_MANAGMENT
if(m_srgb)f->glDisable(GL_FRAMEBUFFER_SRGB);
m_program->setUniformValue("srgb", m_srgb);
#endif
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
@@ -389,7 +399,7 @@ void ImageWidget::initializeGL()
qDebug() << (char*)f->glGetString(GL_RENDERER);
qDebug() << (char*)f->glGetString(GL_VERSION);
MANUAL_MIPMAP_GEN = QString((const char*)f->glGetString(GL_VENDOR)).startsWith("ATI Technologies Inc", Qt::CaseInsensitive);
//MANUAL_MIPMAP_GEN = QString((const char*)f->glGetString(GL_VENDOR)).startsWith("ATI Technologies Inc", Qt::CaseInsensitive);
qDebug() << context()->format();
@@ -422,12 +432,27 @@ void ImageWidget::initializeGL()
m_program->setUniformValue("qt_Texture0", (GLuint)0);
m_program->setUniformValue("scale", 1.0f, 0.0f);
m_debayerProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/debayer.vert");
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/debayer.frag");
if(f3)f3->glBindFragDataLocation(m_debayerProgram->programId(), 0, "color");
m_debayerProgram->bind();
m_debayerProgram->enableAttributeArray("qt_Vertex");
m_debayerProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
m_debayerProgram->enableAttributeArray("qt_MultiTexCoord0");
m_debayerProgram->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);
m_debayerProgram->setUniformValue("qt_Texture0", (GLuint)0);
if(!m_debayerProgram->link())
{
qDebug() << "Link failed" << m_debayerProgram->log();
}
m_vaoThumb->bind();
m_thumbnailProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/thumb.vert");
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag");
if(f3)f3->glBindFragDataLocation(m_program->programId(), 0, "color");
if(f3)f3->glBindFragDataLocation(m_thumbnailProgram->programId(), 0, "color");
m_thumbnailProgram->bind();
m_thumbnailProgram->enableAttributeArray("qt_Vertex");
m_thumbnailProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
@@ -466,6 +491,9 @@ void ImageWidget::initializeGL()
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
m_transferOptions->setAlignment(1);
if(m_rawImage)
setImage(m_rawImage, m_currentImg);
}
void ImageWidget::dragEnterEvent(QDragEnterEvent *event)
@@ -601,6 +629,41 @@ void ImageWidget::thumbSelect(QMouseEvent *event)
}
}
void ImageWidget::debayer()
{
if(m_debayerTex > 0 || !m_superpixel || !m_bwImg || m_imgWidth < 0)return;
QOpenGLFramebufferObject fbo(m_imgWidth, m_imgHeight, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA16);
fbo.bind();
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
m_debayerProgram->bind();
m_image->bind(0);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
fbo.release();
f->glViewport(0, 0, m_width, m_height);
m_debayerTex = fbo.takeTexture();
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
f->glGenerateMipmap(GL_TEXTURE_2D);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
int size = std::max(m_imgWidth, m_imgHeight);
int level = 0;
while(size >>= 1)level++;
int w,h;
f3->glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &w);
f3->glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &h);
uint16_t pixel[w*h*4];
f3->glGetTexImage(GL_TEXTURE_2D, level, GL_RGBA, GL_UNSIGNED_SHORT, pixel);
float maxRGB = std::max(std::max(pixel[0], pixel[1]), pixel[2]);
m_whiteBalance[0] = maxRGB / pixel[0];
m_whiteBalance[1] = maxRGB / pixel[1];
m_whiteBalance[2] = maxRGB / pixel[2];
}
ImageScrollAreaGL::ImageScrollAreaGL(Database *database, QWidget *parent) : QWidget(parent)
{
QGridLayout *layout = new QGridLayout(this);
+4
View File
@@ -32,6 +32,7 @@ class ImageWidget : public QOpenGLWidget
QTimer *m_updateTimer;
std::unique_ptr<QOpenGLShaderProgram> m_program;
std::unique_ptr<QOpenGLShaderProgram> m_thumbnailProgram;
std::unique_ptr<QOpenGLShaderProgram> m_debayerProgram;
std::unique_ptr<QOpenGLBuffer> m_buffer;
std::unique_ptr<QOpenGLBuffer> m_bufferSizes;
std::unique_ptr<QOpenGLTexture> m_image;
@@ -39,6 +40,7 @@ class ImageWidget : public QOpenGLWidget
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
GLuint m_debayerTex = 0;
std::shared_ptr<RawImage> m_rawImage;
std::shared_ptr<WCSData> m_wcs;
int m_width, m_height;
@@ -50,6 +52,7 @@ class ImageWidget : public QOpenGLWidget
float m_range;
float m_dx, m_dy;
float m_scale;
float m_whiteBalance[3];
bool m_blockRepaint;
bool m_bwImg;
bool m_invert;
@@ -88,6 +91,7 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void thumbSelect(QMouseEvent *event);
void debayer();
signals:
void fileDropped(const QString &path);
void status(const QString &value, const QString &pixelCoords, const QString &celestialCoords);
+26 -8
View File
@@ -280,7 +280,7 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image)
char err[100];
fits_get_errstatus(status, err);
info.info.append({QObject::tr("Error"), QString(err)});
qDebug() << err;
qDebug() << "Failed to load FITS file" << err;
}
return true;
@@ -297,7 +297,7 @@ bool loadPCLImage(pcl::XISFReader &xisf, RawImage **image)
#ifdef COLOR_MANAGMENT
pcl::ICCProfile iccProfile = xisf.ReadICCProfile();
if(iccProfile.IsProfile())
if(iccProfile.IsProfile() && iccProfile != sRgbIccProfile)
{
pcl::ICCProfileTransformation iccTran;
iccTran.DisableParallelProcessing();
@@ -349,7 +349,15 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
{
info.fitsHeader.append(fits);
}
auto imageproperties = xisf.ReadImageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSData>(xisf.ImageInfo().width, xisf.ImageInfo().height, info.fitsHeader);
info.info.append({QObject::tr("Width"), QString::number(xisf.ImageInfo().width)});
info.info.append({QObject::tr("Height"), QString::number(xisf.ImageInfo().height)});
if(!info.wcs->valid())info.wcs.reset();
if(floatType && bps == 32)
@@ -368,7 +376,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
catch (pcl::Error err)
{
info.info.append(QPair<QString, QString>("Error", err.FormatInfo().ToUTF8().c_str()));
qDebug() << err.FormatInfo().ToUTF8().c_str();
qDebug() << "Failed to load XISF" << err.FormatInfo().ToUTF8().c_str();
}
info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")});
return false;
@@ -387,26 +395,28 @@ void LoadRunable::run()
RawImage *rawImage = nullptr;
bool raw = false;
timer.start();
if(m_file.endsWith(".CR2", Qt::CaseInsensitive) || m_file.endsWith(".NEF", Qt::CaseInsensitive) || m_file.endsWith(".DNG", Qt::CaseInsensitive))
{
timer.start();
loadRAW(m_file, info, &rawImage);
raw = true;
qDebug() << "LoadRaw" << timer.elapsed();
qDebug() << "LoadRAW" << timer.elapsed();
}
else if(m_file.endsWith(".FIT", Qt::CaseInsensitive) || m_file.endsWith(".FITS", Qt::CaseInsensitive))
{
loadFITS(m_file, info, &rawImage);
qDebug() << "LoadFITS" << timer.elapsed();
}
else if(m_file.endsWith(".XISF", Qt::CaseInsensitive))
{
loadXISF(m_file, info, &rawImage);
qDebug() << "LoadXISF" << timer.elapsed();
}
else
{
QImage img(m_file);
#ifdef COLOR_MANAGMENT
if(img.colorSpace().isValid())
if(img.colorSpace().isValid() && img.colorSpace() != QColorSpace::SRgb)
img.convertToColorSpace(QColorSpace::SRgb);
#endif
@@ -426,8 +436,9 @@ void LoadRunable::run()
{
double mean, median, min, max, mad;
double stdDev;
uint32_t saturated;
timer.start();
rawImage->imageStats(&mean, &stdDev, &median, &min, &max, &mad);
rawImage->imageStats(&mean, &stdDev, &median, &min, &max, &mad, &saturated);
qDebug() << "image stats" << timer.restart();
info.info.append({QObject::tr("Mean"), QString::number(mean)});
info.info.append({QObject::tr("Standart deviation"), QString::number(stdDev)});
@@ -435,6 +446,7 @@ void LoadRunable::run()
info.info.append({QObject::tr("Minimum"), QString::number(min)});
info.info.append({QObject::tr("Maximum"), QString::number(max)});
info.info.append({QObject::tr("MAD"), QString::number(mad)});
info.info.append({QObject::tr("Saturated"), QString::number(100.0 * saturated / rawImage->size()) + "%"});
if(m_analyzeLevel >= Peaks)
{
@@ -452,7 +464,7 @@ void LoadRunable::run()
// drawPeaks(img, peaks);
qDebug() << "draw peaks" << timer.restart();
info.info.append({QObject::tr("Peaks"), QString::number(numPeaks)});
info.info.append({QObject::tr("Peaks draw"), QString::number(peaks.size())});
//info.info.append({QObject::tr("Peaks draw"), QString::number(peaks.size())});
if(m_analyzeLevel>= Stars)
{
@@ -530,6 +542,12 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
{
info.fitsHeader.append(fits);
}
auto imageproperties = xisf.ReadImageProperties();
for(auto prop : imageproperties)
{
info.fitsHeader.append(prop);
}
info.wcs = std::make_shared<WCSData>(xisf.ImageInfo().width, xisf.ImageInfo().height, info.fitsHeader);
if(!info.wcs->valid())info.wcs.reset();
}
-3
View File
@@ -15,9 +15,6 @@ int main(int argc, char *argv[])
format.setMinorVersion(3);
format.setOption(QSurfaceFormat::DebugContext);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
#ifdef COLOR_MANAGMENT
format.setColorSpace(QSurfaceFormat::sRGBColorSpace);
#endif
QSurfaceFormat::setDefaultFormat(format);
QApplication a(argc, argv);
+88 -27
View File
@@ -16,6 +16,8 @@
#include <QGuiApplication>
#include <QThreadPool>
#include <QStatusBar>
#include <QImageReader>
#include <QMimeDatabase>
#include "loadrunable.h"
#include "markedfiles.h"
#include "about.h"
@@ -28,6 +30,8 @@
#include <sys/socket.h>
#endif
bool moveToTrash(const QString &path);
int MainWindow::socketPair[2] = {0, 0};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
@@ -37,6 +41,25 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
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)");
_openFilter.append(tr(";;All files (*)"));
nameFilter.append({"fit", "fits", "xisf", "cr2", "nef", "dng"});
m_info = new ImageInfo(this);
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
infoDock->setWidget(m_info);
@@ -62,7 +85,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
m_ringList = new ImageRingList(m_database, this);
m_ringList = new ImageRingList(m_database, nameFilter, this);
m_filesystem = new FilesystemWidget(m_ringList, this);
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
@@ -108,11 +131,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence::Open);
fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence::Save);
fileMenu->addSeparator();
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()));
fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked()));
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()), Qt::Key_F5);
fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked()), Qt::Key_F6);
fileMenu->addAction(tr("Move marked files to trash"), this, &MainWindow::deleteMarked, QKeySequence::Delete);
fileMenu->addSeparator();
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV);
fileMenu->addSeparator();
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
liveModeAction->setCheckable(true);
@@ -129,8 +154,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
viewMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), Qt::CTRL + Qt::Key_F11);
QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){
if(SettingsDialog::loadThumbsizes())m_ringList->clearThumbnails();
m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageNames());
m_imageGL->imageWidget()->showThumbnail(checked);
m_imageGL->setThumbnails(checked ? m_ringList->imageCount() : 0);
@@ -141,7 +166,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
menuBar()->addMenu(viewMenu);
QMenu *selectMenu = new QMenu(tr("Select"), this);
selectMenu->addAction(tr("Mark"), this, SLOT(markImage()), Qt::Key_F5);
selectMenu->addAction(tr("Mark"), this, SLOT(markImage()), Qt::Key_F7);
selectMenu->addAction(tr("Unmark"), this, SLOT(unmarkImage()), Qt::Key_F8);
selectMenu->addSeparator();
selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M);
@@ -296,7 +321,7 @@ void MainWindow::copyOrMove(bool copy)
QString dest = QFileDialog::getExistingDirectory(this,
tr("Select destination"),
_lastDir,
QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog);
QFileDialog::ShowDirsOnly);
copyOrMove(copy, dest);
}
@@ -310,8 +335,9 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this);
progress.setWindowModality(Qt::WindowModal);
progress.show();
foreach(const QString &file, files)
for(const QString &file : files)
{
bool result = false;
QFileInfo info(file);
QFile srcFile(file);
QFile dstFile(dir.absoluteFilePath(info.fileName()));
@@ -320,7 +346,7 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
continue;
if(progress.wasCanceled())
break;
return;
#ifdef __linux__
if(copy)
{
@@ -330,20 +356,29 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
{
dstFile.remove();
dstFile.close();
qDebug() << dstFile.fileName();
srcFile.copy(dstFile.fileName());
result = srcFile.copy(dstFile.fileName());
}
else result = true;
}
else
{
srcFile.rename(dstFile.fileName());
result = srcFile.rename(dstFile.fileName());
}
#else
if(copy)
srcFile.copy(dstFile.fileName());
result = srcFile.copy(dstFile.fileName());
else
srcFile.rename(dstFile.fileName());
result = srcFile.rename(dstFile.fileName());
#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);
if(button == QMessageBox::Abort)return;
}
progress.setValue(i++);
}
m_database->clearMarkedFiles();
@@ -372,9 +407,7 @@ void MainWindow::loadFile()
QString file = QFileDialog::getOpenFileName(this,
tr("Open file"),
_lastDir,
tr("Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)"),
nullptr,
QFileDialog::DontUseNativeDialog);
_openFilter);
loadFile(file);
}
@@ -401,7 +434,7 @@ void MainWindow::loadFile(int row)
void MainWindow::indexDir()
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir, QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog);
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir, QFileDialog::ShowDirsOnly);
indexDir(dir);
}
@@ -428,7 +461,7 @@ void MainWindow::saveAs()
QString file = QFileDialog::getSaveFileName(this,
tr("Save as"),
_lastDir,
tr("JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)"),
_saveFilter,
&selectedFilter);
auto filterToFormat = [](const QString &file, const QString &filter) -> const char*
{
@@ -522,18 +555,32 @@ void MainWindow::moveMarked()
copyOrMove(false);
}
void MainWindow::toggleFullScreen()
void MainWindow::deleteMarked()
{
if(isFullScreen())
QStringList files = m_database->getMarkedFiles();
if(QMessageBox::question(this, tr("Move files to trash?"), tr("Do you want to move %1 files to trash?").arg(files.size())) != QMessageBox::Yes)
return;
QProgressDialog progress(tr("Moving marked files to trash"), tr("Cancel"), 0, files.size(), this);
progress.setWindowModality(Qt::WindowModal);
progress.show();
int i = 0;
for(const QString &file : files)
{
showNormal();
if(_maximized)showMaximized();
}
else
{
_maximized = isMaximized();
showFullScreen();
if(!QFile::exists(file))
continue;
if(progress.wasCanceled())
return;
if(!moveToTrash(file))
{
QMessageBox::warning(this, tr("Failed to move file to trash"), tr("Failed to move file to trash %1").arg(file));
return;
}
progress.setValue(i++);
}
m_database->clearMarkedFiles();
}
void MainWindow::liveMode(bool active)
@@ -565,9 +612,23 @@ void MainWindow::showMarkFilesDialog()
void MainWindow::showSettingsDialog()
{
SettingsDialog settingsDialog(this);
connect(&settingsDialog, &SettingsDialog::preloadChanged, m_ringList, &ImageRingList::setPreload);
settingsDialog.exec();
}
void MainWindow::exportCSV()
{
QStringList documentDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
if(documentDir.empty())documentDir.append("");
QString file = QFileDialog::getSaveFileName(this,
tr("Save as"),
documentDir.first(),
tr("CSV file (*.csv)"));
if(!file.isEmpty())
m_databaseView->exportCSV(file);
}
void MainWindow::updateWindowTitle()
{
ImagePtr ptr = m_ringList->currentImage();
+4 -1
View File
@@ -28,6 +28,8 @@ class MainWindow : public QMainWindow
QSocketNotifier *socketNotifier;
QString _lastDir;
bool _maximized;
QString _openFilter;
QString _saveFilter;
public:
MainWindow(QWidget *parent = 0);
~MainWindow() override;
@@ -57,13 +59,14 @@ protected slots:
void unmarkAndNext();
void copyMarked();
void moveMarked();
void toggleFullScreen();
void deleteMarked();
void liveMode(bool active);
void imageStats(bool imageStats);
void peakFinder(bool findPeaks);
void starFinder(bool findStars);
void showMarkFilesDialog();
void showSettingsDialog();
void exportCSV();
};
#endif // MAINWINDOW_H
+10 -1
View File
@@ -3,6 +3,7 @@
int THUMB_SIZE = 128;
int THUMB_SIZE_BORDER = 138;
int THUMB_SIZE_BORDER_Y = 158;
double SATURATION = 0.95;
RawImage::ImgType CV2Type(int cvtype)
{
@@ -84,6 +85,7 @@ RawImage::RawImage(const RawImage &d)
m_max = d.m_max;
m_mad = d.m_mad;
m_stats = d.m_stats;
m_saturated = d.m_saturated;
}
RawImage::RawImage(const QImage &img)
@@ -126,7 +128,7 @@ RawImage::RawImage(const QImage &img)
m_stats = false;
}
bool RawImage::imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad)
bool RawImage::imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad, uint32_t *saturated)
{
if(!m_stats)calcStats();
if(mean)*mean = m_mean;
@@ -135,6 +137,7 @@ bool RawImage::imageStats(double *mean, double *stdDev, double *median, double *
if(min)*min = m_min;
if(max)*max = m_max;
if(mad)*mad = m_mad;
if(saturated)*saturated = m_saturated;
return true;
}
@@ -176,6 +179,12 @@ void RawImage::calcStats()
}
}
if(img.type() == CV_32F)m_median /= histSize;
int threshold = SATURATION * histSize;
m_saturated = 0;
for(int i = histSize-1; i >= threshold; i--)
m_saturated += hist.at<float>(0, i);
cv::Mat absDev;
img.convertTo(absDev, CV_32F, 1, -m_median);
absDev = cv::abs(absDev);
+2 -1
View File
@@ -48,6 +48,7 @@ protected:
double m_max;
double m_mad;
float m_thumbAspect;
uint32_t m_saturated;
public:
enum ImgType
{
@@ -66,7 +67,7 @@ public:
RawImage(cv::Mat &img);
RawImage(const RawImage &d);
RawImage(const QImage &img);
bool imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad);
bool imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad, uint32_t *saturated);
void calcStats();
void rect(int &x, int &y, int w, int h, std::vector<double> &r) const;
int findPeaks(double background, double distance, std::vector<Peak> &peaks) const;
+2
View File
@@ -16,6 +16,8 @@
<file>shaders/image.vert</file>
<file>shaders/thumb.frag</file>
<file>shaders/thumb.vert</file>
<file>shaders/debayer.frag</file>
<file>shaders/debayer.vert</file>
</qresource>
<qresource lang="en" prefix="/">
<file alias="help">about/help_en</file>
+33 -1
View File
@@ -3,9 +3,11 @@
#include <QDialogButtonBox>
#include <QLabel>
#include <QSettings>
#include <QApplication>
#include "rawimage.h"
extern int DEFAULT_WIDTH;
extern double SATURATION;
class EvenNumber : public QSpinBox
{
@@ -47,9 +49,21 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
m_thumSize->setValue(settings.value("settings/thumnailsize", THUMB_SIZE).toInt());
m_thumSize->setToolTip(tr("Thumbnail size in pixels"));
m_saturation = new QDoubleSpinBox(this);
m_saturation->setMinimum(0);
m_saturation->setMaximum(100);
m_saturation->setSuffix(" %");
m_saturation->setValue(settings.value("settings/saturation", SATURATION * 100.0).toDouble());
m_saturation->setToolTip(tr("Set threshold value that is considered saturated when showing statistics.\nFor RAW files you may set 22%"));
m_useNativeDialog = new QCheckBox(tr("Don't use native file dialog"), this);
m_useNativeDialog->setChecked(QApplication::testAttribute(Qt::AA_DontUseNativeDialogs));
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.")));
layout->addRow(tr("Saturation"), m_saturation);
layout->addRow(m_useNativeDialog);
//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);
@@ -67,6 +81,18 @@ void SettingsDialog::loadSettings()
THUMB_SIZE_BORDER = THUMB_SIZE + 10;
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
}
bool SettingsDialog::loadThumbsizes()
{
QSettings settings;
int OLD_THUMB_SIZE = THUMB_SIZE;
THUMB_SIZE = settings.value("settings/thumbnailsize", THUMB_SIZE).toInt();
THUMB_SIZE_BORDER = THUMB_SIZE + 10;
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0;
return OLD_THUMB_SIZE != THUMB_SIZE;
}
void SettingsDialog::saveSettings()
@@ -74,4 +100,10 @@ void SettingsDialog::saveSettings()
QSettings settings;
settings.setValue("settings/thumbnailsize", m_thumSize->value());
settings.setValue("settings/preloadimagecount", m_preloadImages->value());
settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked());
settings.setValue("settings/saturation", m_saturation->value());
SATURATION = m_saturation->value() / 100.0;
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked());
if(DEFAULT_WIDTH != m_preloadImages->value())
emit preloadChanged(m_preloadImages->value());
}
+6
View File
@@ -3,6 +3,7 @@
#include <QDialog>
#include <QSpinBox>
#include <QCheckBox>
class SettingsDialog : public QDialog
{
@@ -10,11 +11,16 @@ class SettingsDialog : public QDialog
public:
explicit SettingsDialog(QWidget *parent = nullptr);
static void loadSettings();
static bool loadThumbsizes();
signals:
void preloadChanged(int witdth);
private:
void saveSettings();
QSpinBox *m_preloadImages;
QSpinBox *m_thumSize;
QDoubleSpinBox *m_saturation;
QCheckBox *m_useNativeDialog;
};
#endif // SETTINGSDIALOG_H
+41
View File
@@ -0,0 +1,41 @@
#version 330
uniform sampler2D qt_Texture0;
in vec2 qt_TexCoord0;
in vec2 center;
out vec4 color;
#define f(x, y) texelFetch(qt_Texture0, icenter + ivec2(x, y), 0).r
void main(void)
{
ivec2 texSize = textureSize(qt_Texture0, 0);
ivec2 icenter = ivec2(center);
ivec2 alternate = icenter % 2;
// cross, checker, theta, phi
const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0;
const vec4 kB = vec4( 2.0, 0.0, 0.0, 4.0) / 8.0;
const vec4 kC = vec4( 4.0, 6.0, 5.0, 5.0) / 8.0;
const vec4 kD = vec4( 0.0, 2.0, -1.0, -1.0) / 8.0;
const vec4 kE = vec4(-1.0, -1.5, -1.0, 0.5) / 8.0;
const vec4 kF = vec4( 2.0, 0.0, 4.0, 0.0) / 8.0;
float A = f(0,2) + f(0,-2);
float B = f(0,1) + f(0,-1);
float C = f(0,0);
float D = f(1,1) + f(-1,1) + f(1,-1) + f(-1,-1);
float E = f(2,0) + f(-2,0);
float F = f(1,0) + f(-1,0);
vec4 P = kA*A + kB*B + kC*C + kD*D + kE*E + kF*F;
color.rgb = alternate.y == 0 ?
(alternate.x == 0 ? vec3(C, P.xy) : // even row even col
vec3(P.z, C, P.w)) : // even row odd col
(alternate.x == 0 ? vec3(P.w, C, P.z) : // odd row even col
vec3(P.yx, C)); // odd row odd col
color.a = 1.0;
}
+16
View File
@@ -0,0 +1,16 @@
#version 330
uniform sampler2D qt_Texture0;
in vec2 qt_Vertex;
in vec2 qt_MultiTexCoord0;
out vec2 qt_TexCoord0;
out vec2 center;
void main(void)
{
vec2 texSize = vec2(textureSize(qt_Texture0, 0));
gl_Position = vec4(qt_Vertex, 0.0, 1.0);
qt_TexCoord0 = qt_MultiTexCoord0;
qt_TexCoord0.y = 1.0 - qt_TexCoord0.y;
center = qt_TexCoord0 * texSize.xy;
}
+22 -1
View File
@@ -4,9 +4,18 @@ uniform sampler2D qt_Texture0;
uniform vec3 mtf_param;
uniform bool bw;
uniform bool invert;
uniform bool srgb;
uniform vec3 whiteBalance;
in vec2 qt_TexCoord0;
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)
{
x = (x - m.x) / (m.z - m.x);
@@ -14,13 +23,25 @@ vec4 MTF(vec4 x, vec3 m)
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)
{
color = texture(qt_Texture0, qt_TexCoord0);
if(bw)color = color.rrra;
color = MTF(color, mtf_param);
if(invert)color = vec4(1.0) - color;
if(invert)color.rgb = vec3(1.0) - color.rgb;
color.rgb = mix(checker(), color.rgb, color.a);
if(srgb)color.rgb = Linear2sRGB(color.rgb);
color.rgb *= whiteBalance;
if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0))))
color = vec4(0.0, 0.0, 0.0, 1.0);
+1 -1
View File
@@ -4,6 +4,6 @@ Exec=tenmon %U
Icon=space.nouspiro.tenmon
Comment=FITS Image viewer
Name=Tenmon
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;Science;Astronomy
MimeType=image/fits;image/x-xisf;
Terminal=false
+56 -22
View File
@@ -7,37 +7,71 @@
<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 and PNG images</li>
</ul>
</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 &lt;-&gt; XISF</li>
<li>Convert FITS/XISF -&gt; JPEG/PNG</li>
<li>Image statistics mean, media, min, max</li>
<li>Support for WCS</li>
<li>Thumbnails</li>
</ul>
<p>
</p>
<p>It is intended primarily for viewing astro photos and images. It supports the following formats:</p>
<ul>
<li>FITS 8, 16 bit integer and 32 bit float</li>
<li>XISF 8, 16 bit integer and 32 bit float</li>
<li>RAW CR2, DNG, NEF</li>
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
</ul>
<p>Features of application:</p>
<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 &lt;-&gt; XISF</li>
<li>Convert FITS/XISF -&gt; JPEG/PNG</li>
<li>Image statistics mean, media, min, max</li>
<li>Support for WCS</li>
<li>Thumbnails</li>
<li>Convert CFA images to colour - debayer</li>
<li>Color space aware</li>
</ul>
</description>
<categories>
<category>Graphics</category>
<category>Viewer</category>
<category>Science</category>
<category>Astronomy</category>
</categories>
<keywords>
<keyword>astronomy</keyword>
</keywords>
<url type="homepage">https://nouspiro.space/?page_id=206</url>
<url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url>
<screenshots>
<screenshot type="default">
<image>https://nouspiro.space/wp-content/uploads/2022/04/tenmon-1024x579.png</image>
<image>https://nouspiro.space/wp-content/uploads/2022/12/tenmon2-1024x645.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
<releases>
<release version="20221228" date="2022-12-28">
<description>
<ul>
<li>Add gray world white balance</li>
<li>Export database to CSV</li>
<li>Fine tune of STF when Shift key is pressed</li>
<li>Add saturation statistic</li>
<li>Various fixes</li>
</ul>
</description>
</release>
<release version="20221215" date="2022-12-15">
<description>
<ul>
<li>Add debayer support</li>
<li>Move to trash action</li>
<li>Change copy and move shortcuts to F5 and F6</li>
<li>Change mark and unmark key shortcuts to F7 and F8</li>
<li>Fix not refreshing file tree</li>
<li>Add option to not use native file dialog</li>
</ul>
</description>
</release>
<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"/>
+26 -3
View File
@@ -21,6 +21,8 @@ STFSlider::STFSlider(QWidget *parent) : QWidget(parent)
m_midPoint = 0.5;
m_whitePoint = 1;
m_grabbed = -1;
m_fineTune = false;
setToolTip(tr("Press Shift for fine tuning"));
}
float STFSlider::blackPoint() const
@@ -94,20 +96,34 @@ void STFSlider::mouseMoveEvent(QMouseEvent *event)
else
unsetCursor();
qreal x = (qreal)event->x()/width();
if(event->modifiers() & Qt::ShiftModifier && !m_fineTune)
{
m_fineTune = true;
m_fineTuneX = x;
}
if(!(event->modifiers() & Qt::ShiftModifier) && m_fineTune)
m_fineTune = false;
if(m_fineTune)
{
x = m_fineTuneX + (x - m_fineTuneX) * 0.2;
}
switch(m_grabbed)
{
case 0:
m_blackPoint = clamp((qreal)event->x()/width());
m_blackPoint = clamp(x);
m_whitePoint = std::max(m_whitePoint, m_blackPoint);
QToolTip::showText(event->globalPos(), QString::number(m_blackPoint), this);
break;
case 1:
m_midPoint = ((qreal)event->x()/width() - m_blackPoint) / (m_whitePoint - m_blackPoint);
m_midPoint = (x - m_blackPoint) / (m_whitePoint - m_blackPoint);
m_midPoint = clamp(m_midPoint);
QToolTip::showText(event->globalPos(), QString::number(m_midPoint), this);
break;
case 2:
m_whitePoint = clamp((qreal)event->x()/width());
m_whitePoint = clamp(x);
m_blackPoint = std::min(m_blackPoint, m_whitePoint);
QToolTip::showText(event->globalPos(), QString::number(m_whitePoint), this);
break;
@@ -121,6 +137,12 @@ void STFSlider::mouseMoveEvent(QMouseEvent *event)
void STFSlider::mousePressEvent(QMouseEvent *event)
{
if(event->modifiers() & Qt::ShiftModifier)
{
m_fineTune = true;
m_fineTuneX = (qreal)event->x()/width();
}
if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - event->x()) < 5)
m_grabbed = 1;
else if(std::abs(m_blackPoint*width() - event->x()) < 5)
@@ -134,5 +156,6 @@ void STFSlider::mousePressEvent(QMouseEvent *event)
void STFSlider::mouseReleaseEvent(QMouseEvent *)
{
m_grabbed = -1;
m_fineTune = false;
emit paramChanged(m_blackPoint, midPoint(), m_whitePoint);
}
+2
View File
@@ -11,6 +11,8 @@ class STFSlider : public QWidget
float m_midPoint;
float m_whitePoint;
int m_grabbed;
bool m_fineTune;
float m_fineTuneX;
public:
explicit STFSlider(QWidget *parent = nullptr);
float blackPoint() const;
+2 -2
View File
@@ -34,7 +34,7 @@ StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar")
invertButton->setCheckable(true);
connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool)));
QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Superpixel CFA draw 2x2 pixel as one"));
QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Debayer CFA"));
superPixelButton->setCheckable(true);
connect(superPixelButton, SIGNAL(toggled(bool)), this, SIGNAL(superPixel(bool)));
@@ -49,7 +49,7 @@ void StretchToolbar::stretchImage(Image *img)
if(img->rawImage())
{
double median, mad, max;
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, &max, &mad);
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, &max, &mad, nullptr);
median /= img->rawImage()->norm();
mad /= img->rawImage()->norm();
max /= img->rawImage()->norm();
Binary file not shown.
+92 -23
View File
@@ -154,10 +154,6 @@
<source>Filesystem</source>
<translation>File system</translation>
</message>
<message>
<source>FITS files database</source>
<translation type="vanished">FITS files database</translation>
</message>
<message>
<source>Tenmon</source>
<translation>Tenmon</translation>
@@ -208,7 +204,7 @@
</message>
<message>
<source>Fullscreen</source>
<translation>Fullscreen</translation>
<translation type="vanished">Fullscreen</translation>
</message>
<message>
<source>Select</source>
@@ -294,18 +290,10 @@
<source>Moving</source>
<translation>Moving</translation>
</message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message>
<source>Indexing FITS files</source>
<translation>Indexing FITS files</translation>
</message>
<message>
<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>
</message>
<message>
<source>Reindex files</source>
<translation>Reindex files</translation>
@@ -322,22 +310,78 @@
<source>Star finder</source>
<translation>Star finder</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>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>FITS header editor</translation>
<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 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>;;All files (*)</source>
<translation>;;All files (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Move files to trash?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Do you want to move %1 files to trash?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Failed to move file to trash</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Failed to move file to trash %1</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Move marked files to trash</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Moving marked files to trash</translation>
</message>
<message>
<source>Export database to CSV</source>
<translation>Export database to CSV file</translation>
</message>
<message>
<source>CSV file (*.csv)</source>
<translation>CSV files (*.csv)</translation>
</message>
</context>
<context>
<name>MarkedFiles</name>
@@ -414,7 +458,7 @@
</message>
<message>
<source>Peaks draw</source>
<translation>Peaks draw</translation>
<translation type="vanished">Peaks draw</translation>
</message>
<message>
<source>FWHM X</source>
@@ -428,6 +472,17 @@
<source>Unsupported sample format</source>
<translation>Unsupported sample format</translation>
</message>
<message>
<source>Saturated</source>
<translation>Saturated</translation>
</message>
</context>
<context>
<name>STFSlider</name>
<message>
<source>Press Shift for fine tuning</source>
<translation>Press Shift for fine tuning</translation>
</message>
</context>
<context>
<name>SelectColumnsDialog</name>
@@ -462,6 +517,20 @@
<source>Changes in settings will take effect after program restart.</source>
<translation>Changes in settings will take effect after program restart.</translation>
</message>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>Don&apos;t use native file dialog</translation>
</message>
<message>
<source>Set threshold value that is considered saturated when showing statistics.
For RAW files you may set 22%</source>
<translation>Set threshold value that is considered saturated when showing statistics.
For RAW files you may set 22%</translation>
</message>
<message>
<source>Saturation</source>
<translation>Saturated</translation>
</message>
</context>
<context>
<name>StretchToolbar</name>
@@ -481,13 +550,13 @@
<source>Invert colors</source>
<translation>Invert colors</translation>
</message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Superpixel CFA draw 2x2 pixel as one</translation>
</message>
<message>
<source>Apply auto stretch on load</source>
<translation>Apply auto stretch on load</translation>
</message>
<message>
<source>Debayer CFA</source>
<translation>Debayer CFA</translation>
</message>
</context>
</TS>
Binary file not shown.
+92 -23
View File
@@ -154,10 +154,6 @@
<source>Filesystem</source>
<translation>Système de fichier</translation>
</message>
<message>
<source>FITS files database</source>
<translation type="vanished">FITS files database</translation>
</message>
<message>
<source>Tenmon</source>
<translation>Tenmon</translation>
@@ -208,7 +204,7 @@
</message>
<message>
<source>Fullscreen</source>
<translation>Plein écran</translation>
<translation type="vanished">Plein écran</translation>
</message>
<message>
<source>Select</source>
@@ -294,18 +290,10 @@
<source>Moving</source>
<translation>Déplacement</translation>
</message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message>
<source>Indexing FITS files</source>
<translation>Indexation des fichiers FITS</translation>
</message>
<message>
<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>
</message>
<message>
<source>Reindex files</source>
<translation>-indexer les fichiers</translation>
@@ -322,22 +310,78 @@
<source>Star finder</source>
<translation>Détecteur d&apos;é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>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>Éditeur d&apos;en-tête FITS</translation>
<translation type="vanished">Éditeur d&apos;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>Échec de la copie</translation>
</message>
<message>
<source>Failed to move</source>
<translation>Échec du déplacement</translation>
</message>
<message>
<source>Failed to move from %1 to %2</source>
<translation>Échec du déplacement de %1 vers %2</translation>
</message>
<message>
<source>Failed to copy from %1 to %2</source>
<translation>Échec de la copie de %1 vers %2</translation>
</message>
<message>
<source>;;All files (*)</source>
<translation>;;Tout les fichiers (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Déplacer les fichiers dans la corbeille?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Voulez-vous déplacer le fichier %1 dans la corbeille?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Echec du déplacement dans la corbeille</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Echec du déplacement de %1 dans la corbeille</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Déplacer les fichiers marqués dans la corbeille</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Déplacement des fichiers marqués dans la corbeille</translation>
</message>
<message>
<source>Export database to CSV</source>
<translation>Exporter la base de données vers un fichier CSV</translation>
</message>
<message>
<source>CSV file (*.csv)</source>
<translation>Fichiers CSV (*.csv)</translation>
</message>
</context>
<context>
<name>MarkedFiles</name>
@@ -414,7 +458,7 @@
</message>
<message>
<source>Peaks draw</source>
<translation>Dessin des pic</translation>
<translation type="vanished">Dessin des pic</translation>
</message>
<message>
<source>FWHM X</source>
@@ -428,6 +472,17 @@
<source>Unsupported sample format</source>
<translation>Format non pris en charge</translation>
</message>
<message>
<source>Saturated</source>
<translation>Saturé</translation>
</message>
</context>
<context>
<name>STFSlider</name>
<message>
<source>Press Shift for fine tuning</source>
<translation>Appuyez sur Shift pour un réglage fin</translation>
</message>
</context>
<context>
<name>SelectColumnsDialog</name>
@@ -462,6 +517,20 @@
<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>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>N&apos;utilisez pas la boîte de dialogue de fichier natif</translation>
</message>
<message>
<source>Set threshold value that is considered saturated when showing statistics.
For RAW files you may set 22%</source>
<translation>Définissez la valeur seuil qui est considérée comme saturée lors de l&apos;affichage des statistiques.
Pour les fichiers RAW, vous pouvez définir 22&#xa0;%</translation>
</message>
<message>
<source>Saturation</source>
<translation>Saturé</translation>
</message>
</context>
<context>
<name>StretchToolbar</name>
@@ -481,13 +550,13 @@
<source>Invert colors</source>
<translation>Inverser les couleurs</translation>
</message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Superpixel CFA dessine 2x2 pixels comme un seul</translation>
</message>
<message>
<source>Apply auto stretch on load</source>
<translation>Appliquer la luminosité automatiquement au chargement</translation>
</message>
<message>
<source>Debayer CFA</source>
<translation>Débayeriser CFA</translation>
</message>
</context>
</TS>
Binary file not shown.
+92 -35
View File
@@ -155,14 +155,6 @@
<source>Filesystem</source>
<translation>Zoznam súborov</translation>
</message>
<message>
<source>FITS Editor</source>
<translation type="vanished">FITS editor</translation>
</message>
<message>
<source>FITS files database</source>
<translation type="vanished">Databáza FITS súborov</translation>
</message>
<message>
<source>Tenmon</source>
<translation>Tenmon</translation>
@@ -209,7 +201,7 @@
</message>
<message>
<source>Fullscreen</source>
<translation>Celá obrazovka</translation>
<translation type="vanished">Celá obrazovka</translation>
</message>
<message>
<source>Select</source>
@@ -247,14 +239,6 @@
<source>Open file</source>
<translation>Otvoriť súbor</translation>
</message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</source>
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</translation>
</message>
<message>
<source>Images (*.jpg *.png *.JPG *.PNG)</source>
<translation type="vanished">Obrázky (*.jpg *.png *.JPG *.PNG)</translation>
</message>
<message>
<source>Select destination</source>
<translation>Vybrať cieľ</translation>
@@ -307,18 +291,10 @@
<source>Moving</source>
<translation>Presúvanie</translation>
</message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message>
<source>Indexing FITS files</source>
<translation>Indexovanie FITS/XISF súborov</translation>
</message>
<message>
<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>
</message>
<message>
<source>Reindex files</source>
<translation>Reindexuj súbory</translation>
@@ -335,22 +311,78 @@
<source>Star finder</source>
<translation>Vyhľadávač hviezd</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>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>Editor FITS hlavičky</translation>
<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 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>;;All files (*)</source>
<translation>;;Všetky súbory (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Presunúť súbory do koša?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Presunúť %1 súborov do koša?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Zlyhalo presunutie súbora do koša</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Zlyhalo presunutie súbora do koša %1</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Presunúť označené súbory do koša</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Presúvanie do koša</translation>
</message>
<message>
<source>Export database to CSV</source>
<translation>Exportovať databázu do CSV súboru</translation>
</message>
<message>
<source>CSV file (*.csv)</source>
<translation>Súbory CSV (*.csv)</translation>
</message>
</context>
<context>
<name>MarkedFiles</name>
@@ -431,7 +463,7 @@
</message>
<message>
<source>Peaks draw</source>
<translation>Vykreslené vrcholky</translation>
<translation type="vanished">Vykreslené vrcholky</translation>
</message>
<message>
<source>FWHM X</source>
@@ -441,6 +473,17 @@
<source>FWHM Y</source>
<translation>FWHM Y</translation>
</message>
<message>
<source>Saturated</source>
<translation>Saturované</translation>
</message>
</context>
<context>
<name>STFSlider</name>
<message>
<source>Press Shift for fine tuning</source>
<translation>Stlačte Shift pre jemné ladenie</translation>
</message>
</context>
<context>
<name>SelectColumnsDialog</name>
@@ -475,6 +518,20 @@
<source>Changes in settings will take effect after program restart.</source>
<translation>Zmeny v nastaveniach sa prejavia po reštarte programu.</translation>
</message>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>Nepoužívať natívny súborový dialóg</translation>
</message>
<message>
<source>Set threshold value that is considered saturated when showing statistics.
For RAW files you may set 22%</source>
<translation>Nastavuje prahovú hodnotu ktorá sa považuje za saturovanú.
Pre RAW súbory možno treba nastaviť 22%</translation>
</message>
<message>
<source>Saturation</source>
<translation>Saturované</translation>
</message>
</context>
<context>
<name>StretchToolbar</name>
@@ -494,13 +551,13 @@
<source>Invert colors</source>
<translation>Invertuj farby</translation>
</message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Super pixel CFA kreslenie 2x2 pixelov ako jeden</translation>
</message>
<message>
<source>Apply auto stretch on load</source>
<translation>Aplikuj automatické natiahnutie pri načítaní</translation>
</message>
<message>
<source>Debayer CFA</source>
<translation>Preveď CFA na farbu</translation>
</message>
</context>
</TS>