diff --git a/src/common_video/libyuv/include/webrtc_libyuv.h b/src/common_video/libyuv/include/webrtc_libyuv.h index c6e95712d..bd8ff0bd3 100644 --- a/src/common_video/libyuv/include/webrtc_libyuv.h +++ b/src/common_video/libyuv/include/webrtc_libyuv.h @@ -139,10 +139,18 @@ int MirrorI420UpDown(const VideoFrame* src_frame, VideoFrame* dst_frame); // Compute PSNR for an I420 frame (all planes). +double I420PSNR(const VideoFrame* ref_frame, + const VideoFrame* test_frame); +// Compute SSIM for an I420 frame (all planes). +double I420SSIM(const VideoFrame* ref_frame, + const VideoFrame* test_frame); + +// TODO(mikhal): Remove these functions and keep only the above functionality. +// Compute PSNR for an I420 buffer (all planes). double I420PSNR(const uint8_t* ref_frame, const uint8_t* test_frame, int width, int height); -// Compute SSIM for an I420 frame (all planes). +// Compute SSIM for an I420 buffer (all planes). double I420SSIM(const uint8_t* ref_frame, const uint8_t* test_frame, int width, int height); diff --git a/src/common_video/libyuv/libyuv_unittest.cc b/src/common_video/libyuv/libyuv_unittest.cc index 5ab8c1103..6c2bba87c 100644 --- a/src/common_video/libyuv/libyuv_unittest.cc +++ b/src/common_video/libyuv/libyuv_unittest.cc @@ -158,8 +158,7 @@ TEST_F(TestLibYuv, ConvertTest) { output_file) != static_cast(frame_length_)) { return; } - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); // Optimization Speed- quality trade-off => 45 dB only (platform dependant). EXPECT_GT(ceil(psnr), 44); j++; @@ -171,8 +170,7 @@ TEST_F(TestLibYuv, ConvertTest) { kUYVY, 0, out_uyvy_buffer)); EXPECT_EQ(0, ConvertToI420(kUYVY, out_uyvy_buffer, 0, 0, width_, height_, 0, kRotateNone, &res_i420_frame)); - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); EXPECT_EQ(48.0, psnr); if (fwrite(res_i420_frame.Buffer(), 1, frame_length_, output_file) != static_cast(frame_length_)) { @@ -211,8 +209,7 @@ TEST_F(TestLibYuv, ConvertTest) { return; } - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); EXPECT_EQ(48.0, psnr); j++; delete [] outYV120Buffer; @@ -229,8 +226,7 @@ TEST_F(TestLibYuv, ConvertTest) { output_file) != static_cast(frame_length_)) { return; } - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); EXPECT_EQ(48.0, psnr); // printf("\nConvert #%d I420 <-> RGB565\n", j); @@ -245,8 +241,7 @@ TEST_F(TestLibYuv, ConvertTest) { output_file) != static_cast(frame_length_)) { return; } - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); // TODO(leozwang) Investigate the right psnr should be set for I420ToRGB565, // Another example is I420ToRGB24, the psnr is 44 EXPECT_GT(ceil(psnr), 40); @@ -263,8 +258,7 @@ TEST_F(TestLibYuv, ConvertTest) { output_file) != static_cast(frame_length_)) { return; } - psnr = I420PSNR(orig_frame.Buffer(), res_i420_frame.Buffer(), - width_, height_); + psnr = I420PSNR(&orig_frame, &res_i420_frame); // TODO(leozwang) Investigate the right psnr should be set for I420ToARGB8888, EXPECT_GT(ceil(psnr), 42); diff --git a/src/common_video/libyuv/webrtc_libyuv.cc b/src/common_video/libyuv/webrtc_libyuv.cc index 82dc4a37b..7a7d1d123 100644 --- a/src/common_video/libyuv/webrtc_libyuv.cc +++ b/src/common_video/libyuv/webrtc_libyuv.cc @@ -284,6 +284,69 @@ int MirrorI420UpDown(const VideoFrame* src_frame, width, -height); } +// Compute PSNR for an I420 frame (all planes) +double I420PSNR(const VideoFrame* ref_frame, + const VideoFrame* test_frame) { + if (!ref_frame || !test_frame) + return -1; + else if ((ref_frame->Width() != test_frame->Width()) || + (ref_frame->Height() != test_frame->Height())) + return -1; + else if (ref_frame->Width() == 0u || ref_frame->Height() == 0u) + return -1; + int height = ref_frame->Height() ; + int width = ref_frame->Width(); + int half_width = (width + 1) >> 1; + int half_height = (height + 1) >> 1; + const uint8_t* src_y_a = ref_frame->Buffer(); + const uint8_t* src_u_a = src_y_a + width * height; + const uint8_t* src_v_a = src_u_a + half_width * half_height; + const uint8_t* src_y_b = test_frame->Buffer(); + const uint8_t* src_u_b = src_y_b + width * height; + const uint8_t* src_v_b = src_u_b + half_width * half_height; + // In the following: stride is determined by width. + double psnr = libyuv::I420Psnr(src_y_a, width, + src_u_a, half_width, + src_v_a, half_width, + src_y_b, width, + src_u_b, half_width, + src_v_b, half_width, + width, height); + // LibYuv sets the max psnr value to 128, we restrict it to 48. + // In case of 0 mse in one frame, 128 can skew the results significantly. + return (psnr > 48.0) ? 48.0 : psnr; +} +// Compute SSIM for an I420 frame (all planes) +double I420SSIM(const VideoFrame* ref_frame, + const VideoFrame* test_frame) { + if (!ref_frame || !test_frame) + return -1; + else if ((ref_frame->Width() != test_frame->Width()) || + (ref_frame->Height() != test_frame->Height())) + return -1; + else if (ref_frame->Width() == 0u || ref_frame->Height() == 0u) + return -1; + int height = ref_frame->Height() ; + int width = ref_frame->Width(); + int half_width = (width + 1) >> 1; + int half_height = (height + 1) >> 1; + const uint8_t* src_y_a = ref_frame->Buffer(); + const uint8_t* src_u_a = src_y_a + width * height; + const uint8_t* src_v_a = src_u_a + half_width * half_height; + const uint8_t* src_y_b = test_frame->Buffer(); + const uint8_t* src_u_b = src_y_b + width * height; + const uint8_t* src_v_b = src_u_b + half_width * half_height; + int stride_y = width; + int stride_uv = half_width; + return libyuv::I420Ssim(src_y_a, stride_y, + src_u_a, stride_uv, + src_v_a, stride_uv, + src_y_b, stride_y, + src_u_b, stride_uv, + src_v_b, stride_uv, + width, height); +} + // Compute PSNR for an I420 frame (all planes) double I420PSNR(const uint8_t* ref_frame, const uint8_t* test_frame,