Kstars

fitshistogramview.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Jasem Mutlaq <[email protected]>
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 
21 FITSHistogramView::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 
49  m_HistogramIntensity.resize(3);
50  m_HistogramFrequency.resize(3);
51  for (int i = 0; i < 3; i++)
52  {
53  m_HistogramIntensity[i].resize(256);
54  for (int j = 0; j < 256; j++)
55  m_HistogramIntensity[i][j] = j;
56  m_HistogramFrequency[i].resize(256);
57  }
58 }
59 
60 void FITSHistogramView::showEvent(QShowEvent * event)
61 {
62  Q_UNUSED(event)
63  if (m_ImageData.isNull())
64  return;
65  if (!m_ImageData->isHistogramConstructed())
66  {
67  if (m_Linear)
68  m_ImageData->constructHistogram();
69  else
70  createNonLinearHistogram();
71  }
72  syncGUI();
73 }
74 
75 void FITSHistogramView::reset()
76 {
77  isGUISynced = false;
78 }
79 
80 void FITSHistogramView::syncGUI()
81 {
82  if (isGUISynced)
83  return;
84 
85  bool isColor = m_ImageData->channels() > 1;
86 
87  clearGraphs();
88  graphs.clear();
89 
90  for (int n = 0; n < m_ImageData->channels(); n++)
91  {
92  graphs.append(addGraph());
93 
94  if (!m_Linear)
95  {
96  graphs[n]->setData(m_HistogramIntensity[n], m_HistogramFrequency[n]);
97  numDecimals << 0;
98  }
99  else
100  {
101  graphs[n]->setData(m_ImageData->getHistogramIntensity(n), m_ImageData->getHistogramFrequency(n));
102 
103  double median = m_ImageData->getMedian(n);
104 
105  if (median > 100)
106  numDecimals << 0;
107  else if (median > 1)
108  numDecimals << 2;
109  else if (median > .01)
110  numDecimals << 4;
111  else if (median > .0001)
112  numDecimals << 6;
113  else
114  numDecimals << 10;
115  }
116  }
117 
118  graphs[RED_CHANNEL]->setBrush(QBrush(QColor(170, 40, 80)));
119  graphs[RED_CHANNEL]->setPen(QPen(Qt::red));
120 
121  if (isColor)
122  {
123  graphs[GREEN_CHANNEL]->setBrush(QBrush(QColor(80, 40, 170)));
124  graphs[GREEN_CHANNEL]->setPen(QPen(Qt::green));
125 
126  graphs[BLUE_CHANNEL]->setBrush(QBrush(QColor(170, 40, 80)));
127  graphs[BLUE_CHANNEL]->setPen(QPen(Qt::blue));
128  }
129 
130  axisRect(0)->setRangeDrag(Qt::Horizontal);
131  axisRect(0)->setRangeZoom(Qt::Horizontal);
132 
133  if (m_AxesLabelEnabled)
134  {
135  xAxis->setLabel(i18n("Intensity"));
136  yAxis->setLabel(i18n("Frequency"));
137  }
138 
139  // xAxis->setRange(fits_min - ui->minEdit->singleStep(),
140  // fits_max + ui->maxEdit->singleStep());
141 
142  xAxis->rescale();
143  yAxis->rescale();
144 
145  setInteraction(QCP::iRangeDrag, true);
146  setInteraction(QCP::iRangeZoom, true);
147  setInteraction(QCP::iSelectPlottables, true);
148 
149  replot();
150  resizePlot();
151 
152  isGUISynced = true;
153 }
154 
155 void FITSHistogramView::resizePlot()
156 {
157  if (width() < 300)
158  yAxis->setTickLabels(false);
159  else
160  yAxis->setTickLabels(true);
161  xAxis->ticker()->setTickCount(width() / 100);
162 }
163 
164 void FITSHistogramView::driftMouseOverLine(QMouseEvent * event)
165 {
166  double intensity = xAxis->pixelToCoord(event->localPos().x());
167 
168  uint8_t channels = m_ImageData->channels();
169  QVector<double> freq(3, -1);
170 
171  if (m_Linear)
172  {
173  QVector<bool> inRange(3, false);
174  for (int n = 0; n < channels; n++)
175  {
176  if (intensity >= m_ImageData->getMin(n) && intensity <= m_ImageData->getMax(n))
177  inRange[n] = true;
178  }
179 
180  if ( (channels == 1 && inRange[0] == false) || (!inRange[0] && !inRange[1] && !inRange[2]) )
181  {
183  return;
184  }
185  }
186 
187  if (xAxis->range().contains(intensity))
188  {
189  for (int n = 0; n < channels; n++)
190  {
191  int index = graphs[n]->findBegin(intensity, true);
192  freq[n] = graphs[n]->dataMainValue(index);
193  }
194 
195  if (channels == 1 && freq[0] > 0)
196  {
198  event->globalPos(),
199  i18nc("Histogram tooltip; %1 is intensity; %2 is frequency;",
200  "<table>"
201  "<tr><td>Intensity: </td><td>%1</td></tr>"
202  "<tr><td>R Frequency: </td><td>%2</td></tr>"
203  "</table>",
204  QString::number(intensity, 'f', numDecimals[0]),
205  QString::number(freq[0], 'f', 0)));
206  }
207  else if (freq[1] > 0)
208  {
210  event->globalPos(),
211  i18nc("Histogram tooltip; %1 is intensity; %2 is frequency;",
212  "<table>"
213  "<tr><td>Intensity: </td><td>%1</td></tr>"
214  "<tr><td>R Frequency: </td><td>%2</td></tr>"
215  "<tr><td>G Frequency: </td><td>%3</td></tr>"
216  "<tr><td>B Frequency: </td><td>%4</td></tr>"
217  "</table>",
218  QString::number(intensity, 'f', numDecimals[0]),
219  QString::number(freq[0], 'f', 0),
220  QString::number(freq[1], 'f', 0),
221  QString::number(freq[2], 'f', 0)));
222  }
223  else
225 
226  replot();
227  }
228 }
229 
230 void FITSHistogramView::setImageData(const QSharedPointer<FITSData> &data)
231 {
232  m_ImageData = data;
233 
234  connect(m_ImageData.data(), &FITSData::dataChanged, [this]()
235  {
236  if (m_Linear)
237  {
238  m_ImageData->resetHistogram();
239  m_ImageData->constructHistogram();
240  }
241  else
242  createNonLinearHistogram();
243  isGUISynced = false;
244  syncGUI();
245  });
246 }
247 
248 void FITSHistogramView::createNonLinearHistogram()
249 {
250  isGUISynced = false;
251 
252  int width = m_ImageData->width();
253  int height = m_ImageData->height();
254 
255  const uint8_t channels = m_ImageData->channels();
256 
257  QImage rawImage;
258  if (channels == 1)
259  {
260  rawImage = QImage(width, height, QImage::Format_Indexed8);
261 
262  rawImage.setColorCount(256);
263  for (int i = 0; i < 256; i++)
264  rawImage.setColor(i, qRgb(i, i, i));
265  }
266  else
267  {
268  rawImage = QImage(width, height, QImage::Format_RGB32);
269  }
270 
271  Stretch stretch(width, height, m_ImageData->channels(), m_ImageData->dataType());
272  // Compute new auto-stretch params.
273  StretchParams stretchParams = stretch.computeParams(m_ImageData->getImageBuffer());
274 
275  stretch.setParams(stretchParams);
276  stretch.run(m_ImageData->getImageBuffer(), &rawImage);
277 
278  m_HistogramFrequency[0].fill(0);
279  if (channels > 1)
280  {
281  m_HistogramFrequency[1].fill(0);
282  m_HistogramFrequency[2].fill(0);
283  }
284  uint32_t samples = width * height;
285  const uint32_t sampleBy = (samples > 1000000 ? samples / 1000000 : 1);
286  if (channels == 1)
287  {
288  for (int h = 0; h < height; h += sampleBy)
289  {
290  auto * scanLine = rawImage.scanLine(h);
291  for (int w = 0; w < width; w += sampleBy)
292  m_HistogramFrequency[0][scanLine[w]] += sampleBy;
293  }
294  }
295  else
296  {
297  for (int h = 0; h < height; h += sampleBy)
298  {
299  auto * scanLine = reinterpret_cast<const QRgb *>((rawImage.scanLine(h)));
300  for (int w = 0; w < width; w += sampleBy)
301  {
302  m_HistogramFrequency[0][qRed(scanLine[w])] += sampleBy;
303  m_HistogramFrequency[1][qGreen(scanLine[w])] += sampleBy;
304  m_HistogramFrequency[2][qBlue(scanLine[w])] += sampleBy;
305  }
306  }
307  }
308 
309  syncGUI();
310 
311 }
void showText(const QPoint &pos, const QString &text, QWidget *w)
QString number(int n, int base)
void setColorCount(int colorCount)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString i18n(const char *text, const TYPE &arg...)
Horizontal
@ iSelectPlottables
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
Definition: qcustomplot.h:258
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
uchar * scanLine(int i)
void setColor(int index, QRgb colorValue)
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3735
void hideText()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void mouseMove(QMouseEvent *event)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
Definition: qcustomplot.h:256
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
Definition: qcustomplot.h:255
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Aug 15 2022 04:04:01 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.