Add ColorFilterArray
This commit is contained in:
+97
-5
@@ -90,6 +90,36 @@ static std::unordered_map<Image::ColorSpace, QString> colorSpaceToString;
|
|||||||
static std::unordered_map<int, size_t> vectorTypeSizes;
|
static std::unordered_map<int, size_t> vectorTypeSizes;
|
||||||
static std::unordered_map<int, size_t> matrixTypeSizes;
|
static std::unordered_map<int, size_t> matrixTypeSizes;
|
||||||
|
|
||||||
|
static const std::unordered_map<QString, std::pair<QString, int>> 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)
|
static void byteShuffle(QByteArray &data, int itemSize)
|
||||||
{
|
{
|
||||||
if(itemSize > 1)
|
if(itemSize > 1)
|
||||||
@@ -310,6 +340,16 @@ void Image::setColorSpace(ColorSpace newColorSpace)
|
|||||||
_colorSpace = newColorSpace;
|
_colorSpace = newColorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ColorFilterArray Image::colorFilterArray() const
|
||||||
|
{
|
||||||
|
return _cfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setColorFilterArray(const ColorFilterArray cfa)
|
||||||
|
{
|
||||||
|
_cfa = cfa;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<Property>& Image::imageProperties() const
|
const std::vector<Property>& Image::imageProperties() const
|
||||||
{
|
{
|
||||||
return _properties;
|
return _properties;
|
||||||
@@ -317,14 +357,21 @@ const std::vector<Property>& Image::imageProperties() const
|
|||||||
|
|
||||||
void Image::addProperty(const Property &property)
|
void Image::addProperty(const Property &property)
|
||||||
{
|
{
|
||||||
for(auto &p : _properties)
|
if(_propertiesId.count(property.id))
|
||||||
{
|
|
||||||
if(p.id == property.id)
|
|
||||||
throw Error("Duplicate property id");
|
throw Error("Duplicate property id");
|
||||||
}
|
|
||||||
|
_propertiesId[property.id] = _properties.size();
|
||||||
_properties.push_back(property);
|
_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<FITSKeyword> Image::fitsKeywords() const
|
const std::vector<FITSKeyword> Image::fitsKeywords() const
|
||||||
{
|
{
|
||||||
return _fitsKeywords;
|
return _fitsKeywords;
|
||||||
@@ -335,6 +382,19 @@ void Image::addFITSKeyword(const FITSKeyword &keyword)
|
|||||||
_fitsKeywords.push_back(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()
|
void *Image::imageData()
|
||||||
{
|
{
|
||||||
return _dataBlock.data.data();
|
return _dataBlock.data.data();
|
||||||
@@ -603,9 +663,11 @@ void XISFReader::readImageElement()
|
|||||||
if(_xml->tokenType() == QXmlStreamReader::StartElement)
|
if(_xml->tokenType() == QXmlStreamReader::StartElement)
|
||||||
{
|
{
|
||||||
if(_xml->name() == "Property")
|
if(_xml->name() == "Property")
|
||||||
image._properties.push_back(readPropertyElement());
|
image.addProperty(readPropertyElement());
|
||||||
else if(_xml->name() == "FITSKeyword")
|
else if(_xml->name() == "FITSKeyword")
|
||||||
image._fitsKeywords.push_back(readFITSKeyword());
|
image._fitsKeywords.push_back(readFITSKeyword());
|
||||||
|
else if(_xml->name() == "ColorFilterArray")
|
||||||
|
image._cfa = readCFA();
|
||||||
else if(_xml->name() == "ICCProfile")
|
else if(_xml->name() == "ICCProfile")
|
||||||
{
|
{
|
||||||
DataBlock icc = readDataBlock();
|
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()
|
XISFWriter::XISFWriter()
|
||||||
{
|
{
|
||||||
_xml = std::make_unique<QXmlStreamWriter>();
|
_xml = std::make_unique<QXmlStreamWriter>();
|
||||||
@@ -874,6 +953,8 @@ void XISFWriter::writeImageElement(const Image &image)
|
|||||||
for(auto &fitsKeyword : image._fitsKeywords)
|
for(auto &fitsKeyword : image._fitsKeywords)
|
||||||
writeFITSKeyword(fitsKeyword);
|
writeFITSKeyword(fitsKeyword);
|
||||||
|
|
||||||
|
writeCFA(image);
|
||||||
|
|
||||||
_xml->writeEndElement();
|
_xml->writeEndElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,6 +1060,17 @@ void XISFWriter::writeMetadata()
|
|||||||
_xml->writeEndElement();
|
_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<type>(); \
|
#define REGISTER_METATYPE(type) { int id = qMetaTypeId<type>(); \
|
||||||
typeToId.insert({#type, id}); idToType.insert({id, #type}); }
|
typeToId.insert({#type, id}); idToType.insert({id, #type}); }
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,24 @@ struct LIBXISF_EXPORT FITSKeyword
|
|||||||
QString comment;
|
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<double, double> Bounds;
|
typedef std::pair<double, double> Bounds;
|
||||||
|
|
||||||
class LIBXISF_EXPORT Image
|
class LIBXISF_EXPORT Image
|
||||||
@@ -138,10 +156,17 @@ public:
|
|||||||
void setSampleFormat(SampleFormat newSampleFormat);
|
void setSampleFormat(SampleFormat newSampleFormat);
|
||||||
ColorSpace colorSpace() const;
|
ColorSpace colorSpace() const;
|
||||||
void setColorSpace(ColorSpace newColorSpace);
|
void setColorSpace(ColorSpace newColorSpace);
|
||||||
|
const ColorFilterArray colorFilterArray() const;
|
||||||
|
void setColorFilterArray(const ColorFilterArray cfa);
|
||||||
const std::vector<Property> &imageProperties() const;
|
const std::vector<Property> &imageProperties() const;
|
||||||
void addProperty(const Property &property);
|
void addProperty(const Property &property);
|
||||||
|
void updateProperty(const Property &property);
|
||||||
const std::vector<FITSKeyword> fitsKeywords() const;
|
const std::vector<FITSKeyword> fitsKeywords() const;
|
||||||
void addFITSKeyword(const FITSKeyword &keyword);
|
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();
|
void* imageData();
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -178,7 +203,9 @@ private:
|
|||||||
ColorSpace _colorSpace = Gray;
|
ColorSpace _colorSpace = Gray;
|
||||||
DataBlock _dataBlock;
|
DataBlock _dataBlock;
|
||||||
QByteArray _iccProfile;
|
QByteArray _iccProfile;
|
||||||
|
ColorFilterArray _cfa;
|
||||||
std::vector<Property> _properties;
|
std::vector<Property> _properties;
|
||||||
|
std::map<QString, uint32_t> _propertiesId;
|
||||||
std::vector<FITSKeyword> _fitsKeywords;
|
std::vector<FITSKeyword> _fitsKeywords;
|
||||||
|
|
||||||
friend class XISFReader;
|
friend class XISFReader;
|
||||||
@@ -207,6 +234,7 @@ private:
|
|||||||
void readDataElement(DataBlock &dataBlock);
|
void readDataElement(DataBlock &dataBlock);
|
||||||
DataBlock readDataBlock();
|
DataBlock readDataBlock();
|
||||||
void readCompression(DataBlock &dataBlock);
|
void readCompression(DataBlock &dataBlock);
|
||||||
|
ColorFilterArray readCFA();
|
||||||
|
|
||||||
std::unique_ptr<QIODevice> _io;
|
std::unique_ptr<QIODevice> _io;
|
||||||
std::unique_ptr<QXmlStreamReader> _xml;
|
std::unique_ptr<QXmlStreamReader> _xml;
|
||||||
@@ -230,6 +258,7 @@ private:
|
|||||||
void writePropertyElement(const Property &property);
|
void writePropertyElement(const Property &property);
|
||||||
void writeFITSKeyword(const FITSKeyword &keyword);
|
void writeFITSKeyword(const FITSKeyword &keyword);
|
||||||
void writeMetadata();
|
void writeMetadata();
|
||||||
|
void writeCFA(const Image &image);
|
||||||
std::unique_ptr<QXmlStreamWriter> _xml;
|
std::unique_ptr<QXmlStreamWriter> _xml;
|
||||||
QByteArray _xisfHeader;
|
QByteArray _xisfHeader;
|
||||||
QByteArray _attachmentsData;
|
QByteArray _attachmentsData;
|
||||||
|
|||||||
Reference in New Issue
Block a user