Commit dc3e949a authored by Jasem Mutlaq's avatar Jasem Mutlaq

Add adaptive sampling as a possible solution to load large images in memory constrained systems

parent fb74da14
Pipeline #48373 canceled with stage
......@@ -1737,4 +1737,39 @@ bool RAWToJPEG(const QString &rawImage, const QString &output, QString &errorMes
#endif
}
double getAvailableRAM()
{
#if defined(Q_OS_OSX)
int mib [] = { CTL_HW, HW_MEMSIZE };
size_t length;
length = sizeof(int64_t);
int64_t RAMcheck;
if(sysctl(mib, 2, &RAMcheck, &length, NULL, 0))
return false; // On Error
//Until I can figure out how to get free RAM on Mac
return RAMcheck;
#elif defined(Q_OS_LINUX)
QProcess p;
p.start("awk", QStringList() << "/MemAvailable/ { print $2 }" << "/proc/meminfo");
p.waitForFinished();
QString memory = p.readAllStandardOutput();
p.close();
//kB to bytes
return (memory.toLong() * 1024.0);
#else
MEMORYSTATUSEX memory_status;
ZeroMemory(&memory_status, sizeof(MEMORYSTATUSEX));
memory_status.dwLength = sizeof(MEMORYSTATUSEX);
if (GlobalMemoryStatusEx(&memory_status))
{
return memory_status.ullAvailPhys;
}
else
{
return 0;
}
#endif
return 0;
}
}
......@@ -29,6 +29,7 @@
#include "dms.h"
#include <QPointF>
#include <QProcess>
#include <QSharedPointer>
#include "config-kstars.h"
......@@ -301,4 +302,12 @@ QByteArray getJPLQueryString(const QByteArray &kind, const QByteArray &dataField
* @return True if conversion is successful, false otherwise.
*/
bool RAWToJPEG(const QString &rawImage, const QString &output, QString &errorMessage);
/**
* @brief getAvailableRAM Try to get available and total RAM on system
* @return availableRAM Available (free and unclaimed) system RAM in bytes. 0 if failed to determine RAM
* @return True if RAM was successfully queried, false otherwise.
*/
double getAvailableRAM();
}
......@@ -23,33 +23,33 @@ class QString;
class FITSLabel : public QLabel
{
Q_OBJECT
public:
explicit FITSLabel(FITSView *img, QWidget *parent = nullptr);
virtual ~FITSLabel() override = default;
void setSize(double w, double h);
void centerTelescope(double raJ2000, double decJ2000);
bool getMouseButtonDown();
protected:
virtual void mouseMoveEvent(QMouseEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
virtual void mouseDoubleClickEvent(QMouseEvent *e) override;
private:
bool mouseButtonDown { false };
QPoint lastMousePoint;
FITSView *view { nullptr };
dms ra;
dms dec;
double width { 0 };
double height { 0 };
double size { 0 };
signals:
void newStatus(const QString &msg, FITSBar id);
void pointSelected(int x, int y);
void markerSelected(int x, int y);
Q_OBJECT
public:
explicit FITSLabel(FITSView *img, QWidget *parent = nullptr);
virtual ~FITSLabel() override = default;
void setSize(double w, double h);
void centerTelescope(double raJ2000, double decJ2000);
bool getMouseButtonDown();
protected:
virtual void mouseMoveEvent(QMouseEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
virtual void mouseDoubleClickEvent(QMouseEvent *e) override;
private:
bool mouseButtonDown { false };
QPoint lastMousePoint;
FITSView *view { nullptr };
dms ra;
dms dec;
double width { 0 };
double height { 0 };
double size { 0 };
signals:
void newStatus(const QString &msg, FITSBar id);
void pointSelected(int x, int y);
void markerSelected(int x, int y);
};
......@@ -276,25 +276,25 @@ QHBoxLayout* FITSTab::setupStretchBar()
{
StretchParams params = view->getStretchParams();
params.grey_red.shadows = this->maxShadows * value / 10000.0f;
view->setSampling(Options::stretchPreviewSampling());
view->setPreviewSampling(Options::stretchPreviewSampling());
view->setStretchParams(params);
view->setSampling(1);
view->setPreviewSampling(0);
});
connect(midtonesSlider.get(), &QSlider::sliderMoved, [ = ](int value)
{
StretchParams params = view->getStretchParams();
params.grey_red.midtones = this->maxMidtones * value / 10000.0f;
view->setSampling(Options::stretchPreviewSampling());
view->setPreviewSampling(Options::stretchPreviewSampling());
view->setStretchParams(params);
view->setSampling(1);
view->setPreviewSampling(0);
});
connect(highlightsSlider.get(), &QSlider::sliderMoved, [ = ](int value)
{
StretchParams params = view->getStretchParams();
params.grey_red.highlights = this->maxHighlights * value / 10000.0f;
view->setSampling(Options::stretchPreviewSampling());
view->setPreviewSampling(Options::stretchPreviewSampling());
view->setStretchParams(params);
view->setSampling(1);
view->setPreviewSampling(0);
});
// Make a final full-res display when the slider is released.
......
......@@ -106,7 +106,7 @@ void FITSView::doStretch(QImage *outputImage)
tempParams = stretchParams;
stretch.setParams(tempParams);
stretch.run(imageData->getImageBuffer(), outputImage, sampling);
stretch.run(imageData->getImageBuffer(), outputImage, m_PreviewSampling);
}
// Store stretch parameters, and turn on stretching if it isn't already on.
......@@ -377,6 +377,25 @@ bool FITSView::processData()
imageData->applyFilter(filter);
double availableRAM = 0;
if (Options::adaptiveSampling() && (availableRAM = KSUtils::getAvailableRAM()) > 0)
{
// Possible color maximum image size
double max_size = image_width * image_height * 4;
// Ratio of image size to available RAM size
double ratio = max_size / availableRAM;
// Increase adaptive sampling with more limited RAM
if (ratio < 0.1)
m_AdaptiveSampling = 1;
else if (ratio < 0.2)
m_AdaptiveSampling = 2;
else
m_AdaptiveSampling = 4;
m_PreviewSampling *= m_AdaptiveSampling;
}
// Rescale to fits window on first load
if (firstLoad)
{
......@@ -737,14 +756,14 @@ void FITSView::updateFrameLargeImage()
font.setPixelSize(scaleSize(FONT_SIZE));
painter.setFont(font);
if (sampling == 1)
if (m_PreviewSampling == 1)
{
drawOverlay(&painter, 1.0);
drawStarFilter(&painter, 1.0);
}
image_frame->setPixmap(displayPixmap);
image_frame->resize(((sampling * currentZoom) / 100.0) * image_frame->pixmap()->size());
image_frame->resize(((m_PreviewSampling * currentZoom) / 100.0) * image_frame->pixmap()->size());
}
void FITSView::updateFrameSmallImage()
......@@ -755,13 +774,12 @@ void FITSView::updateFrameSmallImage()
QPainter painter(&displayPixmap);
if (sampling == 1)
if (m_PreviewSampling == 1)
{
drawOverlay(&painter, currentZoom / ZOOM_DEFAULT);
drawStarFilter(&painter, currentZoom / ZOOM_DEFAULT);
}
image_frame->setPixmap(displayPixmap);
image_frame->resize(currentWidth, currentHeight);
}
......@@ -1856,8 +1874,8 @@ void FITSView::initDisplayImage()
{
// Account for leftover when sampling. Thus a 5-wide image sampled by 2
// would result in a width of 3 (samples 0, 2 and 4).
int w = (imageData->width() + sampling - 1) / sampling;
int h = (imageData->height() + sampling - 1) / sampling;
int w = (imageData->width() + m_PreviewSampling - 1) / m_PreviewSampling;
int h = (imageData->height() + m_PreviewSampling - 1) / m_PreviewSampling;
if (imageData->channels() == 1)
{
......
......@@ -231,9 +231,13 @@ class FITSView : public QScrollArea
void setAutoStretchParams();
// When sampling is > 1, we will display the image at a lower resolution.
void setSampling(int value)
// When sampling = 0, reset to the adaptive sampling value
void setPreviewSampling(uint8_t value)
{
if (value > 0) sampling = value;
if (value == 0)
m_PreviewSampling = m_AdaptiveSampling;
else
m_PreviewSampling = value * m_AdaptiveSampling;
}
public slots:
......@@ -356,7 +360,9 @@ class FITSView : public QScrollArea
StretchParams stretchParams;
// Resolution for display. Sampling=2 means display every other sample.
int sampling { 1 };
uint8_t m_PreviewSampling { 1 };
// Adaptive sampling is based on available RAM
uint8_t m_AdaptiveSampling {1};
struct
{
......
......@@ -6,11 +6,11 @@
<rect>
<x>0</x>
<y>0</y>
<width>452</width>
<height>314</height>
<width>443</width>
<height>312</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>3</number>
</property>
......@@ -28,98 +28,168 @@
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QGroupBox" name="FITSViewerGroup">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Look &amp;&amp; Feel</string>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="kcfg_useFITSViewer">
<property name="toolTip">
<string>Automatically display received images in the FITS Viewer</string>
</property>
<property name="text">
<string>Use FITS Viewer</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singlePreviewFITS">
<property name="toolTip">
<string>Display all captured FITS images in a single tab instead of multiple tabs per image.</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Preview Tab</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singleWindowCapturedFITS">
<property name="toolTip">
<string>Display captured FITS images from all cameras in a single FITS Viewer window instead of a dedicated window to each camera.</string>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Window Capture</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singleWindowOpenedFITS">
<property name="toolTip">
<string>Display opened FITS images in a single FITS Viewer window instead of a dedicated window to each file.</string>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Window Open</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_focusFITSOnNewImage">
<property name="toolTip">
<string>Bring the FITSViewer window to the foreground when receiving a new image</string>
</property>
<property name="text">
<string>Focus on receiving an image</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_independentWindowFITS">
<property name="toolTip">
<string>Make FITS Viewer window independent from KStars</string>
</property>
<property name="text">
<string>Independent Window</string>
</property>
</widget>
</item>
</layout>
</widget>
<item>
<widget class="QGroupBox" name="FITSViewerGroup">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Look &amp;&amp; Feel</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="kcfg_useFITSViewer">
<property name="toolTip">
<string>Automatically display received images in the FITS Viewer</string>
</property>
<property name="text">
<string>Use FITS Viewer</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singlePreviewFITS">
<property name="toolTip">
<string>Display all captured FITS images in a single tab instead of multiple tabs per image.</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Preview Tab</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singleWindowCapturedFITS">
<property name="toolTip">
<string>Display captured FITS images from all cameras in a single FITS Viewer window instead of a dedicated window to each camera.</string>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Window Capture</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_singleWindowOpenedFITS">
<property name="toolTip">
<string>Display opened FITS images in a single FITS Viewer window instead of a dedicated window to each file.</string>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string>Single Window Open</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_focusFITSOnNewImage">
<property name="toolTip">
<string>Bring the FITSViewer window to the foreground when receiving a new image</string>
</property>
<property name="text">
<string>Focus on receiving an image</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_independentWindowFITS">
<property name="toolTip">
<string>Make FITS Viewer window independent from KStars</string>
</property>
<property name="text">
<string>Independent Window</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Down Sampling</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="kcfg_AdaptiveSampling">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Automatically down sample images based on available resources.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Adaptive Sampling</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="stretchPreviewSamplingLabel">
<property name="toolTip">
<string>Set the coarseness of the preview shown when sliding the fitsviewer's stretch parameter sliders. 1 is full resolution, but can be slow, 4 would be coarse resolution and fast.</string>
</property>
<property name="text">
<string>Stretch Preview</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="kcfg_StretchPreviewSampling">
<property name="toolTip">
<string>Set the coarseness of the preview shown when sliding the fitsviewer's stretch parameter sliders. 1 is full resolution, but can be slow, 4 would be coarse resolution and fast.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>5</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
......@@ -137,36 +207,6 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutStretch">
<item>
<widget class="QLabel" name="stretchPreviewSamplingLabel">
<property name="toolTip">
<string>Set the coarseness of the preview shown when sliding the fitsviewer's stretch parameter sliders. 1 is full resolution, but can be slow, 4 would be coarse resolution and fast.</string>
</property>
<property name="text">
<string>Stretch Preview</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="kcfg_StretchPreviewSampling">
<property name="toolTip">
<string>Set the coarseness of the preview shown when sliding the fitsviewer's stretch parameter sliders. 1 is full resolution, but can be slow, 4 would be coarse resolution and fast.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>5</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="kcfg_LimitedResourcesMode">
<property name="toolTip">
......@@ -240,7 +280,10 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutHFR">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="hfrSepLabel">
<property name="toolTip">
......
......@@ -1740,6 +1740,10 @@
<label>Set the coarseness of the preview shown when sliding the fitsviewer's stretch parameter sliders. 1 is full resolution, but can be slow, 4 would be coarse resolution and fast.</label>
<default>4</default>
</entry>
<entry name="AdaptiveSampling" type="Bool">
<label>Automatically down sample images based on available resources.</label>
<default>true</default>
</entry>
<entry name="useSummaryPreview" type="Bool">
<label>Display every image captured sequence image in the Ekos summary screen preview window.</label>
<default>!KSUtils::isHardwareLimited()</default>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment