1 /* 2 SPDX-FileCopyrightText: 2004 Jasem Mutlaq <mutlaqja@ikarustech.com> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 6 Some code fragments were adapted from Peter Kirchgessner's FITS plugin 7 SPDX-FileCopyrightText: Peter Kirchgessner <http://members.aol.com/pkirchg> 8 */ 9 10 #pragma once 11 12 #include "config-kstars.h" 13 14 // From StellarSolver 15 #ifdef HAVE_STELLARSOLVER 16 #include <structuredefinitions.h> 17 #else 18 #include "structuredefinitions.h" 19 #endif 20 21 #include "kstarsdatetime.h" 22 #include "bayer.h" 23 #include "skybackground.h" 24 #include "fitscommon.h" 25 #include "fitsstardetector.h" 26 27 #ifdef WIN32 28 // This header must be included before fitsio.h to avoid compiler errors with Visual Studio 29 #include <windows.h> 30 #endif 31 32 #include <fitsio.h> 33 34 #include <QFuture> 35 #include <QObject> 36 #include <QRect> 37 #include <QVariant> 38 #include <QTemporaryFile> 39 40 #ifndef KSTARS_LITE 41 #include <kxmlguiwindow.h> 42 #ifdef HAVE_WCSLIB 43 #include <wcs.h> 44 #endif 45 #endif 46 47 #include "fitsskyobject.h" 48 49 class QProgressDialog; 50 51 class SkyPoint; 52 class FITSHistogramData; 53 class Edge; 54 55 class FITSData : public QObject 56 { 57 Q_OBJECT 58 59 // Name of FITS file 60 Q_PROPERTY(QString filename READ filename) 61 // Size of file in bytes 62 Q_PROPERTY(qint64 size READ size) 63 // Width in pixels 64 Q_PROPERTY(quint16 width READ width) 65 // Height in pixels 66 Q_PROPERTY(quint16 height READ height) 67 // FITS MODE --> Normal, Focus, Guide..etc 68 Q_PROPERTY(FITSMode mode MEMBER m_Mode) 69 // 1 channel (grayscale) or 3 channels (RGB) 70 Q_PROPERTY(quint8 channels READ channels) 71 // Bits per pixel 72 Q_PROPERTY(quint8 bpp READ bpp) 73 // Does FITS have WSC header? 74 Q_PROPERTY(bool hasWCS READ hasWCS) 75 // Does FITS have bayer data? 76 Q_PROPERTY(bool hasDebayer READ hasDebayer) 77 78 public: 79 explicit FITSData(FITSMode fitsMode = FITS_NORMAL); 80 explicit FITSData(const QSharedPointer<FITSData> &other); 81 ~FITSData(); 82 83 /** Structure to hold FITS Header records */ 84 typedef struct 85 { 86 QString key; /** FITS Header Key */ 87 QVariant value; /** FITS Header Value */ 88 QString comment; /** FITS Header Comment, if any */ 89 } Record; 90 91 typedef enum 92 { 93 Idle, 94 Busy, 95 Success, 96 Failure 97 } WCSState; 98 99 //////////////////////////////////////////////////////////////////////////////////////// 100 //////////////////////////////////////////////////////////////////////////////////////// 101 /// Read and Write file/buffer Functions. 102 //////////////////////////////////////////////////////////////////////////////////////// 103 //////////////////////////////////////////////////////////////////////////////////////// 104 /** 105 * @brief loadFITS Loading FITS file asynchronously. 106 * @param inFilename Path to FITS file (or compressed fits.gz) 107 * @param silent If set, error messages are ignored. If set to false, the error message will get displayed in a popup. 108 * @return A QFuture that can be watched until the async operation is complete. 109 */ 110 QFuture<bool> loadFromFile(const QString &inFilename, bool silent = true); 111 112 /** 113 * @brief loadFITSFromMemory Loading FITS from memory buffer. 114 * @param buffer The memory buffer containing the fits data. 115 * @param extension file extension (e.g. "jpg", "fits", "cr2"...etc) 116 * @param inFilename Set filename metadata, does not load from file. 117 * @param silent If set, error messages are ignored. If set to false, the error message will get displayed in a popup. 118 * @return bool indicating success or failure. 119 */ 120 bool loadFromBuffer(const QByteArray &buffer, const QString &extension, const QString &inFilename = QString(), 121 bool silent = true); 122 123 /** 124 * @brief parseSolution Parse the WCS solution information from the header into the given struct. 125 * @param solution Solution structure to fill out. 126 * @return True if parsing successful, false otherwise. 127 */ 128 bool parseSolution(FITSImage::Solution &solution) const; 129 130 /* Save FITS or JPG/PNG*/ 131 bool saveImage(const QString &newFilename); 132 133 // Access functions 134 void clearImageBuffers(); 135 void setImageBuffer(uint8_t *buffer); 136 uint8_t const *getImageBuffer() const; 137 uint8_t *getWritableImageBuffer(); 138 139 //////////////////////////////////////////////////////////////////////////////////////// 140 //////////////////////////////////////////////////////////////////////////////////////// 141 /// Statistics Functions. 142 //////////////////////////////////////////////////////////////////////////////////////// 143 //////////////////////////////////////////////////////////////////////////////////////// 144 // Calculate stats 145 void calculateStats(bool refresh = false); 146 void saveStatistics(FITSImage::Statistic &other); 147 void restoreStatistics(FITSImage::Statistic &other); getStatistics()148 FITSImage::Statistic const &getStatistics() const 149 { 150 return m_Statistics; 151 }; 152 width()153 uint16_t width() const 154 { 155 return m_Statistics.width; 156 } height()157 uint16_t height() const 158 { 159 return m_Statistics.height; 160 } size()161 int64_t size() const 162 { 163 return m_Statistics.size; 164 } channels()165 int channels() const 166 { 167 return m_Statistics.channels; 168 } samplesPerChannel()169 uint32_t samplesPerChannel() const 170 { 171 return m_Statistics.samples_per_channel; 172 } dataType()173 uint32_t dataType() const 174 { 175 return m_Statistics.dataType; 176 } 177 double getMin(uint8_t channel = 0) const 178 { 179 return m_Statistics.min[channel]; 180 } 181 double getMax(uint8_t channel = 0) const 182 { 183 return m_Statistics.max[channel]; 184 } 185 void setMinMax(double newMin, double newMax, uint8_t channel = 0); 186 void getMinMax(double *min, double *max, uint8_t channel = 0) const 187 { 188 *min = m_Statistics.min[channel]; 189 *max = m_Statistics.max[channel]; 190 } 191 void setStdDev(double value, uint8_t channel = 0) 192 { 193 m_Statistics.stddev[channel] = value; 194 } 195 double getStdDev(uint8_t channel = 0) const 196 { 197 return m_Statistics.stddev[channel]; 198 } 199 double getAverageStdDev() const; 200 void setMean(double value, uint8_t channel = 0) 201 { 202 m_Statistics.mean[channel] = value; 203 } 204 double getMean(uint8_t channel = 0) const 205 { 206 return m_Statistics.mean[channel]; 207 } 208 // for single channel, just return the mean for channel zero 209 // for color, return the average 210 double getAverageMean() const; 211 void setMedian(double val, uint8_t channel = 0) 212 { 213 m_Statistics.median[channel] = val; 214 } 215 // for single channel, just return the median for channel zero 216 // for color, return the average 217 double getAverageMedian() const; 218 double getMedian(uint8_t channel = 0) const 219 { 220 return m_Statistics.median[channel]; 221 } 222 getBytesPerPixel()223 int getBytesPerPixel() const 224 { 225 return m_Statistics.bytesPerPixel; 226 } setSNR(double val)227 void setSNR(double val) 228 { 229 m_Statistics.SNR = val; 230 } getSNR()231 double getSNR() const 232 { 233 return m_Statistics.SNR; 234 } bpp()235 uint32_t bpp() const 236 { 237 switch(m_Statistics.dataType) 238 { 239 case TBYTE: 240 return 8; 241 break; 242 case TSHORT: 243 case TUSHORT: 244 return 16; 245 break; 246 case TLONG: 247 case TULONG: 248 case TFLOAT: 249 return 32; 250 break; 251 case TLONGLONG: 252 case TDOUBLE: 253 return 64; 254 break; 255 default: 256 return 8; 257 } 258 } 259 double getADU() const; 260 261 //////////////////////////////////////////////////////////////////////////////////////// 262 //////////////////////////////////////////////////////////////////////////////////////// 263 /// FITS Header Functions. 264 //////////////////////////////////////////////////////////////////////////////////////// 265 //////////////////////////////////////////////////////////////////////////////////////// 266 // FITS Record 267 bool getRecordValue(const QString &key, QVariant &value) const; getRecords()268 const QList<Record> &getRecords() const 269 { 270 return m_HeaderRecords; 271 } 272 273 //////////////////////////////////////////////////////////////////////////////////////// 274 //////////////////////////////////////////////////////////////////////////////////////// 275 /// Star Search & HFR Functions. 276 //////////////////////////////////////////////////////////////////////////////////////// 277 //////////////////////////////////////////////////////////////////////////////////////// 278 // Star Detection - Native KStars implementation setStarAlgorithm(StarAlgorithm algorithm)279 void setStarAlgorithm(StarAlgorithm algorithm) 280 { 281 starAlgorithm = algorithm; 282 } getDetectedStars()283 int getDetectedStars() const 284 { 285 return starCenters.count(); 286 } areStarsSearched()287 bool areStarsSearched() const 288 { 289 return starsSearched; 290 } appendStar(Edge * newCenter)291 void appendStar(Edge *newCenter) 292 { 293 starCenters.append(newCenter); 294 } getStarCenters()295 const QList<Edge *> &getStarCenters() const 296 { 297 return starCenters; 298 } 299 QList<Edge *> getStarCentersInSubFrame(QRect subFrame) const; 300 setStarCenters(const QList<Edge * > & centers)301 void setStarCenters(const QList<Edge*> ¢ers) 302 { 303 starCenters = centers; 304 } 305 QFuture<bool> findStars(StarAlgorithm algorithm = ALGORITHM_CENTROID, const QRect &trackingBox = QRect()); 306 setSkyBackground(const SkyBackground & bg)307 void setSkyBackground(const SkyBackground &bg) 308 { 309 m_SkyBackground = bg; 310 } getSkyBackground()311 const SkyBackground &getSkyBackground() const 312 { 313 return m_SkyBackground; 314 } getSourceExtractorSettings()315 const QVariantMap &getSourceExtractorSettings() const 316 { 317 return m_SourceExtractorSettings; 318 } setSourceExtractorSettings(const QVariantMap & settings)319 void setSourceExtractorSettings(const QVariantMap &settings) 320 { 321 m_SourceExtractorSettings = settings; 322 } 323 // Use SEP (Sextractor Library) to find stars 324 template <typename T> 325 void getFloatBuffer(float *buffer, int x, int y, int w, int h) const; 326 //int findSEPStars(QList<Edge*> &, const QRect &boundary = QRect()) const; 327 328 // Apply ring filter to searched stars 329 int filterStars(const float innerRadius, const float outerRadius); 330 331 // Half Flux Radius getSelectedHFRStar()332 Edge *getSelectedHFRStar() const 333 { 334 return m_SelectedHFRStar; 335 } 336 337 // Calculates the median star eccentricity. 338 double getEccentricity(); 339 340 double getHFR(HFRType type = HFR_AVERAGE); 341 double getHFR(int x, int y); 342 343 //////////////////////////////////////////////////////////////////////////////////////// 344 //////////////////////////////////////////////////////////////////////////////////////// 345 /// Date & Time (WCS) Functions. 346 //////////////////////////////////////////////////////////////////////////////////////// 347 //////////////////////////////////////////////////////////////////////////////////////// 348 getDateTime()349 const KStarsDateTime &getDateTime() const 350 { 351 return m_DateTime; 352 } 353 354 // Set the time, for testing (doesn't set header field) setDateTime(const KStarsDateTime & t)355 void setDateTime(const KStarsDateTime &t) 356 { 357 m_DateTime = t; 358 } 359 360 //////////////////////////////////////////////////////////////////////////////////////// 361 //////////////////////////////////////////////////////////////////////////////////////// 362 /// World Coordinate System (WCS) Functions. 363 //////////////////////////////////////////////////////////////////////////////////////// 364 //////////////////////////////////////////////////////////////////////////////////////// 365 // Check if a particular point exists within the image 366 bool contains(const QPointF &point) const; 367 // Check if image has valid WCS header information and set HasWCS accordingly. Call in loadFITS() 368 bool checkForWCS(); 369 // Does image have valid WCS? hasWCS()370 bool hasWCS() 371 { 372 return HasWCS; 373 } 374 // The WCS can be loaded without pre-computing each pixel's position. This can make certain 375 // operations slow. FullWCS() is true if the pixel positions are pre-calculated. fullWCS()376 bool fullWCS() 377 { 378 return FullWCS; 379 } 380 // Load WCS data 381 bool loadWCS(bool extras = true); 382 // Get WCS State getWCSState()383 WCSState getWCSState() const 384 { 385 return m_WCSState; 386 } 387 388 /** 389 * @brief wcsToPixel Given J2000 (RA0,DE0) coordinates. Find in the image the corresponding pixel coordinates. 390 * @param wcsCoord Coordinates of target 391 * @param wcsPixelPoint Return XY FITS coordinates 392 * @param wcsImagePoint Return XY Image coordinates 393 * @return True if conversion is successful, false otherwise. 394 */ 395 bool wcsToPixel(const SkyPoint &wcsCoord, QPointF &wcsPixelPoint, QPointF &wcsImagePoint); 396 397 /** 398 * @brief pixelToWCS Convert Pixel coordinates to J2000 world coordinates 399 * @param wcsPixelPoint Pixel coordinates in XY Image space. 400 * @param wcsCoord Store back WCS world coordinate in wcsCoord 401 * @return True if successful, false otherwise. 402 */ 403 bool pixelToWCS(const QPointF &wcsPixelPoint, SkyPoint &wcsCoord); 404 405 /** 406 * @brief injectWCS Add WCS keywords to file 407 * @param orientation Solver orientation, degrees E of N. 408 * @param ra J2000 Right Ascension 409 * @param dec J2000 Declination 410 * @param pixscale Pixel scale in arcsecs per pixel 411 * @param eastToTheRight if true, then when the image is rotated so that north is up, then east would be to the right on the image. 412 * @return True if file is successfully updated with WCS info. 413 */ 414 bool injectWCS(double orientation, double ra, double dec, double pixscale, bool eastToTheRight); 415 416 //////////////////////////////////////////////////////////////////////////////////////// 417 //////////////////////////////////////////////////////////////////////////////////////// 418 /// Debayering Functions 419 //////////////////////////////////////////////////////////////////////////////////////// 420 //////////////////////////////////////////////////////////////////////////////////////// 421 422 // Debayer hasDebayer()423 bool hasDebayer() 424 { 425 return HasDebayer; 426 } 427 428 /** 429 * @brief debayer the 1-channel data to 3-channel RGB using the default debayer pattern detected in the FITS header. 430 * @param reload If true, it will read the image again from disk before performing debayering. This is necessary to attempt 431 * subsequent debayering processes on an already debayered image. 432 */ 433 bool debayer(bool reload = false); 434 bool debayer_8bit(); 435 bool debayer_16bit(); 436 void getBayerParams(BayerParams *param); 437 void setBayerParams(BayerParams *param); 438 439 //////////////////////////////////////////////////////////////////////////////////////// 440 //////////////////////////////////////////////////////////////////////////////////////// 441 /// Public Histogram Functions 442 //////////////////////////////////////////////////////////////////////////////////////// 443 //////////////////////////////////////////////////////////////////////////////////////// 444 resetHistogram()445 void resetHistogram() 446 { 447 m_HistogramConstructed = false; 448 } 449 double getHistogramBinWidth(int channel = 0) 450 { 451 return m_HistogramBinWidth[channel]; 452 } 453 454 const QVector<uint32_t> &getCumulativeFrequency(uint8_t channel = 0) const 455 { 456 return m_CumulativeFrequency[channel]; 457 } 458 const QVector<double> &getHistogramIntensity(uint8_t channel = 0) const 459 { 460 return m_HistogramIntensity[channel]; 461 } 462 const QVector<double> &getHistogramFrequency(uint8_t channel = 0) const 463 { 464 return m_HistogramFrequency[channel]; 465 } 466 467 /** 468 * @brief getJMIndex Overall contrast of the image used in find centeroid algorithm. i.e. is the image diffuse? 469 * @return Value of JMIndex 470 */ getJMIndex()471 double getJMIndex() const 472 { 473 return m_JMIndex; 474 } 475 isHistogramConstructed()476 bool isHistogramConstructed() 477 { 478 return m_HistogramConstructed; 479 } 480 void constructHistogram(); 481 482 //////////////////////////////////////////////////////////////////////////////////////// 483 //////////////////////////////////////////////////////////////////////////////////////// 484 /// Filters and Rotations Functions. 485 //////////////////////////////////////////////////////////////////////////////////////// 486 //////////////////////////////////////////////////////////////////////////////////////// 487 // Filter 488 void applyFilter(FITSScale type, uint8_t *image = nullptr, QVector<double> *targetMin = nullptr, 489 QVector<double> *targetMax = nullptr); 490 491 // Rotation counter. We keep count to rotate WCS keywords on save 492 int getRotCounter() const; 493 void setRotCounter(int value); 494 495 // Filename filename()496 const QString &filename() const 497 { 498 return m_Filename; 499 } compressedFilename()500 const QString &compressedFilename() const 501 { 502 return m_compressedFilename; 503 } isCompressed()504 bool isCompressed() const 505 { 506 return m_isCompressed; 507 } 508 509 // Horizontal flip counter. We keep count to rotate WCS keywords on save 510 int getFlipHCounter() const; 511 void setFlipHCounter(int value); 512 513 // Horizontal flip counter. We keep count to rotate WCS keywords on save 514 int getFlipVCounter() const; 515 void setFlipVCounter(int value); 516 517 //////////////////////////////////////////////////////////////////////////////////////// 518 //////////////////////////////////////////////////////////////////////////////////////// 519 /// Object Search Functions. 520 //////////////////////////////////////////////////////////////////////////////////////// 521 //////////////////////////////////////////////////////////////////////////////////////// 522 #ifndef KSTARS_LITE 523 #ifdef HAVE_WCSLIB 524 bool searchObjects(); 525 bool findObjectsInImage(SkyPoint startPoint, SkyPoint endPoint); 526 bool findWCSBounds(double &minRA, double &maxRA, double &minDec, double &maxDec); 527 #endif 528 #endif getSkyObjects()529 const QList<FITSSkyObject *> &getSkyObjects() const 530 { 531 return m_SkyObjects; 532 } 533 534 //////////////////////////////////////////////////////////////////////////////////////// 535 //////////////////////////////////////////////////////////////////////////////////////// 536 /// Image Conversion Functions. 537 //////////////////////////////////////////////////////////////////////////////////////// 538 //////////////////////////////////////////////////////////////////////////////////////// 539 // Create autostretch image from FITS File 540 static QImage FITSToImage(const QString &filename); 541 542 /** 543 * @brief ImageToFITS Convert an image file with supported format to a FITS file. 544 * @param filename full path to filename without extension 545 * @param format file extension. Supported formats are whatever supported by Qt (e.g. PNG, JPG,..etc) 546 * @param output Output temporary file path. The created file is generated by the function and store in output. 547 * @return True if conversion is successful, false otherwise. 548 */ 549 static bool ImageToFITS(const QString &filename, const QString &format, QString &output); 550 551 QString getLastError() const; 552 553 signals: 554 void converted(QImage); 555 556 /** 557 * @brief histogramReady Sends signal when histogram construction is complete. 558 */ 559 void histogramReady(); 560 561 /** 562 * @brief dataChanged Send signal when undelying raw data buffer data changed. 563 */ 564 void dataChanged(); 565 566 private: 567 void loadCommon(const QString &inFilename); 568 /** 569 * @brief privateLoad Load an image (FITS, RAW, or images supported by Qt like jpeg, png). 570 * @param Buffer pointer to image data. If buffer is emtpy, read from disk (m_Filename). 571 * @param silent If true, suppress any messages. 572 * @return true if successfully loaded, false otherwise. 573 */ 574 bool privateLoad(const QByteArray &buffer, const QString &extension, bool silent); 575 576 // Load Qt-supported images. 577 bool loadCanonicalImage(const QByteArray &buffer, const QString &extension, bool silent); 578 // Load FITS images. 579 bool loadFITSImage(const QByteArray &buffer, const QString &extension, bool silent); 580 // Load RAW images. 581 bool loadRAWImage(const QByteArray &buffer, const QString &extension, bool silent); 582 583 void rotWCSFITS(int angle, int mirror); 584 void calculateMinMax(bool refresh = false); 585 void calculateMedian(bool refresh = false); 586 bool checkDebayer(); 587 void readWCSKeys(); 588 589 // Record last FITS error 590 void recordLastError(int errorCode); 591 void logOOMError(uint32_t requiredMemory = 0); 592 593 // FITS Record 594 bool parseHeader(); 595 //int getFITSRecord(QString &recordList, int &nkeys); 596 597 // Templated functions 598 template <typename T> 599 bool debayer(); 600 601 template <typename T> 602 bool rotFITS(int rotate, int mirror); 603 604 // Apply Filter 605 template <typename T> 606 void applyFilter(FITSScale type, uint8_t *targetImage, QVector<double> * min = nullptr, QVector<double> * max = nullptr); 607 608 template <typename T> 609 void calculateMinMax(); 610 template <typename T> 611 void calculateMedian(); 612 613 template <typename T> 614 QPair<T, T> getParitionMinMax(uint32_t start, uint32_t stride); 615 616 /* Calculate the Gaussian blur matrix and apply it to the image using the convolution filter */ 617 QVector<double> createGaussianKernel(int size, double sigma); 618 template <typename T> 619 void convolutionFilter(const QVector<double> &kernel, int kernelSize); 620 template <typename T> 621 void gaussianBlur(int kernelSize, double sigma); 622 623 /* Calculate running average & standard deviation using Welford’s method for computing variance */ 624 template <typename T> 625 void runningAverageStdDev(); 626 template <typename T> 627 QPair<double, double> getSquaredSumAndMean(uint32_t start, uint32_t stride); 628 629 template <typename T> 630 void convertToQImage(double dataMin, double dataMax, double scale, double zero, QImage &image); 631 632 //////////////////////////////////////////////////////////////////////////////////////// 633 //////////////////////////////////////////////////////////////////////////////////////// 634 /// Private Histogram Functions. 635 //////////////////////////////////////////////////////////////////////////////////////// 636 //////////////////////////////////////////////////////////////////////////////////////// 637 template <typename T> void constructHistogramInternal(); 638 639 /// Pointer to CFITSIO FITS file struct 640 fitsfile *fptr { nullptr }; 641 /// Generic data image buffer 642 uint8_t *m_ImageBuffer { nullptr }; 643 /// Above buffer size in bytes 644 uint32_t m_ImageBufferSize { 0 }; 645 /// Is this a temporary file or one loaded from disk? 646 bool m_isTemporary { false }; 647 /// is this file compress (.fits.fz)? 648 bool m_isCompressed { false }; 649 /// Did we search for stars yet? 650 bool starsSearched { false }; 651 ///Star Selection Algorithm 652 StarAlgorithm starAlgorithm { ALGORITHM_GRADIENT }; 653 /// Do we have WCS keywords in this FITS data? 654 bool HasWCS { false }; /// Do we have WCS keywords in this FITS data? 655 /// we can initialize wcs without computing all the image positions. 656 bool FullWCS { false }; 657 /// Is the image debayarable? 658 bool HasDebayer { false }; 659 660 /// Our very own file name 661 QString m_Filename, m_compressedFilename; 662 /// FITS Mode (Normal, WCS, Guide, Focus..etc) 663 FITSMode m_Mode; 664 // FITS Observed UTC date time 665 KStarsDateTime m_DateTime; 666 667 /// How many times the image was rotated? Useful for WCS keywords rotation on save. 668 int rotCounter { 0 }; 669 /// How many times the image was flipped horizontally? 670 int flipHCounter { 0 }; 671 /// How many times the image was flipped vertically? 672 int flipVCounter { 0 }; 673 674 /// WCS Struct 675 struct wcsprm *m_WCSHandle 676 { 677 nullptr 678 }; 679 /// Number of coordinate representations found. 680 int m_nwcs {0}; 681 WCSState m_WCSState { Idle }; 682 /// All the stars we detected, if any. 683 QList<Edge *> starCenters; 684 QList<Edge *> localStarCenters; 685 /// The biggest fattest star in the image. 686 Edge *m_SelectedHFRStar { nullptr }; 687 688 /// Bayer parameters 689 BayerParams debayerParams; 690 QTemporaryFile m_TemporaryDataFile; 691 692 /// Data type of fits pixel in the image. Used when saving FITS again. 693 /// There is bit depth and also data type. They're not the same. 694 /// 16bit can be either SHORT_IMG or USHORT_IMG, so m_FITSBITPIX specifies which is 695 int m_FITSBITPIX {USHORT_IMG}; 696 FITSImage::Statistic m_Statistics; 697 698 // A list of header records 699 QList<Record> m_HeaderRecords; 700 701 // Sky Background 702 SkyBackground m_SkyBackground; 703 704 // Detector Settings 705 QVariantMap m_SourceExtractorSettings; 706 707 QFuture<bool> m_StarFindFuture; 708 709 QList<FITSSkyObject *> m_SkyObjects; 710 bool m_ObjectsSearched {false}; 711 712 QString m_LastError; 713 714 //////////////////////////////////////////////////////////////////////////////////////// 715 //////////////////////////////////////////////////////////////////////////////////////// 716 /// Histogram Variables 717 //////////////////////////////////////////////////////////////////////////////////////// 718 //////////////////////////////////////////////////////////////////////////////////////// 719 QVector<QVector<uint32_t>> m_CumulativeFrequency; 720 QVector<QVector<double>> m_HistogramIntensity; 721 QVector<QVector<double>> m_HistogramFrequency; 722 QVector<double> m_HistogramBinWidth; 723 uint16_t m_HistogramBinCount { 0 }; 724 double m_JMIndex { 1 }; 725 bool m_HistogramConstructed { false }; 726 727 // Cached values for hfr and eccentricity computations 728 double cacheHFR { -1 }; 729 HFRType cacheHFRType { HFR_AVERAGE }; 730 double cacheEccentricity { -1 }; 731 }; 732