HDR encoding tests.
parent
e46f48f0a3
commit
6d843c78cf
@ -0,0 +1,164 @@
|
||||
|
||||
#include "nvmath/Vector.inl"
|
||||
#include "nvcore/StrLib.h"
|
||||
|
||||
namespace nv {
|
||||
|
||||
struct GooglePointSet {
|
||||
GooglePointSet();
|
||||
Vector2 min, max;
|
||||
Array<Vector2> points;
|
||||
const char * legend;
|
||||
Vector3 lineColor;
|
||||
int lineWidth;
|
||||
bool dashed;
|
||||
int lineSegmentLength;
|
||||
int blankSegmentLength;
|
||||
};
|
||||
|
||||
struct GoogleLineChart {
|
||||
GoogleLineChart();
|
||||
void autoScale();
|
||||
void build(nv::StringBuilder & builder) const;
|
||||
|
||||
const char * title;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
int leftMargin;
|
||||
int rightMargin;
|
||||
int topMargin;
|
||||
int bottomMargin;
|
||||
|
||||
int legendPosition;
|
||||
|
||||
Array<GooglePointSet> pointSetArray;
|
||||
};
|
||||
|
||||
|
||||
|
||||
GooglePointSet::GooglePointSet()
|
||||
{
|
||||
min.x = 0;
|
||||
min.y = 0;
|
||||
max.x = 1;
|
||||
max.x = 1;
|
||||
legend = NULL;
|
||||
lineColor = Vector3(1, 0, 0);
|
||||
lineWidth = 2;
|
||||
dashed = false;
|
||||
lineSegmentLength = 4;
|
||||
blankSegmentLength = 1;
|
||||
}
|
||||
|
||||
|
||||
GoogleLineChart::GoogleLineChart()
|
||||
{
|
||||
title = NULL;
|
||||
width = 440;
|
||||
height = 220;
|
||||
|
||||
leftMargin = 5;
|
||||
rightMargin = 5;
|
||||
topMargin = 5;
|
||||
bottomMargin = 25;
|
||||
|
||||
legendPosition = 3; // bottom
|
||||
}
|
||||
|
||||
void GoogleLineChart::autoScale()
|
||||
{
|
||||
Vector2 minimum, maximum;
|
||||
foreach(i, pointSetArray) {
|
||||
foreach(p, pointSetArray[i].points) {
|
||||
Vector2 point = pointSetArray[i].points[p];
|
||||
minimum = min(minimum, point);
|
||||
maximum = max(maximum, point);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(i, pointSetArray) {
|
||||
pointSetArray[i].min = minimum;
|
||||
pointSetArray[i].max = maximum;
|
||||
}
|
||||
}
|
||||
|
||||
void GoogleLineChart::build(nv::StringBuilder & builder) const
|
||||
{
|
||||
const uint lineCount = pointSetArray.count();
|
||||
|
||||
builder.reset();
|
||||
|
||||
// Start LineChartXY.
|
||||
builder.copy("http://chart.apis.google.com/chart?cht=lxy");
|
||||
|
||||
// Size.
|
||||
builder.appendFormat("&chs=%dx%d", width, height);
|
||||
|
||||
// Title.
|
||||
if (title != NULL) builder.appendFormat("&chtt=%s", title);
|
||||
|
||||
// Margins.
|
||||
builder.appendFormat("&chma=%d,%d,%d,%d", leftMargin, rightMargin, topMargin, bottomMargin);
|
||||
|
||||
// Legend position.
|
||||
builder.appendFormat("&chdlp=%c", "lrtb"[legendPosition]);
|
||||
|
||||
// Line colors.
|
||||
builder.append("&chco=");
|
||||
for (uint i = 0; i < lineCount; i++) {
|
||||
const GooglePointSet & set = pointSetArray[i];
|
||||
builder.appendFormat("%.2X%.2X%.2X", int(255 * set.lineColor.x), int(255 * set.lineColor.y), int(255 * set.lineColor.z));
|
||||
if (i != lineCount-1) builder.append(",");
|
||||
}
|
||||
|
||||
// Legends.
|
||||
builder.append("&chdl=");
|
||||
for (uint i = 0; i < lineCount; i++) {
|
||||
const GooglePointSet & set = pointSetArray[i];
|
||||
if (set.legend != NULL) builder.append(set.legend);
|
||||
if (i != lineCount-1) builder.append("|");
|
||||
}
|
||||
|
||||
// Line format.
|
||||
builder.append("&chls=");
|
||||
for (uint i = 0; i < lineCount; i++) {
|
||||
const GooglePointSet & set = pointSetArray[i];
|
||||
builder.appendFormat("%d", set.lineWidth);
|
||||
if (set.dashed) builder.appendFormat(",%d,%d", set.lineSegmentLength, set.blankSegmentLength);
|
||||
if (i != lineCount-1) builder.append("|");
|
||||
}
|
||||
|
||||
// Scaling.
|
||||
builder.append("&chds=");
|
||||
|
||||
for (uint i = 0; i < lineCount; i++) {
|
||||
const GooglePointSet & set = pointSetArray[i];
|
||||
builder.appendFormat("%f,%f,%f,%f", set.min.x, set.max.x, set.min.y, set.max.y);
|
||||
if (i != lineCount-1) builder.append(",");
|
||||
}
|
||||
|
||||
// Data.
|
||||
builder.append("&chd=t:");
|
||||
for (uint i = 0; i < lineCount; i++) {
|
||||
const GooglePointSet & set = pointSetArray[i];
|
||||
|
||||
const uint pointCount = set.points.count();
|
||||
/*for (uint p = 0; p < pointCount; p++) {
|
||||
builder.appendFormat("%f", set.points[p].x);
|
||||
if (p != pointCount-1) builder.append(",");
|
||||
}*/
|
||||
builder.append("-1");
|
||||
|
||||
builder.append("|");
|
||||
|
||||
for (uint p = 0; p < pointCount; p++) {
|
||||
builder.appendFormat("%f", set.points[p].y);
|
||||
if (p != pointCount-1) builder.append(",");
|
||||
}
|
||||
|
||||
if (i != lineCount-1) builder.append("|");
|
||||
}
|
||||
}
|
||||
|
||||
} // nv namespace
|
@ -0,0 +1,359 @@
|
||||
// Copyright (c) 2009-2011 Ignacio Castano <castano@gmail.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include <nvtt/nvtt.h>
|
||||
#include <nvimage/Image.h>
|
||||
#include <nvimage/ImageIO.h>
|
||||
#include <nvimage/BlockDXT.h>
|
||||
#include <nvimage/ColorBlock.h>
|
||||
#include <nvcore/Ptr.h>
|
||||
#include <nvcore/Debug.h>
|
||||
#include <nvcore/StrLib.h>
|
||||
#include <nvcore/StdStream.h>
|
||||
#include <nvcore/TextWriter.h>
|
||||
#include <nvcore/FileSystem.h>
|
||||
#include <nvcore/Timer.h>
|
||||
|
||||
#include <stdlib.h> // free
|
||||
#include <string.h> // memcpy
|
||||
#include <float.h> // FLT_MAX
|
||||
|
||||
#include "../tools/cmdline.h"
|
||||
|
||||
#include "GoogleCharts.h"
|
||||
|
||||
using namespace nv;
|
||||
using namespace nvtt;
|
||||
|
||||
static const char * s_hdrImageSet[] = {
|
||||
"specruin.dds",
|
||||
"cottage.dds",
|
||||
"tower.dds",
|
||||
};
|
||||
|
||||
|
||||
struct MyOutputHandler : public nvtt::OutputHandler
|
||||
{
|
||||
MyOutputHandler() : m_data(NULL), m_ptr(NULL) {}
|
||||
~MyOutputHandler()
|
||||
{
|
||||
free(m_data);
|
||||
}
|
||||
|
||||
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel)
|
||||
{
|
||||
m_size = size;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
free(m_data);
|
||||
m_data = (unsigned char *)malloc(size);
|
||||
m_ptr = m_data;
|
||||
}
|
||||
|
||||
virtual void endImage()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool writeData(const void * data, int size)
|
||||
{
|
||||
memcpy(m_ptr, data, size);
|
||||
m_ptr += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
nvtt::Surface decompress(nvtt::Format format, nvtt::Decoder decoder)
|
||||
{
|
||||
nvtt::Surface img;
|
||||
img.setImage2D(format, decoder, m_width, m_height, m_data);
|
||||
return img;
|
||||
}
|
||||
|
||||
int m_size;
|
||||
int m_width;
|
||||
int m_height;
|
||||
unsigned char * m_data;
|
||||
unsigned char * m_ptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Compare two HDR surfaces tone mapped at the given exposure.
|
||||
float compare(const Surface & hdr0, const Surface & hdr1, float exposure)
|
||||
{
|
||||
Surface ldr0 = hdr0;
|
||||
ldr0.scaleBias(0, exposure, 0);
|
||||
ldr0.scaleBias(1, exposure, 0);
|
||||
ldr0.scaleBias(2, exposure, 0);
|
||||
ldr0.toneMap(nvtt::ToneMapper_Halo, NULL);
|
||||
ldr0.toSrgb();
|
||||
|
||||
Surface ldr1 = hdr1;
|
||||
ldr1.scaleBias(0, exposure, 0);
|
||||
ldr1.scaleBias(1, exposure, 0);
|
||||
ldr1.scaleBias(2, exposure, 0);
|
||||
ldr1.toneMap(nvtt::ToneMapper_Halo, NULL);
|
||||
ldr1.toSrgb();
|
||||
|
||||
return nvtt::rmsError(ldr0, ldr1);
|
||||
}
|
||||
|
||||
// Compare two HDR images at different exposures.
|
||||
void compare(const Surface & hdr0, const Surface & hdr1, const Array<float> & exposures, Array<float> & errors)
|
||||
{
|
||||
const uint exposureCount = exposures.count();
|
||||
|
||||
errors.resize(exposureCount);
|
||||
|
||||
for (uint i = 0; i < exposureCount; i++) {
|
||||
errors[i] = compare(hdr0, hdr1, exposures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void updatePointSet(const Array<float> & exposures, Array<float> & errors, GooglePointSet & pointSet)
|
||||
{
|
||||
uint pointCount = exposures.count();
|
||||
nvDebugCheck(pointCount == errors.count());
|
||||
|
||||
pointSet.points.resize(pointCount);
|
||||
|
||||
for (uint i = 0; i < pointCount; i++) {
|
||||
pointSet.points[i].x = exposures[i];
|
||||
pointSet.points[i].y = errors[i];
|
||||
}
|
||||
}
|
||||
|
||||
Surface loadInput(const char * fileName) {
|
||||
Surface src;
|
||||
src.load(fileName);
|
||||
src.clamp(0, 0, FLT_MAX);
|
||||
src.clamp(1, 0, FLT_MAX);
|
||||
src.clamp(2, 0, FLT_MAX);
|
||||
return src;
|
||||
}
|
||||
|
||||
Surface process(const Surface & src, int method) {
|
||||
Surface dst;
|
||||
|
||||
float rMin, rMax, gMin, gMax, bMin, bMax;
|
||||
src.range(0, &rMin, &rMax);
|
||||
src.range(1, &gMin, &gMax);
|
||||
src.range(2, &bMin, &bMax);
|
||||
|
||||
if (method == 0) {
|
||||
// Only clamp.
|
||||
dst = src;
|
||||
|
||||
dst.scaleBias(0, 1.0f / 4, 0);
|
||||
dst.scaleBias(1, 1.0f / 4, 0);
|
||||
dst.scaleBias(2, 1.0f / 4, 0);
|
||||
|
||||
dst.clamp(0, 0, 1);
|
||||
dst.clamp(1, 0, 1);
|
||||
dst.clamp(2, 0, 1);
|
||||
|
||||
dst.toGamma(0, 2);
|
||||
dst.toGamma(1, 2);
|
||||
dst.toGamma(2, 2);
|
||||
|
||||
dst.quantize(0, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(1, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(2, 10, /*exactEndPoints*/true, false);
|
||||
|
||||
dst.toLinear(0, 2);
|
||||
dst.toLinear(1, 2);
|
||||
dst.toLinear(2, 2);
|
||||
|
||||
dst.scaleBias(0, 4, 0);
|
||||
dst.scaleBias(1, 4, 0);
|
||||
dst.scaleBias(2, 4, 0);
|
||||
}
|
||||
else if (method == 1) {
|
||||
// Scale and bias. Use full range.
|
||||
dst = src;
|
||||
|
||||
float gamma = 3;
|
||||
|
||||
dst.scaleBias(0, 1.0f / rMax, 0);
|
||||
dst.scaleBias(1, 1.0f / gMax, 0);
|
||||
dst.scaleBias(2, 1.0f / bMax, 0);
|
||||
|
||||
dst.clamp(0, 0, 1);
|
||||
dst.clamp(1, 0, 1);
|
||||
dst.clamp(2, 0, 1);
|
||||
|
||||
dst.toGamma(0, gamma);
|
||||
dst.toGamma(1, gamma);
|
||||
dst.toGamma(2, gamma);
|
||||
|
||||
dst.quantize(0, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(1, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(2, 10, /*exactEndPoints*/true, false);
|
||||
|
||||
dst.toLinear(0, gamma);
|
||||
dst.toLinear(1, gamma);
|
||||
dst.toLinear(2, gamma);
|
||||
|
||||
dst.scaleBias(0, rMax, 0);
|
||||
dst.scaleBias(1, gMax, 0);
|
||||
dst.scaleBias(2, bMax, 0);
|
||||
}
|
||||
else if (method == 2) {
|
||||
// Scale and bias. Use full range.
|
||||
dst = src;
|
||||
|
||||
// @@ Experiment with log/exp transform!
|
||||
float gamma = 2.2;
|
||||
|
||||
dst.scaleBias(0, 1.0f / rMax, 0);
|
||||
dst.scaleBias(1, 1.0f / gMax, 0);
|
||||
dst.scaleBias(2, 1.0f / bMax, 0);
|
||||
|
||||
dst.clamp(0, 0, 1);
|
||||
dst.clamp(1, 0, 1);
|
||||
dst.clamp(2, 0, 1);
|
||||
|
||||
//dst.toGamma(0, gamma);
|
||||
//dst.toGamma(1, gamma);
|
||||
//dst.toGamma(2, gamma);
|
||||
dst.toLogScale(0, 2);
|
||||
dst.toLogScale(1, 2);
|
||||
dst.toLogScale(2, 2);
|
||||
|
||||
dst.quantize(0, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(1, 10, /*exactEndPoints*/true, false);
|
||||
dst.quantize(2, 10, /*exactEndPoints*/true, false);
|
||||
|
||||
dst.fromLogScale(0, 2);
|
||||
dst.fromLogScale(1, 2);
|
||||
dst.fromLogScale(2, 2);
|
||||
|
||||
//dst.toLinear(0, gamma);
|
||||
//dst.toLinear(1, gamma);
|
||||
//dst.toLinear(2, gamma);
|
||||
|
||||
dst.scaleBias(0, rMax, 0);
|
||||
dst.scaleBias(1, gMax, 0);
|
||||
dst.scaleBias(2, bMax, 0);
|
||||
}
|
||||
else if (method == 3) {
|
||||
// Scale and bias. Use full range.
|
||||
dst = src;
|
||||
|
||||
// @@ Experiment with log/exp transform!
|
||||
float gamma = 0.5;
|
||||
|
||||
dst.scaleBias(0, 1.0f / rMax, 0);
|
||||
dst.scaleBias(1, 1.0f / gMax, 0);
|
||||
dst.scaleBias(2, 1.0f / bMax, 0);
|
||||
|
||||
dst.clamp(0, 0, 1);
|
||||
dst.clamp(1, 0, 1);
|
||||
dst.clamp(2, 0, 1);
|
||||
|
||||
dst.toGamma(0, gamma);
|
||||
dst.toGamma(1, gamma);
|
||||
dst.toGamma(2, gamma);
|
||||
|
||||
dst.toLogScale(0, 2);
|
||||
dst.toLogScale(1, 2);
|
||||
dst.toLogScale(2, 2);
|
||||
|
||||
dst.quantize(0, 8, /*exactEndPoints*/true, false);
|
||||
dst.quantize(1, 8, /*exactEndPoints*/true, false);
|
||||
dst.quantize(2, 8, /*exactEndPoints*/true, false);
|
||||
|
||||
dst.fromLogScale(0, 2);
|
||||
dst.fromLogScale(1, 2);
|
||||
dst.fromLogScale(2, 2);
|
||||
|
||||
dst.toLinear(0, gamma);
|
||||
dst.toLinear(1, gamma);
|
||||
dst.toLinear(2, gamma);
|
||||
|
||||
dst.scaleBias(0, rMax, 0);
|
||||
dst.scaleBias(1, gMax, 0);
|
||||
dst.scaleBias(2, bMax, 0);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void printImageInfo(const Surface & img) {
|
||||
float rMin, rMax, gMin, gMax, bMin, bMax;
|
||||
img.range(0, &rMin, &rMax);
|
||||
img.range(1, &gMin, &gMax);
|
||||
img.range(2, &bMin, &bMax);
|
||||
|
||||
printf("R: %f %f\n", rMin, rMax);
|
||||
printf("G: %f %f\n", gMin, gMax);
|
||||
printf("B: %f %f\n", bMin, bMax);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MyAssertHandler assertHandler;
|
||||
MyMessageHandler messageHandler;
|
||||
|
||||
GoogleLineChart chart;
|
||||
chart.pointSetArray.resize(2);
|
||||
|
||||
Array<float> errors;
|
||||
Array<float> exposures;
|
||||
for (int i = 0; i < 48; i++) {
|
||||
//exposures.append(8 * float(i)/63);
|
||||
exposures.append(lerp(0.22, 22, float(i)/47));
|
||||
}
|
||||
|
||||
Surface src = loadInput("hdr/34017_03.dds");
|
||||
//Surface src = loadInput("hdr/49002_1F.dds");
|
||||
if (src.isNull()) {
|
||||
printf("Error loading image.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printImageInfo(src);
|
||||
|
||||
//compare(src, process(src, 0), exposures, errors);
|
||||
//updatePointSet(exposures, errors, chart.pointSetArray[0]);
|
||||
//chart.pointSetArray[0].legend = "Clamp";
|
||||
compare(src, process(src, 0), exposures, errors);
|
||||
updatePointSet(exposures, errors, chart.pointSetArray[0]);
|
||||
chart.pointSetArray[0].legend = "Default";
|
||||
|
||||
compare(src, process(src, 3), exposures, errors);
|
||||
updatePointSet(exposures, errors, chart.pointSetArray[1]);
|
||||
chart.pointSetArray[1].legend = "Log + Gamma 2.2";
|
||||
chart.pointSetArray[1].lineColor = Vector3(0.19, 0.45, 0.95);
|
||||
|
||||
|
||||
chart.autoScale();
|
||||
|
||||
StringBuilder builder;
|
||||
chart.build(builder);
|
||||
|
||||
printf("%s\n", builder.str());
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue