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 }
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 }
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.
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...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
@ 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)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:48:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.