6#include "fitsstretchui.h"
16const char kAutoToolTip[] =
"Automatically find stretch parameters";
17const char kStretchOffToolTip[] =
"Stretch the image";
18const char kStretchOnToolTip[] =
"Disable stretching of the image.";
23constexpr double HISTO_SLIDER_MAX = 10000.0;
24constexpr double HISTO_SLIDER_FACTOR = 5.0;
25double midValueFcn(
int x)
27 return pow(10, -(HISTO_SLIDER_FACTOR - (x / (HISTO_SLIDER_MAX / HISTO_SLIDER_FACTOR))));
29int invertMidValueFcn(
double x)
31 return (
int) 0.5 + (HISTO_SLIDER_MAX / HISTO_SLIDER_FACTOR) * (HISTO_SLIDER_FACTOR + log10(x));
35void setupAxisDefaults(
QCPAxis *axis)
60void FITSStretchUI::setupButtons()
67void FITSStretchUI::setupHistoPlot()
70 setupAxisDefaults(histoPlot->yAxis);
71 setupAxisDefaults(histoPlot->xAxis);
72 histoPlot->xAxis->grid()->setZeroLinePen(
Qt::NoPen);
73 histoPlot->setMaximumHeight(75);
75 histoPlot->setVisible(
false);
81void FITSStretchUI::onHistoDoubleClick(
QMouseEvent *event)
84 if (!m_View || !m_View->imageData() || !m_View->imageData()->isHistogramConstructed())
return;
85 const double histogramSize = m_View->imageData()->getHistogramBinCount();
86 histoPlot->xAxis->setRange(0, histogramSize + 1);
91void FITSStretchUI::onHistoMouseMove(
QMouseEvent *event)
93 const auto image = m_View->imageData();
94 if (!image->isHistogramConstructed())
97 const bool rgbHistogram = (image->channels() > 1);
98 const int numPixels = image->width() * image->height();
99 const int histogramSize = image->getHistogramBinCount();
100 const int histoBin = std::max(0, std::min(histogramSize - 1,
101 static_cast<int>(histoPlot->xAxis->pixelToCoord(
event->x()))));
104 if (histoBin >= 0 && histoBin < histogramSize)
106 for (
int c = 0; c < image->channels(); ++c)
109 const double lowRange = intervals[histoBin];
110 const double highRange = lowRange + image->getHistogramBinWidth(c);
113 tip.
append(
QString(
"<font color=\"%1\">").arg(c == 0 ?
"red" : (c == 1) ?
"lightgreen" :
"lightblue"));
115 if (image->getMax(c) > 1.1)
116 tip.
append(
QString(
"%1 %2 %3: ").arg(lowRange, 0,
'f', 0).arg(
QChar(0x2192)).arg(highRange, 0,
'f', 0));
118 tip.
append(
QString(
"%1 %2 %3: ").arg(lowRange, 0,
'f', 4).arg(
QChar(0x2192)).arg(highRange, 0,
'f', 4));
120 const int count = image->getHistogramFrequency(c)[histoBin];
121 const double percentage = count * 100.0 / (double) numPixels;
122 tip.
append(
QString(
"%1 %2%").arg(count).arg(percentage, 0,
'f', 2));
124 tip.
append(
"</font><br/>");
131void FITSStretchUI::setupHistoSlider()
134 histoSlider->setMinimum(0);
135 histoSlider->setMaximum(HISTO_SLIDER_MAX);
136 histoSlider->setMinimumPosition(0);
137 histoSlider->setMaximumPosition(HISTO_SLIDER_MAX);
138 histoSlider->setMidPosition(HISTO_SLIDER_MAX / 2);
143 StretchParams params = m_View->getStretchParams();
144 const double shadowValue = value / HISTO_SLIDER_MAX;
145 if (shadowValue != params.grey_red.shadows)
147 params.grey_red.shadows = shadowValue;
153 m_View->setPreviewSampling(Options::stretchPreviewSampling());
154 m_View->setStretchParams(params);
155 m_View->setPreviewSampling(0);
165 StretchParams params = m_View->getStretchParams();
166 const double highValue = value / HISTO_SLIDER_MAX;
167 if (highValue != params.grey_red.highlights)
169 params.grey_red.highlights = highValue;
170 m_View->setPreviewSampling(Options::stretchPreviewSampling());
171 m_View->setStretchParams(params);
172 m_View->setPreviewSampling(0);
179 StretchParams params = m_View->getStretchParams();
180 const double midValue = midValueFcn(value);
181 if (midValue != params.grey_red.midtones)
183 params.grey_red.midtones = midValue;
184 m_View->setPreviewSampling(Options::stretchPreviewSampling());
185 m_View->setStretchParams(params);
186 m_View->setPreviewSampling(0);
194 connect(histoSlider, &ctk3Slider::released,
this, [ = ](
int minValue,
int midValue,
int maxValue)
196 StretchParams params = m_View->getStretchParams();
197 const double shadowValue = minValue / HISTO_SLIDER_MAX;
198 const double middleValue = midValueFcn(midValue);
199 const double highValue = maxValue / HISTO_SLIDER_MAX;
201 if (middleValue != params.grey_red.midtones ||
202 highValue != params.grey_red.highlights ||
203 shadowValue != params.grey_red.shadows)
205 params.grey_red.shadows = shadowValue;
206 params.grey_red.midtones = middleValue;
207 params.grey_red.highlights = highValue;
208 m_View->setPreviewSampling(0);
209 m_View->setStretchParams(params);
215void FITSStretchUI::setStretchUIValues(
const StretchParams1Channel ¶ms)
217 shadowsVal->setValue(params.shadows);
218 midtonesVal->setValue(params.midtones);
219 highlightsVal->setValue(params.highlights);
221 bool stretchActive = m_View->isImageStretched();
224 stretchButton->setChecked(
true);
225 stretchButton->setToolTip(kStretchOnToolTip);
229 stretchButton->setChecked(
false);
230 stretchButton->setToolTip(kStretchOffToolTip);
234 if (stretchActive && !m_View->getAutoStretch())
236 autoButton->setEnabled(
true);
238 autoButton->setIconSize(
QSize(22, 22));
239 autoButton->setToolTip(kAutoToolTip);
243 autoButton->setEnabled(
false);
244 autoButton->setIcon(
QIcon());
245 autoButton->setIconSize(
QSize(22, 22));
246 autoButton->setToolTip(
"");
248 autoButton->setChecked(m_View->getAutoStretch());
251 shadowsVal->setEnabled(stretchActive);
252 shadowsLabel->setEnabled(stretchActive);
253 midtonesVal->setEnabled(stretchActive);
254 midtonesLabel->setEnabled(stretchActive);
255 highlightsVal->setEnabled(stretchActive);
256 highlightsLabel->setEnabled(stretchActive);
257 histoSlider->setEnabled(stretchActive);
260void FITSStretchUI::setupConnections()
262 connect(m_View.
get(), &FITSView::mouseOverPixel,
this, [
this ](
int x,
int y)
264 if (pixelCursors.size() != m_View->imageData()->channels())
265 pixelCursors.fill(nullptr, m_View->imageData()->channels());
267 if (!m_View || !m_View->imageData() || !m_View->imageData()->isHistogramConstructed()) return;
268 auto image = m_View->imageData();
269 const int nChannels = m_View->imageData()->channels();
270 for (int c = 0; c < nChannels; ++c)
272 if (pixelCursors[c] != nullptr)
274 histoPlot->removeItem(pixelCursors[c]);
275 pixelCursors[c] = nullptr;
277 if (x < 0 || y < 0 || x >= m_View->imageData()->width() ||
278 y >= m_View->imageData()->height())
280 int32_t bin = image->histogramBin(x, y, c);
281 QColor color = Qt::darkGray;
283 color = c == 0 ? QColor(255, 10, 65) : ((c == 1) ? QColor(144, 238, 144, 225) : QColor(173, 216, 230, 175));
285 pixelCursors[c] = setCursor(bin, QPen(color, 2, Qt::SolidLine));
292 StretchParams params = m_View->getStretchParams();
293 params.grey_red.highlights = highlightsVal->value();
295 m_View->setStretchParams(params);
296 histoSlider->setMaximumValue(params.grey_red.highlights * HISTO_SLIDER_MAX);
302 StretchParams params = m_View->getStretchParams();
303 params.grey_red.midtones = midtonesVal->value();
305 m_View->setStretchParams(params);
306 histoSlider->setMidValue(invertMidValueFcn(params.grey_red.midtones));
312 StretchParams params = m_View->getStretchParams();
313 params.grey_red.shadows = shadowsVal->value();
315 m_View->setStretchParams(params);
316 histoSlider->setMinimumValue(params.grey_red.shadows * HISTO_SLIDER_MAX);
323 m_View->setStretch(!m_View->isImageStretched());
331 if (!m_View->getAutoStretch())
332 m_View->setAutoStretchParams();
335 setStretchUIValues(m_View->getStretchParams().grey_red);
340 histoPlot->setVisible(!histoPlot->isVisible());
345 connect(m_View.get(), &FITSView::newStatus,
this, [ = ](
const QString & unused)
348 setStretchUIValues(m_View->getStretchParams().grey_red);
351 connect(m_View.get(), &FITSView::newStretch,
this, [ = ](
const StretchParams & params)
353 histoSlider->setMinimumValue(params.grey_red.shadows * HISTO_SLIDER_MAX);
354 histoSlider->setMaximumValue(params.grey_red.highlights * HISTO_SLIDER_MAX);
355 histoSlider->setMidValue(invertMidValueFcn(params.grey_red.midtones));
366 if (!data->isHistogramConstructed())
368 const double size = data->getHistogramBinCount();
369 return position * size;
378 const double top = histoPlot->yAxis->range().upper;
379 const double bottom = histoPlot->yAxis->range().lower;
380 line->start->
setCoords(position + .5, bottom);
381 line->end->
setCoords(position + .5, top);
385void FITSStretchUI::setCursors(
const StretchParams ¶ms)
389 auto data = m_View->imageData();
390 minCursor = setCursor(toHistogramPosition(params.grey_red.shadows, data), pen);
391 maxCursor = setCursor(toHistogramPosition(params.grey_red.highlights, data), pen);
394void FITSStretchUI::removeCursors()
396 if (minCursor !=
nullptr)
397 histoPlot->removeItem(minCursor);
400 if (maxCursor !=
nullptr)
401 histoPlot->removeItem(maxCursor);
405void FITSStretchUI::generateHistogram()
407 if (!m_View->imageData()->isHistogramConstructed())
408 m_View->imageData()->constructHistogram();
409 if (m_View->imageData()->isHistogramConstructed())
411 histoPlot->clearGraphs();
412 const int nChannels = m_View->imageData()->channels();
413 histoPlot->clearGraphs();
414 histoPlot->clearItems();
415 for (
int i = 0; i < nChannels; ++i)
417 histoPlot->addGraph(histoPlot->xAxis, histoPlot->yAxis);
418 auto graph = histoPlot->graph(i);
420 graph->setVisible(
true);
423 color = i == 0 ?
QColor(255, 0, 0) : ((i == 1) ?
QColor(0, 255, 0, 225) :
QColor(0, 0, 255, 175));
424 graph->setBrush(
QBrush(color));
425 graph->setPen(
QPen(color));
426 const QVector<double> &h = m_View->imageData()->getHistogramFrequency(i);
427 const int size = m_View->imageData()->getHistogramBinCount();
428 for (
int j = 0; j <
size; ++j)
429 graph->addData(j, log1p(h[j]));
431 histoPlot->rescaleAxes();
432 histoPlot->xAxis->setRange(0, m_View->imageData()->getHistogramBinCount() + 1);
436 histoPlot->axisRect()->setRangeZoomAxes(histoPlot->xAxis, 0);
437 histoPlot->axisRect()->setRangeDragAxes(histoPlot->xAxis, 0);
438 histoPlot->xAxis->setTickLabels(
false);
439 histoPlot->yAxis->setTickLabels(
false);
446 if (!m_View || !m_View->imageData() || !m_View->imageData()->isHistogramConstructed()) return;
447 const double histogramSize = m_View->imageData()->getHistogramBinCount();
448 double tLower = newRange.lower;
449 double tUpper = newRange.upper;
450 if (tLower < 0) tLower = 0;
451 if (tUpper > histogramSize + 1) tUpper = histogramSize + 1;
452 if (tLower != newRange.lower || tUpper != newRange.upper)
453 histoPlot->xAxis->setRange(tLower, tUpper);
457void FITSStretchUI::setStretchValues(
double shadows,
double midtones,
double highlights)
459 StretchParams params = m_View->getStretchParams();
460 params.grey_red.shadows = shadows;
461 params.grey_red.midtones = midtones;
462 params.grey_red.highlights = highlights;
464 m_View->setPreviewSampling(0);
465 m_View->setStretchParams(params);
466 histoSlider->setMinimumValue(params.grey_red.shadows * HISTO_SLIDER_MAX);
467 histoSlider->setMidValue(invertMidValueFcn(params.grey_red.midtones));
468 histoSlider->setMaximumValue(params.grey_red.highlights * HISTO_SLIDER_MAX);
Manages a single axis inside a QCustomPlot.
void rangeChanged(const QCPRange &newRange)
void setTickLabelColor(const QColor &color)
void setLabelColor(const QColor &color)
void setBasePen(const QPen &pen)
void setTickPen(const QPen &pen)
void setSubTickPen(const QPen &pen)
@ lsStepLeft
line is drawn as steps where the step height is the value of the left data point
void setZeroLinePen(const QPen &pen)
void setSubGridPen(const QPen &pen)
void setPen(const QPen &pen)
A line from one point to another.
void setPen(const QPen &pen)
void setCoords(double key, double value)
Represents the range an axis is encompassing.
void mouseMove(QMouseEvent *event)
void mouseDoubleClick(QMouseEvent *event)
void maximumPositionChanged(int max)
This signal is emitted when sliderDown is true and the slider moves.
void minimumPositionChanged(int min)
This signal is emitted when sliderDown is true and the slider moves.
void midPositionChanged(int max)
This signal is emitted when sliderDown is true and the slider moves.
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
QIcon fromTheme(const QString &name)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString & append(QChar ch)
qsizetype size() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)