LogLUV conversion now working

Started by
3 comments, last by Marcel Stockli 11 years, 3 months ago

I wrote a small program to test the conversion from HDR RGB to LogLUV. It takes a HDR image (.EXR) and then converts the floating point RGB pixel value to an 32-bit LogLUV value. It then converts it back to floating point RGB and saves the image to a new EXR file.

For some reason, I know not why, it is not working.

[source]

#include <iostream>
#include <FreeImage.h>
#include <glm/glm.hpp>

using namespace glm;

FIBITMAP* LoadImage(const char* filename)
{
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
fif = FreeImage_GetFileType(filename);
if(fif == FIF_UNKNOWN)
{
fif = FreeImage_GetFIFFromFilename(filename);
}
if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif))
{
return FreeImage_Load(fif, filename);
}
return NULL;
}

// M matrix, for encoding
const mat3 M(
0.2209, 0.3390, 0.4184,
0.1138, 0.6780, 0.7319,
0.0102, 0.1130, 0.2969
);

// Inverse M matrix, for decoding
const mat3 InverseM(
6.0014, -2.7008, -1.7996,
-1.3320, 3.1029, -5.7721,
0.3008, -1.0882, 5.6268
);

vec4 LogLuvEncode(vec3 vRGB)
{
vec4 vResult;
vec3 Xp_Y_XYZp = vRGB * M;
Xp_Y_XYZp = glm::max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));
vResult.x = Xp_Y_XYZp.x / Xp_Y_XYZp.z;
vResult.y = Xp_Y_XYZp.y / Xp_Y_XYZp.z;
float Le = 2 * glm::log2(Xp_Y_XYZp.y) + 127;
vResult.w = glm::fract(Le);
vResult.z = (Le - (glm::floor(vResult.w*255.0f))/255.0f)/255.0f;
return vResult;
}

vec3 LogLuvDecode(vec4 vLogLuv)
{
float Le = vLogLuv.z * 255 + vLogLuv.w;
vec3 Xp_Y_XYZp;
Xp_Y_XYZp.y = glm::exp2((Le - 127) / 2);
Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
vec3 vRGB = Xp_Y_XYZp * InverseM;
return glm::max(vRGB, 0);
}

int main()
{
FIBITMAP* dib = LoadImage("in.exr");

int width = FreeImage_GetWidth(dib);
int height = FreeImage_GetHeight(dib);

FIBITMAP* dib2 = FreeImage_AllocateT(FIT_RGBF, width, height);

for(int y = 0; y < height; y++)
{
FIRGBF* InputPixel = (FIRGBF*)FreeImage_GetScanLine(dib, y);
FIRGBF* OutputPixel = (FIRGBF*)FreeImage_GetScanLine(dib2, y);

for(int x = 0; x < width; x++)
{
vec3 input(InputPixel[x].red, InputPixel[x].green, InputPixel[x].blue);

vec4 enc = LogLuvEncode(input);

BYTE r = enc.r * 255.0;
BYTE g = enc.g * 255.0;
BYTE b = enc.b * 255.0;
BYTE a = enc.a * 255.0;

vec3 dec = LogLuvDecode(vec4(r,g,b,a) / 255.0);

OutputPixel[x].red = dec.r;
OutputPixel[x].green = dec.g;
OutputPixel[x].blue = dec.b;
}
}

FreeImage_Save(FIF_EXR, dib2, "out.exr");
}

[/source]

This doesn't work.

[source]

#include <iostream>
#include <FreeImage.h>
#include <glm/glm.hpp>

using namespace glm;

FIBITMAP* LoadImage(const char* filename)
{
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
fif = FreeImage_GetFileType(filename);
if(fif == FIF_UNKNOWN)
{
fif = FreeImage_GetFIFFromFilename(filename);
}
if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif))
{
return FreeImage_Load(fif, filename);
}
return NULL;
}

// M matrix, for encoding
const mat3 M(
0.2209, 0.3390, 0.4184,
0.1138, 0.6780, 0.7319,
0.0102, 0.1130, 0.2969
);

// Inverse M matrix, for decoding
const mat3 InverseM(
6.0014, -2.7008, -1.7996,
-1.3320, 3.1029, -5.7721,
0.3008, -1.0882, 5.6268
);

vec4 LogLuvEncode(vec3 vRGB)
{
vec4 vResult;
vec3 Xp_Y_XYZp = vRGB * M;
Xp_Y_XYZp = glm::max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));
vResult.x = Xp_Y_XYZp.x / Xp_Y_XYZp.z;
vResult.y = Xp_Y_XYZp.y / Xp_Y_XYZp.z;
float Le = 2 * glm::log2(Xp_Y_XYZp.y) + 127;
vResult.w = glm::fract(Le);
vResult.z = (Le - (glm::floor(vResult.w*255.0f))/255.0f)/255.0f;
return vResult;
}

vec3 LogLuvDecode(vec4 vLogLuv)
{
float Le = vLogLuv.z * 255 + vLogLuv.w;
vec3 Xp_Y_XYZp;
Xp_Y_XYZp.y = glm::exp2((Le - 127) / 2);
Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
vec3 vRGB = Xp_Y_XYZp * InverseM;
return glm::max(vRGB, 0);
}

int main()
{
FIBITMAP* dib = LoadImage("in.exr");

int width = FreeImage_GetWidth(dib);
int height = FreeImage_GetHeight(dib);

FIBITMAP* dib2 = FreeImage_AllocateT(FIT_RGBF, width, height);

for(int y = 0; y < height; y++)
{
FIRGBF* InputPixel = (FIRGBF*)FreeImage_GetScanLine(dib, y);
FIRGBF* OutputPixel = (FIRGBF*)FreeImage_GetScanLine(dib2, y);

for(int x = 0; x < width; x++)
{
vec3 input(InputPixel[x].red, InputPixel[x].green, InputPixel[x].blue);

vec4 enc = LogLuvEncode(input);

vec3 dec = LogLuvDecode(enc);

OutputPixel[x].red = dec.r;
OutputPixel[x].green = dec.g;
OutputPixel[x].blue = dec.b;
}
}

FreeImage_Save(FIF_EXR, dib2, "out.exr");
}

[/source]

However, this does.

Advertisement

I don't get it. The algorithm I'm using can be seen all over the Internet. http://realtimecollisiondetection.net/blog/?p=15

But when I test the LogLuvEncode function with values { 1.0, 1.0, 1.0 } I get back { 2.32873, 3.62699, 0.504747, 0.215164 }.

Why are the values not in the range [0, 1]?

Did you checked the matrix order? (row major, column major). Your matrix constructor should have the same order than the HLSL Matrix constructor because the code you are usin, originally was HLSL. The same with the vector-matrix multiplication.

Thanks, that was the problem. This isn't the first time matrix order has done this to me. rolleyes.gif

No problem! Cool you fixed it, that kind of errors are very annoying.

This topic is closed to new replies.

Advertisement