diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc')
-rw-r--r-- | third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc b/third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc new file mode 100644 index 0000000000..efb033317a --- /dev/null +++ b/third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 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 "rtc_tools/frame_analyzer/video_geometry_aligner.h" + +#include <map> + +#include "api/make_ref_counted.h" +#include "api/video/i420_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_tools/frame_analyzer/video_quality_analysis.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +namespace webrtc { +namespace test { + +namespace { + +bool IsValidRegion(const CropRegion& region, + const rtc::scoped_refptr<I420BufferInterface>& frame) { + return region.left >= 0 && region.right >= 0 && region.top >= 0 && + region.bottom >= 0 && region.left + region.right < frame->width() && + region.top + region.bottom < frame->height(); +} + +} // namespace + +rtc::scoped_refptr<I420BufferInterface> CropAndZoom( + const CropRegion& crop_region, + const rtc::scoped_refptr<I420BufferInterface>& frame) { + RTC_CHECK(IsValidRegion(crop_region, frame)); + + const int uv_crop_left = crop_region.left / 2; + const int uv_crop_top = crop_region.top / 2; + + const int cropped_width = + frame->width() - crop_region.left - crop_region.right; + const int cropped_height = + frame->height() - crop_region.top - crop_region.bottom; + + // Crop by only adjusting pointers. + const uint8_t* y_plane = + frame->DataY() + frame->StrideY() * crop_region.top + crop_region.left; + const uint8_t* u_plane = + frame->DataU() + frame->StrideU() * uv_crop_top + uv_crop_left; + const uint8_t* v_plane = + frame->DataV() + frame->StrideV() * uv_crop_top + uv_crop_left; + + // Stretch the cropped frame to the original size using libyuv. + rtc::scoped_refptr<I420Buffer> adjusted_frame = + I420Buffer::Create(frame->width(), frame->height()); + libyuv::I420Scale(y_plane, frame->StrideY(), u_plane, frame->StrideU(), + v_plane, frame->StrideV(), cropped_width, cropped_height, + adjusted_frame->MutableDataY(), adjusted_frame->StrideY(), + adjusted_frame->MutableDataU(), adjusted_frame->StrideU(), + adjusted_frame->MutableDataV(), adjusted_frame->StrideV(), + frame->width(), frame->height(), libyuv::kFilterBox); + + return adjusted_frame; +} + +CropRegion CalculateCropRegion( + const rtc::scoped_refptr<I420BufferInterface>& reference_frame, + const rtc::scoped_refptr<I420BufferInterface>& test_frame) { + RTC_CHECK_EQ(reference_frame->width(), test_frame->width()); + RTC_CHECK_EQ(reference_frame->height(), test_frame->height()); + + CropRegion best_region; + double best_ssim = Ssim(reference_frame, test_frame); + + typedef int CropRegion::*CropParameter; + CropParameter crop_parameters[4] = {&CropRegion::left, &CropRegion::top, + &CropRegion::right, &CropRegion::bottom}; + + while (true) { + // Find the parameter in which direction SSIM improves the most. + CropParameter best_parameter = nullptr; + const CropRegion prev_best_region = best_region; + + for (CropParameter crop_parameter : crop_parameters) { + CropRegion test_region = prev_best_region; + ++(test_region.*crop_parameter); + + if (!IsValidRegion(test_region, reference_frame)) + continue; + + const double ssim = + Ssim(CropAndZoom(test_region, reference_frame), test_frame); + + if (ssim > best_ssim) { + best_ssim = ssim; + best_parameter = crop_parameter; + best_region = test_region; + } + } + + // No improvement among any direction, stop iteration. + if (best_parameter == nullptr) + break; + + // Iterate in the best direction as long as it improves SSIM. + for (CropRegion test_region = best_region; + IsValidRegion(test_region, reference_frame); + ++(test_region.*best_parameter)) { + const double ssim = + Ssim(CropAndZoom(test_region, reference_frame), test_frame); + if (ssim <= best_ssim) + break; + + best_ssim = ssim; + best_region = test_region; + } + } + + return best_region; +} + +rtc::scoped_refptr<I420BufferInterface> AdjustCropping( + const rtc::scoped_refptr<I420BufferInterface>& reference_frame, + const rtc::scoped_refptr<I420BufferInterface>& test_frame) { + return CropAndZoom(CalculateCropRegion(reference_frame, test_frame), + reference_frame); +} + +rtc::scoped_refptr<Video> AdjustCropping( + const rtc::scoped_refptr<Video>& reference_video, + const rtc::scoped_refptr<Video>& test_video) { + class CroppedVideo : public Video { + public: + CroppedVideo(const rtc::scoped_refptr<Video>& reference_video, + const rtc::scoped_refptr<Video>& test_video) + : reference_video_(reference_video), test_video_(test_video) { + RTC_CHECK_EQ(reference_video->number_of_frames(), + test_video->number_of_frames()); + RTC_CHECK_EQ(reference_video->width(), test_video->width()); + RTC_CHECK_EQ(reference_video->height(), test_video->height()); + } + + int width() const override { return test_video_->width(); } + int height() const override { return test_video_->height(); } + size_t number_of_frames() const override { + return test_video_->number_of_frames(); + } + + rtc::scoped_refptr<I420BufferInterface> GetFrame( + size_t index) const override { + const rtc::scoped_refptr<I420BufferInterface> reference_frame = + reference_video_->GetFrame(index); + + // Only calculate cropping region once per frame since it's expensive. + if (!crop_regions_.count(index)) { + crop_regions_[index] = + CalculateCropRegion(reference_frame, test_video_->GetFrame(index)); + } + + return CropAndZoom(crop_regions_[index], reference_frame); + } + + private: + const rtc::scoped_refptr<Video> reference_video_; + const rtc::scoped_refptr<Video> test_video_; + // Mutable since this is a cache that affects performance and not logical + // behavior. + mutable std::map<size_t, CropRegion> crop_regions_; + }; + + return rtc::make_ref_counted<CroppedVideo>(reference_video, test_video); +} + +} // namespace test +} // namespace webrtc |