17bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola/*
27bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
37bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *
47bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  Use of this source code is governed by a BSD-style license
57bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  that can be found in the LICENSE file in the root of the source
67bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  tree. An additional intellectual property rights grant can be found
77bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  in the file PATENTS.  All contributing project authors may
87bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola *  be found in the AUTHORS file in the root of the source tree.
97bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola */
107bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
117bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
127bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#include "vpx_scale/yv12config.h"
137bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#include "math.h"
147bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#include "onyx_int.h"
157bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
167bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#if CONFIG_RUNTIME_CPU_DETECT
177bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#define IF_RTCD(x)  (x)
187bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#else
197bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#define IF_RTCD(x)  NULL
207bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#endif
217bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola// Google version of SSIM
227bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola// SSIM
237bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#define KERNEL 3
247bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola#define KERNEL_SIZE  (2 * KERNEL + 1)
25563321a2582851c653d0863e8e0bba3d483734f9Jim Laskey
26b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindolatypedef unsigned char uint8;
277bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindolatypedef unsigned int uint32;
287bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
297bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindolastatic const int K[KERNEL_SIZE] =
307bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola{
317bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    1, 4, 11, 16, 11, 4, 1    // 16 * exp(-0.3 * i * i)
327bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola};
337bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindolastatic const double ki_w = 1. / 2304.;  // 1 / sum(i:0..6, j..6) K[i]*K[j]
347bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindoladouble get_ssimg(const uint8 *org, const uint8 *rec,
357bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola                 int xo, int yo, int W, int H,
367bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola                 const int stride1, const int stride2
37ac0b6ae358944ae8b2b5a11dc08f52c3ed89f2daChris Lattner                )
387bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola{
39e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    // TODO(skal): use summed tables
40e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    int y, x;
41e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola
42e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
43e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
44e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
45e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
46e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
47e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    // with a diff of 255, squares. That would a max error of 0x8ee0900,
48e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    // which fits into 32 bits integers.
49e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    uint32 w = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
50e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    org += ymin * stride1;
51e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    rec += ymin * stride2;
52e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola
53e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    for (y = ymin; y <= ymax; ++y, org += stride1, rec += stride2)
54e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola    {
55e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola        const int Wy = K[KERNEL + y - yo];
56e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola
57e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola        for (x = xmin; x <= xmax; ++x)
58e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola        {
59e931a37a4eb3e46d73ab0379dd84173dca1214f2Rafael Espindola            const  int Wxy = Wy * K[KERNEL + x - xo];
60563321a2582851c653d0863e8e0bba3d483734f9Jim Laskey            // TODO(skal): inlined assembly
61a0f3d17daac73c9c71aad497b298cbe82848f726Jim Laskey            w   += Wxy;
62563321a2582851c653d0863e8e0bba3d483734f9Jim Laskey            xm  += Wxy * org[x];
63563321a2582851c653d0863e8e0bba3d483734f9Jim Laskey            ym  += Wxy * rec[x];
647bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            xxm += Wxy * org[x] * org[x];
65392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola            xym += Wxy * org[x] * rec[x];
66392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola            yym += Wxy * rec[x] * rec[x];
677bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        }
687bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    }
697bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
707bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    {
717bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        const double iw = 1. / w;
727bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        const double iwx = xm * iw;
737bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        const double iwy = ym * iw;
747bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        double sxx = xxm * iw - iwx * iwx;
757bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        double syy = yym * iw - iwy * iwy;
767bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
777bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        // small errors are possible, due to rounding. Clamp to zero.
787cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola        if (sxx < 0.) sxx = 0.;
796e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola
8032bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola        if (syy < 0.) syy = 0.;
817bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
827bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        {
837bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            const double sxsy = sqrt(sxx * syy);
847bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            const double sxy = xym * iw - iwx * iwy;
857bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            static const double C11 = (0.01 * 0.01) * (255 * 255);
867bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            static const double C22 = (0.03 * 0.03) * (255 * 255);
877bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            static const double C33 = (0.015 * 0.015) * (255 * 255);
887bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
897bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
907bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
917bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            const double s = (sxy + C33) / (sxsy + C33);
927bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            return l * c * s;
937bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
947bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        }
957bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    }
967bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
977bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola}
987bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola
997bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindoladouble get_ssimfull_kernelg(const uint8 *org, const uint8 *rec,
1007bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola                            int xo, int yo, int W, int H,
1017bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola                            const int stride1, const int stride2)
102a0f3d17daac73c9c71aad497b298cbe82848f726Jim Laskey{
1037bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    // TODO(skal): use summed tables
1047bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
1057bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    // with a diff of 255, squares. That would a max error of 0x8ee0900,
1067bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    // which fits into 32 bits integers.
1077bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    int y_, x_;
1087bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    uint32 xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
1094b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    org += (yo - KERNEL) * stride1;
1104b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    org += (xo - KERNEL);
1114b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    rec += (yo - KERNEL) * stride2;
1124b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    rec += (xo - KERNEL);
1134b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola
1144b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    for (y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride1, rec += stride2)
1154b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    {
1161da31ee472b9615d7329c656e2cc17c419ed7c95Chris Lattner        const int Wy = K[y_];
1174b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola
1184b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        for (x_ = 0; x_ < KERNEL_SIZE; ++x_)
1194b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        {
1206f6f69950f5a36d3ae7e4d1d5b96fda204beb79aChris Lattner            const int Wxy = Wy * K[x_];
1216f6f69950f5a36d3ae7e4d1d5b96fda204beb79aChris Lattner            // TODO(skal): inlined assembly
1224b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const int org_x = org[x_];
1234b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const int rec_x = rec[x_];
1244b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            xm  += Wxy * org_x;
1254b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            ym  += Wxy * rec_x;
1264b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            xxm += Wxy * org_x * org_x;
1274b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            xym += Wxy * org_x * rec_x;
1284b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            yym += Wxy * rec_x * rec_x;
1294b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        }
1304b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    }
131392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola
1324b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola    {
1334b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        const double iw = ki_w;
134a1334cdfb2afb44a1f2b952391e1b2fecb1d4bd8Rafael Espindola        const double iwx = xm * iw;
1354b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        const double iwy = ym * iw;
1364b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        double sxx = xxm * iw - iwx * iwx;
1374b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        double syy = yym * iw - iwy * iwy;
1384b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola
1394b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        // small errors are possible, due to rounding. Clamp to zero.
1404b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        if (sxx < 0.) sxx = 0.;
1414b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola
1424b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        if (syy < 0.) syy = 0.;
1434b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola
1444b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola        {
1454b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const double sxsy = sqrt(sxx * syy);
1464b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const double sxy = xym * iw - iwx * iwy;
1474b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            static const double C11 = (0.01 * 0.01) * (255 * 255);
1484b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            static const double C22 = (0.03 * 0.03) * (255 * 255);
1494b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            static const double C33 = (0.015 * 0.015) * (255 * 255);
1504b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
1514b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
1524b442b528a50ef06cd75f0e7c41ad57426175bccRafael Espindola            const double s = (sxy + C33) / (sxsy + C33);
1537bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola            return l * c * s;
1547bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola        }
1557bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    }
1567cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola}
1573ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola
1583ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindoladouble calc_ssimg(const uint8 *org, const uint8 *rec,
1593ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola                  const int image_width, const int image_height,
1607cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola                  const int stride1, const int stride2
1613ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola                 )
1623ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola{
1637cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola    int j, i;
1647cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola    double SSIM = 0.;
1653ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola
1667cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola    for (j = 0; j < KERNEL; ++j)
1673ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola    {
1683ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        for (i = 0; i < image_width; ++i)
1693ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        {
1703ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
1713ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        }
1723ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola    }
1733ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola
1743ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola    for (j = KERNEL; j < image_height - KERNEL; ++j)
1753ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola    {
1763ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        for (i = 0; i < KERNEL; ++i)
1773ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        {
1783ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
1793ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        }
1803ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola
1813ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        for (i = KERNEL; i < image_width - KERNEL; ++i)
1823ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        {
1833ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola            SSIM += get_ssimfull_kernelg(org, rec, i, j,
1843ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola                                         image_width, image_height, stride1, stride2);
1853ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        }
1863ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola
1873ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        for (i = image_width - KERNEL; i < image_width; ++i)
1883ad5e5cf998841681e9d11e08eb82a94ddffd1f8Rafael Espindola        {
1897cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
1907cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola        }
1917cca7c531773f763c1bddc3fefecc99ba56ed10aRafael Espindola    }
1926e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola
1936e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    for (j = image_height - KERNEL; j < image_height; ++j)
1946e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    {
1956e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola        for (i = 0; i < image_width; ++i)
1966e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola        {
1976e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
1986e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola        }
1996e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    }
2006e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola
2016e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    return SSIM;
2026e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola}
2036e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola
2046e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola
2056e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindoladouble vp8_calc_ssimg
2066e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola(
2076e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    YV12_BUFFER_CONFIG *source,
2086e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    YV12_BUFFER_CONFIG *dest,
2096e8c6493f0db238d06549368bd647e29ff3c7821Rafael Espindola    double *ssim_y,
21032bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    double *ssim_u,
21132bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    double *ssim_v
21232bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola)
21332bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola{
21432bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    double ssim_all = 0;
21532bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    int ysize  = source->y_width * source->y_height;
21632bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    int uvsize = ysize / 4;
21732bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola
21832bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    *ssim_y = calc_ssimg(source->y_buffer, dest->y_buffer,
21932bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola                         source->y_width, source->y_height,
22032bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola                         source->y_stride, dest->y_stride);
22132bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola
22232bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola
22332bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola    *ssim_u = calc_ssimg(source->u_buffer, dest->u_buffer,
22432bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola                         source->uv_width, source->uv_height,
22532bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola                         source->uv_stride, dest->uv_stride);
22632bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola
22732bd5f4f6a374f9ab0fcbd2cf6a8561019a6fd56Rafael Espindola
2287bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    *ssim_v = calc_ssimg(source->v_buffer, dest->v_buffer,
2292f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola                         source->uv_width, source->uv_height,
2302f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola                         source->uv_stride, dest->uv_stride);
2312f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola
2322f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    ssim_all = (*ssim_y + *ssim_u + *ssim_v) / (ysize + uvsize + uvsize);
2332f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    *ssim_y /= ysize;
2342f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    *ssim_u /= uvsize;
2352f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    *ssim_v /= uvsize;
2362f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    return ssim_all;
2372f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola}
2382f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola
2392f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola
2402f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindolavoid ssim_parms_c
2412f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola(
242687bc49d1ada3fe0a2cd3fb5c044f12d267f259fRafael Espindola    unsigned char *s,
2432f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    int sp,
24484b19be6ab9544f72eafb11048a1121f5ea77c95Rafael Espindola    unsigned char *r,
24584b19be6ab9544f72eafb11048a1121f5ea77c95Rafael Espindola    int rp,
24684b19be6ab9544f72eafb11048a1121f5ea77c95Rafael Espindola    unsigned long *sum_s,
24784b19be6ab9544f72eafb11048a1121f5ea77c95Rafael Espindola    unsigned long *sum_r,
248392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    unsigned long *sum_sq_s,
249392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    unsigned long *sum_sq_r,
250392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    unsigned long *sum_sxr
25184b19be6ab9544f72eafb11048a1121f5ea77c95Rafael Espindola)
2522f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola{
2532f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola    int i,j;
2540505be03adf561afbd8307516125da10dba8f0c4Rafael Espindola    for(i=0;i<16;i++,s+=sp,r+=rp)
2552f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola     {
2562f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola         for(j=0;j<16;j++)
257563321a2582851c653d0863e8e0bba3d483734f9Jim Laskey         {
25806c1e7eacb11edd1671eabfc11291b7716be2608Rafael Espindola             *sum_s += s[j];
2592f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola             *sum_r += r[j];
2602f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola             *sum_sq_s += s[j] * s[j];
2612f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola             *sum_sq_r += r[j] * r[j];
2622f99b6bd9601ae8d4fd248f9bb701283795c38a8Rafael Espindola             *sum_sxr += s[j] * r[j];
2637bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola         }
2647bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola     }
2657bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola}
2667bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindolavoid ssim_parms_8x8_c
2677bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola(
2687bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    unsigned char *s,
2697bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    int sp,
2707bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    unsigned char *r,
2716f602de3b68cc63d12554ad6ae3c98a4c436c32dRafael Espindola    int rp,
2726f602de3b68cc63d12554ad6ae3c98a4c436c32dRafael Espindola    unsigned long *sum_s,
2737bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    unsigned long *sum_r,
2747bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    unsigned long *sum_sq_s,
2757bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    unsigned long *sum_sq_r,
276ff59d22232f47f138ed3d753975153befd1aa0c0Rafael Espindola    unsigned long *sum_sxr
2777bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola)
2787bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola{
2797bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    int i,j;
2807bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola    for(i=0;i<8;i++,s+=sp,r+=rp)
281b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola     {
282b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola         for(j=0;j<8;j++)
283b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola         {
284b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola             *sum_s += s[j];
285b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola             *sum_r += r[j];
286b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola             *sum_sq_s += s[j] * s[j];
287b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola             *sum_sq_r += r[j] * r[j];
288b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola             *sum_sxr += s[j] * r[j];
289b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola         }
290b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola     }
291b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola}
292b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola
293b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindolaconst static long long c1 =  426148; // (256^2*(.01*255)^2
294b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindolaconst static long long c2 = 3835331; //(256^2*(.03*255)^2
2951c411dee4f406e33d70666898f62ab9ba23bb73dRafael Espindola
296b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindolastatic double similarity
297b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola(
2987367d05cb713d11f8ac1e0815ac6b2eb6b17088cRafael Espindola    unsigned long sum_s,
299b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_r,
300b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_sq_s,
301b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_sq_r,
302b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_sxr,
303b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    int count
304b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola)
3051c411dee4f406e33d70666898f62ab9ba23bb73dRafael Espindola{
3061c411dee4f406e33d70666898f62ab9ba23bb73dRafael Espindola    long long ssim_n = (2*sum_s*sum_r+ c1)*(2*count*sum_sxr-2*sum_s*sum_r+c2);
307b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola
308b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    long long ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
309b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola            (count*sum_sq_s-sum_s*sum_s + count*sum_sq_r-sum_r*sum_r +c2) ;
310b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola
311b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    return ssim_n * 1.0 / ssim_d;
312b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola}
313b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola
314b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindolastatic double ssim_16x16(unsigned char *s,int sp, unsigned char *r,int rp,
315b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola            const vp8_variance_rtcd_vtable_t *rtcd)
316b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola{
317b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
318b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    rtcd->ssimpf(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
319b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola    return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 256);
3201366626e08322aaecd60704d02a5d881b0826725Rafael Espindola}
3211366626e08322aaecd60704d02a5d881b0826725Rafael Espindolastatic double ssim_8x8(unsigned char *s,int sp, unsigned char *r,int rp,
3221366626e08322aaecd60704d02a5d881b0826725Rafael Espindola                const vp8_variance_rtcd_vtable_t *rtcd)
3231366626e08322aaecd60704d02a5d881b0826725Rafael Espindola{
3241366626e08322aaecd60704d02a5d881b0826725Rafael Espindola    unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
3251366626e08322aaecd60704d02a5d881b0826725Rafael Espindola    rtcd->ssimpf_8x8(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
3261366626e08322aaecd60704d02a5d881b0826725Rafael Espindola    return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64);
3271366626e08322aaecd60704d02a5d881b0826725Rafael Espindola}
3281366626e08322aaecd60704d02a5d881b0826725Rafael Espindola
3291366626e08322aaecd60704d02a5d881b0826725Rafael Espindola// TODO: (jbb) tried to scale this function such that we may be able to use it
3301366626e08322aaecd60704d02a5d881b0826725Rafael Espindola// for distortion metric in mode selection code ( provided we do a reconstruction)
331b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindolalong dssim(unsigned char *s,int sp, unsigned char *r,int rp,
332b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola           const vp8_variance_rtcd_vtable_t *rtcd)
333b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola{
334b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
335b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    double ssim3;
336b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    long long ssim_n;
337b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    long long ssim_d;
338b01c4bbb4573e0007444e218b683840e4519e0c8Rafael Espindola
339b97809c9a7a4ef681070ab1cbc7bd4fb18d34ba1Rafael Espindola    rtcd->ssimpf(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
340392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    ssim_n = (2*sum_s*sum_r+ c1)*(2*256*sum_sxr-2*sum_s*sum_r+c2);
341392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola
342392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
343392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola            (256*sum_sq_s-sum_s*sum_s + 256*sum_sq_r-sum_r*sum_r +c2) ;
344392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola
345392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    ssim3 = 256 * (ssim_d-ssim_n) / ssim_d;
346392b1b2ef3ace82b5104ba4c9280fc7957c669d4Rafael Espindola    return (long)( 256*ssim3 * ssim3 );
3477bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola}
3487bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola// TODO: (jbb) this 8x8 window might be too big + we may want to pick pixels
3497bc59bc3952ad7842b1e079753deb32217a768a3Rafael Espindola// such that the window regions overlap block boundaries to penalize blocking
350// artifacts.
351
352double vp8_ssim2
353(
354    unsigned char *img1,
355    unsigned char *img2,
356    int stride_img1,
357    int stride_img2,
358    int width,
359    int height,
360    const vp8_variance_rtcd_vtable_t *rtcd
361)
362{
363    int i,j;
364
365    double ssim_total=0;
366
367    // we can sample points as frequently as we like start with 1 per 8x8
368    for(i=0; i < height; i+=8, img1 += stride_img1*8, img2 += stride_img2*8)
369    {
370        for(j=0; j < width; j+=8 )
371        {
372            ssim_total += ssim_8x8(img1, stride_img1, img2, stride_img2, rtcd);
373        }
374    }
375    ssim_total /= (width/8 * height /8);
376    return ssim_total;
377
378}
379double vp8_calc_ssim
380(
381    YV12_BUFFER_CONFIG *source,
382    YV12_BUFFER_CONFIG *dest,
383    int lumamask,
384    double *weight,
385    const vp8_variance_rtcd_vtable_t *rtcd
386)
387{
388    double a, b, c;
389    double ssimv;
390
391    a = vp8_ssim2(source->y_buffer, dest->y_buffer,
392                 source->y_stride, dest->y_stride, source->y_width,
393                 source->y_height, rtcd);
394
395    b = vp8_ssim2(source->u_buffer, dest->u_buffer,
396                 source->uv_stride, dest->uv_stride, source->uv_width,
397                 source->uv_height, rtcd);
398
399    c = vp8_ssim2(source->v_buffer, dest->v_buffer,
400                 source->uv_stride, dest->uv_stride, source->uv_width,
401                 source->uv_height, rtcd);
402
403    ssimv = a * .8 + .1 * (b + c);
404
405    *weight = 1;
406
407    return ssimv;
408}
409