From 8d3758e6104d2198477d1317454308bf3a12c85d Mon Sep 17 00:00:00 2001 From: Sergey Silkin Date: Wed, 14 Mar 2018 11:28:15 +0100 Subject: [PATCH] Calculate and report PSNR for Y, U, V planes separately. Bug: webrtc:8448 Change-Id: Ia5b2b2f3ebac9ea7d1efbb3079b0bc3438a54a09 Reviewed-on: https://webrtc-review.googlesource.com/61324 Commit-Queue: Sergey Silkin Reviewed-by: Rasmus Brandt Cr-Commit-Position: refs/heads/master@{#22420} --- modules/video_coding/codecs/test/stats.cc | 9 +++ modules/video_coding/codecs/test/stats.h | 10 +++- .../codecs/test/videoprocessor.cc | 57 ++++++++++++------- .../videoprocessor_integrationtest_libvpx.cc | 19 ++++--- 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/modules/video_coding/codecs/test/stats.cc b/modules/video_coding/codecs/test/stats.cc index 1b75069b9d..918d53fadf 100644 --- a/modules/video_coding/codecs/test/stats.cc +++ b/modules/video_coding/codecs/test/stats.cc @@ -230,6 +230,9 @@ VideoStatistics Stats::SliceAndCalcVideoStatistic( Statistics frame_encoding_time_us; Statistics frame_decoding_time_us; + Statistics psnr_y; + Statistics psnr_u; + Statistics psnr_v; Statistics psnr; Statistics ssim; Statistics qp; @@ -290,6 +293,9 @@ VideoStatistics Stats::SliceAndCalcVideoStatistic( video_stat.width = frame_stat.decoded_width; video_stat.height = frame_stat.decoded_height; + psnr_y.AddSample(frame_stat.psnr_y); + psnr_u.AddSample(frame_stat.psnr_u); + psnr_v.AddSample(frame_stat.psnr_v); psnr.AddSample(frame_stat.psnr); ssim.AddSample(frame_stat.ssim); @@ -359,6 +365,9 @@ VideoStatistics Stats::SliceAndCalcVideoStatistic( video_stat.avg_delta_frame_size_bytes = delta_frame_size_bytes.Mean(); video_stat.avg_qp = qp.Mean(); + video_stat.avg_psnr_y = psnr_y.Mean(); + video_stat.avg_psnr_u = psnr_u.Mean(); + video_stat.avg_psnr_v = psnr_v.Mean(); video_stat.avg_psnr = psnr.Mean(); video_stat.min_psnr = psnr.Min(); video_stat.avg_ssim = ssim.Mean(); diff --git a/modules/video_coding/codecs/test/stats.h b/modules/video_coding/codecs/test/stats.h index 8d68116ead..d8dba8b3be 100644 --- a/modules/video_coding/codecs/test/stats.h +++ b/modules/video_coding/codecs/test/stats.h @@ -59,8 +59,11 @@ struct FrameStatistics { int qp = -1; // Quality. - float psnr = 0.0; - float ssim = 0.0; + float psnr_y = 0.0f; + float psnr_u = 0.0f; + float psnr_v = 0.0f; + float psnr = 0.0f; // 10 * log10(255^2 / (mse_y + mse_u + mse_v)). + float ssim = 0.0f; // 0.8 * ssim_y + 0.1 * (ssim_u + ssim_v). }; struct VideoStatistics { @@ -91,6 +94,9 @@ struct VideoStatistics { float avg_delta_frame_size_bytes = 0.0f; float avg_qp = 0.0f; + float avg_psnr_y = 0.0f; + float avg_psnr_u = 0.0f; + float avg_psnr_v = 0.0f; float avg_psnr = 0.0f; float min_psnr = 0.0f; float avg_ssim = 0.0f; diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc index 8117444ddb..38b920c39a 100644 --- a/modules/video_coding/codecs/test/videoprocessor.cc +++ b/modules/video_coding/codecs/test/videoprocessor.cc @@ -25,6 +25,7 @@ #include "rtc_base/checks.h" #include "rtc_base/timeutils.h" #include "test/gtest.h" +#include "third_party/libyuv/include/libyuv/compare.h" #include "third_party/libyuv/include/libyuv/scale.h" namespace webrtc { @@ -115,23 +116,16 @@ void ExtractI420BufferWithSize(const VideoFrame& image, RTC_CHECK_NE(ExtractBuffer(image, length, buffer->data()), -1); } -void CalculateFrameQuality(const VideoFrame& ref_frame, - const VideoFrame& dec_frame, +void CalculateFrameQuality(const I420BufferInterface& ref_buffer, + const I420BufferInterface& dec_buffer, FrameStatistics* frame_stat) { - if (ref_frame.width() == dec_frame.width() || - ref_frame.height() == dec_frame.height()) { - frame_stat->psnr = I420PSNR(&ref_frame, &dec_frame); - frame_stat->ssim = I420SSIM(&ref_frame, &dec_frame); - } else { - RTC_CHECK_GE(ref_frame.width(), dec_frame.width()); - RTC_CHECK_GE(ref_frame.height(), dec_frame.height()); - // Downscale reference frame. Use bilinear interpolation since it is used - // to get lowres inputs for encoder at simulcasting. - // TODO(ssilkin): Sync with VP9 SVC which uses 8-taps polyphase. + if (ref_buffer.width() != dec_buffer.width() || + ref_buffer.height() != dec_buffer.height()) { + RTC_CHECK_GE(ref_buffer.width(), dec_buffer.width()); + RTC_CHECK_GE(ref_buffer.height(), dec_buffer.height()); + // Downscale reference frame. rtc::scoped_refptr scaled_buffer = - I420Buffer::Create(dec_frame.width(), dec_frame.height()); - const I420BufferInterface& ref_buffer = - *ref_frame.video_frame_buffer()->ToI420(); + I420Buffer::Create(dec_buffer.width(), dec_buffer.height()); I420Scale(ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(), ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(), ref_buffer.width(), ref_buffer.height(), @@ -140,10 +134,31 @@ void CalculateFrameQuality(const VideoFrame& ref_frame, scaled_buffer->MutableDataV(), scaled_buffer->StrideV(), scaled_buffer->width(), scaled_buffer->height(), libyuv::kFilterBox); - frame_stat->psnr = - I420PSNR(*scaled_buffer, *dec_frame.video_frame_buffer()->ToI420()); - frame_stat->ssim = - I420SSIM(*scaled_buffer, *dec_frame.video_frame_buffer()->ToI420()); + + CalculateFrameQuality(*scaled_buffer, dec_buffer, frame_stat); + } else { + const uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane( + dec_buffer.DataY(), dec_buffer.StrideY(), ref_buffer.DataY(), + ref_buffer.StrideY(), dec_buffer.width(), dec_buffer.height()); + + const uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane( + dec_buffer.DataU(), dec_buffer.StrideU(), ref_buffer.DataU(), + ref_buffer.StrideU(), dec_buffer.width() / 2, dec_buffer.height() / 2); + + const uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane( + dec_buffer.DataV(), dec_buffer.StrideV(), ref_buffer.DataV(), + ref_buffer.StrideV(), dec_buffer.width() / 2, dec_buffer.height() / 2); + + const size_t num_y_samples = dec_buffer.width() * dec_buffer.height(); + const size_t num_u_samples = + dec_buffer.width() / 2 * dec_buffer.height() / 2; + + frame_stat->psnr_y = libyuv::SumSquareErrorToPsnr(sse_y, num_y_samples); + frame_stat->psnr_u = libyuv::SumSquareErrorToPsnr(sse_u, num_u_samples); + frame_stat->psnr_v = libyuv::SumSquareErrorToPsnr(sse_v, num_u_samples); + frame_stat->psnr = libyuv::SumSquareErrorToPsnr( + sse_y + sse_u + sse_v, num_y_samples + 2 * num_u_samples); + frame_stat->ssim = I420SSIM(ref_buffer, dec_buffer); } } @@ -411,7 +426,9 @@ void VideoProcessor::FrameDecoded(const VideoFrame& decoded_frame) { RTC_CHECK(reference_frame != input_frames_.cend()) << "The codecs are either buffering too much, dropping too much, or " "being too slow relative the input frame rate."; - CalculateFrameQuality(reference_frame->second, decoded_frame, frame_stat); + CalculateFrameQuality( + *reference_frame->second.video_frame_buffer()->ToI420(), + *decoded_frame.video_frame_buffer()->ToI420(), frame_stat); } // Erase all buffered input frames that we have moved past for all diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc index 330b903696..7cd161a164 100644 --- a/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc +++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc @@ -49,17 +49,20 @@ class VideoProcessorIntegrationTestLibvpx void PrintRdPerf(std::map> rd_stats) { printf("--> Summary\n"); - printf("%13s %7s %7s %13s %13s %7s %13s %13s\n", "uplink_kbps", "width", - "height", "downlink_kbps", "framerate_fps", "psnr", "enc_speed_fps", - "dec_speed_fps"); + printf("%11s %5s %6s %13s %13s %5s %7s %7s %7s %13s %13s\n", "uplink_kbps", + "width", "height", "downlink_kbps", "framerate_fps", "psnr", + "psnr_y", "psnr_u", "psnr_v", "enc_speed_fps", "dec_speed_fps"); for (const auto& rd_stat : rd_stats) { const size_t bitrate_kbps = rd_stat.first; for (const auto& layer_stat : rd_stat.second) { - printf("%13zu %7zu %7zu %13zu %13.2f %7.2f %13.2f %13.2f\n", - bitrate_kbps, layer_stat.width, layer_stat.height, - layer_stat.bitrate_kbps, layer_stat.framerate_fps, - layer_stat.avg_psnr, layer_stat.enc_speed_fps, - layer_stat.dec_speed_fps); + printf( + "%11zu %5zu %6zu %13zu %13.2f %5.2f %7.2f %7.2f %7.2f %13.2f " + "%13.2f\n", + bitrate_kbps, layer_stat.width, layer_stat.height, + layer_stat.bitrate_kbps, layer_stat.framerate_fps, + layer_stat.avg_psnr, layer_stat.avg_psnr_y, layer_stat.avg_psnr_u, + layer_stat.avg_psnr_v, layer_stat.enc_speed_fps, + layer_stat.dec_speed_fps); } } }