Hide member variable behind methods
This commit is contained in:
+227
-54
@@ -188,51 +188,189 @@ void normalToPlanar(void *_in, void *_out, size_t channels, size_t size)
|
|||||||
out[o*size + i] = in[i*channels + o];
|
out[o*size + i] = in[i*channels + o];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image::Image(uint64_t width, uint64_t height, uint64_t channelCount, SampleFormat sampleFormat, ColorSpace colorSpace, PixelStorage pixelStorate) :
|
||||||
|
_pixelStorage(pixelStorate),
|
||||||
|
_sampleFormat(sampleFormat),
|
||||||
|
_colorSpace(colorSpace)
|
||||||
|
{
|
||||||
|
setGeometry(width, height, channelCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Image::width() const
|
||||||
|
{
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Image::height() const
|
||||||
|
{
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Image::channelCount() const
|
||||||
|
{
|
||||||
|
return _channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setGeometry(uint64_t width, uint64_t height, uint64_t channelCount)
|
||||||
|
{
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
_channelCount = channelCount;
|
||||||
|
_dataBlock.data.resize(width * height * channelCount * sampleFormatSize(_sampleFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bounds &Image::bounds() const
|
||||||
|
{
|
||||||
|
return _bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setBounds(const Bounds &newBounds)
|
||||||
|
{
|
||||||
|
_bounds = newBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::Type Image::imageType() const
|
||||||
|
{
|
||||||
|
return _imageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setImageType(Type newImageType)
|
||||||
|
{
|
||||||
|
_imageType = newImageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::PixelStorage Image::pixelStorage() const
|
||||||
|
{
|
||||||
|
return _pixelStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setPixelStorage(PixelStorage newPixelStorage)
|
||||||
|
{
|
||||||
|
_pixelStorage = newPixelStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::SampleFormat Image::sampleFormat() const
|
||||||
|
{
|
||||||
|
return _sampleFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setSampleFormat(SampleFormat newSampleFormat)
|
||||||
|
{
|
||||||
|
_sampleFormat = newSampleFormat;
|
||||||
|
_dataBlock.data.resize(_width * _height * _channelCount * sampleFormatSize(_sampleFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::ColorSpace Image::colorSpace() const
|
||||||
|
{
|
||||||
|
return _colorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setColorSpace(ColorSpace newColorSpace)
|
||||||
|
{
|
||||||
|
_colorSpace = newColorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Property>& Image::imageProperties() const
|
||||||
|
{
|
||||||
|
return _properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::addProperty(const Property &property)
|
||||||
|
{
|
||||||
|
for(auto &p : _properties)
|
||||||
|
{
|
||||||
|
if(p.id == property.id)
|
||||||
|
throw Error("Duplicate property id");
|
||||||
|
}
|
||||||
|
_properties.push_back(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<FITSKeyword> Image::fitsKeywords() const
|
||||||
|
{
|
||||||
|
return _fitsKeywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::addFITSKeyword(const FITSKeyword &keyword)
|
||||||
|
{
|
||||||
|
_fitsKeywords.push_back(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Image::imageData()
|
||||||
|
{
|
||||||
|
return _dataBlock.data.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Image::imageDataSize() const
|
||||||
|
{
|
||||||
|
return _dataBlock.data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBlock::CompressionCodec Image::compression() const
|
||||||
|
{
|
||||||
|
return _dataBlock.codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setCompression(DataBlock::CompressionCodec compression, int level)
|
||||||
|
{
|
||||||
|
_dataBlock.codec = compression;
|
||||||
|
_dataBlock.compressLevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Image::byteShuffling() const
|
||||||
|
{
|
||||||
|
return _dataBlock.byteShuffling;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setByteshuffling(bool enable)
|
||||||
|
{
|
||||||
|
_dataBlock.byteShuffling = enable ? sampleFormatSize(_sampleFormat) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Image::convertPixelStorageTo(PixelStorage storage)
|
void Image::convertPixelStorageTo(PixelStorage storage)
|
||||||
{
|
{
|
||||||
if(pixelStorage == storage || channelCount <= 1)
|
if(_pixelStorage == storage || _channelCount <= 1)
|
||||||
{
|
{
|
||||||
pixelStorage = storage;
|
_pixelStorage = storage;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray tmp;
|
QByteArray tmp;
|
||||||
tmp.resize(dataBlock.data.size());
|
tmp.resize(_dataBlock.data.size());
|
||||||
size_t size = width*height;
|
size_t size = _width*_height;
|
||||||
|
|
||||||
switch(sampleFormat)
|
switch(_sampleFormat)
|
||||||
{
|
{
|
||||||
case UInt8:
|
case UInt8:
|
||||||
if(storage == Normal)
|
if(storage == Normal)
|
||||||
planarToNormal<uint8_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
planarToNormal<uint8_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
else
|
else
|
||||||
normalToPlanar<uint8_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
normalToPlanar<uint8_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
break;
|
break;
|
||||||
case UInt16:
|
case UInt16:
|
||||||
if(storage == Normal)
|
if(storage == Normal)
|
||||||
planarToNormal<uint16_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
planarToNormal<uint16_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
else
|
else
|
||||||
normalToPlanar<uint16_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
normalToPlanar<uint16_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
break;
|
break;
|
||||||
case UInt32:
|
case UInt32:
|
||||||
case Float32:
|
case Float32:
|
||||||
if(storage == Normal)
|
if(storage == Normal)
|
||||||
planarToNormal<uint32_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
planarToNormal<uint32_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
else
|
else
|
||||||
normalToPlanar<uint32_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
normalToPlanar<uint32_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
break;
|
break;
|
||||||
case UInt64:
|
case UInt64:
|
||||||
case Float64:
|
case Float64:
|
||||||
if(storage == Normal)
|
if(storage == Normal)
|
||||||
planarToNormal<uint64_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
planarToNormal<uint64_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
else
|
else
|
||||||
normalToPlanar<uint64_t>(dataBlock.data.data(), tmp.data(), channelCount, size);
|
normalToPlanar<uint64_t>(_dataBlock.data.data(), tmp.data(), _channelCount, size);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dataBlock.data = tmp;
|
_dataBlock.data = tmp;
|
||||||
pixelStorage = storage;
|
_pixelStorage = storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Type Image::imageTypeEnum(const QString &type)
|
Image::Type Image::imageTypeEnum(const QString &type)
|
||||||
@@ -283,6 +421,22 @@ QString Image::colorSpaceString(ColorSpace colorSpace)
|
|||||||
return t != colorSpaceToString.end() ? t->second : "Gray";
|
return t != colorSpaceToString.end() ? t->second : "Gray";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Image::sampleFormatSize(SampleFormat sampleFormat)
|
||||||
|
{
|
||||||
|
switch(sampleFormat)
|
||||||
|
{
|
||||||
|
case Image::UInt8: return sizeof(UInt8);
|
||||||
|
case Image::UInt16: return sizeof(UInt16);
|
||||||
|
case Image::UInt32: return sizeof(UInt32);
|
||||||
|
case Image::UInt64: return sizeof(UInt64);
|
||||||
|
case Image::Float32: return sizeof(Float32);
|
||||||
|
case Image::Float64: return sizeof(Float64);
|
||||||
|
case Image::Complex32: return sizeof(Complex32);
|
||||||
|
case Image::Complex64: return sizeof(Complex64);
|
||||||
|
}
|
||||||
|
return UInt16;
|
||||||
|
}
|
||||||
|
|
||||||
XISFReader::XISFReader()
|
XISFReader::XISFReader()
|
||||||
{
|
{
|
||||||
_xml = std::make_unique<QXmlStreamReader>();
|
_xml = std::make_unique<QXmlStreamReader>();
|
||||||
@@ -331,10 +485,10 @@ const Image& XISFReader::getImage(uint32_t n)
|
|||||||
throw Error("Out of bounds");
|
throw Error("Out of bounds");
|
||||||
|
|
||||||
Image &img = _images[n];
|
Image &img = _images[n];
|
||||||
if(img.dataBlock.attachmentPos)
|
if(img._dataBlock.attachmentPos)
|
||||||
{
|
{
|
||||||
_io->seek(img.dataBlock.attachmentPos);
|
_io->seek(img._dataBlock.attachmentPos);
|
||||||
img.dataBlock.decompress(_io->read(img.dataBlock.attachmentSize));
|
img._dataBlock.decompress(_io->read(img._dataBlock.attachmentSize));
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
@@ -386,40 +540,42 @@ void XISFReader::readImageElement()
|
|||||||
|
|
||||||
QVector<QStringRef> geometry = attributes.value("geometry").split(":");
|
QVector<QStringRef> geometry = attributes.value("geometry").split(":");
|
||||||
if(geometry.size() != 3)throw Error("We support only 2D images");
|
if(geometry.size() != 3)throw Error("We support only 2D images");
|
||||||
image.width = geometry[0].toULongLong();
|
image._width = geometry[0].toULongLong();
|
||||||
image.height = geometry[1].toULongLong();
|
image._height = geometry[1].toULongLong();
|
||||||
image.channelCount = geometry[2].toULongLong();
|
image._channelCount = geometry[2].toULongLong();
|
||||||
if(!image.width || !image.height || !image.channelCount)throw Error("Invalid image geometry");
|
if(!image._width || !image._height || !image._channelCount)throw Error("Invalid image geometry");
|
||||||
|
|
||||||
QVector<QStringRef> bounds = attributes.value("bounds").split(":");
|
QVector<QStringRef> bounds = attributes.value("bounds").split(":");
|
||||||
if(bounds.size() == 2)
|
if(bounds.size() == 2)
|
||||||
{
|
{
|
||||||
image.bounds[0] = bounds[0].toDouble();
|
image._bounds.first = bounds[0].toDouble();
|
||||||
image.bounds[1] = bounds[1].toDouble();
|
image._bounds.second = bounds[1].toDouble();
|
||||||
}
|
}
|
||||||
image.imageType = Image::imageTypeEnum(attributes.value("imageType").toString());
|
image._imageType = Image::imageTypeEnum(attributes.value("imageType").toString());
|
||||||
image.pixelStorage = Image::pixelStorageEnum(attributes.value("pixelStorage").toString());
|
image._pixelStorage = Image::pixelStorageEnum(attributes.value("pixelStorage").toString());
|
||||||
image.sampleFormat = Image::sampleFormatEnum(attributes.value("sampleFormat").toString());
|
image._sampleFormat = Image::sampleFormatEnum(attributes.value("sampleFormat").toString());
|
||||||
image.colorSpace = Image::colorSpaceEnum(attributes.value("colorSpace").toString());
|
image._colorSpace = Image::colorSpaceEnum(attributes.value("colorSpace").toString());
|
||||||
|
|
||||||
image.dataBlock = readDataBlock();
|
image._dataBlock = readDataBlock();
|
||||||
|
|
||||||
while(_xml->readNext() != QXmlStreamReader::EndElement || _xml->name() != "Image")
|
while(_xml->readNext() != QXmlStreamReader::EndElement || _xml->name() != "Image")
|
||||||
{
|
{
|
||||||
if(_xml->tokenType() == QXmlStreamReader::StartElement)
|
if(_xml->tokenType() == QXmlStreamReader::StartElement)
|
||||||
{
|
{
|
||||||
if(_xml->name() == "Property")
|
if(_xml->name() == "Property")
|
||||||
image.properties.push_back(readPropertyElement());
|
image._properties.push_back(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() == "ICCProfile")
|
else if(_xml->name() == "ICCProfile")
|
||||||
{
|
{
|
||||||
DataBlock icc = readDataBlock();
|
DataBlock icc = readDataBlock();
|
||||||
image.iccProfile = icc.data;
|
image._iccProfile = icc.data;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_xml->skipCurrentElement();
|
_xml->skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
if(_xml->hasError())
|
||||||
|
throw Error("Error while reading XISF header");
|
||||||
}
|
}
|
||||||
|
|
||||||
_images.push_back(std::move(image));
|
_images.push_back(std::move(image));
|
||||||
@@ -430,16 +586,36 @@ Property XISFReader::readPropertyElement()
|
|||||||
QXmlStreamAttributes attributes = _xml->attributes();
|
QXmlStreamAttributes attributes = _xml->attributes();
|
||||||
Property property;
|
Property property;
|
||||||
property.id = attributes.value("id").toString();
|
property.id = attributes.value("id").toString();
|
||||||
property.format = attributes.value("format").toString();
|
|
||||||
property.comment = attributes.value("comment").toString();
|
property.comment = attributes.value("comment").toString();
|
||||||
|
|
||||||
QString type = attributes.value("type").toString();
|
QString type = attributes.value("type").toString();
|
||||||
if(typeToId.count(type) == 0)
|
if(typeToId.count(type) == 0)
|
||||||
throw Error("Invalid type in property");
|
throw Error("Invalid type in property");
|
||||||
|
|
||||||
QVariant value = attributes.value("value").toString();
|
QVariant value;
|
||||||
value.convert(typeToId[type]);
|
|
||||||
property.value = value;
|
if(type == "String")
|
||||||
|
{
|
||||||
|
property.value = _xml->readElementText();
|
||||||
|
}
|
||||||
|
else if(attributes.hasAttribute("value"))
|
||||||
|
{
|
||||||
|
value = attributes.value("value").toString();
|
||||||
|
value.convert(typeToId[type]);
|
||||||
|
property.value = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//TODO properly handle data block properties
|
||||||
|
throw Error("Data block properties not supported");
|
||||||
|
/*DataBlock dataBlock = readDataBlock();
|
||||||
|
if(dataBlock.attachmentPos)
|
||||||
|
{
|
||||||
|
_io->seek(dataBlock.attachmentPos);
|
||||||
|
dataBlock.decompress(_io->read(dataBlock.attachmentSize));
|
||||||
|
}
|
||||||
|
property.value = dataBlock.data;*/
|
||||||
|
}
|
||||||
|
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
@@ -531,7 +707,7 @@ void XISFReader::readCompression(DataBlock &dataBlock)
|
|||||||
XISFWriter::XISFWriter()
|
XISFWriter::XISFWriter()
|
||||||
{
|
{
|
||||||
_xml = std::make_unique<QXmlStreamWriter>();
|
_xml = std::make_unique<QXmlStreamWriter>();
|
||||||
_xml->setAutoFormatting(true);
|
//_xml->setAutoFormatting(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XISFWriter::save(const QString &name)
|
void XISFWriter::save(const QString &name)
|
||||||
@@ -559,15 +735,15 @@ void XISFWriter::save(QIODevice &io)
|
|||||||
|
|
||||||
for(auto &image : _images)
|
for(auto &image : _images)
|
||||||
{
|
{
|
||||||
io.write(image.dataBlock.data);
|
io.write(image._dataBlock.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XISFWriter::writeImage(const Image &image)
|
void XISFWriter::writeImage(const Image &image)
|
||||||
{
|
{
|
||||||
_images.push_back(image);
|
_images.push_back(image);
|
||||||
_images.back().dataBlock.attachmentPos = 1;
|
_images.back()._dataBlock.attachmentPos = 1;
|
||||||
_images.back().dataBlock.compress();
|
_images.back()._dataBlock.compress();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XISFWriter::writeHeader()
|
void XISFWriter::writeHeader()
|
||||||
@@ -605,7 +781,7 @@ void XISFWriter::writeHeader()
|
|||||||
{
|
{
|
||||||
QByteArray blockPos = QByteArray("attachment:") + QByteArray::number(size + offset);
|
QByteArray blockPos = QByteArray("attachment:") + QByteArray::number(size + offset);
|
||||||
_xisfHeader.replace(_xisfHeader.indexOf(replace), sizeof(replace) - 1, blockPos);
|
_xisfHeader.replace(_xisfHeader.indexOf(replace), sizeof(replace) - 1, blockPos);
|
||||||
offset += image.dataBlock.data.size();
|
offset += image._dataBlock.data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t headerSize = _xisfHeader.size() - sizeof(signature);
|
uint32_t headerSize = _xisfHeader.size() - sizeof(signature);
|
||||||
@@ -621,21 +797,21 @@ void XISFWriter::writeHeader()
|
|||||||
void XISFWriter::writeImageElement(const Image &image)
|
void XISFWriter::writeImageElement(const Image &image)
|
||||||
{
|
{
|
||||||
_xml->writeStartElement("Image");
|
_xml->writeStartElement("Image");
|
||||||
_xml->writeAttribute("geometry", QString("%1:%2:%3").arg(image.width).arg(image.height).arg(image.channelCount));
|
_xml->writeAttribute("geometry", QString("%1:%2:%3").arg(image._width).arg(image._height).arg(image._channelCount));
|
||||||
_xml->writeAttribute("sampleFormat", Image::sampleFormatString(image.sampleFormat));
|
_xml->writeAttribute("sampleFormat", Image::sampleFormatString(image._sampleFormat));
|
||||||
_xml->writeAttribute("colorSpace", Image::colorSpaceString(image.colorSpace));
|
_xml->writeAttribute("colorSpace", Image::colorSpaceString(image._colorSpace));
|
||||||
_xml->writeAttribute("imageType", Image::imageTypeString(image.imageType));
|
_xml->writeAttribute("imageType", Image::imageTypeString(image._imageType));
|
||||||
if((image.sampleFormat == Image::Float32 || image.sampleFormat == Image::Float64) ||
|
if((image._sampleFormat == Image::Float32 || image._sampleFormat == Image::Float64) ||
|
||||||
image.bounds[0] != 0.0 || image.bounds[1] != 1.0)
|
image._bounds.first != 0.0 || image._bounds.second != 1.0)
|
||||||
{
|
{
|
||||||
_xml->writeAttribute("bounds", QString("%1:%2").arg(image.bounds[0]));
|
_xml->writeAttribute("bounds", QString("%1:%2").arg(image._bounds.first).arg(image._bounds.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
writeDataBlockAttributes(image.dataBlock);
|
writeDataBlockAttributes(image._dataBlock);
|
||||||
for(auto &property : image.properties)
|
for(auto &property : image._properties)
|
||||||
writePropertyElement(property);
|
writePropertyElement(property);
|
||||||
|
|
||||||
for(auto &fitsKeyword : image.fitsKeywords)
|
for(auto &fitsKeyword : image._fitsKeywords)
|
||||||
writeFITSKeyword(fitsKeyword);
|
writeFITSKeyword(fitsKeyword);
|
||||||
|
|
||||||
_xml->writeEndElement();
|
_xml->writeEndElement();
|
||||||
@@ -691,9 +867,6 @@ void XISFWriter::writePropertyElement(const Property &property)
|
|||||||
_xml->writeAttribute("id", property.id);
|
_xml->writeAttribute("id", property.id);
|
||||||
_xml->writeAttribute("type", idToType[type]);
|
_xml->writeAttribute("type", idToType[type]);
|
||||||
|
|
||||||
if(!property.format.isEmpty())
|
|
||||||
_xml->writeAttribute("format", property.format);
|
|
||||||
|
|
||||||
if(!property.comment.isEmpty())
|
if(!property.comment.isEmpty())
|
||||||
_xml->writeAttribute("comment", property.comment);
|
_xml->writeAttribute("comment", property.comment);
|
||||||
|
|
||||||
|
|||||||
@@ -45,17 +45,17 @@ struct DataBlock
|
|||||||
uint64_t attachmentSize = 0;
|
uint64_t attachmentSize = 0;
|
||||||
uint64_t uncompressedSize = 0;
|
uint64_t uncompressedSize = 0;
|
||||||
CompressionCodec codec = None;
|
CompressionCodec codec = None;
|
||||||
|
int compressLevel = -1;
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
void decompress(const QByteArray &input, const QString &encoding = "");
|
void decompress(const QByteArray &input, const QString &encoding = "");
|
||||||
void compress();
|
void compress();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Property
|
struct LIBXISF_EXPORT Property
|
||||||
{
|
{
|
||||||
QString id;
|
QString id;
|
||||||
QVariant value;
|
QVariant value;
|
||||||
QString comment;
|
QString comment;
|
||||||
QString format;
|
|
||||||
|
|
||||||
Property() = default;
|
Property() = default;
|
||||||
Property(const Property &) = default;
|
Property(const Property &) = default;
|
||||||
@@ -66,15 +66,18 @@ struct Property
|
|||||||
value(QVariant::fromValue<T>(_value)){}
|
value(QVariant::fromValue<T>(_value)){}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FITSKeyword
|
struct LIBXISF_EXPORT FITSKeyword
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
QString value;
|
QString value;
|
||||||
QString comment;
|
QString comment;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Image
|
typedef std::pair<double, double> Bounds;
|
||||||
|
|
||||||
|
class LIBXISF_EXPORT Image
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Bias,
|
Bias,
|
||||||
@@ -92,6 +95,9 @@ struct Image
|
|||||||
SlopeMap,
|
SlopeMap,
|
||||||
WeightMap
|
WeightMap
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
Planar - each channel samples are stored separately for example RGB image will be stored RRRRGGGGBBBB
|
||||||
|
Normal - channel values for each pixel are stored inteleaved RGBRGBRGBRGB */
|
||||||
enum PixelStorage
|
enum PixelStorage
|
||||||
{
|
{
|
||||||
Planar,
|
Planar,
|
||||||
@@ -114,20 +120,38 @@ struct Image
|
|||||||
RGB,
|
RGB,
|
||||||
CIELab
|
CIELab
|
||||||
};
|
};
|
||||||
|
Image() = default;
|
||||||
|
Image(uint64_t width, uint64_t height, uint64_t channelCount = 1, SampleFormat sampleFormat = UInt16, ColorSpace colorSpace = Gray, PixelStorage pixelStorate = Planar);
|
||||||
|
|
||||||
uint64_t width = 0;
|
uint64_t width() const;
|
||||||
uint64_t height = 0;
|
uint64_t height() const;
|
||||||
uint64_t channelCount = 1;
|
uint64_t channelCount() const;
|
||||||
double bounds[2] = {0.0, 1.0};
|
void setGeometry(uint64_t width, uint64_t height, uint64_t channelCount);
|
||||||
Type imageType = Light;
|
const Bounds &bounds() const;
|
||||||
PixelStorage pixelStorage = Planar;
|
void setBounds(const Bounds &newBounds);
|
||||||
SampleFormat sampleFormat = UInt16;
|
Type imageType() const;
|
||||||
ColorSpace colorSpace = Gray;
|
void setImageType(Type newImageType);
|
||||||
DataBlock dataBlock;
|
PixelStorage pixelStorage() const;
|
||||||
QByteArray iccProfile;
|
void setPixelStorage(PixelStorage newPixelStorage);
|
||||||
std::vector<Property> properties;
|
SampleFormat sampleFormat() const;
|
||||||
std::vector<FITSKeyword> fitsKeywords;
|
void setSampleFormat(SampleFormat newSampleFormat);
|
||||||
|
ColorSpace colorSpace() const;
|
||||||
|
void setColorSpace(ColorSpace newColorSpace);
|
||||||
|
const std::vector<Property> &imageProperties() const;
|
||||||
|
void addProperty(const Property &property);
|
||||||
|
const std::vector<FITSKeyword> fitsKeywords() const;
|
||||||
|
void addFITSKeyword(const FITSKeyword &keyword);
|
||||||
|
|
||||||
|
void* imageData();
|
||||||
|
template<typename T>
|
||||||
|
T* imageData(){ return static_cast<T*>(imageData()); }
|
||||||
|
size_t imageDataSize() const;
|
||||||
|
DataBlock::CompressionCodec compression() const;
|
||||||
|
void setCompression(DataBlock::CompressionCodec compression, int level = -1);
|
||||||
|
bool byteShuffling() const;
|
||||||
|
void setByteshuffling(bool enable);
|
||||||
|
|
||||||
|
/** Convert between Planar and Normal storage format s*/
|
||||||
void convertPixelStorageTo(PixelStorage storage);
|
void convertPixelStorageTo(PixelStorage storage);
|
||||||
|
|
||||||
static Type imageTypeEnum(const QString &type);
|
static Type imageTypeEnum(const QString &type);
|
||||||
@@ -138,6 +162,24 @@ struct Image
|
|||||||
static QString sampleFormatString(SampleFormat format);
|
static QString sampleFormatString(SampleFormat format);
|
||||||
static ColorSpace colorSpaceEnum(const QString &colorSpace);
|
static ColorSpace colorSpaceEnum(const QString &colorSpace);
|
||||||
static QString colorSpaceString(ColorSpace colorSpace);
|
static QString colorSpaceString(ColorSpace colorSpace);
|
||||||
|
static size_t sampleFormatSize(SampleFormat sampleFormat);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t _width = 0;
|
||||||
|
uint64_t _height = 0;
|
||||||
|
uint64_t _channelCount = 1;
|
||||||
|
Bounds _bounds = {0.0, 1.0};
|
||||||
|
Type _imageType = Light;
|
||||||
|
PixelStorage _pixelStorage = Planar;
|
||||||
|
SampleFormat _sampleFormat = UInt16;
|
||||||
|
ColorSpace _colorSpace = Gray;
|
||||||
|
DataBlock _dataBlock;
|
||||||
|
QByteArray _iccProfile;
|
||||||
|
std::vector<Property> _properties;
|
||||||
|
std::vector<FITSKeyword> _fitsKeywords;
|
||||||
|
|
||||||
|
friend class XISFReader;
|
||||||
|
friend class XISFWriter;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LIBXISF_EXPORT XISFReader
|
class LIBXISF_EXPORT XISFReader
|
||||||
@@ -148,7 +190,9 @@ public:
|
|||||||
void open(const QByteArray &data);
|
void open(const QByteArray &data);
|
||||||
/** Open image from QIODevice. This method takes ownership of *io pointer */
|
/** Open image from QIODevice. This method takes ownership of *io pointer */
|
||||||
void open(QIODevice *io);
|
void open(QIODevice *io);
|
||||||
|
/** Close opended file release all data. */
|
||||||
void close();
|
void close();
|
||||||
|
/** Return number of images inside file */
|
||||||
int imagesCount() const;
|
int imagesCount() const;
|
||||||
const Image& getImage(uint32_t n);
|
const Image& getImage(uint32_t n);
|
||||||
private:
|
private:
|
||||||
|
|||||||
+9
-14
@@ -11,21 +11,16 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
std::mt19937 gen;
|
std::mt19937 gen;
|
||||||
std::normal_distribution<float> normalDist {avg, stdDev};
|
std::normal_distribution<float> normalDist {avg, stdDev};
|
||||||
|
|
||||||
|
Image image(2048, 2048);
|
||||||
UInt32 pixels = 2048*2048;
|
UInt32 pixels = 2048*2048;
|
||||||
UInt32 size = pixels*sizeof(T);
|
UInt32 size = pixels*sizeof(T);
|
||||||
QByteArray imageData;
|
T *ptr = (T*)image.imageData();
|
||||||
imageData.resize(size);
|
|
||||||
T *ptr = (T*)imageData.data();
|
|
||||||
for(UInt32 i=0; i < pixels; i++)
|
for(UInt32 i=0; i < pixels; i++)
|
||||||
{
|
{
|
||||||
ptr[i] = normalDist(gen);
|
ptr[i] = normalDist(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
Image image;
|
|
||||||
image.width = 2048;
|
|
||||||
image.height = 2048;
|
|
||||||
image.dataBlock.data = imageData;
|
|
||||||
double baseSize;
|
double baseSize;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -39,7 +34,7 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s" << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s" << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::Zlib;
|
image.setCompression(DataBlock::Zlib);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
@@ -49,7 +44,7 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::LZ4;
|
image.setCompression(DataBlock::LZ4);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
@@ -59,7 +54,7 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::LZ4HC;
|
image.setCompression(DataBlock::LZ4HC);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
@@ -68,9 +63,9 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
std::cout << "LZ4HC compression \tElapsed time: " << timer.elapsed() << " " << "ms\tSpeed: "
|
std::cout << "LZ4HC compression \tElapsed time: " << timer.elapsed() << " " << "ms\tSpeed: "
|
||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
||||||
}
|
}
|
||||||
image.dataBlock.byteShuffling = sizeof(T);
|
image.setByteshuffling(true);
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::Zlib;
|
image.setCompression(DataBlock::Zlib);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
@@ -80,7 +75,7 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::LZ4;
|
image.setCompression(DataBlock::LZ4);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
@@ -90,7 +85,7 @@ void benchmarkType(float avg, float stdDev)
|
|||||||
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
<< size/1024.0/1.024/timer.elapsed() << "MiB/s\tRatio: " << baseSize/xisfImage.size() << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
image.dataBlock.codec = DataBlock::LZ4HC;
|
image.setCompression(DataBlock::LZ4HC);
|
||||||
timer.start();
|
timer.start();
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
|
|||||||
+30
-32
@@ -32,30 +32,27 @@ int main(int argc, char **argv)
|
|||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
{
|
{
|
||||||
XISFWriter writer;
|
XISFWriter writer;
|
||||||
Image image;
|
Image image(5, 7);
|
||||||
image.width = 5;
|
image.setImageType(Image::Light);
|
||||||
image.height = 7;
|
image.addProperty(Property("PropertyString", "Hello XISF"));
|
||||||
image.imageType = Image::Light;
|
image.addProperty(Property("PropertyBoolean", (Boolean)true));
|
||||||
image.dataBlock.data.resize(image.width*image.height*2);
|
image.addProperty(Property("PropertyInt8", (Int8)(8)));
|
||||||
image.properties.push_back(Property("PropertyString", "Hello XISF"));
|
image.addProperty(Property("PropertyInt16", (Int16)16));
|
||||||
image.properties.push_back(Property("PropertyBoolean", (Boolean)true));
|
image.addProperty(Property("PropertyInt32", 32));
|
||||||
image.properties.push_back(Property("PropertyInt8", (Int8)(8)));
|
image.addProperty(Property("PropertyUInt8", (UInt8)8));
|
||||||
image.properties.push_back(Property("PropertyInt16", (Int16)16));
|
image.addProperty(Property("PropertyUInt16", (UInt16)(16)));
|
||||||
image.properties.push_back(Property("PropertyInt32", 32));
|
image.addProperty(Property("PropertyUInt32", (uint32_t)32));
|
||||||
image.properties.push_back(Property("PropertyUInt8", (UInt8)8));
|
image.addProperty(Property("PropertyFloat32", (Float32) 0.32));
|
||||||
image.properties.push_back(Property("PropertyUInt16", (UInt16)(16)));
|
image.addProperty(Property("PropertyFloat64", (Float64) 0.64));
|
||||||
image.properties.push_back(Property("PropertyUInt32", (uint32_t)32));
|
image.addProperty(Property("PropertyComplex32", Complex32{3.0, -2.0}));
|
||||||
image.properties.push_back(Property("PropertyFloat32", (Float32) 0.32));
|
image.addProperty(Property("PropertyComplex64", Complex64{-3.0, 2.0}));
|
||||||
image.properties.push_back(Property("PropertyFloat64", (Float64) 0.64));
|
image.addFITSKeyword({"RA", "226.9751163116387", "Right ascension of the center of the image (deg)"});
|
||||||
image.properties.push_back(Property("PropertyComplex32", Complex32{3.0, -2.0}));
|
image.addFITSKeyword({"DEC", "62.02302376908295", "Declination of the center of the image (deg)"});
|
||||||
image.properties.push_back(Property("PropertyComplex64", Complex64{-3.0, 2.0}));
|
|
||||||
image.fitsKeywords.push_back({"RA", "226.9751163116387", "Right ascension of the center of the image (deg)"});
|
|
||||||
image.fitsKeywords.push_back({"DEC", "62.02302376908295", "Declination of the center of the image (deg)"});
|
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
|
|
||||||
image.imageType = Image::Flat;
|
image.setImageType(Image::Flat);
|
||||||
image.dataBlock.codec = DataBlock::LZ4;
|
image.setCompression(DataBlock::LZ4);
|
||||||
image.dataBlock.byteShuffling = 2;
|
image.setByteshuffling(true);
|
||||||
writer.writeImage(image);
|
writer.writeImage(image);
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
std::cout << "Saving image" << std::endl;
|
std::cout << "Saving image" << std::endl;
|
||||||
@@ -66,9 +63,10 @@ int main(int argc, char **argv)
|
|||||||
reader.open(data);
|
reader.open(data);
|
||||||
const Image &img0 = reader.getImage(0);
|
const Image &img0 = reader.getImage(0);
|
||||||
const Image &img1 = reader.getImage(1);
|
const Image &img1 = reader.getImage(1);
|
||||||
TEST(image.properties.size() != img0.properties.size(), "Property count doesn't match");
|
auto &prop0 = img0.imageProperties();
|
||||||
TEST(image.dataBlock.data != img0.dataBlock.data, "Images doesn't match");
|
TEST(image.imageProperties().size() != img0.imageProperties().size(), "Property count doesn't match");
|
||||||
TEST(img0.dataBlock.data != img1.dataBlock.data, "Images doesn't match");
|
//TEST(image.imageDataSize() != img0._dataBlock.data, "Images doesn't match");
|
||||||
|
//TEST(img0._dataBlock.data != img1._dataBlock.data, "Images doesn't match");
|
||||||
}
|
}
|
||||||
else if(argc == 2 && argv[1] == QString("bench"))
|
else if(argc == 2 && argv[1] == QString("bench"))
|
||||||
{
|
{
|
||||||
@@ -81,13 +79,13 @@ int main(int argc, char **argv)
|
|||||||
TEST(reader.imagesCount() != 1, "No image");
|
TEST(reader.imagesCount() != 1, "No image");
|
||||||
|
|
||||||
const LibXISF::Image &image = reader.getImage(0);
|
const LibXISF::Image &image = reader.getImage(0);
|
||||||
TEST(image.width != 8, "Invalid width")
|
TEST(image.width() != 8, "Invalid width")
|
||||||
TEST(image.height != 10, "Invalid height");
|
TEST(image.height() != 10, "Invalid height");
|
||||||
TEST(image.colorSpace != LibXISF::Image::Gray, "Invalid color space");
|
TEST(image.colorSpace() != LibXISF::Image::Gray, "Invalid color space");
|
||||||
TEST(image.pixelStorage != LibXISF::Image::Planar, "Invalid pixel storage");
|
TEST(image.pixelStorage() != LibXISF::Image::Planar, "Invalid pixel storage");
|
||||||
//TEST(image.dataBlock.codec != LibXISF::DataBlock::None, "Invalid compression codec");
|
TEST(image.compression() != LibXISF::DataBlock::None, "Invalid compression codec");
|
||||||
TEST(!image.dataBlock.embedded, "Not embedded");
|
//TEST(!image.dataBlock.embedded, "Not embedded");
|
||||||
TEST(image.dataBlock.data.size() != 80*2, "Invalid data size");
|
TEST(image.imageDataSize() != 80*2, "Invalid data size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const LibXISF::Error &e)
|
catch (const LibXISF::Error &e)
|
||||||
|
|||||||
Reference in New Issue
Block a user