Kstars

defectmap.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 "defectmap.h"
8#include <QJsonDocument>
9
10//////////////////////////////////////////////////////////////////////////////
11///
12//////////////////////////////////////////////////////////////////////////////
13QJsonObject 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//////////////////////////////////////////////////////////////////////////////
27DefectMap::DefectMap() : QObject()
28{
29
30}
31
32//////////////////////////////////////////////////////////////////////////////
33///
34//////////////////////////////////////////////////////////////////////////////
35bool 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//////////////////////////////////////////////////////////////////////////////
83bool 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//////////////////////////////////////////////////////////////////////////////
124double 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//////////////////////////////////////////////////////////////////////////////
133void 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//////////////////////////////////////////////////////////////////////////////
147double DefectMap::getHotThreshold(uint8_t aggressiveness)
148{
149 return (m_Median + m_StandardDeviation * calculateSigma(aggressiveness));
150}
151
152//////////////////////////////////////////////////////////////////////////////
153///
154//////////////////////////////////////////////////////////////////////////////
155double DefectMap::getColdThreshold(uint8_t aggressiveness)
156{
157 return (m_Median - m_StandardDeviation * calculateSigma(aggressiveness));
158}
159
160//////////////////////////////////////////////////////////////////////////////
161///
162//////////////////////////////////////////////////////////////////////////////
163void 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//////////////////////////////////////////////////////////////////////////////
213template <typename T>
214void 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//////////////////////////////////////////////////////////////////////////////
249void 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//////////////////////////////////////////////////////////////////////////////
273void DefectMap::setHotEnabled(bool enabled)
274{
275 m_HotEnabled = enabled;
276 emit pixelsUpdated(m_HotEnabled ? m_HotPixelsCount : 0, m_ColdPixelsCount);
277}
278
279//////////////////////////////////////////////////////////////////////////////
280///
281//////////////////////////////////////////////////////////////////////////////
282void DefectMap::setColdEnabled(bool enabled)
283{
284 m_ColdEnabled = enabled;
285 emit pixelsUpdated(m_HotPixelsCount, m_ColdEnabled ? m_ColdPixelsCount : 0);
286}
void append(const QJsonValue &value)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
iterator insert(QLatin1StringView key, const QJsonValue &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.