summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/rtc_tools/frame_analyzer/video_geometry_aligner.cc
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
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.cc178
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