Kstars

defectmap.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "defectmap.h"
8 #include <QJsonDocument>
9 
10 //////////////////////////////////////////////////////////////////////////////
11 ///
12 //////////////////////////////////////////////////////////////////////////////
13 QJsonObject BadPixel::json() const
14 {
15  QJsonObject object
16  {
17  {"x", x},
18  {"y", y},
19  {"value", value}
20  };
21  return object;
22 }
23 
24 //////////////////////////////////////////////////////////////////////////////
25 ///
26 //////////////////////////////////////////////////////////////////////////////
27 DefectMap::DefectMap() : QObject()
28 {
29 
30 }
31 
32 //////////////////////////////////////////////////////////////////////////////
33 ///
34 //////////////////////////////////////////////////////////////////////////////
35 bool DefectMap::load(const QString &filename)
36 {
37  QFile input(filename);
38  if (!input.open(QIODevice::ReadOnly))
39  return false;
40 
41  QJsonParseError parserError;
42  auto doc = QJsonDocument::fromJson(input.readAll(), &parserError);
43  if (parserError.error != QJsonParseError::NoError)
44  return false;
45 
46  m_Filename = filename;
47 
48  auto root = doc.object();
49 
50  m_Camera = root["camera"].toString();
51  m_Median = root["median"].toDouble();
52  m_StandardDeviation = root["standardDeviation"].toDouble();
53  m_HotPixelsAggressiveness = root["hotAggressiveness"].toInt();
54  m_ColdPixelsAggressiveness = root["coldAggressiveness"].toInt();
55 
56  QJsonArray hot = root["hot"].toArray();
57  QJsonArray cold = root["cold"].toArray();
58 
59  m_HotPixels.clear();
60  m_ColdPixels.clear();
61 
62  for (const auto &onePixel : qAsConst(hot))
63  {
64  QJsonObject oneObject = onePixel.toObject();
65  m_HotPixels.insert(BadPixel(oneObject["x"].toInt(), oneObject["y"].toInt(), oneObject["value"].toDouble()));
66  }
67 
68  m_HotPixelsCount = m_HotPixels.size();
69 
70  for (const auto &onePixel : qAsConst(cold))
71  {
72  QJsonObject oneObject = onePixel.toObject();
73  m_ColdPixels.insert(BadPixel(oneObject["x"].toInt(), oneObject["y"].toInt(), oneObject["value"].toDouble()));
74  }
75 
76  m_ColdPixelsCount = m_ColdPixels.size();
77  return true;
78 }
79 
80 //////////////////////////////////////////////////////////////////////////////
81 ///
82 //////////////////////////////////////////////////////////////////////////////
83 bool DefectMap::save(const QString &filename, const QString &camera)
84 {
85  QJsonObject root;
86 
87  root.insert("camera", camera);
88  root.insert("median", m_Median);
89  root.insert("standardDeviation", m_StandardDeviation);
90  root.insert("hotAggressiveness", m_HotPixelsAggressiveness);
91  root.insert("coldAggressiveness", m_ColdPixelsAggressiveness);
92 
93  QJsonArray hotArray, coldArray;
94  if (m_HotEnabled)
95  {
96  for (const auto &onePixel : m_HotPixels)
97  hotArray.append(onePixel.json());
98  }
99 
100  if (m_ColdEnabled)
101  {
102  for (const auto &onePixel : m_ColdPixels)
103  coldArray.append(onePixel.json());
104  }
105 
106  root.insert("hot", hotArray);
107  root.insert("cold", coldArray);
108 
109  QFile output(filename);
110 
111  if (!output.open(QIODevice::WriteOnly))
112  return false;
113 
114  m_Filename = filename;
115  output.write(QJsonDocument(root).toJson());
116  output.close();
117 
118  return true;
119 }
120 
121 //////////////////////////////////////////////////////////////////////////////
122 ///
123 //////////////////////////////////////////////////////////////////////////////
124 double DefectMap::calculateSigma(uint8_t aggressiveness)
125 {
126  // Estimated power law fitting to compress values within 0.25 to 9.4 sigma range
127  return 18.61742934980 * exp(-0.00052422221 * (aggressiveness - 9.89915467884) * (aggressiveness - 9.89915467884));
128 }
129 
130 //////////////////////////////////////////////////////////////////////////////
131 ///
132 //////////////////////////////////////////////////////////////////////////////
133 void DefectMap::setDarkData(const QSharedPointer<FITSData> &data)
134 {
135  m_DarkData = data;
136  m_Median = data->getMedian(0);
137  m_StandardDeviation = data->getStdDev(0);
138  if (m_HotPixels.empty() && m_ColdPixels.empty())
139  initBadPixels();
140  else
141  filterPixels();
142 }
143 
144 //////////////////////////////////////////////////////////////////////////////
145 ///
146 //////////////////////////////////////////////////////////////////////////////
147 double DefectMap::getHotThreshold(uint8_t aggressiveness)
148 {
149  return (m_Median + m_StandardDeviation * calculateSigma(aggressiveness));
150 }
151 
152 //////////////////////////////////////////////////////////////////////////////
153 ///
154 //////////////////////////////////////////////////////////////////////////////
155 double DefectMap::getColdThreshold(uint8_t aggressiveness)
156 {
157  return (m_Median - m_StandardDeviation * calculateSigma(aggressiveness));
158 }
159 
160 //////////////////////////////////////////////////////////////////////////////
161 ///
162 //////////////////////////////////////////////////////////////////////////////
163 void DefectMap::initBadPixels()
164 {
165  double hotPixelThreshold = getHotThreshold(100);
166  double coldPixelThreshold = getColdThreshold(100);
167 
168  m_ColdPixels.clear();
169  m_HotPixels.clear();
170 
171  switch (m_DarkData->dataType())
172  {
173  case TBYTE:
174  initBadPixelsInternal<uint8_t>(hotPixelThreshold, coldPixelThreshold);
175  break;
176 
177  case TSHORT:
178  initBadPixelsInternal<int16_t>(hotPixelThreshold, coldPixelThreshold);
179  break;
180 
181  case TUSHORT:
182  initBadPixelsInternal<uint16_t>(hotPixelThreshold, coldPixelThreshold);
183  break;
184 
185  case TLONG:
186  initBadPixelsInternal<int32_t>(hotPixelThreshold, coldPixelThreshold);
187  break;
188 
189  case TULONG:
190  initBadPixelsInternal<uint32_t>(hotPixelThreshold, coldPixelThreshold);
191  break;
192 
193  case TFLOAT:
194  initBadPixelsInternal<float>(hotPixelThreshold, coldPixelThreshold);
195  break;
196 
197  case TLONGLONG:
198  initBadPixelsInternal<int64_t>(hotPixelThreshold, coldPixelThreshold);
199  break;
200 
201  case TDOUBLE:
202  initBadPixelsInternal<double>(hotPixelThreshold, coldPixelThreshold);
203  break;
204 
205  default:
206  break;
207  }
208 }
209 
210 //////////////////////////////////////////////////////////////////////////////
211 ///
212 //////////////////////////////////////////////////////////////////////////////
213 template <typename T>
214 void DefectMap::initBadPixelsInternal(double hotPixelThreshold,
215  double coldPixelThreshold)
216 {
217  T const *buffer = reinterpret_cast<T const*>(m_DarkData->getImageBuffer());
218  const uint32_t width = m_DarkData->width();
219  const uint32_t height = m_DarkData->height();
220 
221  const uint32_t maxSize = 500000;
222  uint32_t samples = m_DarkData->samplesPerChannel();
223  uint8_t downsample = 1;
224  if (samples > maxSize)
225  {
226  downsample = (static_cast<double>(samples) / maxSize) + 0.999;
227  samples /= downsample;
228  }
229 
230  // Need templated function
231  for (uint32_t y = 4; y < height - 4; y += downsample)
232  {
233  for (uint32_t x = 4; x < width - 4; x += downsample)
234  {
235  uint32_t offset = x + y * width;
236  if (buffer[offset] > hotPixelThreshold)
237  m_HotPixels.insert(BadPixel(x, y, buffer[offset]));
238  else if (buffer[offset] < coldPixelThreshold)
239  m_ColdPixels.insert(BadPixel(x, y, buffer[offset]));
240  }
241  }
242 
243  filterPixels();
244 }
245 
246 //////////////////////////////////////////////////////////////////////////////
247 ///
248 //////////////////////////////////////////////////////////////////////////////
249 void DefectMap::filterPixels()
250 {
251  double hotPixelThreshold = getHotThreshold(m_HotPixelsAggressiveness);
252  double coldPixelThreshold = getColdThreshold(m_ColdPixelsAggressiveness);
253 
254  m_HotPixelsThreshold = m_HotPixels.lower_bound(BadPixel(0, 0, hotPixelThreshold));
255  m_ColdPixelsThreshold = m_ColdPixels.lower_bound(BadPixel(0, 0, coldPixelThreshold));
256 
257  if (m_HotPixelsThreshold == m_HotPixels.cend())
258  m_HotPixelsCount = 0;
259  else
260  m_HotPixelsCount = std::distance(m_HotPixelsThreshold, m_HotPixels.cend());
261 
262  if (m_ColdPixelsThreshold == m_ColdPixels.cbegin())
263  m_ColdPixelsCount = 0;
264  else
265  m_ColdPixelsCount = std::distance(m_ColdPixels.cbegin(), m_ColdPixelsThreshold);
266 
267  emit pixelsUpdated(m_HotPixelsCount, m_ColdPixelsCount);
268 }
269 
270 //////////////////////////////////////////////////////////////////////////////
271 ///
272 //////////////////////////////////////////////////////////////////////////////
273 void DefectMap::setHotEnabled(bool enabled)
274 {
275  m_HotEnabled = enabled;
276  emit pixelsUpdated(m_HotEnabled ? m_HotPixelsCount : 0, m_ColdPixelsCount);
277 }
278 
279 //////////////////////////////////////////////////////////////////////////////
280 ///
281 //////////////////////////////////////////////////////////////////////////////
282 void DefectMap::setColdEnabled(bool enabled)
283 {
284  m_ColdEnabled = enabled;
285  emit pixelsUpdated(m_HotPixelsCount, m_ColdEnabled ? m_ColdPixelsCount : 0);
286 }
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject::iterator insert(const QString &key, const QJsonValue &value)
double toDouble(bool *ok) const const
void append(const QJsonValue &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Aug 13 2022 04:01:52 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.