mtsgui: ability to view multi-channel EXRs

metadata
Wenzel Jakob 2014-11-12 15:55:17 +01:00
parent 461827ec11
commit 9f9df009ef
8 changed files with 159 additions and 54 deletions

View File

@ -297,6 +297,22 @@ public:
EDivision
};
/**
* \brief Describes a sub-layer of a multilayer bitmap (e.g. OpenEXR)
*
* A layer is defined as a named collection of bitmap channels and a pixel format
*
* This data structure is used by \ref Bitmap::getLayers()
*/
struct Layer {
/// Descriptive name of the bitmap layer
std::string name;
/// Pixel format that should be associated with underlying combination of bitmap channels
EPixelFormat format;
/// References to bitmap channels
std::vector<int> channels;
};
/**
* \brief Create a bitmap of the specified type and allocate
* the necessary amount of memory
@ -813,9 +829,26 @@ public:
*/
ref<Bitmap> extractChannels(Bitmap::EPixelFormat fmt, const std::vector<int> &channels) const;
/**
* \brief Extracts layer information from the bitmap
*
* This is a convenience function which analyzes the bitmap's
* channel names and extracts groups matching a name
* convention specified by the OpenEXR standard.
*
* A series of channel names referred to as
* 'diffuse.R', 'diffuse.B', and 'diffuse.G'
* will be identified as a single \ref Bitmap::ERGB "layer".
* This works for RGB[A]/XYZ[A], and Luminance[A] images
*/
std::vector<Layer> getLayers() const;
/**
* \brief Split an multi-channel image buffer (e.g. from an OpenEXR image
* with lots of AOVs) into group of RGB[A]/XYZ[A]/Luminance images
* with lots of AOVs) into its constituent layers
*
* This operation internally calls \ref getLayers() to extract bitmap
* layers followed by one or more calls to \ref extractChannels()
*/
std::map<std::string, Bitmap *> split() const;

View File

@ -1875,32 +1875,34 @@ void Bitmap::tonemapReinhard(Float &logAvgLuminance, Float &maxLuminance, Float
}
}
std::map<std::string, Bitmap *> Bitmap::split() const {
std::vector<Bitmap::Layer> Bitmap::getLayers() const {
typedef std::map<std::string, int> ChannelMap;
std::map<std::string, Bitmap *> result;
if (m_channelNames.empty())
Log(EError, "Bitmap::split(): color channel names not available!");
Log(EError, "Bitmap::getLayers(): required color channel names were not available!");
ChannelMap channels;
for (size_t i=0; i<m_channelNames.size(); ++i)
channels[boost::to_lower_copy(m_channelNames[i])] = (int) i;
std::vector<Layer> layers;
for (size_t i=0; i<m_channelNames.size(); ++i) {
std::string name = boost::to_lower_copy(m_channelNames[i]);
if (channels.find(name) == channels.end())
continue;
std::string prefix = "";
std::string prefix = name;
char postfix = '\0';
Layer layer;
if (!name.empty()) {
prefix = name.substr(0, name.length()-1);
postfix = name[name.length()-1];
layer.name = m_channelNames[i];
if (name.length() == 1) {
prefix = layer.name = "";
postfix = name[name.length() - 1];
} if (name.length() >= 3 && name[name.length()-2] == '.') {
prefix = name.substr(0, name.length() - 1);
layer.name = layer.name.substr(0, layer.name.length() - 2);
postfix = name[name.length() - 1];
}
std::vector<int> extractChannels;
EPixelFormat extractFormat;
ChannelMap::iterator
itR = channels.find(prefix + "r"),
itG = channels.find(prefix + "g"),
@ -1915,68 +1917,75 @@ std::map<std::string, Bitmap *> Bitmap::split() const {
bool maybeY = postfix == 'y' || postfix == 'a';
if (maybeRGB && itR != channels.end() && itG != channels.end() && itB != channels.end()) {
extractFormat = ERGB;
extractChannels.push_back(itR->second);
extractChannels.push_back(itG->second);
extractChannels.push_back(itB->second);
layer.format = ERGB;
layer.channels.push_back(itR->second);
layer.channels.push_back(itG->second);
layer.channels.push_back(itB->second);
if (itA != channels.end()) {
extractFormat = ERGBA;
extractChannels.push_back(itA->second);
layer.format = ERGBA;
layer.channels.push_back(itA->second);
channels.erase(prefix + "a");
}
channels.erase(prefix + "r");
channels.erase(prefix + "g");
channels.erase(prefix + "b");
} else if (maybeXYZ && itX != channels.end() && itY != channels.end() && itZ != channels.end()) {
extractFormat = EXYZ;
extractChannels.push_back(itX->second);
extractChannels.push_back(itY->second);
extractChannels.push_back(itZ->second);
layer.format = EXYZ;
layer.channels.push_back(itX->second);
layer.channels.push_back(itY->second);
layer.channels.push_back(itZ->second);
if (itA != channels.end()) {
extractFormat = EXYZA;
extractChannels.push_back(itA->second);
layer.format = EXYZA;
layer.channels.push_back(itA->second);
channels.erase(prefix + "a");
}
channels.erase(prefix + "x");
channels.erase(prefix + "y");
channels.erase(prefix + "z");
} else if (maybeY && itY != channels.end()) {
extractFormat = ELuminance;
extractChannels.push_back(itY->second);
layer.format = ELuminance;
layer.channels.push_back(itY->second);
if (itA != channels.end()) {
extractFormat = ELuminanceAlpha;
extractChannels.push_back(itA->second);
layer.format = ELuminanceAlpha;
layer.channels.push_back(itA->second);
channels.erase(prefix + "a");
}
channels.erase(prefix + "y");
} else {
extractFormat = ELuminance;
extractChannels.push_back((int) i);
if (layer.name.empty())
layer.name = m_channelNames[i];
layer.format = ELuminance;
layer.channels.push_back((int) i);
channels.erase(name);
}
layers.push_back(layer);
}
return layers;
}
std::map<std::string, Bitmap *> Bitmap::split() const {
std::map<std::string, Bitmap *> result;
std::vector<Layer> layers = getLayers();
for (size_t i=0; i<layers.size(); ++i) {
const Layer &layer = layers[i];
std::vector<std::string> channelNames;
for (size_t j=0; j<extractChannels.size(); ++j)
channelNames.push_back(m_channelNames[extractChannels[j]]);
for (size_t j=0; j<layer.channels.size(); ++j)
channelNames.push_back(m_channelNames[layer.channels[j]]);
Bitmap *bitmap = NULL;
{
ref<Bitmap> _bitmap = this->extractChannels(extractFormat, extractChannels);
ref<Bitmap> _bitmap = this->extractChannels(layer.format, layer.channels);
bitmap = _bitmap.get();
bitmap->incRef();
}
bitmap->decRef(false);
bitmap->setChannelNames(channelNames);
prefix = m_channelNames[i];
if (!prefix.empty())
prefix = prefix.substr(0, prefix.length()-1);
if (!prefix.empty() && prefix[prefix.length()-1] == '.')
prefix = prefix.substr(0, prefix.length()-1);
if (result.find(prefix) != result.end())
Log(EError, "Internal error -- encountered two sub-images with the same prefix");
result[prefix] = bitmap;
if (result.find(layer.name) != result.end())
Log(EError, "Internal error -- encountered two layers with the same name \"%s\"", layer.name.c_str());
result[layer.name] = bitmap;
}
return result;
}

View File

@ -771,7 +771,8 @@ void GLRenderer::drawText(const Point2i &_pos,
glLoadIdentity();
font->getTexture()->bind();
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
Point2i pos(_pos);
int initial = pos.x;

View File

@ -1110,7 +1110,7 @@ void TriMesh::writePLY(const fs::path &path) const {
*ptr += (uint8_t) std::max(0.0f, std::min(255.0f, (float) m_colors[i][2] * 255.0f + 0.5f));
}
}
Assert(ptr-vertexStorage == vertexStorageSize);
Assert((size_t) (ptr-vertexStorage) == vertexStorageSize);
os.write((const char *) vertexStorage, vertexStorageSize);
delete[] vertexStorage;
@ -1122,7 +1122,7 @@ void TriMesh::writePLY(const fs::path &path) const {
memcpy(ptr, &m_triangles[i], sizeof(Triangle));
ptr += sizeof(Triangle);
}
Assert(ptr-faceStorage == faceStorageSize);
Assert((size_t) (ptr-faceStorage) == faceStorageSize);
os.write((const char *) faceStorage, faceStorageSize);
delete[] faceStorage;

View File

@ -184,7 +184,9 @@ struct SceneContext {
float progress;
QString eta, progressName;
ref<Bitmap> framebuffer;
std::vector<std::pair<std::string, Bitmap*> > layers;
std::set<VisualWorkUnit, block_comparator> workUnits;
int currentLayer;
EMode mode, cancelMode;
Float gamma, exposure, clamping;
bool srgb;
@ -209,6 +211,7 @@ struct SceneContext {
SceneContext() : scene(NULL), sceneResID(-1),
renderJob(NULL), wasRendering(false),
currentLayer(0),
selectionMode(ENothing),
selectedShape(NULL) { }

View File

@ -220,6 +220,14 @@ void GLWidget::setScene(SceneContext *context) {
m_movementTimer->stop();
m_context = context;
if (context && context->layers.size() > 0) {
m_statusMessage =
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
m_context->layers[m_context->currentLayer].first.c_str(),
m_context->currentLayer+1, m_context->layers.size());
m_statusTimer->reset();
}
if (context && context->scene == NULL)
context = NULL;
@ -503,7 +511,9 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
emit stopRendering();
}
if (event->isAutoRepeat() || !m_context)
if (!m_context ||
(event->isAutoRepeat() && event->key() != Qt::Key_BracketLeft &&
event->key() != Qt::Key_BracketRight))
return;
switch (event->key()) {
@ -526,7 +536,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
case Qt::Key_Down:
m_downKeyDown = true; break;
case Qt::Key_BracketLeft:
if (m_context->showKDTree) {
if (m_context->layers.size() > 0) {
m_context->currentLayer = std::max(0, m_context->currentLayer-1);
m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32);
m_statusMessage =
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
m_context->layers[m_context->currentLayer].first.c_str(),
m_context->currentLayer+1, m_context->layers.size());
m_statusTimer->reset();
} else if (m_context->showKDTree) {
m_context->shownKDTreeLevel
= std::max(0, m_context->shownKDTreeLevel - 1);
updateGL();
@ -534,7 +552,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
}
break;
case Qt::Key_BracketRight:
if (m_context->showKDTree) {
if (m_context->layers.size() > 0) {
m_context->currentLayer = std::min((int) m_context->layers.size() - 1, m_context->currentLayer+1);
m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32);
m_statusMessage =
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
m_context->layers[m_context->currentLayer].first.c_str(),
m_context->currentLayer+1, m_context->layers.size());
m_statusTimer->reset();
} else if (m_context->showKDTree) {
m_context->shownKDTreeLevel++;
updateGL();
return;
@ -1215,11 +1241,17 @@ void GLWidget::paintGL() {
if (m_context->mode == EPreview) {
m_preview->releaseBuffer(entry);
if (m_context->showKDTree) {
std::string message = "kd-tree visualization mode\nPress '[' "
"and ']' to change the shown level";
Vector2i size = m_font->getSize(message);
int pos = m_statusMessage.empty() ? 10 : 30;
m_renderer->setBlendMode(Renderer::EBlendAlpha);
m_renderer->setColor(Spectrum(0.0f), 0.5f);
m_renderer->drawFilledRectangle(Point2i(5, pos-2), Point2i(15, pos+5)+size);
m_renderer->setBlendMode(Renderer::EBlendNone);
m_renderer->setColor(Spectrum(1.0f));
m_renderer->drawText(Point2i(10, pos), m_font,
formatString("kd-tree visualization mode\nPress '[' "
"and ']' to change the shown level"));
m_renderer->setColor(Spectrum(1.0f));
m_renderer->drawText(Point2i(10, pos), m_font, message);
}
}
@ -1244,7 +1276,15 @@ void GLWidget::paintGL() {
m_statusMessage = "Camera type is incompatible with the OpenGL preview!";
}
if (!m_statusMessage.empty() && m_context->mode == EPreview) {
if (!m_statusMessage.empty() && (m_context->mode == EPreview
|| (m_context->mode == ERender && m_context->scene == NULL))) {
m_renderer->setDepthTest(false);
Vector2i size = m_font->getSize(m_statusMessage);
m_renderer->setBlendMode(Renderer::EBlendAlpha);
m_renderer->setColor(Spectrum(0.0f), 0.5f);
m_renderer->drawFilledRectangle(Point2i(5, 7), Point2i(15, 15)+size);
m_renderer->setBlendMode(Renderer::EBlendNone);
m_renderer->setColor(Spectrum(1.0f));
m_renderer->drawText(Point2i(10, 10), m_font, m_statusMessage);
if (m_statusTimer->getMilliseconds() > 2000)
m_statusMessage = "";

View File

@ -398,7 +398,7 @@ bool MainWindow::initWorkersProcessArgv() {
scheduler->registerWorker(new LocalWorker(-1, formatString("wrk%i", 0), m_workerPriority));
}
for (int i=0; i<toBeLoaded.size(); ++i)
for (size_t i=0; i<toBeLoaded.size(); ++i)
loadFile(toBeLoaded[i].first, toBeLoaded[i].second);
scheduler->start();
@ -2157,6 +2157,10 @@ SceneContext::SceneContext(SceneContext *ctx) {
selectionMode = ctx->selectionMode;
originalSize = ctx->originalSize;
doc = ctx->doc.cloneNode(true).toDocument();
layers = ctx->layers;
for (size_t i=0; i<layers.size(); ++i)
layers[i].second->incRef();
currentLayer = ctx->currentLayer;
}
SceneContext::~SceneContext() {
@ -2166,6 +2170,8 @@ SceneContext::~SceneContext() {
previewBuffer.buffer->decRef();
if (previewBuffer.sync)
previewBuffer.sync->decRef();
for (size_t i=0; i<layers.size(); ++i)
layers[i].second->decRef();
}
int SceneContext::detectPathLength() const {

View File

@ -65,12 +65,25 @@ void SceneLoader::run() {
m_result->toneMappingMethod = (EToneMappingMethod) settings.value("preview_toneMappingMethod", EGamma).toInt();
m_result->diffuseSources = settings.value("preview_diffuseSources", true).toBool();
m_result->diffuseReceivers = settings.value("preview_diffuseReceivers", false).toBool();
m_result->currentLayer = 0;
if (suffix == "exr" || suffix == "png" || suffix == "jpg" || suffix == "jpeg" ||
suffix == "hdr" || suffix == "rgbe" || suffix == "pfm" || suffix == "ppm") {
/* This is an image, not a scene */
ref<FileStream> fs = new FileStream(toFsPath(m_filename), FileStream::EReadOnly);
ref<Bitmap> bitmap = new Bitmap(Bitmap::EAuto, fs);
ref<Bitmap> bitmap = new Bitmap(Bitmap::EAuto, fs, (suffix == "exr") ? "*" : "");
if (suffix == "exr" && bitmap->getLayers().size() > 1) {
std::map<std::string, Bitmap*> bitmaps = bitmap->split();
for (std::map<std::string, Bitmap *>::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) {
std::string name = it->first;
Bitmap *layer = it->second;
layer->incRef();
m_result->layers.push_back(std::make_pair(name, layer));
}
bitmap = m_result->layers[m_result->currentLayer].second;
}
bitmap = bitmap->convert(Bitmap::ERGBA, Bitmap::EFloat32);
m_result->mode = ERender;