Skip to content

Commit

Permalink
Fixed some problems with PSNR calculation, added unit tests to check …
Browse files Browse the repository at this point in the history
…that conversion of BCn textures is within a certain threshold.
  • Loading branch information
vk2gpu committed Jan 15, 2018
1 parent b1af02c commit ef27f0d
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 88 deletions.
60 changes: 42 additions & 18 deletions src/image/ispc/image_processing.ispc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ typedef unsigned int16 u16;
typedef int32 i32;
typedef unsigned int32 u32;

export struct Error
{
double r;
double g;
double b;
double a;
};

export struct Color
{
float r;
Expand Down Expand Up @@ -115,11 +123,13 @@ export void ImageProc_LinearToGamma(uniform int w, uniform int h, uniform const
}



export uniform float ImageProc_MSE(uniform int w, uniform int h, uniform const Color base[], uniform const Color compare[])
export void ImageProc_MSE(uniform Error output[], uniform int w, uniform int h, uniform const Color base[], uniform const Color compare[])
{
uniform double numTexels = w * h;
double sumSq = 0.0;
uniform double numTexels = (double)w * (double)h;
double sumSqR = 0.0;
double sumSqG = 0.0;
double sumSqB = 0.0;
double sumSqA = 0.0;

foreach (y = 0 ... h, x = 0 ... w)
{
Expand All @@ -135,18 +145,26 @@ export uniform float ImageProc_MSE(uniform int w, uniform int h, uniform const C
baseTexel.a - compareTexel.a,
};

double error = (diff.r + diff.g + diff.b + diff.a) * 0.25;
sumSq += error * error;
sumSqR += diff.r * diff.r;
sumSqG += diff.g * diff.g;
sumSqB += diff.b * diff.b;
sumSqA += diff.a * diff.a;
}

return reduce_add(sumSq) / numTexels;
output->r = (reduce_add(sumSqR) / numTexels);
output->g = (reduce_add(sumSqG) / numTexels);
output->b = (reduce_add(sumSqB) / numTexels);
output->a = (reduce_add(sumSqA) / numTexels);
}


export uniform float ImageProc_MSE_R8G8B8A8(uniform int w, uniform int h, uniform const Color_R8G8B8A8 base[], uniform const Color_R8G8B8A8 compare[])
export void ImageProc_MSE_R8G8B8A8(uniform Error output[], uniform int w, uniform int h, uniform const Color_R8G8B8A8 base[], uniform const Color_R8G8B8A8 compare[])
{
uniform double numTexels = w * h;
double sumSq = 0.0;
uniform double numTexels = (double)w * (double)h;
double sumSqR = 0.0;
double sumSqG = 0.0;
double sumSqB = 0.0;
double sumSqA = 0.0;

foreach (y = 0 ... h, x = 0 ... w)
{
Expand All @@ -155,18 +173,24 @@ export uniform float ImageProc_MSE_R8G8B8A8(uniform int w, uniform int h, unifor
Color_R8G8B8A8 baseTexel = base[inIdx];
Color_R8G8B8A8 compareTexel = compare[inIdx];

Color_R8G8B8A8 diff = {
baseTexel.r - compareTexel.r,
baseTexel.g - compareTexel.g,
baseTexel.b - compareTexel.b,
baseTexel.a - compareTexel.a,
Color diff = {
(float)baseTexel.r - (float)compareTexel.r,
(float)baseTexel.g - (float)compareTexel.g,
(float)baseTexel.b - (float)compareTexel.b,
(float)baseTexel.a - (float)compareTexel.a,
};

double error = ((double)diff.r + (double)diff.g + (double)diff.b + (double)diff.a) * 0.25;
sumSq += error * error;
sumSqR += diff.r * diff.r;
sumSqG += diff.g * diff.g;
sumSqB += diff.b * diff.b;
sumSqA += diff.a * diff.a;
}

return reduce_add(sumSq) / numTexels;

output->r = (reduce_add(sumSqR) / numTexels);
output->g = (reduce_add(sumSqG) / numTexels);
output->b = (reduce_add(sumSqB) / numTexels);
output->a = (reduce_add(sumSqA) / numTexels);
}

export void ImageProc_Unpack_R8G8B8A8(uniform int numTexels, uniform const Color_R8G8B8A8 input[], uniform Color output[])
Expand Down
6 changes: 5 additions & 1 deletion src/image/private/color.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,12 @@ namespace Image
std::pow(std::abs(rgba.b), 1.0f / 2.4f));
sRGBHi = (sRGBHi * 1.055f) - Math::Vec3(0.055f, 0.055f, 0.055f);
RGBAColor sRGBA = RGBAColor((rgba.r <= 0.0031308) ? sRGBLo.x : sRGBHi.x,
(rgba.g <= 0.0031308) ? sRGBLo.y : sRGBHi.y, (rgba.b <= 0.0031308) ? sRGBLo.z : sRGBHi.z, rgba.a);
(rgba.g <= 0.0031308f) ? sRGBLo.y : sRGBHi.y, (rgba.b <= 0.0031308f) ? sRGBLo.z : sRGBHi.z, rgba.a);
sRGBA *= 255.0f;
sRGBA.r = std::roundf(sRGBA.r);
sRGBA.g = std::roundf(sRGBA.g);
sRGBA.b = std::roundf(sRGBA.b);
sRGBA.a = std::roundf(sRGBA.a);
return SRGBAColor((u8)sRGBA.r, (u8)sRGBA.g, (u8)sRGBA.b, (u8)sRGBA.a);
}

Expand Down
9 changes: 9 additions & 0 deletions src/image/private/dds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,15 @@ namespace Image
return Image::Image();
}

if(ddsHeader.dwWidth == 0)
ddsHeader.dwWidth = 1;
if(ddsHeader.dwHeight == 0)
ddsHeader.dwHeight = 1;
if(ddsHeader.dwDepth == 0)
ddsHeader.dwDepth = 1;
if(ddsHeader.dwMipMapCount == 0)
ddsHeader.dwMipMapCount = 1;

// Calculate size.
auto formatSize = GPU::GetTextureSize(format, ddsHeader.dwWidth, ddsHeader.dwHeight, ddsHeader.dwDepth,
ddsHeader.dwMipMapCount, ddsHeaderDXT10.arraySize > 0 ? ddsHeaderDXT10.arraySize : 1);
Expand Down
89 changes: 74 additions & 15 deletions src/image/private/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "gpu/utils.h"
#include "math/utils.h"

#include <squish.h>

#include <cmath>
#include <utility>

Expand Down Expand Up @@ -75,8 +77,8 @@ namespace Image
return true;
}

auto PadData = [](Core::Vector<u32>& paddedData, const u32* levelData, i32 levelW, i32 levelH, i32 paddedW,
i32 paddedH) {
auto PadData = [](
Core::Vector<u32>& paddedData, const u32* levelData, i32 levelW, i32 levelH, i32 paddedW, i32 paddedH) {
// Always going largest to smallest, so only need to allocate once and reuse the memory
// for subsequent levels.
if(paddedData.empty())
Expand Down Expand Up @@ -182,6 +184,46 @@ namespace Image
}
}

// Compressed input, R8G8B8A8_UNORM output.
if(output.GetFormat() == ImageFormat::R8G8B8A8_UNORM)
{
u32 squishFlags = 0;
switch(input.GetFormat())
{
case ImageFormat::BC1_UNORM:
case ImageFormat::BC1_UNORM_SRGB:
squishFlags = squish::kBc1;
break;
case ImageFormat::BC3_UNORM:
case ImageFormat::BC3_UNORM_SRGB:
squishFlags = squish::kBc3;
break;
case ImageFormat::BC4_UNORM:
//case ImageFormat::BC4_SNORM:
squishFlags = squish::kBc4;
break;
case ImageFormat::BC5_UNORM:
//case ImageFormat::BC5_SNORM:
squishFlags = squish::kBc5;
break;
}

if(squishFlags)
{
i32 levelW = output.GetWidth();
i32 levelH = output.GetHeight();
for(i32 level = 0; level < output.GetLevels(); ++level)
{
squish::DecompressImage(
output.GetMipData<u8>(level), levelW, levelH, input.GetMipData<void>(level), squishFlags);

levelW = Core::Max(levelW >> 1, 1);
levelH = Core::Max(levelH >> 1, 1);
}
return true;
}
}

// Fallback to generic conversion.
auto inFormatInfo = GPU::GetFormatInfo(input.GetFormat());
auto outFormatInfo = GPU::GetFormatInfo(output.GetFormat());
Expand Down Expand Up @@ -302,31 +344,48 @@ namespace Image
}


f32 CalculatePSNR(const Image& base, const Image& compare)
RGBAColor CalculatePSNR(const Image& base, const Image& compare)
{
RGBAColor psnr = INFINITE_PSNR_RGBA;

if(!base || !compare)
return 0.0f;
return RGBAColor(0.0f, 0.0f, 0.0f, 0.0f);
if(base.GetWidth() != compare.GetWidth())
return 0.0f;
return RGBAColor(0.0f, 0.0f, 0.0f, 0.0f);
if(base.GetHeight() != compare.GetHeight())
return 0.0f;
return RGBAColor(0.0f, 0.0f, 0.0f, 0.0f);
if(base.GetFormat() != compare.GetFormat())
return 0.0f;
return RGBAColor(0.0f, 0.0f, 0.0f, 0.0f);

f32 psnr = INFINITE_PSNR;
if(base.GetFormat() == ImageFormat::R8G8B8A8_UNORM)
{
f32 mse = ispc::ImageProc_MSE_R8G8B8A8(base.GetWidth(), base.GetDepth(),
ispc::Error mse;
ispc::ImageProc_MSE_R8G8B8A8(&mse, base.GetWidth(), base.GetHeight(),
base.GetMipData<ispc::Color_R8G8B8A8>(0), compare.GetMipData<ispc::Color_R8G8B8A8>(0));
if(mse > 0.0f)
psnr = Math::AmplitudeRatioToDecibels(255.0f / std::sqrt(mse));

if(mse.r > 0.0f)
psnr.r = Math::AmplitudeRatioToDecibels((f32)((255.0) / std::sqrt(mse.r)));
if(mse.g > 0.0f)
psnr.g = Math::AmplitudeRatioToDecibels((f32)((255.0) / std::sqrt(mse.g)));
if(mse.b > 0.0f)
psnr.b = Math::AmplitudeRatioToDecibels((f32)((255.0) / std::sqrt(mse.b)));
if(mse.a > 0.0f)
psnr.a = Math::AmplitudeRatioToDecibels((f32)((255.0) / std::sqrt(mse.a)));
}
else if(base.GetFormat() == ImageFormat::R32G32B32A32_FLOAT)
{
f32 mse = ispc::ImageProc_MSE(
base.GetWidth(), base.GetDepth(), base.GetMipData<ispc::Color>(0), compare.GetMipData<ispc::Color>(0));
if(mse > 0.0f)
psnr = Math::AmplitudeRatioToDecibels(1.0f / std::sqrt(mse));
ispc::Error mse;
ispc::ImageProc_MSE(&mse, base.GetWidth(), base.GetHeight(), base.GetMipData<ispc::Color>(0),
compare.GetMipData<ispc::Color>(0));

if(mse.r > 0.0f)
psnr.r = Math::AmplitudeRatioToDecibels((f32)(1.0 / std::sqrt(mse.r)));
if(mse.g > 0.0f)
psnr.g = Math::AmplitudeRatioToDecibels((f32)(1.0 / std::sqrt(mse.g)));
if(mse.b > 0.0f)
psnr.b = Math::AmplitudeRatioToDecibels((f32)(1.0 / std::sqrt(mse.b)));
if(mse.a > 0.0f)
psnr.a = Math::AmplitudeRatioToDecibels((f32)(1.0 / std::sqrt(mse.a)));
}

return psnr;
Expand Down
20 changes: 12 additions & 8 deletions src/image/private/save.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,24 @@ namespace Image
switch(fileType)
{
case FileType::BMP:
return 0 != stbi_write_bmp_to_func(
writeFn, &file, image.GetWidth(), image.GetHeight(), 4, image.GetMipBaseAddr(0));
return 0 !=
stbi_write_bmp_to_func(
writeFn, &file, image.GetWidth(), image.GetHeight(), 4, image.GetMipBaseAddr(0));
break;
case FileType::PNG:
return 0 != stbi_write_png_to_func(writeFn, &file, image.GetWidth(), image.GetHeight(), 4,
image.GetMipBaseAddr(0), image.GetWidth() * sizeof(u32));
return 0 !=
stbi_write_png_to_func(writeFn, &file, image.GetWidth(), image.GetHeight(), 4,
image.GetMipBaseAddr(0), image.GetWidth() * sizeof(u32));
break;
case FileType::TGA:
return 0 != stbi_write_tga_to_func(
writeFn, &file, image.GetWidth(), image.GetHeight(), 4, image.GetMipBaseAddr(0));
return 0 !=
stbi_write_tga_to_func(
writeFn, &file, image.GetWidth(), image.GetHeight(), 4, image.GetMipBaseAddr(0));
break;
case FileType::HDR:
return 0 != stbi_write_hdr_to_func(writeFn, &file, image.GetWidth(), image.GetHeight(), 4,
(const float*)image.GetMipBaseAddr(0));
return 0 !=
stbi_write_hdr_to_func(
writeFn, &file, image.GetWidth(), image.GetHeight(), 4, (const float*)image.GetMipBaseAddr(0));
break;

default:
Expand Down
11 changes: 6 additions & 5 deletions src/image/process.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#pragma once

#include "image/types.h"
#include "image/color.h"

namespace Image
{
class Image;

/**
* Value in dB to treat as 'infinite'.
* Useful constants for quality checking with PSNR.
*/
static const f32 INFINITE_PSNR = 9999.0f;
static const f32 INFINITE_PSNR = 99999.0f;
static const RGBAColor INFINITE_PSNR_RGBA = RGBAColor(INFINITE_PSNR, INFINITE_PSNR, INFINITE_PSNR, INFINITE_PSNR);

/**
* Convert @a input image to @a outFormat.
Expand Down Expand Up @@ -52,8 +53,8 @@ namespace Image
* Calculate PSNR.
* @param base Base image.
* @param compare Image to compare.
* @return PSNR in dB
* @return Per channel PSNR in dB
*/
IMAGE_DLL f32 CalculatePSNR(const Image& base, const Image& compare);
IMAGE_DLL RGBAColor CalculatePSNR(const Image& base, const Image& compare);

} // namespace Image
18 changes: 6 additions & 12 deletions src/image/tests/color_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,12 @@ namespace
bool EpsilonCompare(f32 a, f32 b, f32 error) { return std::abs(a - b) < error; }

Image::RGBAColor testNormalizedRGB[] = {
Image::RGBAColor(1.0f, 0.0f, 0.0f, 1.0f),
Image::RGBAColor(0.0f, 1.0f, 0.0f, 1.0f),
Image::RGBAColor(0.0f, 0.0f, 1.0f, 1.0f),
Image::RGBAColor(0.0f, 1.0f, 1.0f, 1.0f),
Image::RGBAColor(1.0f, 0.0f, 1.0f, 1.0f),
Image::RGBAColor(1.0f, 1.0f, 0.0f, 1.0f),
Image::RGBAColor(0.1f, 0.1f, 0.1f, 1.0f),
Image::RGBAColor(0.2f, 0.2f, 0.2f, 1.0f),
Image::RGBAColor(0.2f, 0.2f, 0.2f, 1.0f),
Image::RGBAColor(0.4f, 0.4f, 0.4f, 1.0f),
Image::RGBAColor(0.8f, 0.8f, 0.8f, 1.0f),
Image::RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
Image::RGBAColor(1.0f, 0.0f, 0.0f, 1.0f), Image::RGBAColor(0.0f, 1.0f, 0.0f, 1.0f),
Image::RGBAColor(0.0f, 0.0f, 1.0f, 1.0f), Image::RGBAColor(0.0f, 1.0f, 1.0f, 1.0f),
Image::RGBAColor(1.0f, 0.0f, 1.0f, 1.0f), Image::RGBAColor(1.0f, 1.0f, 0.0f, 1.0f),
Image::RGBAColor(0.1f, 0.1f, 0.1f, 1.0f), Image::RGBAColor(0.2f, 0.2f, 0.2f, 1.0f),
Image::RGBAColor(0.2f, 0.2f, 0.2f, 1.0f), Image::RGBAColor(0.4f, 0.4f, 0.4f, 1.0f),
Image::RGBAColor(0.8f, 0.8f, 0.8f, 1.0f), Image::RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
};
}

Expand Down
Loading

0 comments on commit ef27f0d

Please sign in to comment.