proper metadata support for the Bitmap class (mainly for OpenEXR I/O)

metadata
Wenzel Jakob 2013-01-25 21:12:04 -05:00
parent f6399ad08c
commit 4d75c69d4a
8 changed files with 240 additions and 106 deletions

View File

@ -774,25 +774,23 @@ public:
inline void setGamma(Float gamma) { m_gamma = gamma; }
/// Set a string-valued metadata field
void setString(const std::string &key, const std::string &value);
inline void setMetadataString(const std::string &key, const std::string &value) {
m_metadata.setString(key, value);
}
/// Return a string-valued metadata field
std::string getString(const std::string &key) const;
/// Return a map of all present metadata
inline std::map<std::string, std::string> &getMetadata() {
return m_metadata;
inline std::string getMetadataString(const std::string &key) const {
return m_metadata.getAsString(key);
}
/// Return a map of all present metadata (const version)
inline const std::map<std::string, std::string> &getMetadata() const {
return m_metadata;
}
/// Return a \ref Properties object containing the image metadata
inline Properties &getMetadata() { return m_metadata; }
/// Set the metadata associated with the bitmap
inline void setMetadata(const std::map<std::string, std::string> &metadata) {
m_metadata = metadata;
}
/// Return a \ref Properties object containing the image metadata (const version)
inline const Properties &getMetadata() const { return m_metadata; }
/// Set the a \ref Properties object containing the image metadata
inline void setMetadata(const Properties &metadata) { m_metadata = metadata; }
//! @}
// ======================================================================
@ -910,9 +908,9 @@ protected:
EComponentFormat m_componentFormat;
Vector2i m_size;
uint8_t *m_data;
std::map<std::string, std::string> m_metadata;
Float m_gamma;
int m_channelCount;
Properties m_metadata;
};
/** \brief Bitmap format conversion helper class

View File

@ -71,6 +71,14 @@ public:
struct Data {
uint8_t *ptr;
size_t size;
inline bool operator==(const Data &d) const {
return ptr == d.ptr && size == d.size;
}
inline bool operator!=(const Data &d) const {
return !operator==(d);
}
};
/// Construct an empty property container
@ -172,6 +180,11 @@ public:
/// Get a string (with default)
std::string getString(const std::string &name, const std::string &defVal) const;
/// Return one of the parameters (converting it to a string if necessary)
std::string getAsString(const std::string &name) const;
/// Return one of the parameters (converting it to a string if necessary, with default value)
std::string getAsString(const std::string &name, const std::string &defVal) const;
/// Store an array containing the names of all stored properties
void putPropertyNames(std::vector<std::string> &results) const;
@ -206,6 +219,14 @@ public:
/// Assignment operator
void operator=(const Properties &props);
/// Equality comparison operator
bool operator==(const Properties &props) const;
/// Inequality comparision operator
inline bool operator!=(const Properties &props) const {
return !operator==(props);
}
/// Return a string representation
std::string toString() const;
private:

View File

@ -393,6 +393,16 @@ public:
m_invTransform.serialize(stream);
}
/// Equality comparison operator
inline bool operator==(const Transform &trafo) const {
return m_transform == trafo.m_transform;
}
/// Inequality comparison operator
inline bool operator!=(const Transform &trafo) const {
return m_transform != trafo.m_transform;
}
/// Return a string representation
std::string toString() const;
private:

View File

@ -383,7 +383,7 @@ public:
for (std::map<std::string, std::string>::const_iterator it = m_tags.begin();
it != m_tags.end(); ++it)
bitmap->getMetadata()[it->first] = it->second;
bitmap->setMetadataString(it->first, it->second);
fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string());
@ -400,7 +400,7 @@ public:
if (m_attachLog && logger->readLog(log)) {
log += "\n\n";
log += Statistics::getInstance()->getStats();
bitmap->setString("log", log);
bitmap->setMetadataString("log", log);
}
bitmap->write(m_fileFormat, stream);

View File

@ -353,7 +353,7 @@ public:
for (std::map<std::string, std::string>::const_iterator it = m_tags.begin();
it != m_tags.end(); ++it)
bitmap->getMetadata()[it->first] = it->second;
bitmap->setMetadataString(it->first, it->second);
fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string());

View File

@ -38,6 +38,11 @@
#include <ImfOutputFile.h>
#include <ImfChannelList.h>
#include <ImfStringAttribute.h>
#include <ImfIntAttribute.h>
#include <ImfFloatAttribute.h>
#include <ImfDoubleAttribute.h>
#include <ImfVecAttribute.h>
#include <ImfMatrixAttribute.h>
#include <ImfVersion.h>
#include <ImfIO.h>
#include <ImathBox.h>
@ -386,19 +391,6 @@ int Bitmap::getBytesPerComponent() const {
}
}
void Bitmap::setString(const std::string &key, const std::string &value) {
m_metadata[key] = value;
}
std::string Bitmap::getString(const std::string &key) const {
std::map<std::string, std::string>::const_iterator it = m_metadata.find(key);
if (it != m_metadata.end())
return it->second;
else
return "";
}
Bitmap::~Bitmap() {
if (m_data)
freeAligned(m_data);
@ -1189,12 +1181,17 @@ std::string Bitmap::toString() const {
<< " type = " << m_pixelFormat << endl
<< " componentFormat = " << m_componentFormat << endl
<< " size = " << m_size.toString() << endl;
if (!m_metadata.empty()) {
std::vector<std::string> keys = m_metadata.getPropertyNames();
if (!keys.empty()) {
oss << " metadata = {" << endl;
for (std::map<std::string, std::string>::const_iterator it = m_metadata.begin();
it != m_metadata.end();) {
oss << " \"" << it->first << "\" => \"" << it->second << "\"";
if (++it != m_metadata.end())
for (std::vector<std::string>::const_iterator it = keys.begin(); it != keys.end(); ) {
std::string value = m_metadata.getAsString(*it);
if (value.size() > 50)
value = value.substr(0, 50) + ".. [truncated]";
oss << " \"" << *it << "\" => \"" << value << "\"";
if (++it != keys.end())
oss << ",";
oss << endl;
}
@ -1281,7 +1278,7 @@ void Bitmap::readPNG(Stream *stream) {
png_get_text(png_ptr, info_ptr, &text_ptr, &textIdx);
for (int i=0; i<textIdx; ++i, text_ptr++)
m_metadata[text_ptr->key] = text_ptr->text;
setMetadataString(text_ptr->key, text_ptr->text);
int intent; double gamma;
if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
@ -1357,20 +1354,23 @@ void Bitmap::writePNG(Stream *stream, int compression) const {
png_text *text = NULL;
std::map<std::string, std::string> metadata = m_metadata;
metadata["generated-by"] = "Mitsuba version " MTS_VERSION;
Properties metadata(m_metadata);
metadata.setString("generated-by", "Mitsuba version " MTS_VERSION);
text = new png_text[metadata.size()];
memset(text, 0, sizeof(png_text) * metadata.size());
int textIndex = 0;
for (std::map<std::string, std::string>::iterator it = metadata.begin();
it != metadata.end(); ++it) {
text[textIndex].key = const_cast<char *>(it->first.c_str());
text[textIndex].text = const_cast<char *>(it->second.c_str());
text[textIndex++].compression = PNG_TEXT_COMPRESSION_NONE;
std::vector<std::string> keys = metadata.getPropertyNames();
std::vector<std::string> values(keys.size());
text = new png_text[keys.size()];
memset(text, 0, sizeof(png_text) * keys.size());
for (size_t i = 0; i<keys.size(); ++i) {
values[i] = metadata.getAsString(keys[i]);
text[i].key = const_cast<char *>(keys[i].c_str());
text[i].text = const_cast<char *>(values[i].c_str());
text[i].compression = PNG_TEXT_COMPRESSION_NONE;
}
png_set_text(png_ptr, info_ptr, text, textIndex);
png_set_text(png_ptr, info_ptr, text, keys.size());
if (m_gamma == -1)
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
@ -1665,11 +1665,37 @@ void Bitmap::readOpenEXR(Stream *stream, const std::string &_prefix) {
/* Load metadata if present */
for (Imf::Header::ConstIterator it = header.begin(); it != header.end(); ++it) {
std::string name = it.name(), typeName = it.attribute().typeName();
const Imf::StringAttribute *sattr = NULL;
const Imf::StringAttribute *sattr;
const Imf::IntAttribute *iattr;
const Imf::FloatAttribute *fattr;
const Imf::DoubleAttribute *dattr;
const Imf::V3fAttribute *vattr;
const Imf::M44fAttribute *mattr;
if (typeName == "string" &&
(sattr = header.findTypedAttribute<Imf::StringAttribute>(name.c_str())))
m_metadata[name] = sattr->value();
m_metadata.setString(name, sattr->value());
else if (typeName == "int" &&
(iattr = header.findTypedAttribute<Imf::IntAttribute>(name.c_str())))
m_metadata.setInteger(name, iattr->value());
else if (typeName == "float" &&
(fattr = header.findTypedAttribute<Imf::FloatAttribute>(name.c_str())))
m_metadata.setFloat(name, (Float) fattr->value());
else if (typeName == "double" &&
(dattr = header.findTypedAttribute<Imf::DoubleAttribute>(name.c_str())))
m_metadata.setFloat(name, (Float) dattr->value());
else if (typeName == "v3f" &&
(vattr = header.findTypedAttribute<Imf::V3fAttribute>(name.c_str()))) {
Imath::V3f vec = vattr->value();
m_metadata.setVector(name, Vector(vec.x, vec.y, vec.z));
} else if (typeName == "m44f" &&
(mattr = header.findTypedAttribute<Imf::M44fAttribute>(name.c_str()))) {
Matrix4x4 M;
for (int i=0; i<4; ++i)
for (int j=0; j<4; ++j)
M(i, j) = mattr->value().x[i][j];
m_metadata.setTransform(name, Transform(M));
}
}
updateChannelCount();
@ -1896,13 +1922,45 @@ void Bitmap::writeOpenEXR(Stream *stream,
pixelFormat = ERGBA;
#endif
std::map<std::string, std::string> metadata = m_metadata;
metadata["generated-by"] = "Mitsuba version " MTS_VERSION;
Properties metadata(m_metadata);
metadata.setString("generated-by", "Mitsuba version " MTS_VERSION);
std::vector<std::string> keys = metadata.getPropertyNames();
Imf::Header header(m_size.x, m_size.y);
for (std::map<std::string, std::string>::const_iterator it = metadata.begin();
it != metadata.end(); ++it)
header.insert(it->first.c_str(), Imf::StringAttribute(it->second.c_str()));
for (std::vector<std::string>::const_iterator it = keys.begin(); it != keys.end(); ++it) {
Properties::EPropertyType type = metadata.getType(*it);
switch (type) {
case Properties::EString:
header.insert(it->c_str(), Imf::StringAttribute(metadata.getString(*it)));
break;
case Properties::EInteger:
header.insert(it->c_str(), Imf::IntAttribute(metadata.getInteger(*it)));
break;
case Properties::EFloat:
header.insert(it->c_str(), Imf::FloatAttribute((float) metadata.getFloat(*it)));
break;
case Properties::EPoint: {
Point val = metadata.getPoint(*it);
header.insert(it->c_str(), Imf::V3fAttribute(
Imath::V3f((float) val.x, (float) val.y, (float) val.z)));
}
break;
case Properties::ETransform: {
Matrix4x4 val = metadata.getTransform(*it).getMatrix();
header.insert(it->c_str(), Imf::M44fAttribute(Imath::M44f(
(float) val(0, 0), (float) val(0, 1), (float) val(0, 2), (float) val(0, 3),
(float) val(1, 0), (float) val(1, 1), (float) val(1, 2), (float) val(1, 3),
(float) val(2, 0), (float) val(2, 1), (float) val(2, 2), (float) val(2, 3),
(float) val(3, 0), (float) val(3, 1), (float) val(3, 2), (float) val(3, 3))));
}
break;
default:
header.insert(it->c_str(), Imf::StringAttribute(metadata.getAsString(*it)));
break;
}
}
if (pixelFormat == EXYZ || pixelFormat == EXYZA) {
Imf::addChromaticities(header, Imf::Chromaticities(
@ -2363,14 +2421,16 @@ void Bitmap::writeRGBE(Stream *stream) const {
Log(EError, "writeRGBE(): pixel format must be ERGB or ERGBA!");
stream->writeLine("#?RGBE");
for (std::map<std::string, std::string>::const_iterator it = m_metadata.begin();
it != m_metadata.end(); ++it) {
stream->writeLine(formatString("# Metadata [%s]:", it->first.c_str()));
std::istringstream iss(it->second);
std::vector<std::string> keys = m_metadata.getPropertyNames();
for (std::vector<std::string>::const_iterator it = keys.begin(); it != keys.end(); ) {
stream->writeLine(formatString("# Metadata [%s]:", it->c_str()));
std::istringstream iss(m_metadata.getAsString(*it));
std::string buf;
while (std::getline(iss, buf))
stream->writeLine(formatString("# %s", buf.c_str()));
}
stream->writeLine("FORMAT=32-bit_rle_rgbe\n");
stream->writeLine(formatString("-Y %i +X %i", m_size.y, m_size.x));

View File

@ -45,7 +45,7 @@ struct PropertyElement {
Type Properties::get##TypeName(const std::string &name) const { \
std::map<std::string, PropertyElement>::const_iterator it = m_elements->find(name); \
if (it == m_elements->end()) \
SLog(EError, "Property \"%s\" missing", name.c_str()); \
SLog(EError, "Property \"%s\" has not been specified!", name.c_str()); \
const BaseType *result = boost::get<BaseType>(&it->second.data); \
if (!result) \
SLog(EError, "The property \"%s\" has the wrong type (expected <" #ReadableName ">). The " \
@ -78,18 +78,55 @@ DEFINE_PROPERTY_ACCESSOR(Spectrum, Spectrum, Spectrum, spectrum)
DEFINE_PROPERTY_ACCESSOR(std::string, std::string, String, string)
DEFINE_PROPERTY_ACCESSOR(Properties::Data, Properties::Data, Data, data)
class type_visitor : public boost::static_visitor<Properties::EPropertyType> {
public:
Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; }
Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; }
Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; }
Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; }
Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; }
Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; }
Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; }
Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; }
Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; }
};
namespace {
class TypeVisitor : public boost::static_visitor<Properties::EPropertyType> {
public:
Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; }
Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; }
Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; }
Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; }
Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; }
Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; }
Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; }
Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; }
Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; }
};
class EqualityVisitor : public boost::static_visitor<bool> {
public:
EqualityVisitor(const ElementData *ref) : ref(ref) { }
bool operator()(const bool &v) const { const bool *v2 = boost::get<bool>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get<int64_t>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Float &v) const { const Float *v2 = boost::get<Float>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Point &v) const { const Point *v2 = boost::get<Point>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Vector &v) const { const Vector *v2 = boost::get<Vector>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Transform &v) const { const Transform *v2 = boost::get<Transform>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get<Spectrum>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const std::string &v) const { const std::string *v2 = boost::get<std::string>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get<Properties::Data>(ref); return v2 ? (v == *v2) : false; }
private:
const ElementData *ref;
};
class StringVisitor : public boost::static_visitor<void> {
public:
StringVisitor(std::ostringstream &oss, bool quote) : oss(oss), quote(quote) { }
void operator()(const bool &v) const { oss << (v ? "true" : "false"); }
void operator()(const int64_t &v) const { oss << v; }
void operator()(const Float &v) const { oss << v; }
void operator()(const Point &v) const { oss << v.toString(); }
void operator()(const Vector &v) const { oss << v.toString(); }
void operator()(const Transform &v) const { oss << v.toString(); }
void operator()(const Spectrum &v) const { oss << v.toString(); }
void operator()(const std::string &v) const { oss << (quote ? "\"" : "") << v << (quote ? "\"" : ""); }
void operator()(const Properties::Data &v) const { oss << v.ptr << " (size=" << v.size << ")"; }
private:
std::ostringstream &oss;
bool quote;
};
}
Properties::Properties()
: m_id("unnamed") {
@ -145,13 +182,32 @@ Properties::EPropertyType Properties::getType(const std::string &name) const {
if (it == m_elements->end())
SLog(EError, "Property \"%s\" has not been specified!", name.c_str());
type_visitor myVisitor;
return boost::apply_visitor(myVisitor, it->second.data);
return boost::apply_visitor(TypeVisitor(), it->second.data);
}
std::string Properties::getAsString(const std::string &name, const std::string &defVal) const {
if (m_elements->find(name) == m_elements->end())
return defVal;
return getAsString(name);
}
std::string Properties::getAsString(const std::string &name) const {
std::map<std::string, PropertyElement>::const_iterator it = m_elements->find(name);
if (it == m_elements->end())
SLog(EError, "Property \"%s\" has not been specified!", name.c_str());
std::ostringstream oss;
StringVisitor strVisitor(oss, false);
boost::apply_visitor(strVisitor, it->second.data);
it->second.queried = true;
return oss.str();
}
std::string Properties::toString() const {
std::map<std::string, PropertyElement>::const_iterator it = m_elements->begin();
std::ostringstream oss;
StringVisitor strVisitor(oss, true);
oss << "Properties[" << endl
<< " pluginName = \"" << m_pluginName << "\"," << endl
@ -160,36 +216,7 @@ std::string Properties::toString() const {
while (it != m_elements->end()) {
oss << " \"" << (*it).first << "\" -> ";
const ElementData &data = (*it).second.data;
EPropertyType type = boost::apply_visitor(type_visitor(), data);
switch (type) {
case EBoolean:
oss << (boost::get<bool>(data) ? "true" : "false");
break;
case EInteger:
oss << boost::get<int64_t>(data);
break;
case EFloat:
oss << boost::get<Float>(data);
break;
case EPoint:
oss << boost::get<Point>(data).toString();
break;
case ETransform:
oss << indent(boost::get<Transform>(data).toString());
break;
case ESpectrum:
oss << boost::get<Spectrum>(data).toString();
break;
case EString:
oss << "\"" << boost::get<std::string>(data) << "\"";
break;
case EData:
oss << boost::get<Data>(data).ptr << " (size="
<< boost::get<Data>(data).size << ")";
break;
default:
oss << "<unknown>";
}
boost::apply_visitor(strVisitor, data);
if (++it != m_elements->end())
oss << ",";
oss << endl;
@ -219,6 +246,21 @@ void Properties::putPropertyNames(std::vector<std::string> &results) const {
results.push_back((*it).first);
}
bool Properties::operator==(const Properties &p) const {
if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size())
return false;
std::map<std::string, PropertyElement>::const_iterator it = m_elements->begin();
for (; it != m_elements->end(); ++it) {
const PropertyElement &first = it->second;
const PropertyElement &second = (*p.m_elements)[it->first];
if (!boost::apply_visitor(EqualityVisitor(&first.data), second.data))
return false;
}
return true;
}
ConfigurableObject::ConfigurableObject(Stream *stream, InstanceManager *manager)
: SerializableObject(stream, manager) {

View File

@ -754,6 +754,7 @@ void export_core() {
void (Bitmap::*accumulate_1)(const Bitmap *bitmap, Point2i sourceOffset, Point2i targetOffset, Vector2i size) = &Bitmap::accumulate;
void (Bitmap::*accumulate_2)(const Bitmap *bitmap, Point2i targetOffset) = &Bitmap::accumulate;
const Properties &(Bitmap::*get_metadata)() const = &Bitmap::getMetadata;
BP_CLASS(Bitmap, Object, (bp::init<Bitmap::EPixelFormat, Bitmap::EComponentFormat, const Vector2i &>()))
.def(bp::init<Bitmap::EPixelFormat, Bitmap::EComponentFormat, const Vector2i &, int>())
@ -769,8 +770,10 @@ void export_core() {
.def("accumulate", accumulate_1)
.def("accumulate", accumulate_2)
.def("write", &bitmap_write)
.def("setString", &Bitmap::setString)
.def("getString", &Bitmap::getString, BP_RETURN_VALUE)
.def("setMetadataString", &Bitmap::setMetadataString)
.def("getMetadataString", &Bitmap::getMetadataString, BP_RETURN_VALUE)
.def("setMetadata", &Bitmap::setMetadata)
.def("getMetadata", get_metadata, BP_RETURN_VALUE)
.def("setGamma", &Bitmap::setGamma)
.def("getGamma", &Bitmap::getGamma)
.def("getWidth", &Bitmap::getWidth)