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 <ssilkin@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22420}
This commit is contained in:
Sergey Silkin
2018-03-14 11:28:15 +01:00
committed by Commit Bot
parent 16cba5c18d
commit 8d3758e610
4 changed files with 65 additions and 30 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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<I420Buffer> 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

View File

@ -49,17 +49,20 @@ class VideoProcessorIntegrationTestLibvpx
void PrintRdPerf(std::map<size_t, std::vector<VideoStatistics>> 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);
}
}
}