diff options
Diffstat (limited to 'webrtc/common_video/libyuv/scaler_unittest.cc')
-rw-r--r-- | webrtc/common_video/libyuv/scaler_unittest.cc | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/webrtc/common_video/libyuv/scaler_unittest.cc b/webrtc/common_video/libyuv/scaler_unittest.cc new file mode 100644 index 0000000000..568311bc2e --- /dev/null +++ b/webrtc/common_video/libyuv/scaler_unittest.cc @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2012 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 <math.h> +#include <string.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_video/libyuv/include/scaler.h" +#include "webrtc/system_wrappers/include/tick_util.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { + +class TestScaler : public ::testing::Test { + protected: + TestScaler(); + virtual void SetUp(); + virtual void TearDown(); + + void ScaleSequence(ScaleMethod method, + FILE* source_file, std::string out_name, + int src_width, int src_height, + int dst_width, int dst_height); + // Computes the sequence average PSNR between an input sequence in + // |input_file| and an output sequence with filename |out_name|. |width| and + // |height| are the frame sizes of both sequences. + double ComputeAvgSequencePSNR(FILE* input_file, std::string out_name, + int width, int height); + + Scaler test_scaler_; + FILE* source_file_; + VideoFrame test_frame_; + const int width_; + const int half_width_; + const int height_; + const int half_height_; + const int size_y_; + const int size_uv_; + const size_t frame_length_; +}; + +TestScaler::TestScaler() + : source_file_(NULL), + width_(352), + half_width_(width_ / 2), + height_(288), + half_height_(height_ / 2), + size_y_(width_ * height_), + size_uv_(half_width_ * half_height_), + frame_length_(CalcBufferSize(kI420, width_, height_)) { +} + +void TestScaler::SetUp() { + const std::string input_file_name = + webrtc::test::ResourcePath("foreman_cif", "yuv"); + source_file_ = fopen(input_file_name.c_str(), "rb"); + ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<< + input_file_name << "\n"; + test_frame_.CreateEmptyFrame(width_, height_, + width_, half_width_, half_width_); +} + +void TestScaler::TearDown() { + if (source_file_ != NULL) { + ASSERT_EQ(0, fclose(source_file_)); + } + source_file_ = NULL; +} + +TEST_F(TestScaler, ScaleWithoutSettingValues) { + EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_)); +} + +TEST_F(TestScaler, ScaleBadInitialValues) { + EXPECT_EQ(-1, test_scaler_.Set(0, 288, 352, 288, kI420, kI420, kScalePoint)); + EXPECT_EQ(-1, test_scaler_.Set(704, 0, 352, 288, kI420, kI420, kScaleBox)); + EXPECT_EQ(-1, test_scaler_.Set(704, 576, 352, 0, kI420, kI420, + kScaleBilinear)); + EXPECT_EQ(-1, test_scaler_.Set(704, 576, 0, 288, kI420, kI420, kScalePoint)); +} + +TEST_F(TestScaler, ScaleSendingNullSourcePointer) { + VideoFrame null_src_frame; + EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_)); +} + +TEST_F(TestScaler, ScaleSendingBufferTooSmall) { + // Sending a buffer which is too small (should reallocate and update size) + EXPECT_EQ(0, test_scaler_.Set(width_, height_, + half_width_, half_height_, + kI420, kI420, + kScalePoint)); + VideoFrame test_frame2; + rtc::scoped_ptr<uint8_t[]> orig_buffer(new uint8_t[frame_length_]); + EXPECT_GT(fread(orig_buffer.get(), 1, frame_length_, source_file_), 0U); + test_frame_.CreateFrame(orig_buffer.get(), + orig_buffer.get() + size_y_, + orig_buffer.get() + size_y_ + size_uv_, + width_, height_, + width_, half_width_, half_width_); + EXPECT_EQ(0, test_scaler_.Scale(test_frame_, &test_frame2)); + EXPECT_GT(width_ * height_, test_frame2.allocated_size(kYPlane)); + EXPECT_GT(size_uv_, test_frame2.allocated_size(kUPlane)); + EXPECT_GT(size_uv_, test_frame2.allocated_size(kVPlane)); + EXPECT_EQ(half_width_, test_frame2.width()); + EXPECT_EQ(half_height_, test_frame2.height()); +} + +//TODO (mikhal): Converge the test into one function that accepts the method. +TEST_F(TestScaler, DISABLED_ON_ANDROID(PointScaleTest)) { + double avg_psnr; + FILE* source_file2; + ScaleMethod method = kScalePoint; + std::string out_name = webrtc::test::OutputPath() + + "LibYuvTest_PointScale_176_144.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + half_width_, half_height_); + // Upsample back up and check PSNR. + source_file2 = fopen(out_name.c_str(), "rb"); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_" + "upfrom_176_144.yuv"; + ScaleSequence(method, + source_file2, out_name, + 176, 144, + 352, 288); + avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); + printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " + "original size: %f \n", width_, height_, 176, 144, avg_psnr); + // Average PSNR for lower bound in assert is ~0.1dB lower than the actual + // average PSNR under same conditions. + ASSERT_GT(avg_psnr, 27.9); + ASSERT_EQ(0, fclose(source_file2)); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_320_240.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 320, 240); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_704_576.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + width_ * 2, height_ * 2); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_300_200.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 300, 200); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_400_300.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 400, 300); + // Down-sample to odd size frame and scale back up. + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_282_231.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 282, 231); + source_file2 = fopen(out_name.c_str(), "rb"); + out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_" + "upfrom_282_231.yuv"; + ScaleSequence(method, + source_file2, out_name, + 282, 231, + 352, 288); + avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); + printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " + "original size: %f \n", width_, height_, 282, 231, avg_psnr); + // Average PSNR for lower bound in assert is ~0.1dB lower than the actual + // average PSNR under same conditions. + ASSERT_GT(avg_psnr, 25.8); + ASSERT_EQ(0, fclose(source_file2)); +} + +TEST_F(TestScaler, DISABLED_ON_ANDROID(BiLinearScaleTest)) { + double avg_psnr; + FILE* source_file2; + ScaleMethod method = kScaleBilinear; + std::string out_name = webrtc::test::OutputPath() + + "LibYuvTest_BilinearScale_176_144.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + width_ / 2, height_ / 2); + // Up-sample back up and check PSNR. + source_file2 = fopen(out_name.c_str(), "rb"); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_" + "upfrom_176_144.yuv"; + ScaleSequence(method, + source_file2, out_name, + 176, 144, + 352, 288); + avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); + printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " + "original size: %f \n", width_, height_, 176, 144, avg_psnr); + // Average PSNR for lower bound in assert is ~0.1dB lower than the actual + // average PSNR under same conditions. + ASSERT_GT(avg_psnr, 27.5); + ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); + ASSERT_EQ(0, fclose(source_file2)); + out_name = webrtc::test::OutputPath() + + "LibYuvTest_BilinearScale_320_240.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 320, 240); + out_name = webrtc::test::OutputPath() + + "LibYuvTest_BilinearScale_704_576.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + width_ * 2, height_ * 2); + out_name = webrtc::test::OutputPath() + + "LibYuvTest_BilinearScale_300_200.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 300, 200); + out_name = webrtc::test::OutputPath() + + "LibYuvTest_BilinearScale_400_300.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 400, 300); +} + +TEST_F(TestScaler, DISABLED_ON_ANDROID(BoxScaleTest)) { + double avg_psnr; + FILE* source_file2; + ScaleMethod method = kScaleBox; + std::string out_name = webrtc::test::OutputPath() + + "LibYuvTest_BoxScale_176_144.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + width_ / 2, height_ / 2); + // Up-sample back up and check PSNR. + source_file2 = fopen(out_name.c_str(), "rb"); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_" + "upfrom_176_144.yuv"; + ScaleSequence(method, + source_file2, out_name, + 176, 144, + 352, 288); + avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); + printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " + "original size: %f \n", width_, height_, 176, 144, avg_psnr); + // Average PSNR for lower bound in assert is ~0.1dB lower than the actual + // average PSNR under same conditions. + ASSERT_GT(avg_psnr, 27.5); + ASSERT_EQ(0, fclose(source_file2)); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_320_240.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 320, 240); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_704_576.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + width_ * 2, height_ * 2); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_300_200.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 300, 200); + out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_400_300.yuv"; + ScaleSequence(method, + source_file_, out_name, + width_, height_, + 400, 300); +} + +double TestScaler::ComputeAvgSequencePSNR(FILE* input_file, + std::string out_name, + int width, int height) { + FILE* output_file; + output_file = fopen(out_name.c_str(), "rb"); + assert(output_file != NULL); + rewind(input_file); + rewind(output_file); + + size_t required_size = CalcBufferSize(kI420, width, height); + uint8_t* input_buffer = new uint8_t[required_size]; + uint8_t* output_buffer = new uint8_t[required_size]; + + int frame_count = 0; + double avg_psnr = 0; + VideoFrame in_frame, out_frame; + const int half_width = (width + 1) / 2; + in_frame.CreateEmptyFrame(width, height, width, half_width, half_width); + out_frame.CreateEmptyFrame(width, height, width, half_width, half_width); + while (feof(input_file) == 0) { + if (fread(input_buffer, 1, required_size, input_file) != required_size) { + break; + } + if (fread(output_buffer, 1, required_size, output_file) != required_size) { + break; + } + frame_count++; + EXPECT_EQ(0, ConvertToI420(kI420, input_buffer, 0, 0, width, height, + required_size, kVideoRotation_0, &in_frame)); + EXPECT_EQ(0, ConvertToI420(kI420, output_buffer, 0, 0, width, height, + required_size, kVideoRotation_0, &out_frame)); + double psnr = I420PSNR(&in_frame, &out_frame); + avg_psnr += psnr; + } + avg_psnr = avg_psnr / frame_count; + assert(0 == fclose(output_file)); + delete [] input_buffer; + delete [] output_buffer; + return avg_psnr; +} + +// TODO (mikhal): Move part to a separate scale test. +void TestScaler::ScaleSequence(ScaleMethod method, + FILE* source_file, std::string out_name, + int src_width, int src_height, + int dst_width, int dst_height) { + FILE* output_file; + EXPECT_EQ(0, test_scaler_.Set(src_width, src_height, + dst_width, dst_height, + kI420, kI420, method)); + + output_file = fopen(out_name.c_str(), "wb"); + ASSERT_TRUE(output_file != NULL); + + rewind(source_file); + + VideoFrame input_frame; + VideoFrame output_frame; + int64_t start_clock, total_clock; + total_clock = 0; + int frame_count = 0; + size_t src_required_size = CalcBufferSize(kI420, src_width, src_height); + rtc::scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[src_required_size]); + int size_y = src_width * src_height; + int size_uv = ((src_width + 1) / 2) * ((src_height + 1) / 2); + + // Running through entire sequence. + while (feof(source_file) == 0) { + if (fread(frame_buffer.get(), 1, src_required_size, source_file) != + src_required_size) + break; + + input_frame.CreateFrame(frame_buffer.get(), + frame_buffer.get() + size_y, + frame_buffer.get() + size_y + size_uv, + src_width, src_height, + src_width, (src_width + 1) / 2, + (src_width + 1) / 2); + + start_clock = TickTime::MillisecondTimestamp(); + EXPECT_EQ(0, test_scaler_.Scale(input_frame, &output_frame)); + total_clock += TickTime::MillisecondTimestamp() - start_clock; + if (PrintVideoFrame(output_frame, output_file) < 0) { + return; + } + frame_count++; + } + + if (frame_count) { + printf("Scaling[%d %d] => [%d %d]: ", + src_width, src_height, dst_width, dst_height); + printf("Average time per frame[ms]: %.2lf\n", + (static_cast<double>(total_clock) / frame_count)); + } + ASSERT_EQ(0, fclose(output_file)); +} + +} // namespace webrtc |