/* * Copyright 2013 The LibYuv Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree.
*/
// We need this union trick to be able to initialize constant static __m128i // values. We can't call _mm_set_epi16() for static compile-time initialization. staticconststruct { union {
uint16_t i16_[8];
__m128i m_;
} values_;
} W0 = MAKE_WEIGHT(0), W1 = MAKE_WEIGHT(1), W2 = MAKE_WEIGHT(2),
W3 = MAKE_WEIGHT(3); // ... the rest is symmetric. #undef MAKE_WEIGHT #undef PWEIGHT #endif
// Common final expression for SSIM, once the weighted sums are known. staticdouble FinalizeSSIM(double iw, double xm, double ym, double xxm, double xym, double yym) { constdouble iwx = xm * iw; constdouble iwy = ym * iw; double sxx = xxm * iw - iwx * iwx; double syy = yym * iw - iwy * iwy; // small errors are possible, due to rounding. Clamp to zero. if (sxx < 0.) {
sxx = 0.;
} if (syy < 0.) {
syy = 0.;
} constdouble sxsy = sqrt(sxx * syy); constdouble sxy = xym * iw - iwx * iwy; staticconstdouble C11 = (0.01 * 0.01) * (255 * 255); staticconstdouble C22 = (0.03 * 0.03) * (255 * 255); staticconstdouble C33 = (0.015 * 0.015) * (255 * 255); constdouble l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11); constdouble c = (2. * sxsy + C22) / (sxx + syy + C22); constdouble s = (sxy + C33) / (sxsy + C33); return l * c * s;
}
// GetSSIM() does clipping. GetSSIMFullKernel() does not
// TODO(skal): use summed tables? // Note: worst case of accumulation is a weight of 33 = 11 + 2 * (7 + 3 + 1) // with a diff of 255, squared. The maximum error is thus 0x4388241, // which fits into 32 bits integers. double GetSSIM(const uint8_t* org, const uint8_t* rec, int xo, int yo, int W, int H, int stride) {
uint32_t ws = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
org += (yo - KERNEL) * stride;
org += (xo - KERNEL);
rec += (yo - KERNEL) * stride;
rec += (xo - KERNEL); for (int y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride, rec += stride) { if (((yo - KERNEL + y_) < 0) || ((yo - KERNEL + y_) >= H)) { continue;
} constint Wy = K[y_]; for (int x_ = 0; x_ < KERNEL_SIZE; ++x_) { constint Wxy = Wy * K[x_]; if (((xo - KERNEL + x_) >= 0) && ((xo - KERNEL + x_) < W)) { constint org_x = org[x_]; constint rec_x = rec[x_];
ws += Wxy;
xm += Wxy * org_x;
ym += Wxy * rec_x;
xxm += Wxy * org_x * org_x;
xym += Wxy * org_x * rec_x;
yym += Wxy * rec_x * rec_x;
}
}
} return FinalizeSSIM(1. / ws, xm, ym, xxm, xym, yym);
}
double GetSSIMFullKernel(const uint8_t* org, const uint8_t* rec, int xo, int yo, int stride, double area_weight) {
uint32_t xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
for (int j = 0; j < KERNEL_Y; ++j) { for (int i = 0; i < image_width; ++i) {
SSIM += GetSSIM(org, rec, i, j, image_width, image_height, stride);
}
}
#ifdef _OPENMP #pragma omp parallel for reduction(+ : SSIM) #endif for (int j = KERNEL_Y; j < image_height - KERNEL_Y; ++j) { for (int i = 0; i < KERNEL_X; ++i) {
SSIM += GetSSIM(org, rec, i, j, image_width, image_height, stride);
} for (int i = KERNEL_X; i < start_x; ++i) {
SSIM += GetSSIMFullKernel(org, rec, i, j, stride, kiW[0]);
} if (start_x < image_width) { // GetSSIMFullKernel() needs to be able to read 8 pixels (in SSE2). So we // copy the 8 rightmost pixels on a cache area, and pad this area with // zeros which won't contribute to the overall SSIM value (but we need // to pass the correct normalizing constant!). By using this cache, we can // still call GetSSIMFullKernel() instead of the slower GetSSIM(). // NOTE: we could use similar method for the left-most pixels too. constint kScratchWidth = 8; constint kScratchStride = kScratchWidth + KERNEL + 1;
uint8_t scratch_org[KERNEL_SIZE * kScratchStride] = {0};
uint8_t scratch_rec[KERNEL_SIZE * kScratchStride] = {0};
for (int k = 0; k < KERNEL_SIZE; ++k) { constint offset =
(j - KERNEL + k) * stride + image_width - kScratchWidth;
memcpy(scratch_org + k * kScratchStride, org + offset, kScratchWidth);
memcpy(scratch_rec + k * kScratchStride, rec + offset, kScratchWidth);
} for (int k = 0; k <= KERNEL_X + 1; ++k) {
SSIM += GetSSIMFullKernel(scratch_org, scratch_rec, KERNEL + k, KERNEL,
kScratchStride, kiW[k]);
}
}
}
for (int j = start_y; j < image_height; ++j) { for (int i = 0; i < image_width; ++i) {
SSIM += GetSSIM(org, rec, i, j, image_width, image_height, stride);
}
} return SSIM;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.