Changeset View
Changeset View
Standalone View
Standalone View
kstars/fitsviewer/fitshistogram.cpp
Show All 15 Lines | |||||
16 | #include "fitsdata.h" | 16 | #include "fitsdata.h" | ||
17 | #include "fitstab.h" | 17 | #include "fitstab.h" | ||
18 | #include "fitsview.h" | 18 | #include "fitsview.h" | ||
19 | #include "fitsviewer.h" | 19 | #include "fitsviewer.h" | ||
20 | 20 | | |||
21 | #include <KMessageBox> | 21 | #include <KMessageBox> | ||
22 | 22 | | |||
23 | #include <QtConcurrent> | 23 | #include <QtConcurrent> | ||
24 | #include <type_traits> | ||||
24 | #include <zlib.h> | 25 | #include <zlib.h> | ||
25 | 26 | | |||
26 | histogramUI::histogramUI(QDialog * parent) : QDialog(parent) | 27 | histogramUI::histogramUI(QDialog * parent) : QDialog(parent) | ||
27 | { | 28 | { | ||
28 | setupUi(parent); | 29 | setupUi(parent); | ||
29 | setModal(false); | 30 | setModal(false); | ||
30 | } | 31 | } | ||
31 | 32 | | |||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Line(s) | 195 | { | |||
196 | FITSMin[i] = min; | 197 | FITSMin[i] = min; | ||
197 | FITSMax[i] = max; | 198 | FITSMax[i] = max; | ||
198 | } | 199 | } | ||
199 | 200 | | |||
200 | uint32_t samples = width * height; | 201 | uint32_t samples = width * height; | ||
201 | //binCount = static_cast<uint16_t>(sqrt(samples)); | 202 | //binCount = static_cast<uint16_t>(sqrt(samples)); | ||
202 | binCount = qMin(FITSMax[0] - FITSMin[0], 400.0); | 203 | binCount = qMin(FITSMax[0] - FITSMin[0], 400.0); | ||
203 | if (binCount <= 0) | 204 | if (binCount <= 0) | ||
204 | binCount = 100; | 205 | binCount = 400; | ||
205 | 206 | | |||
206 | for (int n = 0; n < channels; n++) | 207 | for (int n = 0; n < channels; n++) | ||
207 | { | 208 | { | ||
208 | intensity[n].fill(0, binCount); | 209 | intensity[n].fill(0, binCount); | ||
209 | frequency[n].fill(0, binCount); | 210 | frequency[n].fill(0, binCount); | ||
210 | cumulativeFrequency[n].fill(0, binCount); | 211 | cumulativeFrequency[n].fill(0, binCount); | ||
211 | binWidth[n] = (FITSMax[n] - FITSMin[n]) / (binCount - 1); | 212 | binWidth[n] = (FITSMax[n] - FITSMin[n]) / (binCount - 1); | ||
212 | } | 213 | } | ||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Line(s) | |||||
262 | 263 | | |||
263 | futures.clear(); | 264 | futures.clear(); | ||
264 | 265 | | |||
265 | for (int n = 0; n < channels; n++) | 266 | for (int n = 0; n < channels; n++) | ||
266 | { | 267 | { | ||
267 | futures.append(QtConcurrent::run([ = ]() | 268 | futures.append(QtConcurrent::run([ = ]() | ||
268 | { | 269 | { | ||
269 | double median[3] = {0}; | 270 | double median[3] = {0}; | ||
270 | bool cutoffSpikes = ui->hideSaturated->isChecked(); | 271 | const bool cutoffSpikes = ui->hideSaturated->isChecked(); | ||
271 | uint32_t halfCumulative = cumulativeFrequency[n][binCount - 1] / 2; | 272 | const uint32_t halfCumulative = cumulativeFrequency[n][binCount - 1] / 2; | ||
273 | | ||||
274 | // Find which bin contains the median. | ||||
275 | int median_bin = -1; | ||||
272 | for (int i = 0; i < binCount; i++) | 276 | for (int i = 0; i < binCount; i++) | ||
273 | { | 277 | { | ||
274 | if (cumulativeFrequency[n][i] > halfCumulative) | 278 | if (cumulativeFrequency[n][i] > halfCumulative) | ||
275 | { | 279 | { | ||
276 | median[n] = round(i * binWidth[n] + FITSMin[n]); | 280 | median_bin = i; | ||
277 | break; | 281 | break; | ||
278 | } | 282 | } | ||
279 | } | 283 | } | ||
284 | | ||||
285 | if (median_bin >= 0) | ||||
286 | { | ||||
287 | // Resample the data, only looking at samples whose values are in the "median bin". | ||||
288 | // Count the frequency of those samples to determine the median. | ||||
289 | // Since it's possible that type T is double with samples in the 0.0 to 1.0 range, | ||||
290 | // we may need to count sample intervals that aren't integers. | ||||
291 | | ||||
292 | const int sampleBy = samples > 1000000 ? samples / 1000000 : 1; | ||||
293 | // The lowest sample value to consider (bottom of the median bin). | ||||
294 | const T lowValue = (median_bin - 0.5) * binWidth[n] + FITSMin[n]; | ||||
295 | // The highest sample value to consider. | ||||
296 | const T highValue = lowValue + binWidth[n]; | ||||
297 | // discrete_type is true if T is a float or double. | ||||
298 | const bool discrete_type = !(std::is_same<T, double>::value || std::is_same<T, float>::value); | ||||
299 | // If discrete is true, we count how many times the different integers between lowValue | ||||
300 | // and highValue occur. If it's false, we need to break up the range into intervals | ||||
301 | // that are not of size 1.0. | ||||
302 | const bool discrete = discrete_type || binWidth[n] >= 50; | ||||
303 | constexpr int continuous_num_bins = 100; | ||||
304 | const int median_num_bins = discrete ? binWidth[n] : continuous_num_bins; | ||||
305 | const double bin_sample_width = discrete ? 1.0 : binWidth[n] / continuous_num_bins; | ||||
306 | | ||||
307 | // This will contain the frequency counts | ||||
308 | QVector<uint32_t> median_bin_frequency; | ||||
309 | median_bin_frequency.fill(0, median_num_bins); | ||||
310 | | ||||
311 | uint32_t offset = n * samples; | ||||
312 | for (uint32_t i = 0; i < samples; i += sampleBy) | ||||
313 | { | ||||
314 | const T value = buffer[i + offset]; | ||||
315 | if (value >= lowValue && value < highValue) | ||||
316 | { | ||||
317 | const int id = discrete ? | ||||
318 | value - lowValue : | ||||
319 | qMin(median_num_bins - 1, (int) rint((value - lowValue) / bin_sample_width)); | ||||
320 | median_bin_frequency[id] += sampleBy; | ||||
321 | } | ||||
322 | } | ||||
323 | | ||||
324 | // Search the median bin's histogram to find the exact median value. | ||||
325 | uint32_t sum = 0; | ||||
326 | if (median_bin > 0) { | ||||
327 | sum = cumulativeFrequency[n][median_bin - 1]; | ||||
328 | } | ||||
329 | for (int i = 0; i < median_num_bins; ++i) | ||||
330 | { | ||||
331 | sum += median_bin_frequency[i]; | ||||
332 | if (sum >= halfCumulative) | ||||
333 | { | ||||
334 | median[n] = lowValue + i * bin_sample_width; | ||||
335 | break; | ||||
336 | } | ||||
337 | } | ||||
338 | } | ||||
280 | imageData->setMedian(median[n], n); | 339 | imageData->setMedian(median[n], n); | ||
340 | | ||||
281 | if (cutoffSpikes) | 341 | if (cutoffSpikes) | ||
282 | { | 342 | { | ||
283 | QVector<double> sortedFreq = frequency[n]; | 343 | QVector<double> sortedFreq = frequency[n]; | ||
284 | std::sort(sortedFreq.begin(), sortedFreq.end()); | 344 | std::sort(sortedFreq.begin(), sortedFreq.end()); | ||
285 | double cutoff = sortedFreq[binCount * 0.99]; | 345 | double cutoff = sortedFreq[binCount * 0.99]; | ||
286 | for (int i = 0; i < binCount; i++) | 346 | for (int i = 0; i < binCount; i++) | ||
287 | { | 347 | { | ||
288 | if (frequency[n][i] >= cutoff) | 348 | if (frequency[n][i] >= cutoff) | ||
▲ Show 20 Lines • Show All 512 Lines • Show Last 20 Lines |