Add more error estimation methods.

pull/216/head
castano 13 years ago
parent 1aac4215ff
commit 765a0e394a

@ -11,14 +11,14 @@ using namespace nv;
float nv::rmsColorError(const FloatImage * img, const FloatImage * ref, bool alphaWeight)
{
double mse = 0;
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
return FLT_MAX;
}
nvDebugCheck(img->componentNum() == 4);
nvDebugCheck(ref->componentNum() == 4);
double mse = 0;
const uint count = img->width() * img->height();
for (uint i = 0; i < count; i++)
{
@ -34,20 +34,13 @@ float nv::rmsColorError(const FloatImage * img, const FloatImage * ref, bool alp
float r = r0 - r1;
float g = g0 - g1;
float b = b0 - b1;
//float a = a0 - a1;
if (alphaWeight)
{
mse += r * r * a1;
mse += g * g * a1;
mse += b * b * a1;
}
else
{
mse += r * r;
mse += g * g;
mse += b * b;
}
float a = 1;
if (alphaWeight) a = a1;
mse += r * r * a;
mse += g * g * a;
mse += b * b * a;
}
return float(sqrt(mse / count));
@ -55,13 +48,13 @@ float nv::rmsColorError(const FloatImage * img, const FloatImage * ref, bool alp
float nv::rmsAlphaError(const FloatImage * img, const FloatImage * ref)
{
double mse = 0;
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
return FLT_MAX;
}
nvDebugCheck(img->componentNum() == 4 && ref->componentNum() == 4);
double mse = 0;
const uint count = img->width() * img->height();
for (uint i = 0; i < count; i++)
{
@ -76,6 +69,68 @@ float nv::rmsAlphaError(const FloatImage * img, const FloatImage * ref)
return float(sqrt(mse / count));
}
float nv::averageColorError(const FloatImage * img, const FloatImage * ref, bool alphaWeight)
{
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
return FLT_MAX;
}
nvDebugCheck(img->componentNum() == 4);
nvDebugCheck(ref->componentNum() == 4);
double mae = 0;
const uint count = img->width() * img->height();
for (uint i = 0; i < count; i++)
{
float r0 = img->pixel(i + count * 0);
float g0 = img->pixel(i + count * 1);
float b0 = img->pixel(i + count * 2);
//float a0 = img->pixel(i + count * 3);
float r1 = ref->pixel(i + count * 0);
float g1 = ref->pixel(i + count * 1);
float b1 = ref->pixel(i + count * 2);
float a1 = ref->pixel(i + count * 3);
float r = fabs(r0 - r1);
float g = fabs(g0 - g1);
float b = fabs(b0 - b1);
float a = 1;
if (alphaWeight) a = a1;
mae += r * a;
mae += g * a;
mae += b * a;
}
return float(mae / count);
}
float nv::averageAlphaError(const FloatImage * img, const FloatImage * ref)
{
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
return FLT_MAX;
}
nvDebugCheck(img->componentNum() == 4 && ref->componentNum() == 4);
double mae = 0;
const uint count = img->width() * img->height();
for (uint i = 0; i < count; i++)
{
float a0 = img->pixel(i + count * 3);
float a1 = ref->pixel(i + count * 3);
float a = a0 - a1;
mae += fabs(a);
}
return float(mae / count);
}
// Assumes input is in *linear* sRGB color space.
static Vector3 rgbToXyz(Vector3::Arg c)
{
@ -269,5 +324,81 @@ float nv::spatialCieLabError(const FloatImage * img0, const FloatImage * img1)
}
// Assumes input images are normal maps.
float nv::averageAngularError(const FloatImage * img0, const FloatImage * img1)
{
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
return FLT_MAX;
}
nvDebugCheck(img0->componentNum() == 4 && img0->componentNum() == 4);
uint w = img0->width();
uint h = img0->height();
const float * x0 = img0->channel(0);
const float * y0 = img0->channel(1);
const float * z0 = img0->channel(2);
const float * x1 = img1->channel(0);
const float * y1 = img1->channel(1);
const float * z1 = img1->channel(2);
double error = 0.0f;
const uint count = w*h;
for (uint i = 0; i < count; i++)
{
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
n0 = 2 * n0 - Vector3(1);
n1 = 2 * n1 - Vector3(1);
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
error += acos(clamp(dot(n0, n1), -1.0f, 1.0f));
}
return float(error / count);
}
float nv::rmsAngularError(const FloatImage * img0, const FloatImage * img1)
{
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
return FLT_MAX;
}
nvDebugCheck(img0->componentNum() == 4 && img0->componentNum() == 4);
uint w = img0->width();
uint h = img0->height();
const float * x0 = img0->channel(0);
const float * y0 = img0->channel(1);
const float * z0 = img0->channel(2);
const float * x1 = img1->channel(0);
const float * y1 = img1->channel(1);
const float * z1 = img1->channel(2);
double error = 0.0f;
const uint count = w*h;
for (uint i = 0; i < count; i++)
{
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
n0 = 2 * n0 - Vector3(1);
n1 = 2 * n1 - Vector3(1);
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
float angle = acos(clamp(dot(n0, n1), -1.0f, 1.0f));
error += angle * angle;
}
return float(sqrt(error / count));
}

@ -12,4 +12,10 @@ namespace nv
float cieLabError(const FloatImage * img, const FloatImage * ref);
float spatialCieLabError(const FloatImage * img, const FloatImage * ref);
float averageColorError(const FloatImage * img, const FloatImage * ref, bool alphaWeight);
float averageAlphaError(const FloatImage * img, const FloatImage * ref);
float averageAngularError(const FloatImage * img0, const FloatImage * img1);
float rmsAngularError(const FloatImage * img0, const FloatImage * img1);
} // nv namespace

@ -1593,7 +1593,7 @@ void TexImage::transformNormals(NormalTransform xform)
n.y = n.y * t;
n.z = 0.0f;
}
else if (xform == NormalTransform_DualParaboloid) {
else if (xform == NormalTransform_Quartic) {
// Use Newton's method to solve equation:
// f(t) = 1 - zt - (x^2+y^2)t^2 + x^2y^2t^4 = 0
// f'(t) = - z - 2(x^2+y^2)t + 4x^2y^2t^3
@ -1618,6 +1618,9 @@ void TexImage::transformNormals(NormalTransform xform)
n.y = n.y * t;
n.z = 0.0f;
}
/*else if (xform == NormalTransform_DualParaboloid) {
}*/
x = n.x;
y = n.y;
@ -1657,12 +1660,15 @@ void TexImage::reconstructNormals(NormalTransform xform)
n.z = 1.0f - nv::clamp(n.x * n.x + n.y * n.y, 0.0f, 1.0f);
n = normalizeSafe(n, Vector3(0.0f), 0.0f);
}
else if (xform == NormalTransform_DualParaboloid) {
else if (xform == NormalTransform_Quartic) {
n.x = n.x;
n.y = n.y;
n.z = nv::clamp((1 - n.x * n.x) * (1 - n.y * n.y), 0.0f, 1.0f);
n = normalizeSafe(n, Vector3(0.0f), 0.0f);
}
/*else if (xform == NormalTransform_DualParaboloid) {
}*/
x = n.x;
y = n.y;
@ -1753,6 +1759,12 @@ float nvtt::cieLabError(const TexImage & reference, const TexImage & image)
return nv::cieLabError(reference.m->image, image.m->image);
}
float nvtt::angularError(const TexImage & reference, const TexImage & image)
{
//return nv::averageAngularError(reference.m->image, image.m->image);
return nv::rmsAngularError(reference.m->image, image.m->image);
}
TexImage nvtt::diff(const TexImage & reference, const TexImage & image, float scale)
{

@ -391,7 +391,8 @@ namespace nvtt
NormalTransform_Orthographic,
NormalTransform_Stereographic,
NormalTransform_Paraboloid,
NormalTransform_DualParaboloid,
NormalTransform_Quartic,
//NormalTransform_DualParaboloid,
};
/// A texture mipmap.
@ -487,6 +488,7 @@ namespace nvtt
NVTT_API friend float rmsError(const TexImage & reference, const TexImage & img);
NVTT_API friend float rmsAlphaError(const TexImage & reference, const TexImage & img);
NVTT_API friend float cieLabError(const TexImage & reference, const TexImage & img);
NVTT_API friend float angularError(const TexImage & reference, const TexImage & img);
NVTT_API friend TexImage diff(const TexImage & reference, const TexImage & img, float scale);
private:
@ -506,6 +508,7 @@ namespace nvtt
NVTT_API float rmsError(const TexImage & reference, const TexImage & img);
NVTT_API float rmsAlphaError(const TexImage & reference, const TexImage & img);
NVTT_API float cieLabError(const TexImage & reference, const TexImage & img);
NVTT_API float angularError(const TexImage & reference, const TexImage & img);
NVTT_API TexImage diff(const TexImage & reference, const TexImage & img, float scale);

@ -185,7 +185,8 @@ enum Mode {
Mode_BC5_Normal,
Mode_BC5_Normal_Stereographic,
Mode_BC5_Normal_Paraboloid,
Mode_BC5_Normal_DualParaboloid,
Mode_BC5_Normal_Quartic,
//Mode_BC5_Normal_DualParaboloid,
Mode_Count
};
static const char * s_modeNames[] = {
@ -202,7 +203,8 @@ static const char * s_modeNames[] = {
"BC5-Normal", // Mode_BC5_Normal,
"BC5-Normal-Stereographic", // Mode_BC5_Normal_Stereographic,
"BC5-Normal-Paraboloid", // Mode_BC5_Normal_Paraboloid,
"BC5-Normal-DualParaboloid", // Mode_BC5_Normal_DualParaboloid,
"BC5-Normal-Quartic", // Mode_BC5_Normal_Quartic,
//"BC5-Normal-DualParaboloid", // Mode_BC5_Normal_DualParaboloid,
};
nvStaticCheck(NV_ARRAY_SIZE(s_modeNames) == Mode_Count);
@ -215,7 +217,7 @@ static Test s_imageTests[] = {
{"Color", 3, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, Mode_BC3_LUVW}},
{"Alpha", 3, {Mode_BC1_Alpha, Mode_BC2_Alpha, Mode_BC3_Alpha}},
//{"Normal", 3, {Mode_BC1_Normal, Mode_BC3_Normal, Mode_BC5_Normal}},
{"Normal", 4, {Mode_BC5_Normal_DualParaboloid, Mode_BC5_Normal, Mode_BC5_Normal_Stereographic, Mode_BC5_Normal_Paraboloid}},
{"Normal", 4, {Mode_BC5_Normal, Mode_BC5_Normal_Stereographic, Mode_BC5_Normal_Paraboloid, Mode_BC5_Normal_Quartic}},
{"Lightmap", 4, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, Mode_BC3_RGBS}},
};
const int s_imageTestCount = ARRAY_SIZE(s_imageTests);
@ -288,6 +290,12 @@ struct MyOutputHandler : public nvtt::OutputHandler
unsigned char * m_ptr;
};
enum ErrorMode {
ErrorMode_RMSE,
ErrorMode_CieLab,
ErrorMode_AngularRMSE
};
int main(int argc, char *argv[])
{
@ -303,6 +311,7 @@ int main(int argc, char *argv[])
int setIndex = 0;
int testIndex = 0;
int errorMode = 0;
bool fast = false;
bool nocuda = false;
bool showHelp = false;
@ -343,6 +352,13 @@ int main(int argc, char *argv[])
i++;
}
}
else if (strcmp("-err", argv[i]) == 0)
{
if (i+1 < argc && argv[i+1][0] != '-') {
errorMode = atoi(argv[i+1]);
i++;
}
}
else if (strcmp("-fast", argv[i]) == 0)
{
fast = true;
@ -403,9 +419,9 @@ int main(int argc, char *argv[])
for (int i = 0; i < s_imageTestCount; i++) {
printf(" %i: \t%s.\n", i, s_imageTests[i].name);
}
printf(" -dec x \tDecompressor.\n");
printf(" 0: \tReference (D3D10).\n");
printf(" 1: \tReference (D3D9).\n");
printf(" -dec [0:2] \tDecompressor.\n");
printf(" 0: \tReference D3D10 (default).\n");
printf(" 1: \tReference D3D9.\n");
printf(" 2: \tNVIDIA.\n");
printf("Compression options:\n");
@ -414,6 +430,10 @@ int main(int argc, char *argv[])
printf("Output options:\n");
printf(" -out <path> \tOutput directory.\n");
printf(" -err [0:2] \tError mode.\n");
printf(" 0: \tRMSE (default).\n");
printf(" 1: \tCieLab.\n");
printf(" 2: \tAngular RMSE.\n");
return 1;
}
@ -477,10 +497,19 @@ int main(int argc, char *argv[])
// Grid lines.
graphWriter << "&chxt=x,y&chxtc=0,-1000|1,-1000";
// Labels.
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.05,0.01"; // rmse
//graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,4,22,1"; // cielab
graphWriter << "&chdlp=b"; // Labels at the bottom.
// Labels on the left side.
if (errorMode == ErrorMode_RMSE) {
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.05,0.01";
}
else if (errorMode == ErrorMode_CieLab) {
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,4,22,1";
}
else if (errorMode == ErrorMode_AngularRMSE) {
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.05,0.01";
}
// Labels at the bottom.
graphWriter << "&chdlp=b";
// Line colors.
graphWriter << "&chco=";
@ -506,8 +535,15 @@ int main(int argc, char *argv[])
graphWriter << "&chds=";
for (int t = 0; t < test.count; t++)
{
graphWriter << "0,0.05"; // rmse
//graphWriter << "4,22"; // cielab
if (errorMode == ErrorMode_RMSE) {
graphWriter << "0,0.05";
}
else if (errorMode == ErrorMode_CieLab) {
graphWriter << "4,22";
}
else if (errorMode == ErrorMode_AngularRMSE) {
graphWriter << "0,0.05";
}
if (t != test.count-1) graphWriter << ",";
}
@ -520,8 +556,15 @@ int main(int argc, char *argv[])
}
// Title
graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20RMSE";
//graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20CIE-Lab";
if (errorMode == ErrorMode_RMSE) {
graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20RMSE";
}
else if (errorMode == ErrorMode_CieLab) {
graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20CIE-Lab";
}
else if (errorMode == ErrorMode_AngularRMSE) {
graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20Angular RMSE";
}
@ -538,8 +581,7 @@ int main(int argc, char *argv[])
for (int t = 0; t < test.count; t++)
{
float totalTime = 0;
float totalRMSE = 0;
//float totalDeltaE = 0;
float totalError = 0;
Mode mode = test.modes[t];
@ -553,7 +595,7 @@ int main(int argc, char *argv[])
else if (mode == Mode_BC3_Normal) {
format = nvtt::Format_BC3n;
}
else if (mode == Mode_BC5_Normal || mode == Mode_BC5_Normal_Stereographic || mode == Mode_BC5_Normal_Paraboloid || mode == Mode_BC5_Normal_DualParaboloid) {
else if (mode == Mode_BC5_Normal || mode == Mode_BC5_Normal_Stereographic || mode == Mode_BC5_Normal_Paraboloid || mode == Mode_BC5_Normal_Quartic) {
format = nvtt::Format_BC5;
}
@ -663,9 +705,12 @@ int main(int argc, char *argv[])
else if (mode == Mode_BC5_Normal_Paraboloid) {
tmp.transformNormals(nvtt::NormalTransform_Paraboloid);
}
else if (mode == Mode_BC5_Normal_DualParaboloid) {
tmp.transformNormals(nvtt::NormalTransform_DualParaboloid);
else if (mode == Mode_BC5_Normal_Quartic) {
tmp.transformNormals(nvtt::NormalTransform_Quartic);
}
/*else if (mode == Mode_BC5_Normal_DualParaboloid) {
tmp.transformNormals(nvtt::NormalTransform_DualParaboloid);
}*/
printf("Compressing: \t'%s'\n", set.fileNames[i]);
@ -740,9 +785,12 @@ int main(int argc, char *argv[])
else if (mode == Mode_BC5_Normal_Paraboloid) {
img_out.reconstructNormals(nvtt::NormalTransform_Paraboloid);
}
else if (mode == Mode_BC5_Normal_DualParaboloid) {
img_out.reconstructNormals(nvtt::NormalTransform_DualParaboloid);
else if (mode == Mode_BC5_Normal_Quartic) {
img_out.reconstructNormals(nvtt::NormalTransform_Quartic);
}
/*else if (mode == Mode_BC5_Normal_DualParaboloid) {
tmp.transformNormals(nvtt::NormalTransform_DualParaboloid);
}*/
nvtt::TexImage diff = nvtt::diff(img, img_out, 1.0f);
@ -810,18 +858,22 @@ int main(int argc, char *argv[])
}
// Output RMSE.
float rmse = nvtt::rmsError(img, img_out);
if (set.type == ImageType_HDR) rmse *= 4;
totalRMSE += rmse;
printf(" RMSE: \t%.4f\n", rmse);
//float deltae = nvtt::cieLabError(img, img_out);
//totalDeltaE += deltae;
//printf(" CIE-Lab DeltaE:\t%.4f\n", deltae);
float error;
if (errorMode == ErrorMode_RMSE) {
error = nvtt::rmsError(img, img_out);
if (set.type == ImageType_HDR) error *= 4;
}
else if (errorMode == ErrorMode_CieLab) {
error = nvtt::cieLabError(img, img_out);
}
else if (errorMode == ErrorMode_AngularRMSE) {
error = nvtt::angularError(img, img_out);
}
totalError += error;
printf(" Error: \t%.4f\n", error);
graphWriter << rmse;
//graphWriter << deltae;
graphWriter << error;
if (i != set.fileCount-1) graphWriter << ",";
@ -873,14 +925,11 @@ int main(int argc, char *argv[])
fflush(stdout);
}
totalRMSE /= set.fileCount;
//totalDeltaE /= set.fileCount;
//totalDiff /= set.fileCount;
totalError /= set.fileCount;
printf("Total Results:\n");
printf(" Total Time: \t%.3f sec\n", totalTime);
printf(" Average RMSE: \t%.4f\n", totalRMSE);
//printf(" Average CIE-Lab DeltaE:\t%.4f\n", totalDeltaE);
printf(" Average Error: \t%.4f\n", totalError);
if (t != test.count-1) graphWriter << "|";
}

Loading…
Cancel
Save