2007-07-02 09:10:33 +00:00
|
|
|
// Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.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 <nvcore/StrLib.h>
|
|
|
|
#include <nvcore/StdStream.h>
|
|
|
|
|
|
|
|
#include <nvimage/Image.h>
|
|
|
|
#include <nvimage/DirectDrawSurface.h>
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
#include <nvmath/Color.h>
|
2007-10-15 10:36:49 +00:00
|
|
|
#include <nvmath/Vector.h>
|
2007-07-02 10:45:25 +00:00
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
2007-07-02 09:10:33 +00:00
|
|
|
#include "cmdline.h"
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
static bool loadImage(nv::Image & image, const char * fileName)
|
|
|
|
{
|
|
|
|
if (nv::strCaseCmp(nv::Path::extension(fileName), ".dds") == 0)
|
|
|
|
{
|
|
|
|
nv::DirectDrawSurface dds(fileName);
|
|
|
|
if (!dds.isValid())
|
|
|
|
{
|
|
|
|
printf("The file '%s' is not a valid DDS file.\n", fileName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dds.mipmap(&image, 0, 0); // get first image
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Regular image.
|
|
|
|
if (!image.load(fileName))
|
|
|
|
{
|
|
|
|
printf("The file '%s' is not a supported image type.\n", fileName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @@ Compute per-tile errors.
|
|
|
|
struct Error
|
|
|
|
{
|
|
|
|
Error()
|
|
|
|
{
|
|
|
|
samples = 0;
|
|
|
|
mabse = 0.0f;
|
|
|
|
maxabse = 0.0f;
|
2007-09-04 10:00:32 +00:00
|
|
|
mse = 0.0f;
|
2007-07-02 10:45:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void addSample(float e)
|
|
|
|
{
|
|
|
|
samples++;
|
2007-09-04 10:00:32 +00:00
|
|
|
mabse += fabsf(e);
|
2007-07-08 08:08:52 +00:00
|
|
|
maxabse = nv::max(maxabse, fabsf(e));
|
2007-07-02 10:45:25 +00:00
|
|
|
mse += e * e;
|
|
|
|
}
|
|
|
|
|
|
|
|
void done()
|
|
|
|
{
|
|
|
|
mabse /= samples;
|
|
|
|
mse /= samples;
|
2009-03-02 09:21:30 +00:00
|
|
|
rmse = sqrtf(mse);
|
2007-12-03 23:28:29 +00:00
|
|
|
psnr = (rmse == 0) ? 999.0f : 20.0f * log10(255.0f / rmse);
|
2007-07-02 10:45:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void print()
|
|
|
|
{
|
2007-10-25 08:49:23 +00:00
|
|
|
printf(" Mean absolute error: %f\n", mabse);
|
|
|
|
printf(" Max absolute error: %f\n", maxabse);
|
|
|
|
printf(" Root mean squared error: %f\n", rmse);
|
|
|
|
printf(" Peak signal to noise ratio in dB: %f\n", psnr);
|
2007-07-02 10:45:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int samples;
|
|
|
|
float mabse;
|
|
|
|
float maxabse;
|
|
|
|
float mse;
|
|
|
|
float rmse;
|
|
|
|
float psnr;
|
|
|
|
};
|
|
|
|
|
2007-10-14 09:44:52 +00:00
|
|
|
struct NormalError
|
|
|
|
{
|
|
|
|
NormalError()
|
|
|
|
{
|
|
|
|
samples = 0;
|
2007-10-25 08:49:23 +00:00
|
|
|
ade = 0.0f;
|
|
|
|
mse = 0.0f;
|
2007-10-14 09:44:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void addSample(nv::Color32 o, nv::Color32 c)
|
|
|
|
{
|
2007-10-15 10:36:49 +00:00
|
|
|
nv::Vector3 vo = nv::Vector3(o.r, o.g, o.b);
|
|
|
|
nv::Vector3 vc = nv::Vector3(c.r, c.g, c.b);
|
|
|
|
|
2007-10-25 08:49:23 +00:00
|
|
|
// Unpack and normalize.
|
|
|
|
vo = nv::normalize(2.0f * (vo / 255.0f) - 1.0f);
|
|
|
|
vc = nv::normalize(2.0f * (vc / 255.0f) - 1.0f);
|
2007-10-15 10:36:49 +00:00
|
|
|
|
|
|
|
ade += acosf(nv::clamp(dot(vo, vc), -1.0f, 1.0f));
|
2010-05-28 07:45:11 +00:00
|
|
|
mse += lengthSquared((vo - vc) * (255 / 2.0f));
|
2007-10-25 08:49:23 +00:00
|
|
|
|
2007-10-14 09:44:52 +00:00
|
|
|
samples++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void done()
|
|
|
|
{
|
2008-02-15 08:58:02 +00:00
|
|
|
if (samples)
|
|
|
|
{
|
|
|
|
ade /= samples;
|
|
|
|
mse /= samples * 3;
|
2009-03-02 09:21:30 +00:00
|
|
|
rmse = sqrtf(mse);
|
2008-02-15 08:58:02 +00:00
|
|
|
psnr = (rmse == 0) ? 999.0f : 20.0f * log10(255.0f / rmse);
|
|
|
|
}
|
2007-10-14 09:44:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void print()
|
|
|
|
{
|
2007-10-25 08:49:23 +00:00
|
|
|
printf(" Angular deviation error: %f\n", ade);
|
|
|
|
printf(" Root mean squared error: %f\n", rmse);
|
|
|
|
printf(" Peak signal to noise ratio in dB: %f\n", psnr);
|
2007-10-14 09:44:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int samples;
|
|
|
|
float ade;
|
2007-10-25 08:49:23 +00:00
|
|
|
float mse;
|
|
|
|
float rmse;
|
|
|
|
float psnr;
|
2007-10-14 09:44:52 +00:00
|
|
|
};
|
|
|
|
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
MyAssertHandler assertHandler;
|
|
|
|
MyMessageHandler messageHandler;
|
|
|
|
|
2007-07-08 09:50:30 +00:00
|
|
|
bool compareNormal = false;
|
|
|
|
bool compareAlpha = false;
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
nv::Path input0;
|
|
|
|
nv::Path input1;
|
|
|
|
nv::Path output;
|
|
|
|
|
|
|
|
// Parse arguments.
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
|
|
{
|
|
|
|
// Input options.
|
|
|
|
if (strcmp("-normal", argv[i]) == 0)
|
|
|
|
{
|
2007-07-08 09:50:30 +00:00
|
|
|
compareNormal = true;
|
2007-07-02 09:10:33 +00:00
|
|
|
}
|
2007-07-02 10:45:25 +00:00
|
|
|
if (strcmp("-alpha", argv[i]) == 0)
|
|
|
|
{
|
2007-07-08 09:50:30 +00:00
|
|
|
compareAlpha = true;
|
2007-07-02 10:45:25 +00:00
|
|
|
}
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
else if (argv[i][0] != '-')
|
|
|
|
{
|
|
|
|
input0 = argv[i];
|
|
|
|
|
|
|
|
if (i+1 < argc && argv[i+1][0] != '-') {
|
|
|
|
input1 = argv[i+1];
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
if (input0.isNull() || input1.isNull())
|
2007-07-02 09:10:33 +00:00
|
|
|
{
|
|
|
|
printf("NVIDIA Texture Tools - Copyright NVIDIA Corporation 2007\n\n");
|
|
|
|
|
2007-07-08 09:50:30 +00:00
|
|
|
printf("usage: nvimgdiff [options] original_file updated_file [output]\n\n");
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
printf("Diff options:\n");
|
|
|
|
printf(" -normal \tCompare images as if they were normal maps.\n");
|
2007-07-08 09:50:30 +00:00
|
|
|
printf(" -alpha \tCompare alpha weighted images.\n");
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
nv::Image image0, image1;
|
2010-08-31 01:39:08 +00:00
|
|
|
if (!loadImage(image0, input0.str())) return 0;
|
|
|
|
if (!loadImage(image1, input1.str())) return 0;
|
2007-07-02 10:45:25 +00:00
|
|
|
|
|
|
|
const uint w0 = image0.width();
|
|
|
|
const uint h0 = image0.height();
|
|
|
|
const uint w1 = image1.width();
|
|
|
|
const uint h1 = image1.height();
|
|
|
|
const uint w = nv::min(w0, w1);
|
|
|
|
const uint h = nv::min(h0, h1);
|
|
|
|
|
|
|
|
// Compute errors.
|
|
|
|
Error error_r;
|
|
|
|
Error error_g;
|
|
|
|
Error error_b;
|
|
|
|
Error error_a;
|
|
|
|
Error error_total;
|
2007-10-14 09:44:52 +00:00
|
|
|
NormalError error_normal;
|
2007-07-02 10:45:25 +00:00
|
|
|
|
|
|
|
for (uint i = 0; i < h; i++)
|
|
|
|
{
|
|
|
|
for (uint e = 0; e < w; e++)
|
|
|
|
{
|
|
|
|
const nv::Color32 c0(image0.pixel(e, i));
|
|
|
|
const nv::Color32 c1(image1.pixel(e, i));
|
|
|
|
|
2007-12-03 23:28:29 +00:00
|
|
|
float r = float(c0.r - c1.r);
|
|
|
|
float g = float(c0.g - c1.g);
|
|
|
|
float b = float(c0.b - c1.b);
|
|
|
|
float a = float(c0.a - c1.a);
|
2007-07-02 10:45:25 +00:00
|
|
|
|
|
|
|
error_r.addSample(r);
|
|
|
|
error_g.addSample(g);
|
|
|
|
error_b.addSample(b);
|
|
|
|
error_a.addSample(a);
|
2007-10-14 09:44:52 +00:00
|
|
|
|
|
|
|
if (compareNormal)
|
|
|
|
{
|
|
|
|
error_normal.addSample(c0, c1);
|
|
|
|
}
|
2007-07-08 09:50:30 +00:00
|
|
|
|
|
|
|
if (compareAlpha)
|
|
|
|
{
|
|
|
|
error_total.addSample(r * c0.a / 255.0f);
|
|
|
|
error_total.addSample(g * c0.a / 255.0f);
|
|
|
|
error_total.addSample(b * c0.a / 255.0f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error_total.addSample(r);
|
|
|
|
error_total.addSample(g);
|
|
|
|
error_total.addSample(b);
|
|
|
|
}
|
2007-07-02 10:45:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error_r.done();
|
|
|
|
error_g.done();
|
|
|
|
error_b.done();
|
|
|
|
error_a.done();
|
|
|
|
error_total.done();
|
2007-10-14 09:44:52 +00:00
|
|
|
error_normal.done();
|
|
|
|
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
printf("Image size compared: %dx%d\n", w, h);
|
|
|
|
if (w != w0 || w != w1 || h != h0 || h != h1) {
|
|
|
|
printf("--- NOTE: only the overlap between the 2 images (%d,%d) and (%d,%d) was compared\n", w0, h0, w1, h1);
|
|
|
|
}
|
|
|
|
printf("Total pixels: %d\n", w*h);
|
|
|
|
|
2007-07-08 09:50:30 +00:00
|
|
|
printf("Color:\n");
|
2007-07-02 10:45:25 +00:00
|
|
|
error_total.print();
|
|
|
|
|
2007-10-15 10:36:49 +00:00
|
|
|
if (compareNormal)
|
|
|
|
{
|
2007-10-25 08:49:23 +00:00
|
|
|
printf("Normal:\n");
|
2007-10-15 10:36:49 +00:00
|
|
|
error_normal.print();
|
|
|
|
}
|
|
|
|
|
2007-07-08 09:50:30 +00:00
|
|
|
if (compareAlpha)
|
|
|
|
{
|
|
|
|
printf("Alpha:\n");
|
|
|
|
error_a.print();
|
|
|
|
}
|
|
|
|
|
2007-07-02 10:45:25 +00:00
|
|
|
// @@ Write image difference.
|
2007-07-02 09:10:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|