Kstars

fitshistogramview.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "fitshistogramview.h"
8
9#include "fits_debug.h"
10
11#include "fitsdata.h"
12#include "fitstab.h"
13#include "fitsview.h"
14#include "fitsviewer.h"
15
16#include <KMessageBox>
17
18#include <QtConcurrent>
19#include <type_traits>
20
21FITSHistogramView::FITSHistogramView(QWidget *parent) : QCustomPlot(parent)
22{
23 setBackground(QBrush(Qt::black));
24
25 xAxis->setBasePen(QPen(Qt::white, 1));
26 yAxis->setBasePen(QPen(Qt::white, 1));
27
28 xAxis->setTickPen(QPen(Qt::white, 1));
29 yAxis->setTickPen(QPen(Qt::white, 1));
30
31 xAxis->setSubTickPen(QPen(Qt::white, 1));
32 yAxis->setSubTickPen(QPen(Qt::white, 1));
33
34 xAxis->setTickLabelColor(Qt::white);
35 yAxis->setTickLabelColor(Qt::white);
36
37 xAxis->setLabelColor(Qt::white);
38 yAxis->setLabelColor(Qt::white);
39
40 xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
41 yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
42 xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
43 yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
44 xAxis->grid()->setZeroLinePen(Qt::NoPen);
45 yAxis->grid()->setZeroLinePen(Qt::NoPen);
46
47 connect(this, &QCustomPlot::mouseMove, this, &FITSHistogramView::driftMouseOverLine);
48 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onXRangeChanged(QCPRange)));
49 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onYRangeChanged(QCPRange)));
50
51 m_HistogramIntensity.resize(3);
52 m_HistogramFrequency.resize(3);
53 for (int i = 0; i < 3; i++)
54 {
55 m_HistogramIntensity[i].resize(256);
56 for (int j = 0; j < 256; j++)
57 m_HistogramIntensity[i][j] = j;
58 m_HistogramFrequency[i].resize(256);
59 }
60}
61
62void FITSHistogramView::showEvent(QShowEvent * event)
63{
64 Q_UNUSED(event)
65 if (m_ImageData.isNull())
66 return;
67 if (!m_ImageData->isHistogramConstructed())
68 {
69 if (m_Linear)
70 m_ImageData->constructHistogram();
71 else
72 createNonLinearHistogram();
73 }
74 syncGUI();
75}
76
77void FITSHistogramView::reset()
78{
79 isGUISynced = false;
80}
81
82void FITSHistogramView::syncGUI()
83{
84 if (isGUISynced)
85 return;
86
87 bool isColor = m_ImageData->channels() > 1;
88
90 graphs.clear();
91
92 for (int n = 0; n < m_ImageData->channels(); n++)
93 {
94 graphs.append(addGraph());
95
96 if (!m_Linear)
97 {
98 graphs[n]->setData(m_HistogramIntensity[n], m_HistogramFrequency[n]);
99 numDecimals << 0;
100 }
101 else
102 {
103 graphs[n]->setData(m_ImageData->getHistogramIntensity(n), m_ImageData->getHistogramFrequency(n));
104
105 double median = m_ImageData->getMedian(n);
106
107 if (median > 100)
108 numDecimals << 0;
109 else if (median > 1)
110 numDecimals << 2;
111 else if (median > .01)
112 numDecimals << 4;
113 else if (median > .0001)
114 numDecimals << 6;
115 else
116 numDecimals << 10;
117 }
118 }
119
120 graphs[RED_CHANNEL]->setBrush(QBrush(QColor(170, 40, 80)));
121 graphs[RED_CHANNEL]->setPen(QPen(Qt::red));
122
123 if (isColor)
124 {
125 graphs[GREEN_CHANNEL]->setBrush(QBrush(QColor(80, 40, 170)));
126 graphs[GREEN_CHANNEL]->setPen(QPen(Qt::green));
127
128 graphs[BLUE_CHANNEL]->setBrush(QBrush(QColor(170, 40, 80)));
129 graphs[BLUE_CHANNEL]->setPen(QPen(Qt::blue));
130 }
131
134
135 if (m_AxesLabelEnabled)
136 {
137 xAxis->setLabel(i18n("Intensity"));
138 yAxis->setLabel(i18n("Frequency"));
139 }
140
141 // xAxis->setRange(fits_min - ui->minEdit->singleStep(),
142 // fits_max + ui->maxEdit->singleStep());
143
144 xAxis->rescale();
145 yAxis->rescale();
146
150
151 replot();
152 resizePlot();
153
154 isGUISynced = true;
155}
156
157void FITSHistogramView::resizePlot()
158{
159 if (width() < 300)
160 yAxis->setTickLabels(false);
161 else
162 yAxis->setTickLabels(true);
163 xAxis->ticker()->setTickCount(width() / 100);
164}
165
166void FITSHistogramView::driftMouseOverLine(QMouseEvent * event)
167{
168 double intensity = xAxis->pixelToCoord(event->localPos().x());
169
170 uint8_t channels = m_ImageData->channels();
171 QVector<double> freq(3, -1);
172
173 if (m_Linear)
174 {
175 QVector<bool> inRange(3, false);
176 for (int n = 0; n < channels; n++)
177 {
178 if (intensity >= m_ImageData->getMin(n) && intensity <= m_ImageData->getMax(n))
179 inRange[n] = true;
180 }
181
182 if ( (channels == 1 && inRange[0] == false) || (!inRange[0] && !inRange[1] && !inRange[2]) )
183 {
185 return;
186 }
187 }
188
189 if (xAxis->range().contains(intensity))
190 {
191 for (int n = 0; n < channels; n++)
192 {
193 int index = graphs[n]->findBegin(intensity, true);
194 freq[n] = graphs[n]->dataMainValue(index);
195 }
196
197 if (channels == 1 && freq[0] > 0)
198 {
200 event->globalPos(),
201 i18nc("Histogram tooltip; %1 is intensity; %2 is frequency;",
202 "<table>"
203 "<tr><td>Intensity: </td><td>%1</td></tr>"
204 "<tr><td>R Frequency: </td><td>%2</td></tr>"
205 "</table>",
206 QString::number(intensity, 'f', numDecimals[0]),
207 QString::number(freq[0], 'f', 0)));
208 }
209 else if (freq[1] > 0)
210 {
212 event->globalPos(),
213 i18nc("Histogram tooltip; %1 is intensity; %2 is frequency;",
214 "<table>"
215 "<tr><td>Intensity: </td><td>%1</td></tr>"
216 "<tr><td>R Frequency: </td><td>%2</td></tr>"
217 "<tr><td>G Frequency: </td><td>%3</td></tr>"
218 "<tr><td>B Frequency: </td><td>%4</td></tr>"
219 "</table>",
220 QString::number(intensity, 'f', numDecimals[0]),
221 QString::number(freq[0], 'f', 0),
222 QString::number(freq[1], 'f', 0),
223 QString::number(freq[2], 'f', 0)));
224 }
225 else
227
228 replot();
229 }
230}
231
232void FITSHistogramView::setImageData(const QSharedPointer<FITSData> &data)
233{
234 m_ImageData = data;
235
236 connect(m_ImageData.data(), &FITSData::dataChanged, [this]()
237 {
238 if (m_Linear)
239 {
240 m_ImageData->resetHistogram();
241 m_ImageData->constructHistogram();
242 }
243 else
244 createNonLinearHistogram();
245 isGUISynced = false;
246 syncGUI();
247 });
248}
249
250void FITSHistogramView::createNonLinearHistogram()
251{
252 isGUISynced = false;
253
254 int width = m_ImageData->width();
255 int height = m_ImageData->height();
256
257 const uint8_t channels = m_ImageData->channels();
258
259 QImage rawImage;
260 if (channels == 1)
261 {
263
264 rawImage.setColorCount(256);
265 for (int i = 0; i < 256; i++)
266 rawImage.setColor(i, qRgb(i, i, i));
267 }
268 else
269 {
271 }
272
273 Stretch stretch(width, height, m_ImageData->channels(), m_ImageData->dataType());
274 // Compute new auto-stretch params.
275 StretchParams stretchParams = stretch.computeParams(m_ImageData->getImageBuffer());
276
277 stretch.setParams(stretchParams);
278 stretch.run(m_ImageData->getImageBuffer(), &rawImage);
279
280 m_HistogramFrequency[0].fill(0);
281 if (channels > 1)
282 {
283 m_HistogramFrequency[1].fill(0);
284 m_HistogramFrequency[2].fill(0);
285 }
286 uint32_t samples = width * height;
287 const uint32_t sampleBy = (samples > 1000000 ? samples / 1000000 : 1);
288 if (channels == 1)
289 {
290 for (int h = 0; h < height; h += sampleBy)
291 {
292 auto * scanLine = rawImage.scanLine(h);
293 for (int w = 0; w < width; w += sampleBy)
294 m_HistogramFrequency[0][scanLine[w]] += sampleBy;
295 }
296 }
297 else
298 {
299 for (int h = 0; h < height; h += sampleBy)
300 {
301 auto * scanLine = reinterpret_cast<const QRgb *>((rawImage.scanLine(h)));
302 for (int w = 0; w < width; w += sampleBy)
303 {
304 m_HistogramFrequency[0][qRed(scanLine[w])] += sampleBy;
305 m_HistogramFrequency[1][qGreen(scanLine[w])] += sampleBy;
306 m_HistogramFrequency[2][qBlue(scanLine[w])] += sampleBy;
307 }
308 }
309 }
310
311 syncGUI();
312
313}
314
315void FITSHistogramView::onXRangeChanged(const QCPRange &range)
316{
317 QCPRange boundedRange = range;
318 if(boundedRange.lower < 0) { // restrict max zoom in
319 boundedRange.lower = 0;
320 boundedRange.upper = boundedRange.size();
321 }
322 xAxis->setRange(boundedRange);
323}
324void FITSHistogramView::onYRangeChanged(const QCPRange &range)
325{
326 QCPRange boundedRange = range;
327 if(boundedRange.lower < 0) { // restrict max zoom in
328 boundedRange.lower = 0;
329 boundedRange.upper = boundedRange.size();
330 }
331 yAxis->setRange(boundedRange);
332}
void setRangeZoom(Qt::Orientations orientations)
void setRangeDrag(Qt::Orientations orientations)
void setTickLabels(bool show)
void setLabel(const QString &str)
void rescale(bool onlyVisiblePlottables=false)
double pixelToCoord(double value) const
QSharedPointer< QCPAxisTicker > ticker() const
Q_SLOT void setRange(const QCPRange &range)
Represents the range an axis is encompassing.
double size() const
bool contains(double value) const
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
QCPGraph * addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr)
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
void mouseMove(QMouseEvent *event)
QCPAxis * xAxis
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
QCPAxisRect * axisRect(int index=0) const
QCPAxis * yAxis
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iSelectPlottables
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
uchar * scanLine(int i)
void setColor(int index, QRgb colorValue)
void setColorCount(int colorCount)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T * data() const const
bool isNull() const const
QString number(double n, char format, int precision)
Horizontal
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void hideText()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
virtual bool event(QEvent *event) override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.