Browse Source

Added Image Quality Assessment.

Branimir Karadžić 9 years ago
parent
commit
f3b9fed29a

+ 32 - 0
3rdparty/iqa/LICENSE

@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */

+ 36 - 0
3rdparty/iqa/README.txt

@@ -0,0 +1,36 @@
+Doxygen documentation can be found at: http://tdistler.com/iqa
+
+BUILD:
+
+  All build artifacts end up in build/<configuration>, where <configuration> is
+  'debug' or 'release'.
+
+  Windows:
+    - Open iqa.sln, select 'Debug' or 'Release', and build. The output is a 
+      static library 'iqa.lib'.
+    - To run the tests under the debugger, first right-click the 'test' project,
+      select Properties -> Configuration Properties -> Debugging and set
+      'Working Directory' to '$(OutDir)'. Then start the application.
+
+  Linux:
+    - Change directories into the root of the IQA branch you want to build.
+    - Type `make` for a debug build, or `make RELEASE=1` for a release build.
+      The output is a static library 'libiqa.a'.
+    - Type `make test` (or `make test RELEASE=1`) to build the unit tests.
+    - Type `make clean` (or `make clean RELEASE=1`) to delete all build
+      artifacts.
+    - To run the tests, `cd` to the build/<configuration> directory and type
+      `./test`.
+
+
+USE:
+
+  - Include 'iqa.h' in your source file.
+  - Call iqa_* methods.
+  - Link against the IQA library.
+
+
+HELP & SUPPORT:
+
+  Further help can be found at: https://sourceforge.net/projects/iqa/support
+

+ 111 - 0
3rdparty/iqa/include/convolve.h

@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CONVOLVE_H_
+#define _CONVOLVE_H_
+
+typedef float (*_iqa_get_pixel)(const float *img, int w, int h, int x, int y, float bnd_const);
+
+/** Out-of-bounds array values are a mirrored reflection of the border values*/
+float KBND_SYMMETRIC(const float *img, int w, int h, int x, int y, float bnd_const);
+/** Out-of-bounds array values are set to the nearest border value */
+float KBND_REPLICATE(const float *img, int w, int h, int x, int y, float bnd_const);
+/** Out-of-bounds array values are set to 'bnd_const' */
+float KBND_CONSTANT(const float *img, int w, int h, int x, int y, float bnd_const);
+
+
+/** Defines a convolution kernel */
+struct _kernel {
+    float *kernel;          /**< Pointer to the kernel values */
+    int w;                  /**< The kernel width */
+    int h;                  /**< The kernel height */
+    int normalized;         /**< 1 if the kernel values add up to 1. 0 otherwise */
+    _iqa_get_pixel bnd_opt; /**< Defines how out-of-bounds image values are handled */
+    float bnd_const;        /**< If 'bnd_opt' is KBND_CONSTANT, this specifies the out-of-bounds value */
+};
+
+/**
+ * @brief Applies the specified kernel to the image.
+ * The kernel will be applied to all areas where it fits completely within
+ * the image. The resulting image will be smaller by half the kernel width 
+ * and height (w - kw/2 and h - kh/2).
+ *
+ * @param img Image to modify
+ * @param w Image width
+ * @param h Image height
+ * @param k The kernel to apply
+ * @param result Buffer to hold the resulting image ((w-kw)*(h-kh), where kw
+ *               and kh are the kernel width and height). If 0, the result
+ *               will be written to the original image buffer.
+ * @param rw Optional. The width of the resulting image will be stored here.
+ * @param rh Optional. The height of the resulting image will be stored here.
+ */
+void _iqa_convolve(float *img, int w, int h, const struct _kernel *k, float *result, int *rw, int *rh);
+
+/**
+ * The same as _iqa_convolve() except the kernel is applied to the entire image.
+ * In other words, the kernel is applied to all areas where the top-left corner
+ * of the kernel is in the image. Out-of-bound pixel value (off the right and
+ * bottom edges) are chosen based on the 'bnd_opt' and 'bnd_const' members of
+ * the kernel structure. The resulting array is the same size as the input
+ * image.
+ *
+ * @param img Image to modify
+ * @param w Image width
+ * @param h Image height
+ * @param k The kernel to apply
+ * @param result Buffer to hold the resulting image ((w-kw)*(h-kh), where kw
+ *               and kh are the kernel width and height). If 0, the result
+ *               will be written to the original image buffer.
+ * @return 0 if successful. Non-zero otherwise.
+ */
+int _iqa_img_filter(float *img, int w, int h, const struct _kernel *k, float *result);
+
+/**
+ * Returns the filtered version of the specified pixel. If no kernel is given,
+ * the raw pixel value is returned.
+ * 
+ * @param img Source image
+ * @param w Image width
+ * @param h Image height
+ * @param x The x location of the pixel to filter
+ * @param y The y location of the pixel to filter
+ * @param k Optional. The convolution kernel to apply to the pixel.
+ * @param kscale The scale of the kernel (for normalization). 1 for normalized
+ *               kernels. Required if 'k' is not null.
+ * @return The filtered pixel value.
+ */
+float _iqa_filter_pixel(const float *img, int w, int h, int x, int y, const struct _kernel *k, const float kscale);
+
+
+#endif /*_CONVOLVE_H_*/

+ 55 - 0
3rdparty/iqa/include/decimate.h

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DECIMATE_H_
+#define _DECIMATE_H_
+
+#include "convolve.h"
+
+/**
+ * @brief Downsamples (decimates) an image.
+ *
+ * @param img Image to modify
+ * @param w Image width
+ * @param h Image height
+ * @param factor Decimation factor
+ * @param k The kernel to apply (e.g. low-pass filter). Can be 0.
+ * @param result Buffer to hold the resulting image (w/factor*h/factor). If 0,
+ *               the result will be written to the original image buffer.
+ * @param rw Optional. The width of the resulting image will be stored here.
+ * @param rh Optional. The height of the resulting image will be stored here.
+ * @return 0 on success.
+ */
+int _iqa_decimate(float *img, int w, int h, int factor, const struct _kernel *k, float *result, int *rw, int *rh);
+
+#endif /*_DECIMATE_H_*/

+ 134 - 0
3rdparty/iqa/include/iqa.h

@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IQA_H_
+#define _IQA_H_
+
+#include "iqa_os.h"
+
+/**
+ * Allows fine-grain control of the SSIM algorithm.
+ */
+struct iqa_ssim_args {
+    float alpha;    /**< luminance exponent */
+    float beta;     /**< contrast exponent */
+    float gamma;    /**< structure exponent */
+    int L;          /**< dynamic range (2^8 - 1)*/
+    float K1;       /**< stabilization constant 1 */
+    float K2;       /**< stabilization constant 2 */
+    int f;          /**< scale factor. 0=default scaling, 1=no scaling */
+};
+
+/**
+ * Allows fine-grain control of the MS-SSIM algorithm.
+ */
+struct iqa_ms_ssim_args {
+    int wang;             /**< 1=original algorithm by Wang, et al. 0=MS-SSIM* by Rouse/Hemami (default). */
+    int gaussian;         /**< 1=11x11 Gaussian window (default). 0=8x8 linear window. */
+    int scales;           /**< Number of scaled images to use. Default is 5. */
+    const float *alphas;  /**< Pointer to array of alpha values for each scale. Required if 'scales' isn't 5. */
+    const float *betas;   /**< Pointer to array of beta values for each scale. Required if 'scales' isn't 5. */
+    const float *gammas;  /**< Pointer to array of gamma values for each scale. Required if 'scales' isn't 5. */
+};
+
+/**
+ * Calculates the Mean Squared Error between 2 equal-sized 8-bit images.
+ * @note The images must have the same width, height, and stride.
+ * @param ref Original reference image
+ * @param cmp Distorted image
+ * @param w Width of the images
+ * @param h Height of the images
+ * @param stride The length (in bytes) of each horizontal line in the image.
+ *               This may be different from the image width.
+ * @return The MSE.
+ */
+float iqa_mse(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride);
+
+/**
+ * Calculates the Peak Signal-to-Noise-Ratio between 2 equal-sized 8-bit
+ * images.
+ * @note The images must have the same width, height, and stride.
+ * @param ref Original reference image
+ * @param cmp Distorted image
+ * @param w Width of the images
+ * @param h Height of the images
+ * @param stride The length (in bytes) of each horizontal line in the image.
+ *               This may be different from the image width.
+ * @return The PSNR.
+ */
+float iqa_psnr(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride);
+
+/**
+ * Calculates the Structural SIMilarity between 2 equal-sized 8-bit images.
+ *
+ * See https://ece.uwaterloo.ca/~z70wang/publications/ssim.html
+ * @note The images must have the same width, height, and stride.
+ * @param ref Original reference image
+ * @param cmp Distorted image
+ * @param w Width of the images
+ * @param h Height of the images
+ * @param stride The length (in bytes) of each horizontal line in the image.
+ *               This may be different from the image width.
+ * @param gaussian 0 = 8x8 square window, 1 = 11x11 circular-symmetric Gaussian
+ * weighting.
+ * @param args Optional SSIM arguments for fine control of the algorithm. 0 for
+ * defaults. Defaults are a=b=g=1.0, L=255, K1=0.01, K2=0.03
+ * @return The mean SSIM over the entire image (MSSIM), or INFINITY if error.
+ */
+float iqa_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride, 
+    int gaussian, const struct iqa_ssim_args *args);
+
+/**
+ * Calculates the Multi-Scale Structural SIMilarity between 2 equal-sized 8-bit
+ * images. The default algorithm is MS-SSIM* proposed by Rouse/Hemami 2008.
+ *
+ * See https://ece.uwaterloo.ca/~z70wang/publications/msssim.pdf and
+ * http://foulard.ece.cornell.edu/publications/dmr_hvei2008_paper.pdf
+ *
+ * @note 1. The images must have the same width, height, and stride.
+ * @note 2. The minimum image width or height is 2^(scales-1) * filter, where 'filter' is 11
+ * if a Gaussian window is being used, or 9 otherwise.
+ * @param ref Original reference image
+ * @param cmp Distorted image
+ * @param w Width of the images.
+ * @param h Height of the images.
+ * @param stride The length (in bytes) of each horizontal line in the image.
+ *               This may be different from the image width.
+ * @param args Optional MS-SSIM arguments for fine control of the algorithm. 0
+ * for defaults. Defaults are wang=0, scales=5, gaussian=1.
+ * @return The mean MS-SSIM over the entire image, or INFINITY if error.
+ */
+float iqa_ms_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride, 
+    const struct iqa_ms_ssim_args *args);
+
+#endif /*_IQA_H_*/

+ 66 - 0
3rdparty/iqa/include/iqa_os.h

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OS_H_
+#define _OS_H_
+
+/* Microsoft tends to implement features early, but they have a high legacy
+ * cost because they won't break existing implementations. As such, certain
+ * features we take for granted on other platforms (like C99) aren't fully
+ * implemented. This file is meant to rectify that.
+ */
+
+#ifdef WIN32
+
+#include <windows.h>
+#define IQA_INLINE __inline
+
+#ifndef INFINITY
+    #define INFINITY (float)HUGE_VAL /**< Defined in C99 (Windows is C89) */
+#endif /*INFINITY*/
+
+#ifndef NAN
+    static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};
+    #define NAN (*(const float *) __nan) /**< Defined in C99 (Windows is C99) */
+#endif
+
+#define IQA_EXPORT __declspec(dllexport)
+
+#else /* !Windows */
+
+#define IQA_INLINE inline
+#define IQA_EXPORT
+
+#endif
+
+#endif /* _OS_H_ */

+ 64 - 0
3rdparty/iqa/include/math_utils.h

@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MATH_UTILS_H_
+#define _MATH_UTILS_H_
+
+#include "iqa_os.h"
+#include <math.h>
+
+/**
+ * Rounds a float to the nearest integer.
+ */
+IQA_EXPORT IQA_INLINE int _round(float a);
+
+IQA_EXPORT IQA_INLINE int _max(int x, int y);
+
+IQA_EXPORT IQA_INLINE int _min(int x, int y);
+
+
+/** 
+ * Compares 2 floats to the specified digit of precision.
+ * @return 0 if equal, 1 otherwise.
+ */
+IQA_EXPORT IQA_INLINE int _cmp_float(float a, float b, int digits);
+
+
+/** 
+ * Compares 2 matrices with the specified precision. 'b' is assumed to be the
+ * same size as 'a' or smaller.
+ * @return 0 if equal, 1 otherwise
+ */
+IQA_EXPORT IQA_INLINE int _matrix_cmp(const float *a, const float *b, int w, int h, int digits);
+
+#endif /*_MATH_UTILS_H_*/

+ 117 - 0
3rdparty/iqa/include/ssim.h

@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SSIM_H_
+#define _SSIM_H_
+
+#include "convolve.h"
+
+/*
+ * Circular-symmetric Gaussian weighting.
+ * h(x,y) = hg(x,y)/SUM(SUM(hg)) , for normalization to 1.0
+ * hg(x,y) = e^( -0.5*( (x^2+y^2)/sigma^2 ) ) , where sigma was 1.5
+ */
+#define GAUSSIAN_LEN 11
+static const float g_gaussian_window[GAUSSIAN_LEN][GAUSSIAN_LEN] = {
+    {0.000001f, 0.000008f, 0.000037f, 0.000112f, 0.000219f, 0.000274f, 0.000219f, 0.000112f, 0.000037f, 0.000008f, 0.000001f},
+    {0.000008f, 0.000058f, 0.000274f, 0.000831f, 0.001619f, 0.002021f, 0.001619f, 0.000831f, 0.000274f, 0.000058f, 0.000008f},
+    {0.000037f, 0.000274f, 0.001296f, 0.003937f, 0.007668f, 0.009577f, 0.007668f, 0.003937f, 0.001296f, 0.000274f, 0.000037f},
+    {0.000112f, 0.000831f, 0.003937f, 0.011960f, 0.023294f, 0.029091f, 0.023294f, 0.011960f, 0.003937f, 0.000831f, 0.000112f},
+    {0.000219f, 0.001619f, 0.007668f, 0.023294f, 0.045371f, 0.056662f, 0.045371f, 0.023294f, 0.007668f, 0.001619f, 0.000219f},
+    {0.000274f, 0.002021f, 0.009577f, 0.029091f, 0.056662f, 0.070762f, 0.056662f, 0.029091f, 0.009577f, 0.002021f, 0.000274f},
+    {0.000219f, 0.001619f, 0.007668f, 0.023294f, 0.045371f, 0.056662f, 0.045371f, 0.023294f, 0.007668f, 0.001619f, 0.000219f},
+    {0.000112f, 0.000831f, 0.003937f, 0.011960f, 0.023294f, 0.029091f, 0.023294f, 0.011960f, 0.003937f, 0.000831f, 0.000112f},
+    {0.000037f, 0.000274f, 0.001296f, 0.003937f, 0.007668f, 0.009577f, 0.007668f, 0.003937f, 0.001296f, 0.000274f, 0.000037f},
+    {0.000008f, 0.000058f, 0.000274f, 0.000831f, 0.001619f, 0.002021f, 0.001619f, 0.000831f, 0.000274f, 0.000058f, 0.000008f},
+    {0.000001f, 0.000008f, 0.000037f, 0.000112f, 0.000219f, 0.000274f, 0.000219f, 0.000112f, 0.000037f, 0.000008f, 0.000001f},
+};
+
+/*
+ * Equal weight square window.
+ * Each pixel is equally weighted (1/64) so that SUM(x) = 1.0
+ */
+#define SQUARE_LEN 8
+static const float g_square_window[SQUARE_LEN][SQUARE_LEN] = {
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+    {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f},
+};
+
+/* Holds intermediate SSIM values for map-reduce operation. */
+struct _ssim_int {
+    double l;
+    double c;
+    double s;
+};
+
+/* Defines the pointers to the map-reduce functions. */
+typedef int (*_map)(const struct _ssim_int *, void *);
+typedef float (*_reduce)(int, int, void *);
+
+/* Arguments for map-reduce. The 'context' is user-defined. */
+struct _map_reduce {
+    _map map;
+    _reduce reduce;
+    void *context;
+};
+
+/**
+ * Private method that calculates the SSIM value on a pre-processed image.
+ *
+ * The input images must have stride==width. This method does not scale.
+ *
+ * @note Image buffers are modified.
+ *
+ * Map-reduce is used for doing the final SSIM calculation. The map function is
+ * called for every pixel, and the reduce is called at the end. The context is
+ * caller-defined and *not* modified by this method.
+ *
+ * @param ref Original reference image
+ * @param cmp Distorted image
+ * @param w Width of the images
+ * @param h Height of the images
+ * @param k The kernel used as the window function
+ * @param mr Optional map-reduce functions to use to calculate SSIM. Required
+ *           if 'args' is not null. Ignored if 'args' is null.
+ * @param args Optional SSIM arguments for fine control of the algorithm. 0 for defaults.
+ *             Defaults are a=b=g=1.0, L=255, K1=0.01, K2=0.03
+ * @return The mean SSIM over the entire image (MSSIM), or INFINITY if error.
+ */
+float _iqa_ssim(float *ref, float *cmp, int w, int h, const struct _kernel *k, const struct _map_reduce *mr, const struct iqa_ssim_args *args);
+
+#endif /* _SSIM_H_ */

+ 195 - 0
3rdparty/iqa/source/convolve.c

@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "convolve.h"
+#include <stdlib.h>
+
+float KBND_SYMMETRIC(const float *img, int w, int h, int x, int y, float bnd_const)
+{
+    (void)bnd_const;
+    if (x<0) x=-1-x;
+    else if (x>=w) x=(w-(x-w))-1;
+    if (y<0) y=-1-y;
+    else if (y>=h) y=(h-(y-h))-1;
+    return img[y*w + x];
+}
+
+float KBND_REPLICATE(const float *img, int w, int h, int x, int y, float bnd_const)
+{
+    (void)bnd_const;
+    if (x<0) x=0;
+    if (x>=w) x=w-1;
+    if (y<0) y=0;
+    if (y>=h) y=h-1;
+    return img[y*w + x];
+}
+
+float KBND_CONSTANT(const float *img, int w, int h, int x, int y, float bnd_const)
+{
+    if (x<0) x=0;
+    if (y<0) y=0;
+    if (x>=w || y>=h)
+        return bnd_const;
+    return img[y*w + x];
+}
+
+static float _calc_scale(const struct _kernel *k)
+{
+    int ii,k_len;
+    double sum=0.0;
+
+    if (k->normalized)
+        return 1.0f;
+    else {
+        k_len = k->w * k->h;
+        for (ii=0; ii<k_len; ++ii)
+            sum += k->kernel[ii];
+        if (sum != 0.0)
+            return (float)(1.0 / sum);
+        return 1.0f;
+    }
+}
+
+void _iqa_convolve(float *img, int w, int h, const struct _kernel *k, float *result, int *rw, int *rh)
+{
+    int x,y,kx,ky,u,v;
+    int uc = k->w/2;
+    int vc = k->h/2;
+    int kw_even = (k->w&1)?0:1;
+    int kh_even = (k->h&1)?0:1;
+    int dst_w = w - k->w + 1;
+    int dst_h = h - k->h + 1;
+    int img_offset,k_offset;
+    double sum;
+    float scale, *dst=result;
+
+    if (!dst)
+        dst = img; /* Convolve in-place */
+
+    /* Kernel is applied to all positions where the kernel is fully contained
+     * in the image */
+    scale = _calc_scale(k);
+    for (y=0; y < dst_h; ++y) {
+        for (x=0; x < dst_w; ++x) {
+            sum = 0.0;
+            k_offset = 0;
+            ky = y+vc;
+            kx = x+uc;
+            for (v=-vc; v <= vc-kh_even; ++v) {
+                img_offset = (ky+v)*w + kx;
+                for (u=-uc; u <= uc-kw_even; ++u, ++k_offset) {
+                    sum += img[img_offset+u] * k->kernel[k_offset];
+                }
+            }
+            dst[y*dst_w + x] = (float)(sum * scale);
+        }
+    }
+
+    if (rw) *rw = dst_w;
+    if (rh) *rh = dst_h;
+}
+
+int _iqa_img_filter(float *img, int w, int h, const struct _kernel *k, float *result)
+{
+    int x,y;
+    int img_offset;
+    float scale, *dst=result;
+
+    if (!k || !k->bnd_opt)
+        return 1;
+
+    if (!dst) {
+        dst = (float*)malloc(w*h*sizeof(float));
+        if (!dst)
+            return 2;
+    }
+
+    scale = _calc_scale(k);
+
+    /* Kernel is applied to all positions where top-left corner is in the image */
+    for (y=0; y < h; ++y) {
+        for (x=0; x < w; ++x) {
+            dst[y*w + x] = _iqa_filter_pixel(img, w, h, x, y, k, scale);
+        }
+    }
+
+    /* If no result buffer given, copy results to image buffer */
+    if (!result) {
+        for (y=0; y<h; ++y) {
+            img_offset = y*w;
+            for (x=0; x<w; ++x, ++img_offset) {
+                img[img_offset] = dst[img_offset];
+            }
+        }
+        free(dst);
+    }
+    return 0;
+}
+
+float _iqa_filter_pixel(const float *img, int w, int h, int x, int y, const struct _kernel *k, const float kscale)
+{
+    int u,v,uc,vc;
+    int kw_even,kh_even;
+    int x_edge_left,x_edge_right,y_edge_top,y_edge_bottom;
+    int edge,img_offset,k_offset;
+    double sum;
+
+    if (!k)
+        return img[y*w + x];
+
+    uc = k->w/2;
+    vc = k->h/2;
+    kw_even = (k->w&1)?0:1;
+    kh_even = (k->h&1)?0:1;
+    x_edge_left  = uc;
+    x_edge_right = w-uc;
+    y_edge_top = vc;
+    y_edge_bottom = h-vc;
+
+    edge = 0;
+    if (x < x_edge_left || y < y_edge_top || x >= x_edge_right || y >= y_edge_bottom)
+        edge = 1;
+
+    sum = 0.0;
+    k_offset = 0;
+    for (v=-vc; v <= vc-kh_even; ++v) {
+        img_offset = (y+v)*w + x;
+        for (u=-uc; u <= uc-kw_even; ++u, ++k_offset) {
+            if (!edge)
+                sum += img[img_offset+u] * k->kernel[k_offset];
+            else
+                sum += k->bnd_opt(img, w, h, x+u, y+v, k->bnd_const) * k->kernel[k_offset];
+        }
+    }
+    return (float)(sum * kscale);
+}

+ 59 - 0
3rdparty/iqa/source/decimate.c

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "decimate.h"
+#include <stdlib.h>
+
+int _iqa_decimate(float *img, int w, int h, int factor, const struct _kernel *k, float *result, int *rw, int *rh)
+{
+    int x,y;
+    int sw = w/factor + (w&1);
+    int sh = h/factor + (h&1);
+    int dst_offset;
+    float *dst=img;
+
+    if (result)
+        dst = result;
+
+    /* Downsample */
+    for (y=0; y<sh; ++y) {
+        dst_offset = y*sw;
+        for (x=0; x<sw; ++x,++dst_offset) {
+            dst[dst_offset] = _iqa_filter_pixel(img, w, h, x*factor, y*factor, k, 1.0f);
+        }
+    }
+    
+    if (rw) *rw = sw;
+    if (rh) *rh = sh;
+    return 0;
+}

+ 82 - 0
3rdparty/iqa/source/math_utils.c

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "math_utils.h"
+#include <math.h>
+
+int _round(float a)
+{
+    int sign_a = a > 0.0f ? 1 : -1;
+    return a-(int)a >= 0.5 ? (int)a + sign_a : (int)a;
+}
+
+int _max(int x, int y)
+{
+    return x >= y ? x : y;
+}
+
+int _min(int x, int y)
+{
+    return x <= y ? x : y;
+}
+
+int _cmp_float(float a, float b, int digits)
+{
+    /* Round */
+    int sign_a = a > 0.0f ? 1 : -1;
+    int sign_b = b > 0.0f ? 1 : -1;
+    double scale = pow(10.0, (double)digits);
+    double ax = a * scale;
+    double bx = b * scale;
+    int ai = ax-(int)ax >= 0.5 ? (int)ax + sign_a : (int)ax;
+    int bi = bx-(int)bx >= 0.5 ? (int)bx + sign_b : (int)bx;
+
+    /* Compare */
+    return ai == bi ? 0 : 1;
+}
+
+int _matrix_cmp(const float *a, const float *b, int w, int h, int digits)
+{
+    int offset;
+    int result=0;
+    int len=w*h;
+    for (offset=0; offset<len; ++offset) {
+        if (_cmp_float(a[offset], b[offset], digits)) {
+            result = 1;
+            break;
+        }
+    }
+
+    return result;
+}
+

+ 277 - 0
3rdparty/iqa/source/ms_ssim.c

@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "iqa.h"
+#include "ssim.h"
+#include "decimate.h"
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Default number of scales */
+#define SCALES  5
+
+/* Low-pass filter for down-sampling (9/7 biorthogonal wavelet filter) */
+#define LPF_LEN 9
+static const float g_lpf[LPF_LEN][LPF_LEN] = {
+   { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f},
+   {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f},
+   {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f},
+   { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f},
+   { 0.016114f,-0.010146f,-0.047149f, 0.160885f, 0.363505f, 0.160885f,-0.047149f,-0.010146f, 0.016114f},
+   { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f},
+   {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f},
+   {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f},
+   { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f},
+};
+
+/* Alpha, beta, and gamma values for each scale */
+static float g_alphas[] = { 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.1333f };
+static float g_betas[]  = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f };
+static float g_gammas[] = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f };
+
+
+struct _context {
+    double l;  /* Luminance */
+    double c;  /* Contrast */
+    double s;  /* Structure */
+    float alpha;
+    float beta;
+    float gamma;
+};
+
+/* Called for each pixel */
+int _ms_ssim_map(const struct _ssim_int *si, void *ctx)
+{
+    struct _context *ms_ctx = (struct _context*)ctx;
+    ms_ctx->l += si->l;
+    ms_ctx->c += si->c;
+    ms_ctx->s += si->s;
+    return 0;
+}
+
+/* Called to calculate the final result */
+float _ms_ssim_reduce(int w, int h, void *ctx)
+{
+    double size = (double)(w*h);
+    struct _context *ms_ctx = (struct _context*)ctx;
+    ms_ctx->l = pow(ms_ctx->l / size, (double)ms_ctx->alpha);
+    ms_ctx->c = pow(ms_ctx->c / size, (double)ms_ctx->beta);
+    ms_ctx->s = pow(fabs(ms_ctx->s / size), (double)ms_ctx->gamma);
+    return (float)(ms_ctx->l * ms_ctx->c * ms_ctx->s);
+}
+
+/* Releases the scaled buffers */
+void _free_buffers(float **buf, int scales)
+{
+    int idx;
+    for (idx=0; idx<scales; ++idx)
+        free(buf[idx]);
+}
+
+/* Allocates the scaled buffers. If error, all buffers are free'd */
+int _alloc_buffers(float **buf, int w, int h, int scales)
+{
+    int idx;
+    int cur_w = w;
+    int cur_h = h;
+    for (idx=0; idx<scales; ++idx) {
+        buf[idx] = (float*)malloc(cur_w*cur_h*sizeof(float));
+        if (!buf[idx]) {
+            _free_buffers(buf, idx);
+            return 1;
+        }
+        cur_w = cur_w/2 + (cur_w&1);
+        cur_h = cur_h/2 + (cur_h&1);
+    }
+    return 0;
+}
+
+/*
+ * MS_SSIM(X,Y) = Lm(x,y)^aM * MULT[j=1->M]( Cj(x,y)^bj  *  Sj(x,y)^gj )
+ * where,
+ *  L = mean
+ *  C = variance
+ *  S = cross-correlation
+ *
+ *  b1=g1=0.0448, b2=g2=0.2856, b3=g3=0.3001, b4=g4=0.2363, a5=b5=g5=0.1333
+ */
+float iqa_ms_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, 
+    int stride, const struct iqa_ms_ssim_args *args)
+{
+    int wang=0;
+    int scales=SCALES;
+    int gauss=1;
+    const float *alphas=g_alphas, *betas=g_betas, *gammas=g_gammas;
+    int idx,x,y,cur_w,cur_h;
+    int offset,src_offset;
+    float **ref_imgs, **cmp_imgs; /* Array of pointers to scaled images */
+    float msssim;
+    struct _kernel lpf, window;
+    struct iqa_ssim_args s_args;
+    struct _map_reduce mr;
+    struct _context ms_ctx;
+
+    if (args) {
+        wang   = args->wang;
+        gauss  = args->gaussian;
+        scales = args->scales;
+        if (args->alphas)
+            alphas = args->alphas;
+        if (args->betas)
+            betas  = args->betas;
+        if (args->gammas)
+            gammas = args->gammas;
+    }
+
+    /* Make sure we won't scale below 1x1 */
+    cur_w = w;
+    cur_h = h;
+    for (idx=0; idx<scales; ++idx) {
+        if ( gauss ? cur_w<GAUSSIAN_LEN || cur_h<GAUSSIAN_LEN : cur_w<LPF_LEN || cur_h<LPF_LEN )
+            return INFINITY;
+        cur_w /= 2;
+        cur_h /= 2;
+    }
+
+    window.kernel = (float*)g_square_window;
+    window.w = window.h = SQUARE_LEN;
+    window.normalized = 1;
+    window.bnd_opt = KBND_SYMMETRIC;
+    if (gauss) {
+        window.kernel = (float*)g_gaussian_window;
+        window.w = window.h = GAUSSIAN_LEN;
+    }
+
+    mr.map     = _ms_ssim_map;
+    mr.reduce  = _ms_ssim_reduce;
+
+    /* Allocate the scaled image buffers */
+    ref_imgs = (float**)malloc(scales*sizeof(float*));
+    cmp_imgs = (float**)malloc(scales*sizeof(float*));
+    if (!ref_imgs || !cmp_imgs) {
+        if (ref_imgs) free(ref_imgs);
+        if (cmp_imgs) free(cmp_imgs);
+        return INFINITY;
+    }
+    if (_alloc_buffers(ref_imgs, w, h, scales)) {
+        free(ref_imgs);
+        free(cmp_imgs);
+        return INFINITY;
+    }
+    if (_alloc_buffers(cmp_imgs, w, h, scales)) {
+        _free_buffers(ref_imgs, scales);
+        free(ref_imgs);
+        free(cmp_imgs);
+        return INFINITY;
+    }
+
+    /* Copy original images into first scale buffer, forcing stride = width. */
+    for (y=0; y<h; ++y) {
+        src_offset = y*stride;
+        offset = y*w;
+        for (x=0; x<w; ++x, ++offset, ++src_offset) {
+            ref_imgs[0][offset] = (float)ref[src_offset];
+            cmp_imgs[0][offset] = (float)cmp[src_offset];
+        }
+    }
+
+    /* Create scaled versions of the images */
+    cur_w=w;
+    cur_h=h;
+    lpf.kernel = (float*)g_lpf;
+    lpf.w = lpf.h = LPF_LEN;
+    lpf.normalized = 1;
+    lpf.bnd_opt = KBND_SYMMETRIC;
+    for (idx=1; idx<scales; ++idx) {
+        if (_iqa_decimate(ref_imgs[idx-1], cur_w, cur_h, 2, &lpf, ref_imgs[idx], 0, 0) ||
+            _iqa_decimate(cmp_imgs[idx-1], cur_w, cur_h, 2, &lpf, cmp_imgs[idx], &cur_w, &cur_h))
+        {
+            _free_buffers(ref_imgs, scales);
+            _free_buffers(cmp_imgs, scales);
+            free(ref_imgs);
+            free(cmp_imgs);
+            return INFINITY;
+        }
+    }
+
+    cur_w=w;
+    cur_h=h;
+    msssim = 1.0;
+    for (idx=0; idx<scales; ++idx) {
+
+        ms_ctx.l = 0;
+        ms_ctx.c = 0;
+        ms_ctx.s = 0;
+        ms_ctx.alpha = alphas[idx];
+        ms_ctx.beta  = betas[idx];
+        ms_ctx.gamma = gammas[idx];
+
+        if (!wang) {
+            /* MS-SSIM* (Rouse/Hemami) */
+            s_args.alpha = 1.0f;
+            s_args.beta  = 1.0f;
+            s_args.gamma = 1.0f;
+            s_args.K1 = 0.0f; /* Force stabilization constants to 0 */
+            s_args.K2 = 0.0f;
+            s_args.L  = 255;
+            s_args.f  = 1; /* Don't resize */
+            mr.context = &ms_ctx;
+            msssim *= _iqa_ssim(ref_imgs[idx], cmp_imgs[idx], cur_w, cur_h, &window, &mr, &s_args);
+        }
+        else {
+            /* MS-SSIM (Wang) */
+            s_args.alpha = 1.0f;
+            s_args.beta  = 1.0f;
+            s_args.gamma = 1.0f;
+            s_args.K1 = 0.01f;
+            s_args.K2 = 0.03f;
+            s_args.L  = 255;
+            s_args.f  = 1; /* Don't resize */
+            mr.context = &ms_ctx;
+            msssim *= _iqa_ssim(ref_imgs[idx], cmp_imgs[idx], cur_w, cur_h, &window, &mr, &s_args);
+        }
+
+        if (msssim == INFINITY)
+            break;
+        cur_w = cur_w/2 + (cur_w&1);
+        cur_h = cur_h/2 + (cur_h&1);
+    }
+
+    _free_buffers(ref_imgs, scales);
+    _free_buffers(cmp_imgs, scales);
+    free(ref_imgs);
+    free(cmp_imgs);
+
+    return msssim;
+}

+ 50 - 0
3rdparty/iqa/source/mse.c

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "iqa.h"
+
+/* MSE(a,b) = 1/N * SUM((a-b)^2) */
+float iqa_mse(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride)
+{
+    int error, offset;
+    unsigned long long sum=0;
+    int ww,hh;
+    for (hh=0; hh<h; ++hh) {
+        offset = hh*stride;
+        for (ww=0; ww<w; ++ww, ++offset) {
+            error = ref[offset] - cmp[offset];
+            sum += error * error;
+        }
+    }
+    return (float)( (double)sum / (double)(w*h) );
+}

+ 42 - 0
3rdparty/iqa/source/psnr.c

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "iqa.h"
+#include <math.h>
+
+/* PSNR(a,b) = 10*log10(L^2 / MSE(a,b)), where L=2^b - 1 (8bit = 255) */
+float iqa_psnr(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride)
+{
+    const int L_sqd = 255 * 255;
+    return (float)( 10.0 * log10( L_sqd / iqa_mse(ref,cmp,w,h,stride) ) );
+}

+ 322 - 0
3rdparty/iqa/source/ssim.c

@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2011, Tom Distler (http://tdistler.com)
+ * All rights reserved.
+ *
+ * The BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the tdistler.com nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "iqa.h"
+#include "convolve.h"
+#include "decimate.h"
+#include "math_utils.h"
+#include "ssim.h"
+#include <stdlib.h>
+#include <math.h>
+
+
+/* Forward declarations. */
+IQA_INLINE static double _calc_luminance(float, float, float, float);
+IQA_INLINE static double _calc_contrast(double, float, float, float, float);
+IQA_INLINE static double _calc_structure(float, double, float, float, float, float);
+static int _ssim_map(const struct _ssim_int *, void *);
+static float _ssim_reduce(int, int, void *);
+
+/* 
+ * SSIM(x,y)=(2*ux*uy + C1)*(2sxy + C2) / (ux^2 + uy^2 + C1)*(sx^2 + sy^2 + C2)
+ * where,
+ *  ux = SUM(w*x)
+ *  sx = (SUM(w*(x-ux)^2)^0.5
+ *  sxy = SUM(w*(x-ux)*(y-uy))
+ *
+ * Returns mean SSIM. MSSIM(X,Y) = 1/M * SUM(SSIM(x,y))
+ */
+float iqa_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride,
+    int gaussian, const struct iqa_ssim_args *args)
+{
+    int scale;
+    int x,y,src_offset,offset;
+    float *ref_f,*cmp_f;
+    struct _kernel low_pass;
+    struct _kernel window;
+    float result;
+    double ssim_sum=0.0;
+    struct _map_reduce mr;
+
+    /* Initialize algorithm parameters */
+    scale = _max( 1, _round( (float)_min(w,h) / 256.0f ) );
+    if (args) {
+        if(args->f)
+            scale = args->f;
+        mr.map     = _ssim_map;
+        mr.reduce  = _ssim_reduce;
+        mr.context = (void*)&ssim_sum;
+    }
+    window.kernel = (float*)g_square_window;
+    window.w = window.h = SQUARE_LEN;
+    window.normalized = 1;
+    window.bnd_opt = KBND_SYMMETRIC;
+    if (gaussian) {
+        window.kernel = (float*)g_gaussian_window;
+        window.w = window.h = GAUSSIAN_LEN;
+    }
+
+    /* Convert image values to floats. Forcing stride = width. */
+    ref_f = (float*)malloc(w*h*sizeof(float));
+    cmp_f = (float*)malloc(w*h*sizeof(float));
+    if (!ref_f || !cmp_f) {
+        if (ref_f) free(ref_f);
+        if (cmp_f) free(cmp_f);
+        return INFINITY;
+    }
+    for (y=0; y<h; ++y) {
+        src_offset = y*stride;
+        offset = y*w;
+        for (x=0; x<w; ++x, ++offset, ++src_offset) {
+            ref_f[offset] = (float)ref[src_offset];
+            cmp_f[offset] = (float)cmp[src_offset];
+        }
+    }
+
+    /* Scale the images down if required */
+    if (scale > 1) {
+        /* Generate simple low-pass filter */
+        low_pass.kernel = (float*)malloc(scale*scale*sizeof(float));
+        if (!low_pass.kernel) {
+            free(ref_f);
+            free(cmp_f);
+            return INFINITY;
+        }
+        low_pass.w = low_pass.h = scale;
+        low_pass.normalized = 0;
+        low_pass.bnd_opt = KBND_SYMMETRIC;
+        for (offset=0; offset<scale*scale; ++offset)
+            low_pass.kernel[offset] = 1.0f/(scale*scale);
+
+        /* Resample */
+        if (_iqa_decimate(ref_f, w, h, scale, &low_pass, 0, 0, 0) ||
+            _iqa_decimate(cmp_f, w, h, scale, &low_pass, 0, &w, &h)) { /* Update w/h */
+            free(ref_f);
+            free(cmp_f);
+            free(low_pass.kernel);
+            return INFINITY;
+        }
+        free(low_pass.kernel);
+    }
+
+    result = _iqa_ssim(ref_f, cmp_f, w, h, &window, &mr, args);
+    
+    free(ref_f);
+    free(cmp_f);
+
+    return result;
+}
+
+
+/* _iqa_ssim */
+float _iqa_ssim(float *ref, float *cmp, int w, int h, const struct _kernel *k, const struct _map_reduce *mr, const struct iqa_ssim_args *args)
+{
+    float alpha=1.0f, beta=1.0f, gamma=1.0f;
+    int L=255;
+    float K1=0.01f, K2=0.03f;
+    float C1,C2,C3;
+    int x,y,offset;
+    float *ref_mu,*cmp_mu,*ref_sigma_sqd,*cmp_sigma_sqd,*sigma_both;
+    double ssim_sum, numerator, denominator;
+    double luminance_comp, contrast_comp, structure_comp, sigma_root;
+    struct _ssim_int sint;
+
+    /* Initialize algorithm parameters */
+    if (args) {
+        if (!mr)
+            return INFINITY;
+        alpha = args->alpha;
+        beta  = args->beta;
+        gamma = args->gamma;
+        L     = args->L;
+        K1    = args->K1;
+        K2    = args->K2;
+    }
+    C1 = (K1*L)*(K1*L);
+    C2 = (K2*L)*(K2*L);
+    C3 = C2 / 2.0f;
+
+    ref_mu = (float*)malloc(w*h*sizeof(float));
+    cmp_mu = (float*)malloc(w*h*sizeof(float));
+    ref_sigma_sqd = (float*)malloc(w*h*sizeof(float));
+    cmp_sigma_sqd = (float*)malloc(w*h*sizeof(float));
+    sigma_both = (float*)malloc(w*h*sizeof(float));
+    if (!ref_mu || !cmp_mu || !ref_sigma_sqd || !cmp_sigma_sqd || !sigma_both) {
+        if (ref_mu) free(ref_mu);
+        if (cmp_mu) free(cmp_mu);
+        if (ref_sigma_sqd) free(ref_sigma_sqd);
+        if (cmp_sigma_sqd) free(cmp_sigma_sqd);
+        if (sigma_both) free(sigma_both);
+        return INFINITY;
+    }
+
+    /* Calculate mean */
+    _iqa_convolve(ref, w, h, k, ref_mu, 0, 0);
+    _iqa_convolve(cmp, w, h, k, cmp_mu, 0, 0);
+
+    for (y=0; y<h; ++y) {
+        offset = y*w;
+        for (x=0; x<w; ++x, ++offset) {
+            ref_sigma_sqd[offset] = ref[offset] * ref[offset];
+            cmp_sigma_sqd[offset] = cmp[offset] * cmp[offset];
+            sigma_both[offset] = ref[offset] * cmp[offset];
+        }
+    }
+
+    /* Calculate sigma */
+    _iqa_convolve(ref_sigma_sqd, w, h, k, 0, 0, 0);
+    _iqa_convolve(cmp_sigma_sqd, w, h, k, 0, 0, 0);
+    _iqa_convolve(sigma_both, w, h, k, 0, &w, &h); /* Update the width and height */
+
+    /* The convolution results are smaller by the kernel width and height */
+    for (y=0; y<h; ++y) {
+        offset = y*w;
+        for (x=0; x<w; ++x, ++offset) {
+            ref_sigma_sqd[offset] -= ref_mu[offset] * ref_mu[offset];
+            cmp_sigma_sqd[offset] -= cmp_mu[offset] * cmp_mu[offset];
+            sigma_both[offset] -= ref_mu[offset] * cmp_mu[offset];
+        }
+    }
+
+    ssim_sum = 0.0;
+    for (y=0; y<h; ++y) {
+        offset = y*w;
+        for (x=0; x<w; ++x, ++offset) {
+
+            if (!args) {
+                /* The default case */
+                numerator   = (2.0 * ref_mu[offset] * cmp_mu[offset] + C1) * (2.0 * sigma_both[offset] + C2);
+                denominator = (ref_mu[offset]*ref_mu[offset] + cmp_mu[offset]*cmp_mu[offset] + C1) * 
+                    (ref_sigma_sqd[offset] + cmp_sigma_sqd[offset] + C2);
+                ssim_sum += numerator / denominator;
+            }
+            else {
+                /* User tweaked alpha, beta, or gamma */
+
+                /* passing a negative number to sqrt() cause a domain error */
+                if (ref_sigma_sqd[offset] < 0.0f)
+                    ref_sigma_sqd[offset] = 0.0f;
+                if (cmp_sigma_sqd[offset] < 0.0f)
+                    cmp_sigma_sqd[offset] = 0.0f;
+                sigma_root = sqrt(ref_sigma_sqd[offset] * cmp_sigma_sqd[offset]);
+
+                luminance_comp = _calc_luminance(ref_mu[offset], cmp_mu[offset], C1, alpha);
+                contrast_comp  = _calc_contrast(sigma_root, ref_sigma_sqd[offset], cmp_sigma_sqd[offset], C2, beta);
+                structure_comp = _calc_structure(sigma_both[offset], sigma_root, ref_sigma_sqd[offset], cmp_sigma_sqd[offset], C3, gamma);
+
+                sint.l = luminance_comp;
+                sint.c = contrast_comp;
+                sint.s = structure_comp;
+
+                if (mr->map(&sint, mr->context))
+                    return INFINITY;
+            }
+        }
+    }
+
+    free(ref_mu);
+    free(cmp_mu);
+    free(ref_sigma_sqd);
+    free(cmp_sigma_sqd);
+    free(sigma_both);
+
+    if (!args)
+        return (float)(ssim_sum / (double)(w*h));
+    return mr->reduce(w, h, mr->context);
+}
+
+
+/* _ssim_map */
+int _ssim_map(const struct _ssim_int *si, void *ctx)
+{
+    double *ssim_sum = (double*)ctx;
+    *ssim_sum += si->l * si->c * si->s;
+    return 0;
+}
+
+/* _ssim_reduce */
+float _ssim_reduce(int w, int h, void *ctx)
+{
+    double *ssim_sum = (double*)ctx;
+    return (float)(*ssim_sum / (double)(w*h));
+}
+
+
+/* _calc_luminance */
+IQA_INLINE static double _calc_luminance(float mu1, float mu2, float C1, float alpha)
+{
+    double result;
+    float sign;
+    /* For MS-SSIM* */
+    if (C1 == 0 && mu1*mu1 == 0 && mu2*mu2 == 0)
+        return 1.0;
+    result = (2.0 * mu1 * mu2 + C1) / (mu1*mu1 + mu2*mu2 + C1);
+    if (alpha == 1.0f)
+        return result;
+    sign = result < 0.0 ? -1.0f : 1.0f;
+    return sign * pow(fabs(result),(double)alpha);
+}
+
+/* _calc_contrast */
+IQA_INLINE static double _calc_contrast(double sigma_comb_12, float sigma1_sqd, float sigma2_sqd, float C2, float beta)
+{
+    double result;
+    float sign;
+    /* For MS-SSIM* */
+    if (C2 == 0 && sigma1_sqd + sigma2_sqd == 0)
+        return 1.0;
+    result = (2.0 * sigma_comb_12 + C2) / (sigma1_sqd + sigma2_sqd + C2);
+    if (beta == 1.0f)
+        return result;
+    sign = result < 0.0 ? -1.0f : 1.0f;
+    return sign * pow(fabs(result),(double)beta);
+}
+
+/* _calc_structure */
+IQA_INLINE static double _calc_structure(float sigma_12, double sigma_comb_12, float sigma1, float sigma2, float C3, float gamma)
+{
+    double result;
+    float sign;
+    /* For MS-SSIM* */
+    if (C3 == 0 && sigma_comb_12 == 0) {
+        if (sigma1 == 0 && sigma2 == 0)
+            return 1.0;
+        else if (sigma1 == 0 || sigma2 == 0)
+            return 0.0;
+    }
+    result = (sigma_12 + C3) / (sigma_comb_12 + C3);
+    if (gamma == 1.0f)
+        return result;
+    sign = result < 0.0 ? -1.0f : 1.0f;
+    return sign * pow(fabs(result),(double)gamma);
+}

+ 3 - 0
scripts/texturec.lua

@@ -13,6 +13,7 @@ project "texturec"
 		path.join(BGFX_DIR, "src"),
 		path.join(BGFX_DIR, "3rdparty"),
 		path.join(BGFX_DIR, "3rdparty/nvtt"),
+		path.join(BGFX_DIR, "3rdparty/iqa/include"),
 	}
 
 	files {
@@ -31,6 +32,8 @@ project "texturec"
 		path.join(BGFX_DIR, "3rdparty/pvrtc/**.h"),
 		path.join(BGFX_DIR, "3rdparty/tinyexr/**.cc"),
 		path.join(BGFX_DIR, "3rdparty/tinyexr/**.h"),
+		path.join(BGFX_DIR, "3rdparty/iqa/include/**.h"),
+		path.join(BGFX_DIR, "3rdparty/iqa/source/**.c"),
 		path.join(BGFX_DIR, "tools/texturec/**.cpp"),
 		path.join(BGFX_DIR, "tools/texturec/**.h"),
 	}