aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjmarshallnz <jmarshallnz@svn>2010-03-23 23:20:43 +0000
committerjmarshallnz <jmarshallnz@svn>2010-03-23 23:20:43 +0000
commit5cb3e419d160f8bc61bc5de0f8cdbc4a4330482b (patch)
treec9a7803c4beab8bdcbcf5cec2bf0f8faeb0da9c8
parente136c5e179f197db1a9843397555aa1559517462 (diff)
changed: switch to a weighted MSE for DXT compression evaluation which gives 5 times the weight to "single" colour blocks which perceptually suffer more from quantisation.
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@28782 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
-rw-r--r--xbmc/lib/libsquish/squish.cpp79
1 files changed, 64 insertions, 15 deletions
diff --git a/xbmc/lib/libsquish/squish.cpp b/xbmc/lib/libsquish/squish.cpp
index 0d1a09fc78..ff9b445cad 100644
--- a/xbmc/lib/libsquish/squish.cpp
+++ b/xbmc/lib/libsquish/squish.cpp
@@ -248,9 +248,59 @@ static double ErrorSq(double x, double y)
return (x - y) * (x - y);
}
+static void ComputeBlockWMSE(u8 const *original, u8 const *compressed, unsigned int w, unsigned int h, double &cmse, double &amse)
+{
+ // Computes the MSE for the block and weights it by the variance of the original block.
+ // If the variance of the original block is less than 4 (i.e. a standard deviation of 1 per channel)
+ // then the block is close to being a single colour. Quantisation errors in single colour blocks
+ // are easier to see than similar errors in blocks that contain more colours, particularly when there
+ // are many such blocks in a large area (eg a blue sky background) as they cause banding. Given that
+ // banding is easier to see than small errors in "complex" blocks, we weight the errors by a factor
+ // of 5. This implies that images with large, single colour areas will have a higher potential WMSE
+ // than images with lots of detail.
+
+ cmse = amse = 0;
+ unsigned int sum_p[4]; // per channel sum of pixels
+ unsigned int sum_p2[4]; // per channel sum of pixels squared
+ memset(sum_p, 0, sizeof(sum_p));
+ memset(sum_p2, 0, sizeof(sum_p2));
+ for( unsigned int py = 0; py < 4; ++py )
+ {
+ for( unsigned int px = 0; px < 4; ++px )
+ {
+ if( px < w && py < h )
+ {
+ double pixelCMSE = 0;
+ for( int i = 0; i < 3; ++i )
+ {
+ pixelCMSE += ErrorSq(original[i], compressed[i]);
+ sum_p[i] += original[i];
+ sum_p2[i] += (unsigned int)original[i]*original[i];
+ }
+ if( original[3] == 0 && compressed[3] == 0 )
+ pixelCMSE = 0; // transparent in both, so colour is inconsequential
+ amse += ErrorSq(original[3], compressed[3]);
+ cmse += pixelCMSE;
+ sum_p[3] += original[3];
+ sum_p2[3] += (unsigned int)original[3]*original[3];
+ }
+ original += 4;
+ compressed += 4;
+ }
+ }
+ unsigned int variance = 0;
+ for( int i = 0; i < 4; ++i )
+ variance += w*h*sum_p2[i] - sum_p[i]*sum_p[i];
+ if( variance < 4 * w * w * h * h )
+ {
+ amse *= 5;
+ cmse *= 5;
+ }
+}
+
void ComputeMSE( u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
{
- ComputeMSE(rgba, width, height, width*4, dxt, flags, colourMSE, alphaMSE);
+ ComputeMSE(rgba, width, height, width*4, dxt, flags, colourMSE, alphaMSE);
}
void ComputeMSE( u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
@@ -271,34 +321,33 @@ void ComputeMSE( u8 const *rgba, int width, int height, int pitch, u8 const *dxt
// decompress the block
u8 targetRgba[4*16];
Decompress( targetRgba, sourceBlock, flags );
-
- // write the decompressed pixels to the correct image locations
u8 const* sourcePixel = targetRgba;
+
+ // copy across to a similar pixel block
+ u8 originalRgba[4*16];
+ u8* originalPixel = originalRgba;
+
for( int py = 0; py < 4; ++py )
{
for( int px = 0; px < 4; ++px )
{
- // get the target location
int sx = x + px;
int sy = y + py;
if( sx < width && sy < height )
{
u8 const* targetPixel = rgba + pitch*sy + 4*sx;
- u8 colour[4];
- CopyRGBA(targetPixel, colour, flags);
- // compute the MSE of colour and alpha
- double cmse = 0;
- for( int i = 0; i < 3; ++i )
- cmse += ErrorSq(sourcePixel[i], colour[i]);
- if (colour[3] == 0 && sourcePixel[3] == 0) // transparent source and dest double cmse = 0;
- cmse = 0; // transparent in both, so colour is inconsequential
- alphaMSE += ErrorSq(colour[3], sourcePixel[3]);
- colourMSE += cmse;
+ CopyRGBA(targetPixel, originalPixel, flags);
}
sourcePixel += 4;
+ originalPixel += 4;
}
}
-
+
+ // compute the weighted MSE of the block
+ double blockCMSE, blockAMSE;
+ ComputeBlockWMSE(originalRgba, targetRgba, std::min(4, width - x), std::min(4, height - y), blockCMSE, blockAMSE);
+ colourMSE += blockCMSE;
+ alphaMSE += blockAMSE;
// advance
sourceBlock += bytesPerBlock;
}