Skip to content

Commit

Permalink
Make gamma configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom94 committed Jun 26, 2018
1 parent b64fc89 commit 9258661
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 35 deletions.
10 changes: 8 additions & 2 deletions include/tev/ImageCanvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class ImageCanvas : public nanogui::GLCanvas {
mOffset = offset;
}

void setGamma(float gamma) {
mGamma = gamma;
}

float applyExposureAndOffset(float value);

void setImage(std::shared_ptr<Image> image) {
Expand Down Expand Up @@ -78,9 +82,9 @@ class ImageCanvas : public nanogui::GLCanvas {
mTonemap = tonemap;
}

static Eigen::Vector3f applyTonemap(const Eigen::Vector3f& value, ETonemap tonemap);
static Eigen::Vector3f applyTonemap(const Eigen::Vector3f& value, float gamma, ETonemap tonemap);
Eigen::Vector3f applyTonemap(const Eigen::Vector3f& value) const {
return applyTonemap(value, mTonemap);
return applyTonemap(value, mGamma, mTonemap);
}

EMetric metric() const {
Expand Down Expand Up @@ -136,6 +140,8 @@ class ImageCanvas : public nanogui::GLCanvas {
float mPixelRatio = 1;
float mExposure = 0;
float mOffset = 0;
float mGamma = 2.2f;

std::shared_ptr<Image> mImage;
std::shared_ptr<Image> mReference;

Expand Down
9 changes: 9 additions & 0 deletions include/tev/ImageViewer.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class ImageViewer : public nanogui::Screen {

void setOffset(float value);

float gamma() {
return mGammaSlider->value();
}

void setGamma(float value);

void normalizeExposureAndOffset();
void resetImage();

Expand Down Expand Up @@ -159,6 +165,9 @@ class ImageViewer : public nanogui::Screen {
nanogui::Label* mOffsetLabel;
nanogui::Slider* mOffsetSlider;

nanogui::Label* mGammaLabel;
nanogui::Slider* mGammaSlider;

nanogui::Widget* mTonemapButtonContainer;
nanogui::Widget* mMetricButtonContainer;

Expand Down
3 changes: 3 additions & 0 deletions include/tev/UberShader.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class UberShader {
const Eigen::Matrix3f& transformImage,
float exposure,
float offset,
float gamma,
ETonemap tonemap
);

Expand All @@ -38,6 +39,7 @@ class UberShader {
const Eigen::Matrix3f& transformReference,
float exposure,
float offset,
float gamma,
ETonemap tonemap,
EMetric metric
);
Expand All @@ -58,6 +60,7 @@ class UberShader {
const Eigen::Matrix3f& transformImage,
float exposure,
float offset,
float gamma,
ETonemap tonemap
);

Expand Down
5 changes: 3 additions & 2 deletions src/ImageCanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void ImageCanvas::drawGL() {
transform(mImage.get()).inverse().matrix(),
mExposure,
mOffset,
mGamma,
mTonemap
);
return;
Expand All @@ -78,6 +79,7 @@ void ImageCanvas::drawGL() {
transform(mReference.get()).inverse().matrix(),
mExposure,
mOffset,
mGamma,
mTonemap,
mMetric
);
Expand Down Expand Up @@ -288,7 +290,7 @@ void ImageCanvas::getValuesAtNanoPos(Vector2i mousePos, vector<float>& result) {
}
}

Vector3f ImageCanvas::applyTonemap(const Vector3f& value, ETonemap tonemap) {
Vector3f ImageCanvas::applyTonemap(const Vector3f& value, float gamma, ETonemap tonemap) {
Vector3f result;
switch (tonemap) {
case ETonemap::SRGB:
Expand All @@ -298,7 +300,6 @@ Vector3f ImageCanvas::applyTonemap(const Vector3f& value, ETonemap tonemap) {
}
case ETonemap::Gamma:
{
static const float gamma = 2.2f;
result = {pow(value.x(), 1 / gamma), pow(value.y(), 1 / gamma), pow(value.z(), 1 / gamma)};
break;
}
Expand Down
96 changes: 69 additions & 27 deletions src/ImageViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <nanogui/popupbutton.h>
#include <nanogui/screen.h>
#include <nanogui/textbox.h>
#include <nanogui/theme.h>
#include <nanogui/vscrollpanel.h>

#include <chrono>
Expand Down Expand Up @@ -54,7 +55,7 @@ ImageViewer::ImageViewer(shared_ptr<Ipc> ipc, shared_ptr<SharedQueue<ImageAdditi

mImageCanvas = new ImageCanvas{horizontalScreenSplit, pixelRatio()};

// Exposure label and slider
// Tonemapping section
{
auto panel = new Widget{mSidebarLayout};
panel->setLayout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 5});
Expand All @@ -63,39 +64,56 @@ ImageViewer::ImageViewer(shared_ptr<Ipc> ipc, shared_ptr<SharedQueue<ImageAdditi
"Various tonemapping options. Hover the individual controls to learn more!"
);

panel = new Widget{mSidebarLayout};
panel->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 5});
// Exposure label and slider
{
panel = new Widget{mSidebarLayout};
panel->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 5});

mExposureLabel = new Label{panel, "", "sans-bold", 15};
mExposureLabel = new Label{panel, "", "sans-bold", 15};

mExposureSlider = new Slider{panel};
mExposureSlider->setRange({-5.0f, 5.0f});
mExposureSlider->setCallback([this](float value) {
setExposure(value);
});
setExposure(0);
mExposureSlider = new Slider{panel};
mExposureSlider->setRange({-5.0f, 5.0f});
mExposureSlider->setCallback([this](float value) {
setExposure(value);
});
setExposure(0);

panel->setTooltip(
"Exposure scales the brightness of an image prior to tonemapping by 2^Exposure.\n\n"
"Keyboard shortcuts:\nE and Shift+E"
);
panel->setTooltip(
"Exposure scales the brightness of an image prior to tonemapping by 2^Exposure.\n\n"
"Keyboard shortcuts:\nE and Shift+E"
);
}

panel = new Widget{mSidebarLayout};
panel->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 5});
// Offset/Gamma label and slider
{
panel = new Widget{mSidebarLayout};
panel->setLayout(new GridLayout{Orientation::Vertical, 2, Alignment::Fill, 5, 0});

mOffsetLabel = new Label{panel, "", "sans-bold", 15};
mOffsetLabel = new Label{panel, "", "sans-bold", 15};

mOffsetSlider = new Slider{panel};
mOffsetSlider->setRange({-1.0f, 1.0f});
mOffsetSlider->setCallback([this](float value) {
setOffset(value);
});
setOffset(0);
mOffsetSlider = new Slider{panel};
mOffsetSlider->setRange({-1.0f, 1.0f});
mOffsetSlider->setCallback([this](float value) {
setOffset(value);
});
setOffset(0);

panel->setTooltip(
"The offset is added to the image after exposure has been applied.\n\n"
"Keyboard shortcuts:\nO and Shift+O"
);
mGammaLabel = new Label{panel, "", "sans-bold", 15};

mGammaSlider = new Slider{panel};
mGammaSlider->setRange({0.01f, 5.0f});
mGammaSlider->setCallback([this](float value) {
setGamma(value);
});
setGamma(2.2f);

panel->setTooltip(
"The offset is added to the image after exposure has been applied.\n"
"Keyboard shortcuts: O and Shift+O\n\n"
"Gamma is the exponent used when gamma-tonemapping.\n"
"Keyboard shortcuts: G and Shift+G\n\n"
);
}
}

// Exposure/offset buttons
Expand Down Expand Up @@ -636,6 +654,16 @@ bool ImageViewer::keyboardEvent(int key, int scancode, int action, int modifiers
}
}

if (mGammaSlider->enabled()) {
if (key == GLFW_KEY_G) {
if (modifiers & GLFW_MOD_SHIFT) {
setGamma(gamma() - 0.1f);
} else {
setGamma(gamma() + 0.1f);
}
}
}

if (key == GLFW_KEY_W && modifiers & SYSTEM_COMMAND_MOD) {
removeImage(mCurrentImage);
} else if (key == GLFW_KEY_UP || key == GLFW_KEY_W || key == GLFW_KEY_PAGE_UP) {
Expand Down Expand Up @@ -1067,6 +1095,14 @@ void ImageViewer::setOffset(float value) {
mImageCanvas->setOffset(value);
}

void ImageViewer::setGamma(float value) {
value = round(value, 2.0f);
mGammaSlider->setValue(value);
mGammaLabel->setCaption(tfm::format("Gamma: %+.2f", value));

mImageCanvas->setGamma(value);
}

void ImageViewer::normalizeExposureAndOffset() {
if (!mCurrentImage) {
return;
Expand All @@ -1093,6 +1129,7 @@ void ImageViewer::normalizeExposureAndOffset() {
void ImageViewer::resetImage() {
setExposure(0);
setOffset(0);
setGamma(2.2f);
mImageCanvas->resetTransform();
}

Expand All @@ -1103,6 +1140,11 @@ void ImageViewer::setTonemap(ETonemap tonemap) {
Button* b = dynamic_cast<Button*>(buttons[i]);
b->setPushed((ETonemap)i == tonemap);
}

mGammaSlider->setEnabled(tonemap == ETonemap::Gamma);
mGammaLabel->setColor(
tonemap == ETonemap::Gamma ? mGammaLabel->theme()->mTextColor : Color{0.5f, 1.0f}
);
}

void ImageViewer::setMetric(EMetric metric) {
Expand Down
11 changes: 8 additions & 3 deletions src/UberShader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ UberShader::UberShader()
uniform float exposure;
uniform float offset;
uniform float gamma;
uniform int tonemap;
uniform int metric;
Expand Down Expand Up @@ -101,7 +102,7 @@ UberShader::UberShader()
vec3 applyTonemap(vec3 col) {
switch (tonemap) {
case SRGB: return vec3(sRGB(col.r), sRGB(col.g), sRGB(col.b));
case GAMMA: return pow(col, vec3(1.0 / 2.2));
case GAMMA: return pow(col, vec3(1.0 / gamma));
// Here grayscale is compressed such that the darkest color is is 1/1024th as bright as the brightest color.
case FALSE_COLOR: return falseColor(log2(average(col)) / 10.0 + 0.5);
case POS_NEG: return vec3(-average(min(col, vec3(0.0))) * 2.0, average(max(col, vec3(0.0))) * 2.0, 0.0);
Expand Down Expand Up @@ -198,11 +199,12 @@ void UberShader::draw(
const Matrix3f& transformImage,
float exposure,
float offset,
float gamma,
ETonemap tonemap
) {
mShader.bind();
bindCheckerboardData(pixelSize, checkerSize);
bindImageData(textureImage, transformImage, exposure, offset, tonemap);
bindImageData(textureImage, transformImage, exposure, offset, gamma, tonemap);
mShader.setUniform("hasImage", true);
mShader.setUniform("hasReference", false);
mShader.drawIndexed(GL_TRIANGLES, 0, 2);
Expand All @@ -217,12 +219,13 @@ void UberShader::draw(
const Matrix3f& transformReference,
float exposure,
float offset,
float gamma,
ETonemap tonemap,
EMetric metric
) {
mShader.bind();
bindCheckerboardData(pixelSize, checkerSize);
bindImageData(textureImage, transformImage, exposure, offset, tonemap);
bindImageData(textureImage, transformImage, exposure, offset, gamma, tonemap);
bindReferenceData(textureReference, transformReference, metric);
mShader.setUniform("hasImage", true);
mShader.setUniform("hasReference", true);
Expand All @@ -240,6 +243,7 @@ void UberShader::bindImageData(
const Matrix3f& transformImage,
float exposure,
float offset,
float gamma,
ETonemap tonemap
) {
glActiveTexture(GL_TEXTURE0);
Expand All @@ -251,6 +255,7 @@ void UberShader::bindImageData(

mShader.setUniform("exposure", exposure);
mShader.setUniform("offset", offset);
mShader.setUniform("gamma", gamma);
mShader.setUniform("tonemap", static_cast<int>(tonemap));

glActiveTexture(GL_TEXTURE2);
Expand Down
10 changes: 9 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ int mainFunc(const vector<string>& arguments) {
{'f', "filter"},
};

ValueFlag<float> gammaFlag{
parser,
"GAMMA",
"The exponent used when TONEMAP is 'Gamma'. Default is 2.2.",
{'g', "gamma"},
};

HelpFlag helpFlag{
parser,
"HELP",
Expand Down Expand Up @@ -95,7 +102,7 @@ int mainFunc(const vector<string>& arguments) {
"The tonemapping algorithm to use. "
"The available tonemaps are:\n"
"sRGB - sRGB\n"
"Gamma - Gamma curve (2.2)\n"
"Gamma - Gamma curve\n"
"FC - False Color\n"
"PN - Positive=Green, Negative=Red\n"
"Default is sRGB.",
Expand Down Expand Up @@ -209,6 +216,7 @@ int mainFunc(const vector<string>& arguments) {
// Apply parameter flags
if (exposureFlag) { app->setExposure(get(exposureFlag)); }
if (filterFlag) { app->setFilter(get(filterFlag)); }
if (gammaFlag) { app->setGamma(get(gammaFlag)); }
if (metricFlag) { app->setMetric(toMetric(get(metricFlag))); }
if (offsetFlag) { app->setOffset(get(offsetFlag)); }
if (tonemapFlag) { app->setTonemap(toTonemap(get(tonemapFlag))); }
Expand Down

0 comments on commit 9258661

Please sign in to comment.