From c264ca12eb4e083d93309b2ba0faed18e786849e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Sun, 5 Feb 2023 09:20:56 +0100 Subject: [PATCH] Add ColorFilterArray --- libxisf.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++++++--- libxisf.h | 29 +++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/libxisf.cpp b/libxisf.cpp index ae84920..a5097b1 100644 --- a/libxisf.cpp +++ b/libxisf.cpp @@ -90,6 +90,36 @@ static std::unordered_map colorSpaceToString; static std::unordered_map vectorTypeSizes; static std::unordered_map matrixTypeSizes; +static const std::unordered_map> fitsNameToPropertyIdTypeConvert = { + {"OBSERVER", {"Observer:Name", QMetaType::QString}}, + {"RADECSYS", {"Observation:CelestialReferenceSystem", QMetaType::QString}}, + {"CRVAL1", {"Observation:Center:Dec", QMetaType::Double}}, + {"CRVAL2", {"Observation:Center:RA", QMetaType::Double}}, + {"CRPIX1", {"Observation:Center:X", QMetaType::Double}}, + {"CRPIX2", {"Observation:Center:Y", QMetaType::Double}}, + {"EQUINOX", {"Observation:Equinox", QMetaType::Double}}, + {"SITELAT", {"Observation:Location:Latitude", QMetaType::Double}}, + {"SITELONG", {"Observation:Location:Longitude", QMetaType::Double}}, + {"OBJECT", {"Observation:Object:Name", QMetaType::QString}}, + {"DEC", {"Observation:Object:Dec", QMetaType::Double}}, + {"RA", {"Observation:Object:RA", QMetaType::Double}}, + {"DATE-OBS", {"Observation:Time:Start", QMetaType::QDateTime}}, + {"DATE-END", {"Observation:Time:End", QMetaType::QDateTime}}, + {"GAIN", {"Instrument:Camera:Gain", QMetaType::Float}}, + {"ISOSPEED", {"Instrument:Camera:ISOSpeed", QMetaType::Int}}, + {"INSTRUME", {"Instrument:Camera:Name", QMetaType::QString}}, + {"ROTATANG", {"Instrument:Camera:Rotation", QMetaType::Float}}, + {"XBINNING", {"Instrument:Camera:XBinning", QMetaType::Int}}, + {"YBINNING", {"Instrument:Camera:YBinning", QMetaType::Int}}, + {"EXPTIME", {"Instrument:ExposureTime", QMetaType::Float}}, + {"FILTER", {"Instrument:Filter:Name", QMetaType::QString}}, + {"FOCUSPOS", {"Instrument:Focuser:Position", QMetaType::Float}}, + {"CCD-TEMP", {"Instrument:Sensor:Temperature", QMetaType::Float}}, + {"APTDIA", {"Instrument:Telescope:Aperture", QMetaType::Float}}, + {"FOCALLEN", {"Instrument:Telescope:FocalLength", QMetaType::Float}}, + {"TELESCOP", {"Instrument:Telescope:Name", QMetaType::QString}}, +}; + static void byteShuffle(QByteArray &data, int itemSize) { if(itemSize > 1) @@ -310,6 +340,16 @@ void Image::setColorSpace(ColorSpace newColorSpace) _colorSpace = newColorSpace; } +const ColorFilterArray Image::colorFilterArray() const +{ + return _cfa; +} + +void Image::setColorFilterArray(const ColorFilterArray cfa) +{ + _cfa = cfa; +} + const std::vector& Image::imageProperties() const { return _properties; @@ -317,14 +357,21 @@ const std::vector& Image::imageProperties() const void Image::addProperty(const Property &property) { - for(auto &p : _properties) - { - if(p.id == property.id) - throw Error("Duplicate property id"); - } + if(_propertiesId.count(property.id)) + throw Error("Duplicate property id"); + + _propertiesId[property.id] = _properties.size(); _properties.push_back(property); } +void Image::updateProperty(const Property &property) +{ + if(!_propertiesId.count(property.id)) + addProperty(property); + else + _properties[_propertiesId[property.id]] = property; +} + const std::vector Image::fitsKeywords() const { return _fitsKeywords; @@ -335,6 +382,19 @@ void Image::addFITSKeyword(const FITSKeyword &keyword) _fitsKeywords.push_back(keyword); } +bool Image::addFITSKeywordAsProperty(const QString &name, const QVariant &value) +{ + if(fitsNameToPropertyIdTypeConvert.count(name)) + { + auto &c = fitsNameToPropertyIdTypeConvert.at(name); + Property prop(c.first, value); + prop.value.convert(c.second); + updateProperty(prop); + return true; + } + return false; +} + void *Image::imageData() { return _dataBlock.data.data(); @@ -603,9 +663,11 @@ void XISFReader::readImageElement() if(_xml->tokenType() == QXmlStreamReader::StartElement) { if(_xml->name() == "Property") - image._properties.push_back(readPropertyElement()); + image.addProperty(readPropertyElement()); else if(_xml->name() == "FITSKeyword") image._fitsKeywords.push_back(readFITSKeyword()); + else if(_xml->name() == "ColorFilterArray") + image._cfa = readCFA(); else if(_xml->name() == "ICCProfile") { DataBlock icc = readDataBlock(); @@ -764,6 +826,23 @@ void XISFReader::readCompression(DataBlock &dataBlock) } } +ColorFilterArray XISFReader::readCFA() +{ + ColorFilterArray cfa; + QXmlStreamAttributes attributes = _xml->attributes(); + if(attributes.hasAttribute("pattern") && attributes.hasAttribute("width") && attributes.hasAttribute("height")) + { + cfa.pattern = attributes.value("pattern").toString(); + cfa.width = attributes.value("width").toInt(); + cfa.height = attributes.value("height").toInt(); + } + else + { + throw Error("ColorFilterArray element missing one of mandatory attributes"); + } + return cfa; +} + XISFWriter::XISFWriter() { _xml = std::make_unique(); @@ -874,6 +953,8 @@ void XISFWriter::writeImageElement(const Image &image) for(auto &fitsKeyword : image._fitsKeywords) writeFITSKeyword(fitsKeyword); + writeCFA(image); + _xml->writeEndElement(); } @@ -979,6 +1060,17 @@ void XISFWriter::writeMetadata() _xml->writeEndElement(); } +void XISFWriter::writeCFA(const Image &image) +{ + if(image._cfa.width && image._cfa.height) + { + _xml->writeEmptyElement("ColorFilterArray"); + _xml->writeAttribute("pattern", image._cfa.pattern); + _xml->writeAttribute("width", QString::number(image._cfa.width)); + _xml->writeAttribute("height", QString::number(image._cfa.height)); + } +} + #define REGISTER_METATYPE(type) { int id = qMetaTypeId(); \ typeToId.insert({#type, id}); idToType.insert({id, #type}); } diff --git a/libxisf.h b/libxisf.h index 726f8ee..d8ef683 100644 --- a/libxisf.h +++ b/libxisf.h @@ -74,6 +74,24 @@ struct LIBXISF_EXPORT FITSKeyword QString comment; }; +/** +Describe color filter array. Each letter in pattern describe color of element. +0 - A nonexistent or undefined CFA element +R - Red +G - Green +B - Blue +W - White or panchromatic +C - Cyan +M - Magenta +Y - Yellow +*/ +struct LIBXISF_EXPORT ColorFilterArray +{ + int width = 0; + int height = 0; + QString pattern; +}; + typedef std::pair Bounds; class LIBXISF_EXPORT Image @@ -138,10 +156,17 @@ public: void setSampleFormat(SampleFormat newSampleFormat); ColorSpace colorSpace() const; void setColorSpace(ColorSpace newColorSpace); + const ColorFilterArray colorFilterArray() const; + void setColorFilterArray(const ColorFilterArray cfa); const std::vector &imageProperties() const; void addProperty(const Property &property); + void updateProperty(const Property &property); const std::vector fitsKeywords() const; void addFITSKeyword(const FITSKeyword &keyword); + /** Add image property while doing automatic conversion of FITS name to XISF property + * For example OBSERVER => Observer:Name, SITELAT => Observation:Location:Latitude + */ + bool addFITSKeywordAsProperty(const QString &name, const QVariant &value); void* imageData(); template @@ -178,7 +203,9 @@ private: ColorSpace _colorSpace = Gray; DataBlock _dataBlock; QByteArray _iccProfile; + ColorFilterArray _cfa; std::vector _properties; + std::map _propertiesId; std::vector _fitsKeywords; friend class XISFReader; @@ -207,6 +234,7 @@ private: void readDataElement(DataBlock &dataBlock); DataBlock readDataBlock(); void readCompression(DataBlock &dataBlock); + ColorFilterArray readCFA(); std::unique_ptr _io; std::unique_ptr _xml; @@ -230,6 +258,7 @@ private: void writePropertyElement(const Property &property); void writeFITSKeyword(const FITSKeyword &keyword); void writeMetadata(); + void writeCFA(const Image &image); std::unique_ptr _xml; QByteArray _xisfHeader; QByteArray _attachmentsData;