Compare commits

..

15 Commits

Author SHA1 Message Date
nou 4e952873e3 Fix metainfo 2024-02-02 00:09:42 +01:00
nou fb24800050 Add DBus for MacOS to fix build issue 2024-02-01 23:27:40 +01:00
nou ea0dcc226a Update metainfo 2024-02-01 23:26:08 +01:00
nou 6a7b677b95 Translatiotions 2024-02-01 23:22:35 +01:00
nou 0cee4c9c53 Add thumbnail quality to settings 2024-02-01 23:03:21 +01:00
nou d5f2351905 Only degress should show negative sign 2024-01-22 21:32:15 +01:00
nou 18732a8cbf Limit image info to 1024 characters 2024-01-22 21:31:28 +01:00
nou 8c9c1d8d06 Flip image according to ROWORDER 2024-01-22 21:30:39 +01:00
nou e5f425ff8d Fix some edge cases when stretch 2024-01-18 16:10:11 +01:00
nou 428f9c360a Small optimization in resample 2024-01-15 08:58:14 +01:00
nou a8a1509db7 Show error message in main window when image fail to load 2024-01-14 14:32:01 +01:00
nou 6539c78c57 Add box resize algorithm 2024-01-14 14:28:28 +01:00
nou 0e0d29320e Set unlimited image size so it doesn't fail to load big images 2024-01-14 14:04:54 +01:00
nou 1efe8e6974 Fix date in meta info 2024-01-11 13:30:30 +01:00
nou dae10182d1 Fix SSE instricts ifdef 2024-01-09 15:40:27 +01:00
20 changed files with 612 additions and 25 deletions
+2 -1
View File
@@ -66,6 +66,7 @@ if(WIN32)
set(tenmon_ICON "") set(tenmon_ICON "")
elseif(APPLE) elseif(APPLE)
set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/resources/tenmon.icns) set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/resources/tenmon.icns)
find_package(Qt6 COMPONENTS DBus REQUIRED)
set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
else() else()
set(tenmon_ICON "") set(tenmon_ICON "")
@@ -85,7 +86,7 @@ endif()
target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF) target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF)
if(APPLE) if(APPLE)
target_link_libraries(tenmon PRIVATE "-framework CoreFoundation") target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation")
elseif(UNIX) elseif(UNIX)
target_link_libraries(tenmon PRIVATE Qt6::DBus ${GIO_LDFLAGS}) target_link_libraries(tenmon PRIVATE Qt6::DBus ${GIO_LDFLAGS})
endif(APPLE) endif(APPLE)
+2 -2
View File
@@ -81,7 +81,7 @@ void ImageInfo::setInfo(const ImageInfoData &info)
QTreeWidgetItem *fitsHeader = new QTreeWidgetItem({tr("FITS Header")}); QTreeWidgetItem *fitsHeader = new QTreeWidgetItem({tr("FITS Header")});
for(const FITSRecord &record : info.fitsHeader) for(const FITSRecord &record : info.fitsHeader)
{ {
new QTreeWidgetItem(fitsHeader, {record.key, record.value.toString(), record.comment}); new QTreeWidgetItem(fitsHeader, {record.key, record.value.toString().left(1024), record.comment});
} }
addTopLevelItem(fitsHeader); addTopLevelItem(fitsHeader);
} }
@@ -279,7 +279,7 @@ QString SkyPoint::toString() const
t = t.addSecs(ra * 240); t = t.addSecs(ra * 240);
double deg, min, sec; double deg, min, sec;
min = std::modf(dec, &deg) * 60; min = std::abs(std::modf(dec, &deg) * 60);
sec = std::modf(min, &min) * 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'); 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');
} }
+24 -4
View File
@@ -90,14 +90,27 @@ ImageWidget::~ImageWidget()
void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index) void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
{ {
if(image == nullptr)return; m_currentImg = index;
if(!image || !image->valid())
{
m_imgWidth = 0;
m_imgHeight = 0;
m_error = tr("Failed to load image");
m_rawImage.reset();
update();
return;
}
m_error.clear();
makeCurrent(); makeCurrent();
m_rawImage = image; m_rawImage = image;
m_rawImage->downscaleTo(m_maxTextureSize); if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize)
m_rawImage->resize(std::min(m_maxTextureSize, (int)image->width()), std::min(m_maxTextureSize, (int)image->height()));
m_imgWidth = image->width(); m_imgWidth = image->width();
m_imgHeight = image->height(); m_imgHeight = image->height();
m_currentImg = index;
if(!m_image)return; if(!m_image)return;
@@ -290,7 +303,7 @@ void ImageWidget::thumbnailLoaded(const Image *image)
{ {
makeCurrent(); makeCurrent();
const RawImage *raw = image->thumbnail(); const RawImage *raw = image->thumbnail();
if(!raw)return; if(!raw || !raw->valid())return;
m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGBA, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get()); m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGBA, 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() };
@@ -375,6 +388,13 @@ void ImageWidget::paintGL()
} }
} }
} }
else if(!m_error.isEmpty())
{
QPainter painter(this);
painter.setPen(Qt::red);
painter.setFont(QFont("Sans", 24, QFont::Bold));
painter.drawText(0, 0, width(), height(), Qt::AlignCenter | Qt::AlignHCenter, m_error);
}
else else
{ {
debayer(); debayer();
+1
View File
@@ -69,6 +69,7 @@ class ImageWidget : public QOpenGLWidget
QVector<ImageThumb> m_thumnails; QVector<ImageThumb> m_thumnails;
Database *m_database = nullptr; Database *m_database = nullptr;
QPointF m_lastPos; QPointF m_lastPos;
QString m_error;
public: public:
explicit ImageWidget(Database *database, QWidget *parent = nullptr); explicit ImageWidget(Database *database, QWidget *parent = nullptr);
~ImageWidget() override; ~ImageWidget() override;
+1 -1
Submodule libXISF updated: aa356443d3...033a34e248
+16 -2
View File
@@ -274,6 +274,15 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
if(file) if(file)
loadFITSHeader(file, info); loadFITSHeader(file, info);
if(image)
{
for(auto fits : info.fitsHeader)
{
if(fits.key == "ROWORDER" && fits.value == "BOTTOM-UP")
image->flip();
}
}
fits_close_file(file, &status); fits_close_file(file, &status);
if(status) if(status)
{ {
@@ -478,14 +487,19 @@ void LoadRunable::run()
if(m_thumbnail) if(m_thumbnail)
{ {
if(rawImage) if(rawImage && rawImage->valid())
{ {
if(QUALITY_RESIZE)
rawImage->resize(THUMB_SIZE, THUMB_SIZE);
rawImage->convertToThumbnail(); rawImage->convertToThumbnail();
QMetaObject::invokeMethod(m_receiver, "thumbnailLoadFinish", Qt::QueuedConnection, Q_ARG(std::shared_ptr<RawImage>, rawImage));
} }
QMetaObject::invokeMethod(m_receiver, "thumbnailLoadFinish", Qt::QueuedConnection, Q_ARG(std::shared_ptr<RawImage>, rawImage));
} }
else else
{
QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(std::shared_ptr<RawImage>, rawImage), Q_ARG(ImageInfoData, info)); QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(std::shared_ptr<RawImage>, rawImage), Q_ARG(ImageInfoData, info));
}
} }
catch(std::exception e) catch(std::exception e)
{ {
+1
View File
@@ -62,6 +62,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.cr3 *.nef *.dng)"); _openFilter.append("*.fit *.fits *.xisf *.cr2 *.cr3 *.nef *.dng)");
_openFilter.append(tr(";;All files (*)")); _openFilter.append(tr(";;All files (*)"));
nameFilter.append({"fit", "fits", "xisf", "cr2", "cr3", "nef", "dng"}); nameFilter.append({"fit", "fits", "xisf", "cr2", "cr3", "nef", "dng"});
QImageReader::setAllocationLimit(0);
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);
+96 -7
View File
@@ -7,9 +7,12 @@ int THUMB_SIZE = 128;
int THUMB_SIZE_BORDER = 138; int THUMB_SIZE_BORDER = 138;
int THUMB_SIZE_BORDER_Y = 158; int THUMB_SIZE_BORDER_Y = 158;
double SATURATION = 0.95; double SATURATION = 0.95;
bool QUALITY_RESIZE = true;
#ifdef __SSE2__
template<typename T, int ch> template<typename T, int ch>
void fromPlanarSSE(const void *in, void *out, size_t count); void fromPlanarSSE(const void *in, void *out, size_t count);
#endif
size_t RawImage::typeSize(RawImage::DataType type) size_t RawImage::typeSize(RawImage::DataType type)
{ {
@@ -386,7 +389,11 @@ const void *RawImage::origData(uint32_t row, uint32_t col) const
void RawImage::convertToThumbnail() void RawImage::convertToThumbnail()
{ {
m_thumbAspect = (float)width() / height(); if(!valid())
return;
if(m_thumbAspect == 0.0f)
m_thumbAspect = (float)width() / height();
std::unique_ptr<PixelType[]> outptr = std::make_unique<PixelType[]>(THUMB_SIZE * THUMB_SIZE * 4 * sizeof(uint16_t)); std::unique_ptr<PixelType[]> outptr = std::make_unique<PixelType[]>(THUMB_SIZE * THUMB_SIZE * 4 * sizeof(uint16_t));
uint16_t *out = reinterpret_cast<uint16_t*>(outptr.get()); uint16_t *out = reinterpret_cast<uint16_t*>(outptr.get());
@@ -398,6 +405,7 @@ void RawImage::convertToThumbnail()
{ {
int idx = (i*THUMB_SIZE + o)*4; int idx = (i*THUMB_SIZE + o)*4;
int idx2 = ((i * m_height / THUMB_SIZE * m_width) + (o * m_width / THUMB_SIZE)) * m_ch; int idx2 = ((i * m_height / THUMB_SIZE * m_width) + (o * m_width / THUMB_SIZE)) * m_ch;
if(m_channels == 1) if(m_channels == 1)
{ {
out[idx] = out[idx + 1] = out[idx + 2] = in[idx2] * scale; out[idx] = out[idx + 1] = out[idx + 2] = in[idx2] * scale;
@@ -557,14 +565,81 @@ bool RawImage::pixel(int x, int y, double &r, double &g, double &b) const
return true; return true;
} }
void RawImage::downscaleTo(uint32_t size) template<typename T>
void boxResample(uint32_t w, uint32_t h, uint32_t ch, uint32_t oldw, uint32_t oldh, const uint8_t *in_, uint8_t *out_)
{ {
/*if(size < width() || size < height()) if(oldw == 0 || oldh == 0)return;
const T *in = reinterpret_cast<const T*>(in_);
T *out = reinterpret_cast<T*>(out_);
float max = 255.0f;
if constexpr(std::is_same<T, uint16_t>::value)
max = UINT16_MAX;
float sx = (float)w / oldw;
float sy = (float)h / oldh;
for(uint32_t y = 0; y < h; y++)//iterate over destination Y
{ {
double s = (double)size / std::max(width(), height()); for(uint32_t x = 0; x < w; x++)//iterate over destination X
cv::Size dsize(std::floor(width() * s), std::floor(height() * s)); {
cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_AREA); float p[4] = {0.0f};
}*/ uint32_t xx = x * oldw / w;//calculate source rect
uint32_t yy = y * oldh / h;
uint32_t xe = std::min((x + 1) * oldw / w, oldw - 1);
uint32_t ye = std::min((y + 1) * oldh / h, oldh - 1);
for(uint32_t o = yy; o <= ye; o++)//iterate over source Y
{
float cy = o * sy - y;
if(cy < 0.0f)cy = sy + cy;
else if(sy + cy > 1.0f)cy = 1.0f - cy;
else cy = sy;
if(yy==ye)cy = 1.0f;
for(uint32_t i = xx; i <= xe; i++)//iterate over source X
{
float cx = i * sx - x;
if(cx < 0.0f)cx = sx + cx;
else if(sx + cx > 1.0f)cx = 1.0f - cx;
else cx = sx;
if(xx==xe)cx = 1.0f;
for(uint32_t z = 0; z < ch; z++)
p[z] += in[(o * oldw + i) * ch + z] * cy * cx;
}
}
for(uint32_t z = 0; z < ch; z++)
if constexpr(std::is_floating_point<T>::value)
out[(y * w + x) * ch + z] = p[z];
else
out[(y * w + x) * ch + z] = std::clamp(std::round(p[z]), 0.0f, max);
}
}
}
void RawImage::resize(uint32_t w, uint32_t h)
{
if(!valid())return;
std::unique_ptr<PixelType[]> old_pixels = std::move(m_pixels);
uint32_t oldw = m_width;
uint32_t oldh = m_height;
m_thumbAspect = (float)m_width / m_height;
allocate(w, h, m_channels, m_type);
switch(m_type)
{
case RawImage::UINT8:
boxResample<uint8_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break;
case RawImage::UINT16:
boxResample<uint16_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break;
case RawImage::FLOAT32:
boxResample<float>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
break;
default:
qWarning() << "Resizing format not supported";
break;
}
} }
std::pair<float, float> RawImage::unitScale() const std::pair<float, float> RawImage::unitScale() const
@@ -584,6 +659,15 @@ std::pair<float, float> RawImage::unitScale() const
return {1.0f, 0.0f}; return {1.0f, 0.0f};
} }
void RawImage::flip()
{
std::unique_ptr<PixelType[]> tmp = std::move(m_pixels);
allocate(m_width, m_height, m_channels, m_type);
uint32_t rowSize = m_width * m_ch * typeSize(m_type);
for(uint32_t i=0; i<m_height; i++)
std::memcpy(m_pixels.get() + rowSize * (m_height - i - 1), tmp.get() + rowSize * i, rowSize);
}
std::shared_ptr<RawImage> RawImage::fromPlanar(const RawImage &img) std::shared_ptr<RawImage> RawImage::fromPlanar(const RawImage &img)
{ {
return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type()); return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type());
@@ -690,3 +774,8 @@ std::vector<RawImage> RawImage::split() const
return planes; return planes;
} }
bool RawImage::valid() const
{
return m_width > 0 && m_height > 0;
}
+4 -1
View File
@@ -12,6 +12,7 @@
extern int THUMB_SIZE; extern int THUMB_SIZE;
extern int THUMB_SIZE_BORDER; extern int THUMB_SIZE_BORDER;
extern int THUMB_SIZE_BORDER_Y; extern int THUMB_SIZE_BORDER_Y;
extern bool QUALITY_RESIZE;
class Peak class Peak
{ {
@@ -97,13 +98,15 @@ public:
void convertToGLFormat(); void convertToGLFormat();
float thumbAspect() const; float thumbAspect() const;
bool pixel(int x, int y, double &r, double &g, double &b) const; bool pixel(int x, int y, double &r, double &g, double &b) const;
void downscaleTo(uint32_t size); void resize(uint32_t w, uint32_t h);
std::pair<float, float> unitScale() const; std::pair<float, float> unitScale() const;
void flip();
static std::shared_ptr<RawImage> fromPlanar(const RawImage &img); static std::shared_ptr<RawImage> fromPlanar(const RawImage &img);
static std::shared_ptr<RawImage> fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type); static std::shared_ptr<RawImage> fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type);
static size_t typeSize(DataType type); static size_t typeSize(DataType type);
std::vector<RawImage> split() const; std::vector<RawImage> split() const;
bool valid() const;
}; };
//Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr); //Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr);
+4
View File
@@ -1,4 +1,6 @@
#include "rawimage.h" #include "rawimage.h"
#ifdef __SSE2__
#include <x86intrin.h> #include <x86intrin.h>
template<typename T, int ch> template<typename T, int ch>
@@ -109,3 +111,5 @@ template void fromPlanarSSE<uint32_t, 3>(const void *in, void *out, size_t count
template void fromPlanarSSE<uint32_t, 4>(const void *in, void *out, size_t count); template void fromPlanarSSE<uint32_t, 4>(const void *in, void *out, size_t count);
template void fromPlanarSSE<float, 3>(const void *in, void *out, size_t count); template void fromPlanarSSE<float, 3>(const void *in, void *out, size_t count);
template void fromPlanarSSE<float, 4>(const void *in, void *out, size_t count); template void fromPlanarSSE<float, 4>(const void *in, void *out, size_t count);
#endif
+9 -1
View File
@@ -71,11 +71,16 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
m_filtering->addItems({tr("Nearest"), tr("Linear"), tr("Cubic")}); m_filtering->addItems({tr("Nearest"), tr("Linear"), tr("Cubic")});
m_filtering->setCurrentIndex(FILTERING); m_filtering->setCurrentIndex(FILTERING);
m_qualityThumbnail = new QCheckBox(tr("Smooth thumbnails"), this);
m_qualityThumbnail->setChecked(QUALITY_RESIZE);
m_qualityThumbnail->setToolTip(tr("Use box filter when downsampling thumbnails instead of nearest. Slightly slower."));
layout->addRow(tr("Image preload count"), m_preloadImages); layout->addRow(tr("Image preload count"), m_preloadImages);
layout->addRow(tr("Thumbnails size"), m_thumSize); layout->addRow(tr("Thumbnails size"), m_thumSize);
layout->addRow(tr("Saturation"), m_saturation); layout->addRow(tr("Saturation"), m_saturation);
layout->addRow(tr("Slideshow interval"), m_slideShowTime); layout->addRow(tr("Slideshow interval"), m_slideShowTime);
layout->addRow(tr("Image filtering"), m_filtering); layout->addRow(tr("Image interpolation"), m_filtering);
layout->addRow(m_qualityThumbnail);
layout->addRow(m_useNativeDialog); layout->addRow(m_useNativeDialog);
//layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart."))); //layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart.")));
@@ -97,6 +102,7 @@ void SettingsDialog::loadSettings()
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt(); DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0; SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0;
FILTERING = settings.value("settings/filtering", FILTERING).toInt(); FILTERING = settings.value("settings/filtering", FILTERING).toInt();
QUALITY_RESIZE = settings.value("settings/qualitythumbnail", QUALITY_RESIZE).toBool();
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool()); QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
} }
@@ -118,6 +124,8 @@ void SettingsDialog::saveSettings()
settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked()); settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked());
settings.setValue("settings/saturation", m_saturation->value()); settings.setValue("settings/saturation", m_saturation->value());
settings.setValue("settings/slideshowtime", m_slideShowTime->value()); settings.setValue("settings/slideshowtime", m_slideShowTime->value());
settings.setValue("settings/qualitythumbnail", m_qualityThumbnail->isChecked());
QUALITY_RESIZE = m_qualityThumbnail->isChecked();
FILTERING = m_filtering->currentIndex(); FILTERING = m_filtering->currentIndex();
settings.setValue("settings/filtering", FILTERING); settings.setValue("settings/filtering", FILTERING);
SATURATION = m_saturation->value() / 100.0; SATURATION = m_saturation->value() / 100.0;
+1
View File
@@ -23,6 +23,7 @@ private:
QDoubleSpinBox *m_slideShowTime; QDoubleSpinBox *m_slideShowTime;
QDoubleSpinBox *m_saturation; QDoubleSpinBox *m_saturation;
QCheckBox *m_useNativeDialog; QCheckBox *m_useNativeDialog;
QCheckBox *m_qualityThumbnail;
QComboBox *m_filtering; QComboBox *m_filtering;
}; };
+19 -2
View File
@@ -4,6 +4,10 @@
<launchable type="desktop-id">space.nouspiro.tenmon.desktop</launchable> <launchable type="desktop-id">space.nouspiro.tenmon.desktop</launchable>
<name>Tenmon</name> <name>Tenmon</name>
<summary>FITS/XISF image viewer, converter, index and search</summary> <summary>FITS/XISF image viewer, converter, index and search</summary>
<developer id="nouspiro.space">
<name>Dušan Poizl</name>
</developer>
<developer_name>Dušan Poizl</developer_name>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license> <project_license>GPL-3.0</project_license>
<description> <description>
@@ -11,7 +15,7 @@
<ul> <ul>
<li>FITS 8, 16, 32 bit integer and 32, 64 bit float</li> <li>FITS 8, 16, 32 bit integer and 32, 64 bit float</li>
<li>XISF 8, 16, 32 bit integer and 32, 64 bit float</li> <li>XISF 8, 16, 32 bit integer and 32, 64 bit float</li>
<li>RAW CR2, DNG, NEF</li> <li>RAW CR2/CR3, DNG, NEF</li>
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li> <li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
</ul> </ul>
<p>Features of application:</p> <p>Features of application:</p>
@@ -43,13 +47,26 @@
<url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url> <url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>Main window with image</caption>
<image>https://nouspiro.space/wp-content/uploads/2022/04/tenmon-1024x579.png</image> <image>https://nouspiro.space/wp-content/uploads/2022/04/tenmon-1024x579.png</image>
</screenshot>
<screenshot type="default">
<caption>Thumnail view</caption>
<image>https://nouspiro.space/wp-content/uploads/2022/12/tenmon2-1024x645.png</image> <image>https://nouspiro.space/wp-content/uploads/2022/12/tenmon2-1024x645.png</image>
</screenshot> </screenshot>
</screenshots> </screenshots>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>
<releases> <releases>
<release version="20240108" date="2023-01-08"> <release version="20240201" date="2024-02-01">
<description>
<ul>
<li>Smooth thumbnails</li>
<li>Respect ROWORDER</li>
<li>Bugfixes</li>
</ul>
</description>
</release>
<release version="20240108" date="2024-01-08">
<description> <description>
<ul> <ul>
<li>Update to Qt6</li> <li>Update to Qt6</li>
+7 -4
View File
@@ -120,12 +120,15 @@ void StretchToolbar::stretchImage(Image *img)
mad = stats.m_mad[i]; mad = stats.m_mad[i];
max = stats.m_max[i]; max = stats.m_max[i];
median /= img->rawImage()->norm(); median /= img->rawImage()->norm();
bool a = median > 0.5 ? true : false;
mad /= img->rawImage()->norm(); mad /= img->rawImage()->norm();
max /= img->rawImage()->norm(); max = 1.0f;// /= img->rawImage()->norm();
float bp = median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA; float bp = a || mad == 0.0f ? 0.0f : std::clamp(median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA, 0.0, 1.0);
float mid = MTF(median - bp, TARGET_BACKGROUND); if(a && mad != 0.0f)
max = std::clamp(median - mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA, 0.0, 1.0);
float mid = !a ? MTF(median - bp, TARGET_BACKGROUND) : MTF(TARGET_BACKGROUND, max - median);
m_mtfParam.blackPoint[i-o] = bp; m_mtfParam.blackPoint[i-o] = bp;
m_mtfParam.midPoint[i-o] = mid / max; m_mtfParam.midPoint[i-o] = mid;// / max;
m_mtfParam.whitePoint[i-o] = max; m_mtfParam.whitePoint[i-o] = max;
bp2 += bp; bp2 += bp;
mid2 += mid; mid2 += mid;
Binary file not shown.
+143
View File
@@ -8,6 +8,89 @@
<translation>About Tenmon</translation> <translation>About Tenmon</translation>
</message> </message>
</context> </context>
<context>
<name>BatchProcessing</name>
<message>
<source>Batch Processing</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Input files and directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove all</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browse</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Log</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Interrupt running script?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory path doesn&apos;t exist or is not writable</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>DataBaseView</name> <name>DataBaseView</name>
<message> <message>
@@ -165,6 +248,10 @@
<source>R:%1 G:%2 B:%3</source> <source>R:%1 G:%2 B:%3</source>
<translation>R:%1 G:%2 B:%3</translation> <translation>R:%1 G:%2 B:%3</translation>
</message> </message>
<message>
<source>Failed to load image</source>
<translation>Failed to load image</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
@@ -408,6 +495,30 @@
<source>Histogram</source> <source>Histogram</source>
<translation>Histogram</translation> <translation>Histogram</translation>
</message> </message>
<message>
<source>Bayer mask</source>
<translation>Bayer mask</translation>
</message>
<message>
<source>RGGB</source>
<translation>RGGB</translation>
</message>
<message>
<source>GRBG</source>
<translation>GRBG</translation>
</message>
<message>
<source>GBRG</source>
<translation>GBRG</translation>
</message>
<message>
<source>BGGR</source>
<translation>BGGR</translation>
</message>
<message>
<source>Slideshow</source>
<translation>Slideshow</translation>
</message>
</context> </context>
<context> <context>
<name>MarkedFiles</name> <name>MarkedFiles</name>
@@ -537,6 +648,38 @@ For RAW files you may set 22%</translation>
<source>Saturation</source> <source>Saturation</source>
<translation>Saturated</translation> <translation>Saturated</translation>
</message> </message>
<message>
<source>Nearest</source>
<translation>Nearest</translation>
</message>
<message>
<source>Linear</source>
<translation>Linear</translation>
</message>
<message>
<source>Cubic</source>
<translation>Cubic</translation>
</message>
<message>
<source>Smooth thumbnails</source>
<translation>Smooth thumbnails</translation>
</message>
<message>
<source>Use box filter when downsampling thumbnails instead of nearest. Slightly slower.</source>
<translation>Use box filter when downsampling thumbnails instead of nearest. Slightly slower.</translation>
</message>
<message>
<source>Slideshow interval</source>
<translation>Slideshow interval</translation>
</message>
<message>
<source>Image filtering</source>
<translation type="obsolete">Image sampling</translation>
</message>
<message>
<source>Image interpolation</source>
<translation>Image interpolation</translation>
</message>
</context> </context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>
Binary file not shown.
+143
View File
@@ -8,6 +8,89 @@
<translation>A propos de Tenmon</translation> <translation>A propos de Tenmon</translation>
</message> </message>
</context> </context>
<context>
<name>BatchProcessing</name>
<message>
<source>Batch Processing</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Input files and directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove all</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browse</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Log</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Interrupt running script?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory path doesn&apos;t exist or is not writable</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>DataBaseView</name> <name>DataBaseView</name>
<message> <message>
@@ -165,6 +248,10 @@
<source>R:%1 G:%2 B:%3</source> <source>R:%1 G:%2 B:%3</source>
<translation>R:%1 G:%2 B:%3</translation> <translation>R:%1 G:%2 B:%3</translation>
</message> </message>
<message>
<source>Failed to load image</source>
<translation>Échec du chargement de l&apos;image</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
@@ -408,6 +495,30 @@
<source>Histogram</source> <source>Histogram</source>
<translation>Histogramme</translation> <translation>Histogramme</translation>
</message> </message>
<message>
<source>Bayer mask</source>
<translation>Masque Bayer</translation>
</message>
<message>
<source>RGGB</source>
<translation>RGGB</translation>
</message>
<message>
<source>GRBG</source>
<translation>GRBG</translation>
</message>
<message>
<source>GBRG</source>
<translation>GBRG</translation>
</message>
<message>
<source>BGGR</source>
<translation>BGGR</translation>
</message>
<message>
<source>Slideshow</source>
<translation>Diaporama</translation>
</message>
</context> </context>
<context> <context>
<name>MarkedFiles</name> <name>MarkedFiles</name>
@@ -537,6 +648,38 @@ Pour les fichiers RAW, vous pouvez définir 22&#xa0;%</translation>
<source>Saturation</source> <source>Saturation</source>
<translation>Saturé</translation> <translation>Saturé</translation>
</message> </message>
<message>
<source>Nearest</source>
<translation>Voisin le plus proche</translation>
</message>
<message>
<source>Linear</source>
<translation>Linéaire</translation>
</message>
<message>
<source>Cubic</source>
<translation>Cubique</translation>
</message>
<message>
<source>Smooth thumbnails</source>
<translation>Miniatures fluides</translation>
</message>
<message>
<source>Use box filter when downsampling thumbnails instead of nearest. Slightly slower.</source>
<translation>Utilisez un filtre encadré lors du sous-échantillonnage des vignettes au lieu des plus proches. Un peu plus lent.</translation>
</message>
<message>
<source>Slideshow interval</source>
<translation>Intervalle du diaporama</translation>
</message>
<message>
<source>Image filtering</source>
<translation type="obsolete">Échantillonnage d&apos;images</translation>
</message>
<message>
<source>Image interpolation</source>
<translation>Interpolation d&apos;images</translation>
</message>
</context> </context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>
Binary file not shown.
+139
View File
@@ -8,6 +8,89 @@
<translation>O Tenmon</translation> <translation>O Tenmon</translation>
</message> </message>
</context> </context>
<context>
<name>BatchProcessing</name>
<message>
<source>Batch Processing</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Input files and directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add directories</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove all</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browse</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Log</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop script</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Interrupt running script?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select files</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid output directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Output directory path doesn&apos;t exist or is not writable</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>DataBaseView</name> <name>DataBaseView</name>
<message> <message>
@@ -166,6 +249,10 @@
<source>R:%1 G:%2 B:%3</source> <source>R:%1 G:%2 B:%3</source>
<translation>R:%1 G:%2 B:%3</translation> <translation>R:%1 G:%2 B:%3</translation>
</message> </message>
<message>
<source>Failed to load image</source>
<translation>Zlyhalo načítanie obrázka</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
@@ -409,6 +496,30 @@
<source>Histogram</source> <source>Histogram</source>
<translation>Histogram</translation> <translation>Histogram</translation>
</message> </message>
<message>
<source>Bayer mask</source>
<translation>Bayerova maska</translation>
</message>
<message>
<source>RGGB</source>
<translation>RGGB</translation>
</message>
<message>
<source>GRBG</source>
<translation>GRBG</translation>
</message>
<message>
<source>GBRG</source>
<translation>GBRG</translation>
</message>
<message>
<source>BGGR</source>
<translation>BGGR</translation>
</message>
<message>
<source>Slideshow</source>
<translation>Prezentácia</translation>
</message>
</context> </context>
<context> <context>
<name>MarkedFiles</name> <name>MarkedFiles</name>
@@ -538,6 +649,34 @@ Pre RAW súbory možno treba nastaviť 22%</translation>
<source>Saturation</source> <source>Saturation</source>
<translation>Saturované</translation> <translation>Saturované</translation>
</message> </message>
<message>
<source>Nearest</source>
<translation>Žiadna</translation>
</message>
<message>
<source>Linear</source>
<translation>Lineárna</translation>
</message>
<message>
<source>Cubic</source>
<translation>Kubická</translation>
</message>
<message>
<source>Smooth thumbnails</source>
<translation>Hladké náhľady</translation>
</message>
<message>
<source>Use box filter when downsampling thumbnails instead of nearest. Slightly slower.</source>
<translation>Pri zemnšovaní obrázkov na náhľady sa pixely spriemerujú namiesto najblžšej hodnoty. Trocha pomalšie.</translation>
</message>
<message>
<source>Slideshow interval</source>
<translation>Interval medzi obrázkami</translation>
</message>
<message>
<source>Image interpolation</source>
<translation>Interpolácia obrázku</translation>
</message>
</context> </context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>