diff --git a/src/modules/video_coding/main/source/video_coding_test.gyp b/src/modules/video_coding/main/source/video_coding_test.gyp index 89e62eff6..574f19252 100644 --- a/src/modules/video_coding/main/source/video_coding_test.gyp +++ b/src/modules/video_coding/main/source/video_coding_test.gyp @@ -37,6 +37,7 @@ '../test/release_test.h', '../test/rtp_player.h', '../test/test_util.h', + '../test/video_metrics.h', '../test/video_source.h', # sources @@ -52,6 +53,7 @@ '../test/rtp_player.cc', '../test/test_util.cc', '../test/tester_main.cc', + '../test/video_metrics.cc', '../test/video_rtp_play_mt.cc', '../test/video_rtp_play.cc', '../test/video_source.cc', diff --git a/src/modules/video_coding/main/test/codec_database_test.cc b/src/modules/video_coding/main/test/codec_database_test.cc index f29c56252..7a1f75bf3 100644 --- a/src/modules/video_coding/main/test/codec_database_test.cc +++ b/src/modules/video_coding/main/test/codec_database_test.cc @@ -12,15 +12,18 @@ // testing is done via the VCM module, no specific CodecDataBase module functionality. #include "codec_database_test.h" -#include "vp8.h" // for external codecs test -#include "../source/event.h" -#include "test_macros.h" -#include "test_util.h" -#include "../../../../engine_configurations.h" #include #include +#include "../../../../engine_configurations.h" +#include "../source/event.h" +#include "test_macros.h" +#include "test_util.h" +#include "video_metrics.h" +#include "vp8.h" // for external codecs test + + using namespace webrtc; int CodecDataBaseTest::RunTest(CmdArgs& args) @@ -362,7 +365,7 @@ CodecDataBaseTest::Perform(CmdArgs& args) // closing and calculating PSNR for prior encoder-decoder test TearDown(); // closing open files double psnr = 0; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); + PsnrFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); printf(" \n @ %d KBPS: ", sendCodec.startBitrate); printf("PSNR from encoder-decoder send-receive control test is %f \n \n", psnr); } // end of #codecs >1 diff --git a/src/modules/video_coding/main/test/media_opt_test.cc b/src/modules/video_coding/main/test/media_opt_test.cc index 58f5dad9c..a6ff6e9dc 100644 --- a/src/modules/video_coding/main/test/media_opt_test.cc +++ b/src/modules/video_coding/main/test/media_opt_test.cc @@ -11,18 +11,21 @@ // Implementation of Media Optimization Test // testing is done via the VCM module, no specific Media opt functionality. -#include "receiver_tests.h" // receive side callbacks -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "test_macros.h" -#include "test_util.h" // send side callback #include "media_opt_test.h" -#include "../source/event.h" #include #include -#include #include +#include + +#include "../source/event.h" +#include "receiver_tests.h" // receive side callbacks +#include "rtp_rtcp.h" +#include "test_macros.h" +#include "test_util.h" // send side callback +#include "video_coding.h" +#include "video_metrics.h" + using namespace webrtc; @@ -465,7 +468,7 @@ MediaOptTest::Print(int mode) double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate)); double actualBitRate = ActualBitRate / 1000.0; double psnr; - PSNRfromFiles(_actualSourcename.c_str(), _outname.c_str(), _width, _height, &psnr); + PsnrFromFiles(_actualSourcename.c_str(), _outname.c_str(), _width, _height, &psnr); (_log) << "VCM: Media Optimization Test Cycle Completed!" << std::endl; (_log) << "Input file: " << _inname << std::endl; diff --git a/src/modules/video_coding/main/test/normal_test.cc b/src/modules/video_coding/main/test/normal_test.cc index d4329354b..3453602b5 100644 --- a/src/modules/video_coding/main/test/normal_test.cc +++ b/src/modules/video_coding/main/test/normal_test.cc @@ -9,17 +9,21 @@ */ #include "normal_test.h" -#include "../source/event.h" -#include "tick_time.h" -#include "common_types.h" -#include "trace.h" -#include "test_macros.h" -#include "test_util.h" + #include #include #include #include +#include "../source/event.h" +#include "common_types.h" +#include "test_macros.h" +#include "test_util.h" +#include "tick_time.h" +#include "trace.h" +#include "video_metrics.h" + + using namespace webrtc; int NormalTest::RunTest(CmdArgs& args) @@ -141,7 +145,7 @@ VCMNTEncodeCompleteCallback::SkipCnt() return _skipCnt; } -// Decoded Frame Callback Implmentation +// Decoded Frame Callback Implementation VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback() { // @@ -359,8 +363,8 @@ NormalTest::Print() double avgDecTime = _totalDecodeTime / _frameCnt; double psnr; double ssim; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); - SSIMfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &ssim); + PsnrFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); + SsimFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &ssim); printf("Actual bitrate: %f kbps\n", actualBitRate); printf("Target bitrate: %f kbps\n", _bitRate); ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; diff --git a/src/modules/video_coding/main/test/test_util.cc b/src/modules/video_coding/main/test/test_util.cc index 469882a84..3d67cfadf 100644 --- a/src/modules/video_coding/main/test/test_util.cc +++ b/src/modules/video_coding/main/test/test_util.cc @@ -8,7 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "system_wrappers/interface/cpu_features_wrapper.h" #include "test_util.h" #include "test_macros.h" #include "rtp_dump.h" @@ -420,282 +419,6 @@ PacketRequester::ResendPackets(const WebRtc_UWord16* sequenceNumbers, return _rtp.SendNACK(sequenceNumbers, length); } -WebRtc_Word32 -PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, - WebRtc_Word32 width, WebRtc_Word32 height, double *YPSNRptr) -{ - FILE *refFp = fopen(refFileName, "rb"); - if( refFp == NULL ) { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if( testFp == NULL ) { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - double mse = 0.0; - double mseLogSum = 0.0; - WebRtc_Word32 frames = 0; - - // Allocating size for one I420 frame. - WebRtc_Word32 frameBytes = 3 * width * height >> 1; - WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; - WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; - - WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); - WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); - - while( refBytes == frameBytes && testBytes == frameBytes ) - { - mse = 0.0; - - int sh = 8;//boundary offset - for( int k2 = sh; k2 < height-sh;k2++) - for( int k = sh; k < width-sh;k++) - { - int kk = k2*width + k; - mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]); - } - - // divide by number of pixels - mse /= (double) (width * height); - - // accumulate for total average - mseLogSum += std::log10( mse ); - frames++; - - refBytes = (int) fread(ref, 1, frameBytes, refFp); - testBytes = (int) fread(test, 1, frameBytes, testFp); - } - // for identical reproduction: - if (mse == 0) - { - *YPSNRptr = 48; - } - else - { - *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames; - } - - - delete [] ref; - delete [] test; - - fclose(refFp); - fclose(testFp); - - return 0; -} -static double -similarity(unsigned long sum_s, unsigned long sum_r, unsigned long sum_sq_s, - unsigned long sum_sq_r, unsigned long sum_sxr, int count -) -{ - int64_t ssim_n, ssim_d; - int64_t c1, c2; - const int64_t cc1 = 26634; // (64^2*(.01*255)^2 - const int64_t cc2 = 239708; // (64^2*(.03*255)^2 - - //scale the constants by number of pixels - c1 = (cc1*count*count)>>12; - c2 = (cc2*count*count)>>12; - - ssim_n = (2*sum_s*sum_r+ c1)*((int64_t) 2*count*sum_sxr- - (int64_t) 2*sum_s*sum_r+c2); - - ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)* - ((int64_t)count*sum_sq_s-(int64_t)sum_s*sum_s + - (int64_t)count*sum_sq_r-(int64_t) sum_r*sum_r +c2) ; - - return ssim_n * 1.0 / ssim_d; -} - -static double -ssim_8x8_c(unsigned char *s, int sp, unsigned char *r, int rp) -{ - unsigned long sum_s = 0; - unsigned long sum_r = 0; - unsigned long sum_sq_s = 0; - unsigned long sum_sq_r = 0; - unsigned long sum_sxr = 0; - - int i,j; - for (i = 0;i < 8; i++, s += sp,r += rp) - { - for (j = 0; j < 8; j++) - { - sum_s += s[j]; - sum_r += r[j]; - sum_sq_s += s[j] * s[j]; - sum_sq_r += r[j] * r[j]; - sum_sxr += s[j] * r[j]; - } - } - return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64); -} - -#if defined(WEBRTC_USE_SSE2) -#include -static double -ssim_8x8_sse2(unsigned char *s, int sp, unsigned char *r, int rp) -{ - int i; - const __m128i z = _mm_setzero_si128(); - __m128i sum_s_16 = _mm_setzero_si128(); - __m128i sum_r_16 = _mm_setzero_si128(); - __m128i sum_sq_s_32 = _mm_setzero_si128(); - __m128i sum_sq_r_32 = _mm_setzero_si128(); - __m128i sum_sxr_32 = _mm_setzero_si128(); - - for (i = 0;i < 8; i++,s += sp,r += rp) - { - const __m128i s_8 = _mm_loadl_epi64((__m128i*)(s)); - const __m128i r_8 = _mm_loadl_epi64((__m128i*)(r)); - - const __m128i s_16 = _mm_unpacklo_epi8(s_8,z); - const __m128i r_16 = _mm_unpacklo_epi8(r_8,z); - - sum_s_16 = _mm_adds_epu16(sum_s_16, s_16); - sum_r_16 = _mm_adds_epu16(sum_r_16, r_16); - const __m128i sq_s_32 = _mm_madd_epi16(s_16, s_16); - sum_sq_s_32 = _mm_add_epi32(sum_sq_s_32, sq_s_32); - const __m128i sq_r_32 = _mm_madd_epi16(r_16, r_16); - sum_sq_r_32 = _mm_add_epi32(sum_sq_r_32, sq_r_32); - const __m128i sxr_32 = _mm_madd_epi16(s_16, r_16); - sum_sxr_32 = _mm_add_epi32(sum_sxr_32, sxr_32); - } - - const __m128i sum_s_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_s_16, z), - _mm_unpacklo_epi16(sum_s_16, z)); - const __m128i sum_r_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_r_16, z), - _mm_unpacklo_epi16(sum_r_16, z)); - - unsigned long sum_s_64[2]; - unsigned long sum_r_64[2]; - unsigned long sum_sq_s_64[2]; - unsigned long sum_sq_r_64[2]; - unsigned long sum_sxr_64[2]; - - _mm_store_si128 ((__m128i*)sum_s_64, - _mm_add_epi64(_mm_unpackhi_epi32(sum_s_32, z), - _mm_unpacklo_epi32(sum_s_32, z))); - _mm_store_si128 ((__m128i*)sum_r_64, - _mm_add_epi64(_mm_unpackhi_epi32(sum_r_32, z), - _mm_unpacklo_epi32(sum_r_32, z))); - _mm_store_si128 ((__m128i*)sum_sq_s_64, - _mm_add_epi64(_mm_unpackhi_epi32(sum_sq_s_32, z), - _mm_unpacklo_epi32(sum_sq_s_32, z))); - _mm_store_si128 ((__m128i*)sum_sq_r_64, - _mm_add_epi64(_mm_unpackhi_epi32(sum_sq_r_32, z), - _mm_unpacklo_epi32(sum_sq_r_32, z))); - _mm_store_si128 ((__m128i*)sum_sxr_64, - _mm_add_epi64(_mm_unpackhi_epi32(sum_sxr_32, z), - _mm_unpacklo_epi32(sum_sxr_32, z))); - - const unsigned long sum_s = sum_s_64[0] + sum_s_64[1]; - const unsigned long sum_r = sum_r_64[0] + sum_r_64[1]; - const unsigned long sum_sq_s = sum_sq_s_64[0] + sum_sq_s_64[1]; - const unsigned long sum_sq_r = sum_sq_r_64[0] + sum_sq_r_64[1]; - const unsigned long sum_sxr = sum_sxr_64[0] + sum_sxr_64[1]; - - return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64); -} -#endif - -double -SSIM_frame(unsigned char *img1, unsigned char *img2, int stride_img1, - int stride_img2,int width, int height) -{ - int i,j; - unsigned int samples = 0; - double ssim_total = 0; - double (*ssim_8x8)(unsigned char*, int, unsigned char*, int rp); - - ssim_8x8 = ssim_8x8_c; - if (WebRtc_GetCPUInfo(kSSE2)) - { -#if defined(WEBRTC_USE_SSE2) - ssim_8x8 = ssim_8x8_sse2; -#endif - } - - // sample point start with each 4x4 location - for (i = 0; i < height-8; i += 4, img1 += stride_img1 * 4, - img2 += stride_img2 * 4) - { - for (j = 0; j < width - 8; j += 4 ) - { - double v = ssim_8x8(img1 + j, stride_img1, img2 + j, stride_img2); - ssim_total += v; - samples++; - } - } - ssim_total /= samples; - return ssim_total; -} - -WebRtc_Word32 -SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, - WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr) -{ - FILE *refFp = fopen(refFileName, "rb"); - if ( refFp == NULL ) - { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if ( testFp == NULL ) - { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - int frames = 0; - - const int frameBytes = 3 * width *height / 2; // bytes in one frame I420 - unsigned char *ref = new unsigned char[frameBytes]; - unsigned char *test = new unsigned char[frameBytes]; - - int refBytes = (int) fread(ref, 1, frameBytes, refFp); - int testBytes = (int) fread(test, 1, frameBytes, testFp); - - double ssimScene = 0.0; //avgerage SSIM for sequence - - while( refBytes == frameBytes && testBytes == frameBytes ) - { - ssimScene += SSIM_frame(ref, test, width, width, width, height); - - frames++; - - refBytes = (int) fread(ref, 1, frameBytes, refFp); - testBytes = (int) fread(test, 1, frameBytes, testFp); - } - - //SSIM: normalize/average for sequence - ssimScene = ssimScene / frames; - *SSIMptr = ssimScene; - - - delete [] ref; - delete [] test; - - - fclose(refFp); - fclose(testFp); - - return 0; - -} - RTPVideoCodecTypes ConvertCodecType(const char* plname) diff --git a/src/modules/video_coding/main/test/test_util.h b/src/modules/video_coding/main/test/test_util.h index 9831cbd6e..8522c55b5 100644 --- a/src/modules/video_coding/main/test/test_util.h +++ b/src/modules/video_coding/main/test/test_util.h @@ -262,17 +262,6 @@ private: webrtc::RtpRtcp& _rtp; }; -// PSNR & SSIM calculations -WebRtc_Word32 -PSNRfromFiles(const WebRtc_Word8 *refFileName, - const WebRtc_Word8 *testFileName, WebRtc_Word32 width, - WebRtc_Word32 height, double *YPSNRptr); - -WebRtc_Word32 -SSIMfromFiles(const WebRtc_Word8 *refFileName, - const WebRtc_Word8 *testFileName, WebRtc_Word32 width, - WebRtc_Word32 height, double *SSIMptr); - // Codec type conversion webrtc::RTPVideoCodecTypes ConvertCodecType(const char* plname); diff --git a/src/modules/video_coding/main/test/video_metrics.cc b/src/modules/video_coding/main/test/video_metrics.cc new file mode 100644 index 000000000..ca58010dc --- /dev/null +++ b/src/modules/video_coding/main/test/video_metrics.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2011 The WebRTC 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. + */ + +#include +#include "system_wrappers/interface/cpu_features_wrapper.h" +#include "test_util.h" + +WebRtc_Word32 +PsnrFromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, + WebRtc_Word32 width, WebRtc_Word32 height, double *YPsnrPtr) +{ + FILE *refFp = fopen(refFileName, "rb"); + if (refFp == NULL ) + { + // cannot open reference file + fprintf(stderr, "Cannot open file %s\n", refFileName); + return -1; + } + + FILE *testFp = fopen(testFileName, "rb"); + if (testFp == NULL ) + { + // cannot open test file + fprintf(stderr, "Cannot open file %s\n", testFileName); + return -2; + } + + double mse = 0.0; + double mseLogSum = 0.0; + WebRtc_Word32 frames = 0; + + // Allocating size for one I420 frame. + WebRtc_Word32 frameBytes = 3 * width * height >> 1; + WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; + WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; + + WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); + WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); + + while (refBytes == frameBytes && testBytes == frameBytes) + { + mse = 0.0; + + WebRtc_Word32 sh = 8; //boundary offset + for (WebRtc_Word32 k2 = sh; k2 < height - sh; k2++) + for (WebRtc_Word32 k = sh; k < width - sh; k++) + { + WebRtc_Word32 kk = k2 * width + k; + mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]); + } + + // divide by number of pixels + mse /= (double) (width * height); + + // accumulate for total average + mseLogSum += std::log10(mse); + frames++; + + refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); + testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); + } + // for identical reproduction: + if (mse == 0) + { + *YPsnrPtr = 48; + } + else + { + *YPsnrPtr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames; + } + + + delete [] ref; + delete [] test; + + fclose(refFp); + fclose(testFp); + + return 0; +} + +static double +Similarity(WebRtc_UWord64 sum_s, WebRtc_UWord64 sum_r, WebRtc_UWord64 sum_sq_s, + WebRtc_UWord64 sum_sq_r, WebRtc_UWord64 sum_sxr, WebRtc_Word32 count) +{ + WebRtc_Word64 ssim_n, ssim_d; + WebRtc_Word64 c1, c2; + const WebRtc_Word64 cc1 = 26634; // (64^2*(.01*255)^2 + const WebRtc_Word64 cc2 = 239708; // (64^2*(.03*255)^2 + + // Scale the constants by number of pixels + c1 = (cc1 * count * count) >> 12; + c2 = (cc2 * count * count) >> 12; + + ssim_n = (2 * sum_s * sum_r + c1) * ((WebRtc_Word64) 2 * count * sum_sxr- + (WebRtc_Word64) 2 * sum_s * sum_r + c2); + + ssim_d = (sum_s * sum_s + sum_r * sum_r + c1)* + ((WebRtc_Word64)count * sum_sq_s - (WebRtc_Word64)sum_s * sum_s + + (WebRtc_Word64)count * sum_sq_r - (WebRtc_Word64) sum_r * sum_r + c2); + + return ssim_n * 1.0 / ssim_d; +} + +static double +Ssim8x8C(WebRtc_UWord8 *s, WebRtc_Word32 sp, + WebRtc_UWord8 *r, WebRtc_Word32 rp) +{ + WebRtc_UWord64 sum_s = 0; + WebRtc_UWord64 sum_r = 0; + WebRtc_UWord64 sum_sq_s = 0; + WebRtc_UWord64 sum_sq_r = 0; + WebRtc_UWord64 sum_sxr = 0; + + WebRtc_Word32 i, j; + for (i = 0; i < 8; i++, s += sp,r += rp) + { + for (j = 0; j < 8; j++) + { + sum_s += s[j]; + sum_r += r[j]; + sum_sq_s += s[j] * s[j]; + sum_sq_r += r[j] * r[j]; + sum_sxr += s[j] * r[j]; + } + } + return Similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64); +} + +#if defined(WEBRTC_USE_SSE2) +#include +static double +Ssim8x8Sse2(WebRtc_UWord8 *s, WebRtc_Word32 sp, + WebRtc_UWord8 *r, WebRtc_Word32 rp) +{ + WebRtc_Word32 i; + const __m128i z = _mm_setzero_si128(); + __m128i sum_s_16 = _mm_setzero_si128(); + __m128i sum_r_16 = _mm_setzero_si128(); + __m128i sum_sq_s_32 = _mm_setzero_si128(); + __m128i sum_sq_r_32 = _mm_setzero_si128(); + __m128i sum_sxr_32 = _mm_setzero_si128(); + + for (i = 0; i < 8; i++, s += sp,r += rp) + { + const __m128i s_8 = _mm_loadl_epi64((__m128i*)(s)); + const __m128i r_8 = _mm_loadl_epi64((__m128i*)(r)); + + const __m128i s_16 = _mm_unpacklo_epi8(s_8,z); + const __m128i r_16 = _mm_unpacklo_epi8(r_8,z); + + sum_s_16 = _mm_adds_epu16(sum_s_16, s_16); + sum_r_16 = _mm_adds_epu16(sum_r_16, r_16); + const __m128i sq_s_32 = _mm_madd_epi16(s_16, s_16); + sum_sq_s_32 = _mm_add_epi32(sum_sq_s_32, sq_s_32); + const __m128i sq_r_32 = _mm_madd_epi16(r_16, r_16); + sum_sq_r_32 = _mm_add_epi32(sum_sq_r_32, sq_r_32); + const __m128i sxr_32 = _mm_madd_epi16(s_16, r_16); + sum_sxr_32 = _mm_add_epi32(sum_sxr_32, sxr_32); + } + + const __m128i sum_s_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_s_16, z), + _mm_unpacklo_epi16(sum_s_16, z)); + const __m128i sum_r_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_r_16, z), + _mm_unpacklo_epi16(sum_r_16, z)); + + WebRtc_UWord64 sum_s_64[2]; + WebRtc_UWord64 sum_r_64[2]; + WebRtc_UWord64 sum_sq_s_64[2]; + WebRtc_UWord64 sum_sq_r_64[2]; + WebRtc_UWord64 sum_sxr_64[2]; + + _mm_store_si128 ((__m128i*)sum_s_64, + _mm_add_epi64(_mm_unpackhi_epi32(sum_s_32, z), + _mm_unpacklo_epi32(sum_s_32, z))); + _mm_store_si128 ((__m128i*)sum_r_64, + _mm_add_epi64(_mm_unpackhi_epi32(sum_r_32, z), + _mm_unpacklo_epi32(sum_r_32, z))); + _mm_store_si128 ((__m128i*)sum_sq_s_64, + _mm_add_epi64(_mm_unpackhi_epi32(sum_sq_s_32, z), + _mm_unpacklo_epi32(sum_sq_s_32, z))); + _mm_store_si128 ((__m128i*)sum_sq_r_64, + _mm_add_epi64(_mm_unpackhi_epi32(sum_sq_r_32, z), + _mm_unpacklo_epi32(sum_sq_r_32, z))); + _mm_store_si128 ((__m128i*)sum_sxr_64, + _mm_add_epi64(_mm_unpackhi_epi32(sum_sxr_32, z), + _mm_unpacklo_epi32(sum_sxr_32, z))); + + const WebRtc_UWord64 sum_s = sum_s_64[0] + sum_s_64[1]; + const WebRtc_UWord64 sum_r = sum_r_64[0] + sum_r_64[1]; + const WebRtc_UWord64 sum_sq_s = sum_sq_s_64[0] + sum_sq_s_64[1]; + const WebRtc_UWord64 sum_sq_r = sum_sq_r_64[0] + sum_sq_r_64[1]; + const WebRtc_UWord64 sum_sxr = sum_sxr_64[0] + sum_sxr_64[1]; + + return Similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64); +} +#endif + +double +SsimFrame(WebRtc_UWord8 *img1, WebRtc_UWord8 *img2, WebRtc_Word32 stride_img1, + WebRtc_Word32 stride_img2, WebRtc_Word32 width, WebRtc_Word32 height) +{ + WebRtc_Word32 i,j; + WebRtc_UWord32 samples = 0; + double ssim_total = 0; + double (*ssim_8x8)(WebRtc_UWord8*, WebRtc_Word32, + WebRtc_UWord8*, WebRtc_Word32 rp); + + ssim_8x8 = Ssim8x8C; + if (WebRtc_GetCPUInfo(kSSE2)) + { +#if defined(WEBRTC_USE_SSE2) + ssim_8x8 = Ssim8x8Sse2; +#endif + } + + // Sample point start with each 4x4 location + for (i = 0; i < height - 8; i += 4, img1 += stride_img1 * 4, + img2 += stride_img2 * 4) + { + for (j = 0; j < width - 8; j += 4 ) + { + double v = ssim_8x8(img1 + j, stride_img1, img2 + j, stride_img2); + ssim_total += v; + samples++; + } + } + ssim_total /= samples; + return ssim_total; +} + +WebRtc_Word32 +SsimFromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, + WebRtc_Word32 width, WebRtc_Word32 height, double *YSsimPtr) +{ + FILE *refFp = fopen(refFileName, "rb"); + if (refFp == NULL) + { + // cannot open reference file + fprintf(stderr, "Cannot open file %s\n", refFileName); + return -1; + } + + FILE *testFp = fopen(testFileName, "rb"); + if (testFp == NULL) + { + // cannot open test file + fprintf(stderr, "Cannot open file %s\n", testFileName); + return -2; + } + + WebRtc_Word32 frames = 0; + + // Bytes in one frame I420 + const WebRtc_Word32 frameBytes = 3 * width * height / 2; + WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; + WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; + + WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); + WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); + + double ssimScene = 0.0; //average SSIM for sequence + + while (refBytes == frameBytes && testBytes == frameBytes ) + { + ssimScene += SsimFrame(ref, test, width, width, width, height); + + frames++; + + refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); + testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); + } + + // SSIM: normalize/average for sequence + ssimScene = ssimScene / frames; + *YSsimPtr = ssimScene; + + delete [] ref; + delete [] test; + + fclose(refFp); + fclose(testFp); + + return 0; +} diff --git a/src/modules/video_coding/main/test/video_metrics.h b/src/modules/video_coding/main/test/video_metrics.h new file mode 100644 index 000000000..592b696f3 --- /dev/null +++ b/src/modules/video_coding/main/test/video_metrics.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 The WebRTC 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. + */ + + +#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_METRICS_H_ +#define WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_METRICS_H_ + + +// PSNR & SSIM calculations +WebRtc_Word32 +PsnrFromFiles(const WebRtc_Word8 *refFileName, + const WebRtc_Word8 *testFileName, WebRtc_Word32 width, + WebRtc_Word32 height, double *YPsnrPtr); + +WebRtc_Word32 +SsimFromFiles(const WebRtc_Word8 *refFileName, + const WebRtc_Word8 *testFileName, WebRtc_Word32 width, + WebRtc_Word32 height, double *YSsimPtr); + + +#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_METRICS_H_