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/interface/tick_util.h"
     17 #include "webrtc/test/testsupport/fileutils.h"
     18 #include "webrtc/test/testsupport/gtest_disable.h"
     19 
     20 namespace webrtc {
     21 
     22 class TestScaler : public ::testing::Test {
     23  protected:
     24   TestScaler();
     25   virtual void SetUp();
     26   virtual void TearDown();
     27 
     28   void ScaleSequence(ScaleMethod method,
     29                      FILE* source_file, std::string out_name,
     30                      int src_width, int src_height,
     31                      int dst_width, int dst_height);
     32   // Computes the sequence average PSNR between an input sequence in
     33   // |input_file| and an output sequence with filename |out_name|. |width| and
     34   // |height| are the frame sizes of both sequences.
     35   double ComputeAvgSequencePSNR(FILE* input_file, std::string out_name,
     36                                 int width, int height);
     37 
     38   Scaler test_scaler_;
     39   FILE* source_file_;
     40   I420VideoFrame test_frame_;
     41   const int width_;
     42   const int half_width_;
     43   const int height_;
     44   const int half_height_;
     45   const int size_y_;
     46   const int size_uv_;
     47   const int frame_length_;
     48 };
     49 
     50 TestScaler::TestScaler()
     51     : source_file_(NULL),
     52       width_(352),
     53       half_width_(width_ / 2),
     54       height_(288),
     55       half_height_(height_ / 2),
     56       size_y_(width_ * height_),
     57       size_uv_(half_width_ * half_height_),
     58       frame_length_(CalcBufferSize(kI420, width_, height_)) {
     59 }
     60 
     61 void TestScaler::SetUp() {
     62   const std::string input_file_name =
     63       webrtc::test::ResourcePath("foreman_cif", "yuv");
     64   source_file_  = fopen(input_file_name.c_str(), "rb");
     65   ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<<
     66                                        input_file_name << "\n";
     67   test_frame_.CreateEmptyFrame(width_, height_,
     68                                width_, half_width_, half_width_);
     69 }
     70 
     71 void TestScaler::TearDown() {
     72   if (source_file_ != NULL) {
     73     ASSERT_EQ(0, fclose(source_file_));
     74   }
     75   source_file_ = NULL;
     76 }
     77 
     78 TEST_F(TestScaler, ScaleWithoutSettingValues) {
     79   EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_));
     80 }
     81 
     82 TEST_F(TestScaler, ScaleBadInitialValues) {
     83   EXPECT_EQ(-1, test_scaler_.Set(0, 288, 352, 288, kI420, kI420, kScalePoint));
     84   EXPECT_EQ(-1, test_scaler_.Set(704, 0, 352, 288, kI420, kI420, kScaleBox));
     85   EXPECT_EQ(-1, test_scaler_.Set(704, 576, 352, 0, kI420, kI420,
     86                                  kScaleBilinear));
     87   EXPECT_EQ(-1, test_scaler_.Set(704, 576, 0, 288, kI420, kI420, kScalePoint));
     88 }
     89 
     90 TEST_F(TestScaler, ScaleSendingNullSourcePointer) {
     91   I420VideoFrame null_src_frame;
     92   EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_));
     93 }
     94 
     95 TEST_F(TestScaler, ScaleSendingBufferTooSmall) {
     96   // Sending a buffer which is too small (should reallocate and update size)
     97   EXPECT_EQ(0, test_scaler_.Set(width_, height_,
     98                                 half_width_, half_height_,
     99                                 kI420, kI420,
    100                                 kScalePoint));
    101   I420VideoFrame test_frame2;
    102   scoped_ptr<uint8_t[]> orig_buffer(new uint8_t[frame_length_]);
    103   EXPECT_GT(fread(orig_buffer.get(), 1, frame_length_, source_file_), 0U);
    104   test_frame_.CreateFrame(size_y_, orig_buffer.get(),
    105                           size_uv_, orig_buffer.get() + size_y_,
    106                           size_uv_, orig_buffer.get() + size_y_ + size_uv_,
    107                           width_, height_,
    108                           width_, half_width_, half_width_);
    109   EXPECT_EQ(0, test_scaler_.Scale(test_frame_, &test_frame2));
    110   EXPECT_GT(width_ * height_, test_frame2.allocated_size(kYPlane));
    111   EXPECT_GT(size_uv_, test_frame2.allocated_size(kUPlane));
    112   EXPECT_GT(size_uv_, test_frame2.allocated_size(kVPlane));
    113   EXPECT_EQ(half_width_, test_frame2.width());
    114   EXPECT_EQ(half_height_, test_frame2.height());
    115 }
    116 
    117 //TODO (mikhal): Converge the test into one function that accepts the method.
    118 TEST_F(TestScaler, DISABLED_ON_ANDROID(PointScaleTest)) {
    119   double avg_psnr;
    120   FILE* source_file2;
    121   ScaleMethod method = kScalePoint;
    122   std::string out_name = webrtc::test::OutputPath() +
    123                          "LibYuvTest_PointScale_176_144.yuv";
    124   ScaleSequence(method,
    125                 source_file_, out_name,
    126                 width_, height_,
    127                 half_width_, half_height_);
    128   // Upsample back up and check PSNR.
    129   source_file2 = fopen(out_name.c_str(), "rb");
    130   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_"
    131       "upfrom_176_144.yuv";
    132   ScaleSequence(method,
    133                 source_file2, out_name,
    134                 176, 144,
    135                 352, 288);
    136   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    137   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    138       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    139   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    140   // average PSNR under same conditions.
    141   ASSERT_GT(avg_psnr, 27.9);
    142   ASSERT_EQ(0, fclose(source_file2));
    143   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_320_240.yuv";
    144   ScaleSequence(method,
    145                 source_file_, out_name,
    146                 width_, height_,
    147                 320, 240);
    148   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_704_576.yuv";
    149   ScaleSequence(method,
    150                 source_file_, out_name,
    151                 width_, height_,
    152                 width_ * 2, height_ * 2);
    153   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_300_200.yuv";
    154   ScaleSequence(method,
    155                 source_file_, out_name,
    156                 width_, height_,
    157                 300, 200);
    158   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_400_300.yuv";
    159   ScaleSequence(method,
    160                 source_file_, out_name,
    161                 width_, height_,
    162                 400, 300);
    163   // Down-sample to odd size frame and scale back up.
    164   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_282_231.yuv";
    165   ScaleSequence(method,
    166                 source_file_, out_name,
    167                 width_, height_,
    168                 282, 231);
    169   source_file2 = fopen(out_name.c_str(), "rb");
    170   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_"
    171       "upfrom_282_231.yuv";
    172   ScaleSequence(method,
    173                 source_file2, out_name,
    174                 282, 231,
    175                 352, 288);
    176   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    177   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    178       "original size: %f \n", width_, height_, 282, 231, avg_psnr);
    179   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    180   // average PSNR under same conditions.
    181   ASSERT_GT(avg_psnr, 25.8);
    182   ASSERT_EQ(0, fclose(source_file2));
    183   // Up-sample to odd size frame and scale back down.
    184   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_699_531.yuv";
    185   ScaleSequence(method,
    186                 source_file_, out_name,
    187                 width_, height_,
    188                 699, 531);
    189   source_file2 = fopen(out_name.c_str(), "rb");
    190   out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_"
    191       "downfrom_699_531.yuv";
    192   ScaleSequence(method,
    193                 source_file2, out_name,
    194                 699, 531,
    195                 352, 288);
    196   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    197   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    198       "original size: %f \n", width_, height_, 699, 531, avg_psnr);
    199   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    200   // average PSNR under same conditions.
    201   ASSERT_GT(avg_psnr, 37.8);
    202   ASSERT_EQ(0, fclose(source_file2));
    203 }
    204 
    205 TEST_F(TestScaler, DISABLED_ON_ANDROID(BiLinearScaleTest)) {
    206   double avg_psnr;
    207   FILE* source_file2;
    208   ScaleMethod method = kScaleBilinear;
    209   std::string out_name = webrtc::test::OutputPath() +
    210                          "LibYuvTest_BilinearScale_176_144.yuv";
    211   ScaleSequence(method,
    212                 source_file_, out_name,
    213                 width_, height_,
    214                 width_ / 2, height_ / 2);
    215   // Up-sample back up and check PSNR.
    216   source_file2 = fopen(out_name.c_str(), "rb");
    217   out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_"
    218       "upfrom_176_144.yuv";
    219   ScaleSequence(method,
    220                 source_file2, out_name,
    221                 176, 144,
    222                 352, 288);
    223   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    224   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    225       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    226   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    227   // average PSNR under same conditions.
    228   ASSERT_GT(avg_psnr, 27.5);
    229   ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    230   ASSERT_EQ(0, fclose(source_file2));
    231   out_name = webrtc::test::OutputPath() +
    232              "LibYuvTest_BilinearScale_320_240.yuv";
    233   ScaleSequence(method,
    234                 source_file_, out_name,
    235                 width_, height_,
    236                 320, 240);
    237   out_name = webrtc::test::OutputPath() +
    238              "LibYuvTest_BilinearScale_704_576.yuv";
    239   ScaleSequence(method,
    240                 source_file_, out_name,
    241                 width_, height_,
    242                 width_ * 2, height_ * 2);
    243   out_name = webrtc::test::OutputPath() +
    244              "LibYuvTest_BilinearScale_300_200.yuv";
    245   ScaleSequence(method,
    246                 source_file_, out_name,
    247                 width_, height_,
    248                 300, 200);
    249   out_name = webrtc::test::OutputPath() +
    250              "LibYuvTest_BilinearScale_400_300.yuv";
    251   ScaleSequence(method,
    252                 source_file_, out_name,
    253                 width_, height_,
    254                 400, 300);
    255   // Down-sample to odd size frame and scale back up.
    256   out_name = webrtc::test::OutputPath() +
    257       "LibYuvTest_BilinearScale_282_231.yuv";
    258   ScaleSequence(method,
    259                 source_file_, out_name,
    260                 width_, height_,
    261                 282, 231);
    262   source_file2 = fopen(out_name.c_str(), "rb");
    263   out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_"
    264       "upfrom_282_231.yuv";
    265   ScaleSequence(method,
    266                 source_file2, out_name,
    267                 282, 231,
    268                 width_, height_);
    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_, 282, 231, 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, 29.7);
    275   ASSERT_EQ(0, fclose(source_file2));
    276   // Upsample to odd size frame and scale back down.
    277   out_name = webrtc::test::OutputPath() +
    278       "LibYuvTest_BilinearScale_699_531.yuv";
    279   ScaleSequence(method,
    280                 source_file_, out_name,
    281                 width_, height_,
    282                 699, 531);
    283   source_file2 = fopen(out_name.c_str(), "rb");
    284   out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_"
    285       "downfrom_699_531.yuv";
    286   ScaleSequence(method,
    287                 source_file2, out_name,
    288                 699, 531,
    289                 width_, height_);
    290   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    291   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    292       "original size: %f \n", width_, height_, 699, 531, avg_psnr);
    293   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    294   // average PSNR under same conditions.
    295   ASSERT_GT(avg_psnr, 31.4);
    296   ASSERT_EQ(0, fclose(source_file2));
    297 }
    298 
    299 TEST_F(TestScaler, DISABLED_ON_ANDROID(BoxScaleTest)) {
    300   double avg_psnr;
    301   FILE* source_file2;
    302   ScaleMethod method = kScaleBox;
    303   std::string out_name = webrtc::test::OutputPath() +
    304                          "LibYuvTest_BoxScale_176_144.yuv";
    305   ScaleSequence(method,
    306                 source_file_, out_name,
    307                 width_, height_,
    308                 width_ / 2, height_ / 2);
    309   // Up-sample back up and check PSNR.
    310   source_file2 = fopen(out_name.c_str(), "rb");
    311   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_"
    312       "upfrom_176_144.yuv";
    313   ScaleSequence(method,
    314                 source_file2, out_name,
    315                 176, 144,
    316                 352, 288);
    317   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    318   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    319       "original size: %f \n", width_, height_, 176, 144, avg_psnr);
    320   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    321   // average PSNR under same conditions.
    322   ASSERT_GT(avg_psnr, 27.5);
    323   ASSERT_EQ(0, fclose(source_file2));
    324   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_320_240.yuv";
    325   ScaleSequence(method,
    326                 source_file_, out_name,
    327                 width_, height_,
    328                 320, 240);
    329   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_704_576.yuv";
    330   ScaleSequence(method,
    331                 source_file_, out_name,
    332                 width_, height_,
    333                 width_ * 2, height_ * 2);
    334   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_300_200.yuv";
    335   ScaleSequence(method,
    336                 source_file_, out_name,
    337                 width_, height_,
    338                 300, 200);
    339   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_400_300.yuv";
    340   ScaleSequence(method,
    341                 source_file_, out_name,
    342                 width_, height_,
    343                 400, 300);
    344   // Down-sample to odd size frame and scale back up.
    345   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_282_231.yuv";
    346   ScaleSequence(method,
    347                 source_file_, out_name,
    348                 width_, height_,
    349                 282, 231);
    350   source_file2 = fopen(out_name.c_str(), "rb");
    351   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_"
    352        "upfrom_282_231.yuv";
    353   ScaleSequence(method,
    354                 source_file2, out_name,
    355                 282, 231,
    356                 width_, height_);
    357   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    358   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    359       "original size: %f \n", width_, height_, 282, 231, avg_psnr);
    360   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    361   // average PSNR under same conditions.
    362   ASSERT_GT(avg_psnr, 29.7);
    363   ASSERT_EQ(0, fclose(source_file2));
    364   // Up-sample to odd size frame and scale back down.
    365   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_699_531.yuv";
    366   ScaleSequence(method,
    367                 source_file_, out_name,
    368                 width_, height_,
    369                 699, 531);
    370   source_file2 = fopen(out_name.c_str(), "rb");
    371   out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_"
    372        "downfrom_699_531.yuv";
    373   ScaleSequence(method,
    374                 source_file2, out_name,
    375                 699, 531,
    376                 width_, height_);
    377   avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_);
    378   printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to "
    379        "original size: %f \n", width_, height_, 699, 531, avg_psnr);
    380   // Average PSNR for lower bound in assert is ~0.1dB lower than the actual
    381   // average PSNR under same conditions.
    382   ASSERT_GT(avg_psnr, 31.4);
    383   ASSERT_EQ(0, fclose(source_file2));
    384 }
    385 
    386 double TestScaler::ComputeAvgSequencePSNR(FILE* input_file,
    387                                           std::string out_name,
    388                                           int width, int height) {
    389   FILE* output_file;
    390   output_file = fopen(out_name.c_str(), "rb");
    391   assert(output_file != NULL);
    392   rewind(input_file);
    393   rewind(output_file);
    394 
    395   int required_size = CalcBufferSize(kI420, width, height);
    396   uint8_t* input_buffer = new uint8_t[required_size];
    397   uint8_t* output_buffer = new uint8_t[required_size];
    398 
    399   int frame_count = 0;
    400   double avg_psnr = 0;
    401   I420VideoFrame in_frame, out_frame;
    402   while (feof(input_file) == 0) {
    403     if ((size_t)required_size !=
    404         fread(input_buffer, 1, required_size, input_file)) {
    405       break;
    406     }
    407     if ((size_t)required_size !=
    408         fread(output_buffer, 1, required_size, output_file)) {
    409       break;
    410     }
    411     frame_count++;
    412     ConvertFromI420(in_frame, kI420, 0, input_buffer);
    413     ConvertFromI420(out_frame, kI420, 0, output_buffer);
    414     double psnr = I420PSNR(&in_frame, &out_frame);
    415     avg_psnr += psnr;
    416   }
    417   avg_psnr = avg_psnr / frame_count;
    418   assert(0 == fclose(output_file));
    419   delete [] input_buffer;
    420   delete [] output_buffer;
    421   return avg_psnr;
    422 }
    423 
    424 // TODO (mikhal): Move part to a separate scale test.
    425 void TestScaler::ScaleSequence(ScaleMethod method,
    426                    FILE* source_file, std::string out_name,
    427                    int src_width, int src_height,
    428                    int dst_width, int dst_height) {
    429   FILE* output_file;
    430   EXPECT_EQ(0, test_scaler_.Set(src_width, src_height,
    431                                dst_width, dst_height,
    432                                kI420, kI420, method));
    433 
    434   output_file = fopen(out_name.c_str(), "wb");
    435   ASSERT_TRUE(output_file != NULL);
    436 
    437   rewind(source_file);
    438 
    439   I420VideoFrame input_frame;
    440   I420VideoFrame output_frame;
    441   int64_t start_clock, total_clock;
    442   total_clock = 0;
    443   int frame_count = 0;
    444   int src_required_size = CalcBufferSize(kI420, src_width, src_height);
    445   scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[src_required_size]);
    446   int size_y = src_width * src_height;
    447   int size_uv = ((src_width + 1) / 2) * ((src_height + 1) / 2);
    448 
    449   // Running through entire sequence.
    450   while (feof(source_file) == 0) {
    451     if ((size_t)src_required_size !=
    452       fread(frame_buffer.get(), 1, src_required_size, source_file))
    453       break;
    454 
    455     input_frame.CreateFrame(size_y, frame_buffer.get(),
    456                             size_uv, frame_buffer.get() + size_y,
    457                             size_uv, frame_buffer.get() + size_y + size_uv,
    458                             src_width, src_height,
    459                             src_width, (src_width + 1) / 2,
    460                             (src_width + 1) / 2);
    461 
    462     start_clock = TickTime::MillisecondTimestamp();
    463     EXPECT_EQ(0, test_scaler_.Scale(input_frame, &output_frame));
    464     total_clock += TickTime::MillisecondTimestamp() - start_clock;
    465     if (PrintI420VideoFrame(output_frame, output_file) < 0) {
    466         return;
    467     }
    468     frame_count++;
    469   }
    470 
    471   if (frame_count) {
    472     printf("Scaling[%d %d] => [%d %d]: ",
    473            src_width, src_height, dst_width, dst_height);
    474     printf("Average time per frame[ms]: %.2lf\n",
    475              (static_cast<double>(total_clock) / frame_count));
    476   }
    477   ASSERT_EQ(0, fclose(output_file));
    478 }
    479 
    480 }  // namespace webrtc
    481