Home | History | Annotate | Download | only in libyuv
      1 /*
      2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <math.h>
     12 #include <string.h>
     13 
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "webrtc/common_video/libyuv/include/scaler.h"
     16 #include "webrtc/system_wrappers/include/tick_util.h"
     17 #include "webrtc/test/testsupport/fileutils.h"
     18 
     19 namespace webrtc {
     20 
     21 class TestScaler : public ::testing::Test {
     22  protected:
     23   TestScaler();
     24   virtual void SetUp();
     25   virtual void TearDown();
     26 
     27   void ScaleSequence(ScaleMethod method,
     28                      FILE* source_file, std::string out_name,
     29                      int src_width, int src_height,
     30                      int dst_width, int dst_height);
     31   // Computes the sequence average PSNR between an input sequence in
     32   // |input_file| and an output sequence with filename |out_name|. |width| and
     33   // |height| are the frame sizes of both sequences.
     34   double ComputeAvgSequencePSNR(FILE* input_file, std::string out_name,
     35                                 int width, int height);
     36 
     37   Scaler test_scaler_;
     38   FILE* source_file_;
     39   VideoFrame test_frame_;
     40   const int width_;
     41   const int half_width_;
     42   const int height_;
     43   const int half_height_;
     44   const int size_y_;
     45   const int size_uv_;
     46   const size_t frame_length_;
     47 };
     48 
     49 TestScaler::TestScaler()
     50     : source_file_(NULL),
     51       width_(352),
     52       half_width_(width_ / 2),
     53       height_(288),
     54       half_height_(height_ / 2),
     55       size_y_(width_ * height_),
     56       size_uv_(half_width_ * half_height_),
     57       frame_length_(CalcBufferSize(kI420, width_, height_)) {
     58 }
     59 
     60 void TestScaler::SetUp() {
     61   const std::string input_file_name =
     62       webrtc::test::ResourcePath("foreman_cif", "yuv");
     63   source_file_  = fopen(input_file_name.c_str(), "rb");
     64   ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<<
     65                                        input_file_name << "\n";
     66   test_frame_.CreateEmptyFrame(width_, height_,
     67                                width_, half_width_, half_width_);
     68 }
     69 
     70 void TestScaler::TearDown() {
     71   if (source_file_ != NULL) {
     72     ASSERT_EQ(0, fclose(source_file_));
     73   }
     74   source_file_ = NULL;
     75 }
     76 
     77 TEST_F(TestScaler, ScaleWithoutSettingValues) {
     78   EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_));
     79 }
     80 
     81 TEST_F(TestScaler, ScaleBadInitialValues) {
     82   EXPECT_EQ(-1, test_scaler_.Set(0, 288, 352, 288, kI420, kI420, kScalePoint));
     83   EXPECT_EQ(-1, test_scaler_.Set(704, 0, 352, 288, kI420, kI420, kScaleBox));
     84   EXPECT_EQ(-1, test_scaler_.Set(704, 576, 352, 0, kI420, kI420,
     85                                  kScaleBilinear));
     86   EXPECT_EQ(-1, test_scaler_.Set(704, 576, 0, 288, kI420, kI420, kScalePoint));
     87 }
     88 
     89 TEST_F(TestScaler, ScaleSendingNullSourcePointer) {
     90   VideoFrame null_src_frame;
     91   EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_));
     92 }
     93 
     94 TEST_F(TestScaler, ScaleSendingBufferTooSmall) {
     95   // Sending a buffer which is too small (should reallocate and update size)
     96   EXPECT_EQ(0, test_scaler_.Set(width_, height_,
     97                                 half_width_, half_height_,
     98                                 kI420, kI420,
     99                                 kScalePoint));
    100   VideoFrame test_frame2;
    101   rtc::scoped_ptr<uint8_t[]> orig_buffer(new uint8_t[frame_length_]);
    102   EXPECT_GT(fread(orig_buffer.get(), 1, frame_length_, source_file_), 0U);
    103   test_frame_.CreateFrame(orig_buffer.get(),
    104                           orig_buffer.get() + size_y_,
    105                           orig_buffer.get() + size_y_ + size_uv_,
    106                           width_, height_,
    107                           width_, half_width_, half_width_);
    108   EXPECT_EQ(0, test_scaler_.Scale(test_frame_, &test_frame2));
    109   EXPECT_GT(width_ * height_, test_frame2.allocated_size(kYPlane));
    110   EXPECT_GT(size_uv_, test_frame2.allocated_size(kUPlane));
    111   EXPECT_GT(size_uv_, test_frame2.allocated_size(kVPlane));
    112   EXPECT_EQ(half_width_, test_frame2.width());
    113   EXPECT_EQ(half_height_, test_frame2.height());
    114 }
    115 
    116 // TODO(mikhal): Converge the test into one function that accepts the method.
    117 #if defined(WEBRTC_ANDROID)
    118 #define MAYBE_PointScaleTest DISABLED_PointScaleTest
    119 #else
    120 #define MAYBE_PointScaleTest PointScaleTest
    121 #endif
    122 TEST_F(TestScaler, MAYBE_PointScaleTest) {
    123   double avg_psnr;
    124   FILE* source_file2;
    125   ScaleMethod method = kScalePoint;
    126   std::string out_name = webrtc::test::OutputPath() +
    127                          "LibYuvTest_PointScale_176_144.yuv";
    128   ScaleSequence(method,
    129                 source_file_, out_name,
    130                 width_, height_,
    131                 half_width_, half_height_);
    132   // Upsample back up and check PSNR.
    133   source_file2 = fopen(out_name.c_str(), "rb");
    134   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_"
    135       "upfrom_176_144.yuv";
    136   ScaleSequence(method,
    137                 source_file2, out_name,
    138                 176, 144,
    139                 352, 288);
    140   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    141   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    142       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    143   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    144   // average PSNR under same conditions.
    145   ASSERT_GT(avg_psnr, 27.9);
    146   ASSERT_EQ(0, fclose(source_file2));
    147   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_320_240.yuv";
    148   ScaleSequence(method,
    149                 source_file_, out_name,
    150                 width_, height_,
    151                 320, 240);
    152   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_704_576.yuv";
    153   ScaleSequence(method,
    154                 source_file_, out_name,
    155                 width_, height_,
    156                 width_ * 2, height_ * 2);
    157   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_300_200.yuv";
    158   ScaleSequence(method,
    159                 source_file_, out_name,
    160                 width_, height_,
    161                 300, 200);
    162   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_400_300.yuv";
    163   ScaleSequence(method,
    164                 source_file_, out_name,
    165                 width_, height_,
    166                 400, 300);
    167   // Down-sample to odd size frame and scale back up.
    168   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_282_231.yuv";
    169   ScaleSequence(method,
    170                 source_file_, out_name,
    171                 width_, height_,
    172                 282, 231);
    173   source_file2 = fopen(out_name.c_str(), "rb");
    174   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_"
    175       "upfrom_282_231.yuv";
    176   ScaleSequence(method,
    177                 source_file2, out_name,
    178                 282, 231,
    179                 352, 288);
    180   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    181   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    182       "original size: %f \n", width_, height_, 282, 231, avg_psnr);
    183   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    184   // average PSNR under same conditions.
    185   ASSERT_GT(avg_psnr, 25.8);
    186   ASSERT_EQ(0, fclose(source_file2));
    187 }
    188 
    189 #if defined(WEBRTC_ANDROID)
    190 #define MAYBE_BilinearScaleTest DISABLED_BiLinearScaleTest
    191 #else
    192 #define MAYBE_BilinearScaleTest BiLinearScaleTest
    193 #endif
    194 TEST_F(TestScaler, MAYBE_BiLinearScaleTest) {
    195   double avg_psnr;
    196   FILE* source_file2;
    197   ScaleMethod method = kScaleBilinear;
    198   std::string out_name = webrtc::test::OutputPath() +
    199                          "LibYuvTest_BilinearScale_176_144.yuv";
    200   ScaleSequence(method,
    201                 source_file_, out_name,
    202                 width_, height_,
    203                 width_ / 2, height_ / 2);
    204   // Up-sample back up and check PSNR.
    205   source_file2 = fopen(out_name.c_str(), "rb");
    206   out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_"
    207       "upfrom_176_144.yuv";
    208   ScaleSequence(method,
    209                 source_file2, out_name,
    210                 176, 144,
    211                 352, 288);
    212   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    213   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    214       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    215   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    216   // average PSNR under same conditions.
    217   ASSERT_GT(avg_psnr, 27.5);
    218   ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    219   ASSERT_EQ(0, fclose(source_file2));
    220   out_name = webrtc::test::OutputPath() +
    221              "LibYuvTest_BilinearScale_320_240.yuv";
    222   ScaleSequence(method,
    223                 source_file_, out_name,
    224                 width_, height_,
    225                 320, 240);
    226   out_name = webrtc::test::OutputPath() +
    227              "LibYuvTest_BilinearScale_704_576.yuv";
    228   ScaleSequence(method,
    229                 source_file_, out_name,
    230                 width_, height_,
    231                 width_ * 2, height_ * 2);
    232   out_name = webrtc::test::OutputPath() +
    233              "LibYuvTest_BilinearScale_300_200.yuv";
    234   ScaleSequence(method,
    235                 source_file_, out_name,
    236                 width_, height_,
    237                 300, 200);
    238   out_name = webrtc::test::OutputPath() +
    239              "LibYuvTest_BilinearScale_400_300.yuv";
    240   ScaleSequence(method,
    241                 source_file_, out_name,
    242                 width_, height_,
    243                 400, 300);
    244 }
    245 
    246 #if defined(WEBRTC_ANDROID)
    247 #define MAYBE_BoxScaleTest DISABLED_BoxScaleTest
    248 #else
    249 #define MAYBE_BoxScaleTest BoxScaleTest
    250 #endif
    251 TEST_F(TestScaler, MAYBE_BoxScaleTest) {
    252   double avg_psnr;
    253   FILE* source_file2;
    254   ScaleMethod method = kScaleBox;
    255   std::string out_name = webrtc::test::OutputPath() +
    256                          "LibYuvTest_BoxScale_176_144.yuv";
    257   ScaleSequence(method,
    258                 source_file_, out_name,
    259                 width_, height_,
    260                 width_ / 2, height_ / 2);
    261   // Up-sample back up and check PSNR.
    262   source_file2 = fopen(out_name.c_str(), "rb");
    263   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_"
    264       "upfrom_176_144.yuv";
    265   ScaleSequence(method,
    266                 source_file2, out_name,
    267                 176, 144,
    268                 352, 288);
    269   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    270   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    271       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    272   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    273   // average PSNR under same conditions.
    274   ASSERT_GT(avg_psnr, 27.5);
    275   ASSERT_EQ(0, fclose(source_file2));
    276   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_320_240.yuv";
    277   ScaleSequence(method,
    278                 source_file_, out_name,
    279                 width_, height_,
    280                 320, 240);
    281   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_704_576.yuv";
    282   ScaleSequence(method,
    283                 source_file_, out_name,
    284                 width_, height_,
    285                 width_ * 2, height_ * 2);
    286   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_300_200.yuv";
    287   ScaleSequence(method,
    288                 source_file_, out_name,
    289                 width_, height_,
    290                 300, 200);
    291   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_400_300.yuv";
    292   ScaleSequence(method,
    293                 source_file_, out_name,
    294                 width_, height_,
    295                 400, 300);
    296 }
    297 
    298 double TestScaler::ComputeAvgSequencePSNR(FILE* input_file,
    299                                           std::string out_name,
    300                                           int width, int height) {
    301   FILE* output_file;
    302   output_file = fopen(out_name.c_str(), "rb");
    303   assert(output_file != NULL);
    304   rewind(input_file);
    305   rewind(output_file);
    306 
    307   size_t required_size = CalcBufferSize(kI420, width, height);
    308   uint8_t* input_buffer = new uint8_t[required_size];
    309   uint8_t* output_buffer = new uint8_t[required_size];
    310 
    311   int frame_count = 0;
    312   double avg_psnr = 0;
    313   VideoFrame in_frame, out_frame;
    314   const int half_width = (width + 1) / 2;
    315   in_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
    316   out_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
    317   while (feof(input_file) == 0) {
    318     if (fread(input_buffer, 1, required_size, input_file) != required_size) {
    319       break;
    320     }
    321     if (fread(output_buffer, 1, required_size, output_file) != required_size) {
    322       break;
    323     }
    324     frame_count++;
    325     EXPECT_EQ(0, ConvertToI420(kI420, input_buffer, 0, 0, width, height,
    326                                required_size, kVideoRotation_0, &in_frame));
    327     EXPECT_EQ(0, ConvertToI420(kI420, output_buffer, 0, 0, width, height,
    328                                required_size, kVideoRotation_0, &out_frame));
    329     double psnr = I420PSNR(&in_frame, &out_frame);
    330     avg_psnr += psnr;
    331   }
    332   avg_psnr = avg_psnr / frame_count;
    333   assert(0 == fclose(output_file));
    334   delete [] input_buffer;
    335   delete [] output_buffer;
    336   return avg_psnr;
    337 }
    338 
    339 // TODO(mikhal): Move part to a separate scale test.
    340 void TestScaler::ScaleSequence(ScaleMethod method,
    341                    FILE* source_file, std::string out_name,
    342                    int src_width, int src_height,
    343                    int dst_width, int dst_height) {
    344   FILE* output_file;
    345   EXPECT_EQ(0, test_scaler_.Set(src_width, src_height,
    346                                dst_width, dst_height,
    347                                kI420, kI420, method));
    348 
    349   output_file = fopen(out_name.c_str(), "wb");
    350   ASSERT_TRUE(output_file != NULL);
    351 
    352   rewind(source_file);
    353 
    354   VideoFrame input_frame;
    355   VideoFrame output_frame;
    356   int64_t start_clock, total_clock;
    357   total_clock = 0;
    358   int frame_count = 0;
    359   size_t src_required_size = CalcBufferSize(kI420, src_width, src_height);
    360   rtc::scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[src_required_size]);
    361   int size_y = src_width * src_height;
    362   int size_uv = ((src_width + 1) / 2) * ((src_height + 1) / 2);
    363 
    364   // Running through entire sequence.
    365   while (feof(source_file) == 0) {
    366     if (fread(frame_buffer.get(), 1, src_required_size, source_file) !=
    367         src_required_size)
    368       break;
    369 
    370     input_frame.CreateFrame(frame_buffer.get(),
    371                             frame_buffer.get() + size_y,
    372                             frame_buffer.get() + size_y + size_uv,
    373                             src_width, src_height,
    374                             src_width, (src_width + 1) / 2,
    375                             (src_width + 1) / 2);
    376 
    377     start_clock = TickTime::MillisecondTimestamp();
    378     EXPECT_EQ(0, test_scaler_.Scale(input_frame, &output_frame));
    379     total_clock += TickTime::MillisecondTimestamp() - start_clock;
    380     if (PrintVideoFrame(output_frame, output_file) < 0) {
    381         return;
    382     }
    383     frame_count++;
    384   }
    385 
    386   if (frame_count) {
    387     printf("Scaling[%d %d] => [%d %d]: ",
    388            src_width, src_height, dst_width, dst_height);
    389     printf("Average time per frame[ms]: %.2lf\n",
    390              (static_cast<double>(total_clock) / frame_count));
    391   }
    392   ASSERT_EQ(0, fclose(output_file));
    393 }
    394 
    395 }  // namespace webrtc
    396