From f3b9fed29aaa027053f541749e130f2cccadcf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Thu, 7 Apr 2016 20:05:20 -0700 Subject: [PATCH] Added Image Quality Assessment. --- 3rdparty/iqa/LICENSE | 32 +++ 3rdparty/iqa/README.txt | 36 ++++ 3rdparty/iqa/include/convolve.h | 111 ++++++++++ 3rdparty/iqa/include/decimate.h | 55 +++++ 3rdparty/iqa/include/iqa.h | 134 +++++++++++++ 3rdparty/iqa/include/iqa_os.h | 66 ++++++ 3rdparty/iqa/include/math_utils.h | 64 ++++++ 3rdparty/iqa/include/ssim.h | 117 +++++++++++ 3rdparty/iqa/source/convolve.c | 195 ++++++++++++++++++ 3rdparty/iqa/source/decimate.c | 59 ++++++ 3rdparty/iqa/source/math_utils.c | 82 ++++++++ 3rdparty/iqa/source/ms_ssim.c | 277 +++++++++++++++++++++++++ 3rdparty/iqa/source/mse.c | 50 +++++ 3rdparty/iqa/source/psnr.c | 42 ++++ 3rdparty/iqa/source/ssim.c | 322 ++++++++++++++++++++++++++++++ scripts/texturec.lua | 3 + 16 files changed, 1645 insertions(+) create mode 100644 3rdparty/iqa/LICENSE create mode 100644 3rdparty/iqa/README.txt create mode 100644 3rdparty/iqa/include/convolve.h create mode 100644 3rdparty/iqa/include/decimate.h create mode 100644 3rdparty/iqa/include/iqa.h create mode 100644 3rdparty/iqa/include/iqa_os.h create mode 100644 3rdparty/iqa/include/math_utils.h create mode 100644 3rdparty/iqa/include/ssim.h create mode 100644 3rdparty/iqa/source/convolve.c create mode 100644 3rdparty/iqa/source/decimate.c create mode 100644 3rdparty/iqa/source/math_utils.c create mode 100644 3rdparty/iqa/source/ms_ssim.c create mode 100644 3rdparty/iqa/source/mse.c create mode 100644 3rdparty/iqa/source/psnr.c create mode 100644 3rdparty/iqa/source/ssim.c diff --git a/3rdparty/iqa/LICENSE b/3rdparty/iqa/LICENSE new file mode 100644 index 000000000..ff67944bf --- /dev/null +++ b/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. + */ diff --git a/3rdparty/iqa/README.txt b/3rdparty/iqa/README.txt new file mode 100644 index 000000000..2028d4692 --- /dev/null +++ b/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/, where 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/ 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 + diff --git a/3rdparty/iqa/include/convolve.h b/3rdparty/iqa/include/convolve.h new file mode 100644 index 000000000..a5e2e71a2 --- /dev/null +++ b/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_*/ diff --git a/3rdparty/iqa/include/decimate.h b/3rdparty/iqa/include/decimate.h new file mode 100644 index 000000000..40f1a8c04 --- /dev/null +++ b/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_*/ diff --git a/3rdparty/iqa/include/iqa.h b/3rdparty/iqa/include/iqa.h new file mode 100644 index 000000000..408675e5a --- /dev/null +++ b/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_*/ diff --git a/3rdparty/iqa/include/iqa_os.h b/3rdparty/iqa/include/iqa_os.h new file mode 100644 index 000000000..12aecfbac --- /dev/null +++ b/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 +#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_ */ diff --git a/3rdparty/iqa/include/math_utils.h b/3rdparty/iqa/include/math_utils.h new file mode 100644 index 000000000..0445caa42 --- /dev/null +++ b/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 + +/** + * 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_*/ diff --git a/3rdparty/iqa/include/ssim.h b/3rdparty/iqa/include/ssim.h new file mode 100644 index 000000000..5653afe80 --- /dev/null +++ b/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_ */ diff --git a/3rdparty/iqa/source/convolve.c b/3rdparty/iqa/source/convolve.c new file mode 100644 index 000000000..c9159070f --- /dev/null +++ b/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 + +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; iikernel[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; yw/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); +} diff --git a/3rdparty/iqa/source/decimate.c b/3rdparty/iqa/source/decimate.c new file mode 100644 index 000000000..91c6a9be3 --- /dev/null +++ b/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 + +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 + +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 +#include +#include + +/* 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; idxM]( 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 + +/* 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) ) ); +} diff --git a/3rdparty/iqa/source/ssim.c b/3rdparty/iqa/source/ssim.c new file mode 100644 index 000000000..d1acccb40 --- /dev/null +++ b/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 +#include + + +/* 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 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; offsetalpha; + 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; ymap(&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); +} \ No newline at end of file diff --git a/scripts/texturec.lua b/scripts/texturec.lua index dac357b6b..28e9bcc8b 100644 --- a/scripts/texturec.lua +++ b/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"), }