1040 lines
44 KiB
C++
1040 lines
44 KiB
C++
// Copyright 2019 The MediaPipe Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "mediapipe/examples/desktop/autoflip/quality/utils.h"
|
|
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
#include "mediapipe/examples/desktop/autoflip/autoflip_messages.pb.h"
|
|
#include "mediapipe/examples/desktop/autoflip/quality/cropping.pb.h"
|
|
#include "mediapipe/framework/port/gmock.h"
|
|
#include "mediapipe/framework/port/gtest.h"
|
|
#include "mediapipe/framework/port/integral_types.h"
|
|
#include "mediapipe/framework/port/opencv_core_inc.h"
|
|
#include "mediapipe/framework/port/status_matchers.h"
|
|
|
|
namespace mediapipe {
|
|
namespace autoflip {
|
|
namespace {
|
|
|
|
using ::testing::HasSubstr;
|
|
|
|
const int64 kTimestamp = 0;
|
|
const int kOriginalWidth = 100;
|
|
const int kOriginalHeight = 100;
|
|
const double kTargetAspectRatio = 1.5;
|
|
const int kNumKeyFrames = 5;
|
|
const int64 kKeyFrameTimestampDiff = 1e6 / kNumKeyFrames;
|
|
const int kTargetWidth = 50;
|
|
const int kTargetHeight = 50;
|
|
|
|
// Makes a rectangle given the corner (x, y) and the size (width, height).
|
|
Rect MakeRect(const int x, const int y, const int width, const int height) {
|
|
Rect rect;
|
|
rect.set_x(x);
|
|
rect.set_y(y);
|
|
rect.set_width(width);
|
|
rect.set_height(height);
|
|
return rect;
|
|
}
|
|
|
|
// Makes a rectangle given the center (center_x, center_y) and the half size
|
|
// (half_width, half_height).
|
|
Rect MakeRectFromCenterAndHalfSize(const float center_x, const float center_y,
|
|
const float half_width,
|
|
const float half_height) {
|
|
Rect rect;
|
|
rect.set_x(center_x - half_width);
|
|
rect.set_y(center_y - half_height);
|
|
rect.set_width(half_width * 2);
|
|
rect.set_height(half_height * 2);
|
|
return rect;
|
|
}
|
|
|
|
// Computes the center of a rectangle as a pair (center_x, center_y).
|
|
std::pair<float, float> RectCenter(const Rect& rect) {
|
|
return std::make_pair(rect.x() + rect.width() / 2.0,
|
|
rect.y() + rect.height() / 2.0);
|
|
}
|
|
|
|
// Makes a normalized rectangle given the corner (x, y) and the size (width,
|
|
// height).
|
|
RectF MakeRectF(const double x, const double y, const double width,
|
|
const double height) {
|
|
RectF rectf;
|
|
rectf.set_x(x);
|
|
rectf.set_y(y);
|
|
rectf.set_width(width);
|
|
rectf.set_height(height);
|
|
return rectf;
|
|
}
|
|
|
|
// Adds a detection to the detection set given location.
|
|
void AddDetectionFromLocation(const Rect& loc, DetectionSet* detections) {
|
|
auto* detection = detections->add_detections();
|
|
*(detection->mutable_location()) = loc;
|
|
}
|
|
|
|
// Adds a detection to the detection set given normalized location.
|
|
void AddDetectionFromNormalizedLocation(const RectF& normalized_loc,
|
|
DetectionSet* detections) {
|
|
auto* detection = detections->add_detections();
|
|
*(detection->mutable_location_normalized()) = normalized_loc;
|
|
}
|
|
|
|
// Checks whether two rectangles are the same, with an optional scaling factor.
|
|
bool CheckRectsEqual(const Rect& rect1, const Rect& rect2,
|
|
const double scale_x = 1.0, const double scale_y = 1.0) {
|
|
return (static_cast<int>(round(scale_x * rect2.x())) == rect1.x() &&
|
|
static_cast<int>(round(scale_y * rect2.y())) == rect1.y() &&
|
|
static_cast<int>(round(scale_x * rect2.width())) == rect1.width() &&
|
|
static_cast<int>(round(scale_y * rect2.height())) == rect1.height());
|
|
}
|
|
|
|
// Adds a detection to the detection set given its score and whether it is
|
|
// required.
|
|
void AddDetectionFromScoreAndIsRequired(const double score,
|
|
const bool is_required,
|
|
DetectionSet* detections) {
|
|
auto* detection = detections->add_detections();
|
|
detection->set_score(score);
|
|
detection->set_is_required(is_required);
|
|
}
|
|
|
|
// Returns default settings for KeyFrameCropOptions. Populates target size to be
|
|
// the default target size.
|
|
KeyFrameCropOptions GetDefaultKeyFrameCropOptions() {
|
|
KeyFrameCropOptions key_frame_crop_options;
|
|
key_frame_crop_options.set_target_width(kTargetWidth);
|
|
key_frame_crop_options.set_target_height(kTargetHeight);
|
|
return key_frame_crop_options;
|
|
}
|
|
|
|
// Returns default values for KeyFrameCropResults. Sets each frame to have
|
|
// covered all the required regions and non-required regions, and have required
|
|
// crop region (10, 10+20) x (10, 10+20), (full) crop region (0, 50) x (0, 50),
|
|
// and region score 1.0.
|
|
std::vector<KeyFrameCropResult> GetDefaultKeyFrameCropResults() {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(kNumKeyFrames);
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
key_frame_crop_results[i].set_are_required_regions_covered_in_target_size(
|
|
true);
|
|
key_frame_crop_results[i].set_fraction_non_required_covered(1.0);
|
|
key_frame_crop_results[i].set_region_is_empty(false);
|
|
key_frame_crop_results[i].set_required_region_is_empty(false);
|
|
*(key_frame_crop_results[i].mutable_region()) = MakeRect(0, 0, 50, 50);
|
|
*(key_frame_crop_results[i].mutable_required_region()) =
|
|
MakeRect(10, 10, 20, 20);
|
|
key_frame_crop_results[i].set_region_score(1.0);
|
|
key_frame_crop_results[i].set_timestamp_ms(kKeyFrameTimestampDiff * i);
|
|
}
|
|
return key_frame_crop_results;
|
|
}
|
|
|
|
// Checks that ScaleRect properly scales Rect and RectF objects.
|
|
TEST(UtilTest, ScaleRect) {
|
|
Rect scaled_rect;
|
|
ScaleRect(MakeRect(10, 10, 20, 30), 1.5, 2.0, &scaled_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(scaled_rect, MakeRect(15, 20, 30, 60)));
|
|
|
|
ScaleRect(MakeRectF(0.5, 0.9, 1.36, 0.748), 100, 50, &scaled_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(scaled_rect, MakeRect(50, 45, 136, 37)));
|
|
}
|
|
|
|
// Checks that NormalizedRectToRect properly converts a RectF object to a Rect
|
|
// object given width and height.
|
|
TEST(UtilTest, NormalizedRectToRect) {
|
|
const RectF normalized_rect = MakeRectF(0.1, 1.0, 2.5, 0.9);
|
|
Rect rect;
|
|
NormalizedRectToRect(normalized_rect, 100, 100, &rect);
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(10, 100, 250, 90)));
|
|
}
|
|
|
|
// Checks that ClampRect properly clamps a Rect object in [x0, y0] and [x1, y1].
|
|
TEST(UtilTest, ClampRect) {
|
|
// Overlaps at a corner.
|
|
Rect rect = MakeRect(-10, -10, 80, 20);
|
|
MP_EXPECT_OK(ClampRect(0, 0, 100, 100, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(0, 0, 70, 10)));
|
|
// Overlaps on a side.
|
|
rect = MakeRect(10, -10, 80, 20);
|
|
MP_EXPECT_OK(ClampRect(0, 0, 100, 100, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(10, 0, 80, 10)));
|
|
// Inside.
|
|
rect = MakeRect(10, 10, 80, 10);
|
|
MP_EXPECT_OK(ClampRect(0, 0, 100, 100, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(10, 10, 80, 10)));
|
|
// Outside.
|
|
rect = MakeRect(-10, 10, 0, 0);
|
|
EXPECT_FALSE(ClampRect(0, 0, 100, 100, &rect).ok());
|
|
}
|
|
|
|
// Checks that ClampRect properly clamps a Rect object in [0, 0] and [width,
|
|
// height].
|
|
TEST(UtilTest, ClampRectConvenienceFunction) {
|
|
Rect rect = MakeRect(-10, 0, 80, 10);
|
|
MP_EXPECT_OK(ClampRect(kOriginalWidth, kOriginalHeight, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(0, 0, 70, 10)));
|
|
rect = MakeRect(-10, 0, 120, 10);
|
|
MP_EXPECT_OK(ClampRect(kOriginalWidth, kOriginalHeight, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(0, 0, 100, 10)));
|
|
rect = MakeRect(10, 0, 70, 120);
|
|
MP_EXPECT_OK(ClampRect(kOriginalWidth, kOriginalHeight, &rect));
|
|
EXPECT_TRUE(CheckRectsEqual(rect, MakeRect(10, 0, 70, 100)));
|
|
}
|
|
|
|
// Checks that RectUnion properly takes the union of two Rect objects.
|
|
TEST(UtilTest, RectUnion) {
|
|
// Base rectangle and new rectangle are partially overlapping.
|
|
Rect base_rect = MakeRect(40, 40, 40, 40);
|
|
RectUnion(MakeRect(20, 30, 40, 40), &base_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(base_rect, MakeRect(20, 30, 60, 50)));
|
|
// Base rectangle contains new rectangle.
|
|
base_rect = MakeRect(40, 40, 40, 40);
|
|
RectUnion(MakeRect(50, 50, 10, 10), &base_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(base_rect, MakeRect(40, 40, 40, 40)));
|
|
// Base rectangle is contained by new rectangle.
|
|
base_rect = MakeRect(40, 40, 40, 40);
|
|
RectUnion(MakeRect(30, 30, 50, 50), &base_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(base_rect, MakeRect(30, 30, 50, 50)));
|
|
// Base rectangle and new rectangle are disjoint.
|
|
base_rect = MakeRect(40, 40, 40, 40);
|
|
RectUnion(MakeRect(15, 25, 20, 5), &base_rect);
|
|
EXPECT_TRUE(CheckRectsEqual(base_rect, MakeRect(15, 25, 65, 55)));
|
|
}
|
|
|
|
// Checks that PackCropFrame fails on nullptr return object.
|
|
TEST(UtilTest, PackKeyFrameInfoFailsOnNullObject) {
|
|
DetectionSet detections;
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo* key_frame_info_ptr = nullptr;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, key_frame_info_ptr);
|
|
EXPECT_FALSE(status.ok());
|
|
}
|
|
|
|
// Checks that PackCropFrame fails on invalid frame size.
|
|
TEST(UtilTest, PackKeyFrameInfoFailsOnInvalidFrameSize) {
|
|
DetectionSet detections;
|
|
const int feature_width = -1, feature_height = 0;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info)
|
|
.ToString();
|
|
EXPECT_THAT(status, testing::HasSubstr("Invalid frame size"));
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly packs timestamp.
|
|
TEST(UtilTest, PackKeyFrameInfoPacksTimestamp) {
|
|
DetectionSet detections;
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
EXPECT_EQ(key_frame_info.timestamp_ms(), kTimestamp);
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly packs detections.
|
|
TEST(UtilTest, PackKeyFrameInfoPacksDetections) {
|
|
DetectionSet detections;
|
|
AddDetectionFromLocation(MakeRect(0, 0, 10, 10), &detections);
|
|
AddDetectionFromLocation(MakeRect(20, 20, 30, 10), &detections);
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
EXPECT_EQ(key_frame_info.detections().detections_size(),
|
|
detections.detections_size());
|
|
for (int i = 0; i < detections.detections_size(); ++i) {
|
|
const auto& original_rect = detections.detections(i).location();
|
|
const auto& rect = key_frame_info.detections().detections(i).location();
|
|
EXPECT_TRUE(CheckRectsEqual(rect, original_rect));
|
|
}
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly converts normalized location to location.
|
|
TEST(UtilTest, PackKeyFrameInfoUnnormalizesLocations) {
|
|
DetectionSet detections;
|
|
AddDetectionFromNormalizedLocation(MakeRectF(0.1, 0.1, 0.1, 0.1),
|
|
&detections);
|
|
AddDetectionFromNormalizedLocation(MakeRectF(0.242, 0.256, 0.378, 0.399),
|
|
&detections);
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
const auto& out_rect1 = key_frame_info.detections().detections(0).location();
|
|
const auto& out_rect2 = key_frame_info.detections().detections(1).location();
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect1, MakeRect(10, 10, 10, 10)));
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect2, MakeRect(24, 26, 38, 40)));
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly scales location.
|
|
TEST(UtilTest, PackKeyFrameInfoScalesLocations) {
|
|
DetectionSet detections;
|
|
AddDetectionFromLocation(MakeRect(10, 10, 10, 10), &detections);
|
|
AddDetectionFromLocation(MakeRect(20, 20, 30, 30), &detections);
|
|
const double scaling = 2.0;
|
|
const int feature_width = kOriginalWidth / scaling;
|
|
const int feature_height = kOriginalHeight / scaling;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
EXPECT_EQ(key_frame_info.detections().detections_size(),
|
|
detections.detections_size());
|
|
for (int i = 0; i < detections.detections_size(); ++i) {
|
|
const auto& original_rect = detections.detections(i).location();
|
|
const auto& rect = key_frame_info.detections().detections(i).location();
|
|
EXPECT_TRUE(CheckRectsEqual(rect, original_rect, scaling, scaling));
|
|
}
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly clamps location to be within frame size.
|
|
TEST(UtilTest, PackKeyFrameInfoClampsLocations) {
|
|
DetectionSet detections;
|
|
AddDetectionFromLocation(MakeRect(10, 10, 100, 10), &detections);
|
|
AddDetectionFromLocation(MakeRect(0, -10, 110, 100), &detections);
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
EXPECT_EQ(key_frame_info.detections().detections_size(),
|
|
detections.detections_size());
|
|
const auto& out_rect1 = key_frame_info.detections().detections(0).location();
|
|
const auto& out_rect2 = key_frame_info.detections().detections(1).location();
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect1, MakeRect(10, 10, 90, 10)));
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect2, MakeRect(0, 0, 100, 90)));
|
|
}
|
|
|
|
// Checks that PackCropFrame correctly clamps normalized location to be within
|
|
// frame size.
|
|
TEST(UtilTest, PackKeyFrameInfoClampsNormalizedLocations) {
|
|
DetectionSet detections;
|
|
AddDetectionFromNormalizedLocation(MakeRectF(-0.05, 0.3, 0.4, 0.8),
|
|
&detections);
|
|
AddDetectionFromNormalizedLocation(MakeRectF(0.05, -0.1, 1.0, 1.1),
|
|
&detections);
|
|
const int feature_width = kOriginalWidth, feature_height = kOriginalHeight;
|
|
|
|
KeyFrameInfo key_frame_info;
|
|
const auto status =
|
|
PackKeyFrameInfo(kTimestamp, detections, kOriginalWidth, kOriginalHeight,
|
|
feature_width, feature_height, &key_frame_info);
|
|
|
|
MP_EXPECT_OK(status);
|
|
EXPECT_EQ(key_frame_info.detections().detections_size(),
|
|
detections.detections_size());
|
|
const auto& out_rect1 = key_frame_info.detections().detections(0).location();
|
|
const auto& out_rect2 = key_frame_info.detections().detections(1).location();
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect1, MakeRect(0, 30, 35, 70)));
|
|
EXPECT_TRUE(CheckRectsEqual(out_rect2, MakeRect(5, 0, 95, 100)));
|
|
}
|
|
|
|
// Checks that SortDetections correctly handles empty regions.
|
|
TEST(UtilTest, SortDetectionsHandlesEmptyRegions) {
|
|
DetectionSet detections;
|
|
std::vector<SalientRegion> required, non_required;
|
|
MP_EXPECT_OK(SortDetections(detections, &required, &non_required));
|
|
EXPECT_EQ(detections.detections_size(),
|
|
required.size() + non_required.size());
|
|
}
|
|
|
|
// Checks that SortDetections correctly separates required and non-required
|
|
// salient regions.
|
|
TEST(UtilTest, SortDetectionsSeparatesRequiredAndNonRequiredRegions) {
|
|
DetectionSet detections;
|
|
auto gen_bool = std::bind(std::uniform_int_distribution<>(0, 1),
|
|
std::default_random_engine());
|
|
for (int i = 0; i < 100; ++i) {
|
|
const bool is_required = gen_bool();
|
|
AddDetectionFromScoreAndIsRequired(1.0, is_required, &detections);
|
|
}
|
|
|
|
std::vector<SalientRegion> required, non_required;
|
|
MP_EXPECT_OK(SortDetections(detections, &required, &non_required));
|
|
EXPECT_EQ(detections.detections_size(),
|
|
required.size() + non_required.size());
|
|
for (int i = 0; i < required.size(); ++i) {
|
|
EXPECT_TRUE(required[i].is_required());
|
|
}
|
|
for (int i = 0; i < non_required.size(); ++i) {
|
|
EXPECT_FALSE(non_required[i].is_required());
|
|
}
|
|
}
|
|
|
|
// Checks that SortDetections correctly sorts regions based on scores.
|
|
TEST(UtilTest, SortDetectionsSortsRegions) {
|
|
DetectionSet detections;
|
|
auto gen_score = std::bind(std::uniform_real_distribution<>(0.0, 1.0),
|
|
std::default_random_engine());
|
|
auto gen_bool = std::bind(std::uniform_int_distribution<>(0, 1),
|
|
std::default_random_engine());
|
|
for (int i = 0; i < 100; ++i) {
|
|
const double score = gen_score();
|
|
const bool is_required = gen_bool();
|
|
AddDetectionFromScoreAndIsRequired(score, is_required, &detections);
|
|
}
|
|
|
|
std::vector<SalientRegion> required, non_required;
|
|
MP_EXPECT_OK(SortDetections(detections, &required, &non_required));
|
|
EXPECT_EQ(detections.detections_size(),
|
|
required.size() + non_required.size());
|
|
for (int i = 0; i < required.size() - 1; ++i) {
|
|
EXPECT_GE(required[i].score(), required[i + 1].score());
|
|
}
|
|
for (int i = 0; i < non_required.size() - 1; ++i) {
|
|
EXPECT_GE(non_required[i].score(), non_required[i + 1].score());
|
|
}
|
|
}
|
|
|
|
// Checks that SetKeyFrameCropTarget checks KeyFrameCropOptions is not null.
|
|
TEST(UtilTest, SetKeyFrameCropTargetChecksKeyFrameCropOptionsNotNull) {
|
|
const auto status = SetKeyFrameCropTarget(kOriginalWidth, kOriginalHeight,
|
|
kTargetAspectRatio, nullptr);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
testing::HasSubstr("KeyFrameCropOptions is null."));
|
|
}
|
|
|
|
// Checks that SetKeyFrameCropTarget checks frame size and target aspect ratio
|
|
// are valid.
|
|
TEST(UtilTest, SetKeyFrameCropTargetChecksFrameSizeAndTargetAspectRatioValid) {
|
|
KeyFrameCropOptions crop_options;
|
|
auto status = SetKeyFrameCropTarget(0, kOriginalHeight, kTargetAspectRatio,
|
|
&crop_options);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
testing::HasSubstr("Frame width is non-positive."));
|
|
|
|
status =
|
|
SetKeyFrameCropTarget(kOriginalWidth, kOriginalHeight, 0, &crop_options);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
testing::HasSubstr("Target aspect ratio is non-positive."));
|
|
}
|
|
|
|
// Checks that SetKeyFrameCropTarget correctly sets the target crop size.
|
|
TEST(UtilTest, SetKeyFrameCropTargetSetsTargetSizeCorrectly) {
|
|
KeyFrameCropOptions crop_options;
|
|
// 1a) square -> square.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(101, 101, 1.0, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 101);
|
|
EXPECT_EQ(crop_options.target_height(), 101);
|
|
// 1b) square -> landscape.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(101, 101, 1.5, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 101);
|
|
EXPECT_EQ(crop_options.target_height(), 67);
|
|
// 1c) square -> vertical.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(101, 101, 0.5, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 51);
|
|
EXPECT_EQ(crop_options.target_height(), 101);
|
|
// 2a) landscape -> square.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(128, 72, 1.0, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 72);
|
|
EXPECT_EQ(crop_options.target_height(), 72);
|
|
// 2b) landscape -> more landscape.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(128, 72, 2.0, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 128);
|
|
EXPECT_EQ(crop_options.target_height(), 64);
|
|
// 2c) landscape -> vertical.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(128, 72, 0.7, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 50);
|
|
EXPECT_EQ(crop_options.target_height(), 72);
|
|
// 3a) vertical -> square.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(90, 160, 1.0, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 90);
|
|
EXPECT_EQ(crop_options.target_height(), 90);
|
|
// 3b) vertical -> more vertical.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(90, 160, 0.36, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 58);
|
|
EXPECT_EQ(crop_options.target_height(), 160);
|
|
// 3c) vertical -> landscape.
|
|
MP_EXPECT_OK(SetKeyFrameCropTarget(90, 160, 1.2, &crop_options));
|
|
EXPECT_EQ(crop_options.target_width(), 90);
|
|
EXPECT_EQ(crop_options.target_height(), 75);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults checks output pointer is not null.
|
|
TEST(UtilTest, AggregateKeyFrameResultsChecksOutputNotNull) {
|
|
const auto status = AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), GetDefaultKeyFrameCropResults(),
|
|
kOriginalWidth, kOriginalHeight, nullptr);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
HasSubstr("Output SceneKeyFrameCropSummary is null."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults handles the case of no key frames.
|
|
TEST(UtilTest, AggregateKeyFrameResultsHandlesNoKeyFrames) {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(0);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults checks that frame size is valid.
|
|
TEST(UtilTest, AggregateKeyFrameResultsChecksFrameSizeValid) {
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
const auto status = AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), GetDefaultKeyFrameCropResults(),
|
|
kOriginalWidth, 0, &scene_summary);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(), HasSubstr("Non-positive frame height."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults checks that target size is valid.
|
|
TEST(UtilTest, AggregateKeyFrameResultsChecksTargetSizeValid) {
|
|
KeyFrameCropOptions key_frame_crop_options;
|
|
key_frame_crop_options.set_target_width(0);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
const auto status = AggregateKeyFrameResults(
|
|
key_frame_crop_options, GetDefaultKeyFrameCropResults(), kOriginalWidth,
|
|
kOriginalHeight, &scene_summary);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(), HasSubstr("Non-positive target width."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults checks that target size does not exceed
|
|
// frame size.
|
|
TEST(UtilTest, AggregateKeyFrameResultsChecksTargetSizeNotExceedFrameSize) {
|
|
auto key_frame_crop_options = GetDefaultKeyFrameCropOptions();
|
|
key_frame_crop_options.set_target_width(kOriginalWidth + 1);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
const auto status = AggregateKeyFrameResults(
|
|
key_frame_crop_options, GetDefaultKeyFrameCropResults(), kOriginalWidth,
|
|
kOriginalHeight, &scene_summary);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
HasSubstr("Target width exceeds frame width."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults packs KeyFrameCompactInfos.
|
|
TEST(UtilTest, AggregateKeyFrameResultsPacksKeyFrameCompactInfos) {
|
|
const auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
|
|
EXPECT_EQ(scene_summary.num_key_frames(), kNumKeyFrames);
|
|
EXPECT_EQ(scene_summary.key_frame_compact_infos_size(), kNumKeyFrames);
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
const auto& compact_info = scene_summary.key_frame_compact_infos(i);
|
|
EXPECT_EQ(compact_info.timestamp_ms(),
|
|
key_frame_crop_results[i].timestamp_ms());
|
|
const auto center = RectCenter(key_frame_crop_results[i].region());
|
|
EXPECT_FLOAT_EQ(compact_info.center_x(), center.first);
|
|
EXPECT_FLOAT_EQ(compact_info.center_y(), center.second);
|
|
EXPECT_FLOAT_EQ(compact_info.score(),
|
|
key_frame_crop_results[i].region_score());
|
|
}
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults ensures the centered region of target
|
|
// size fits in frame bound.
|
|
TEST(UtilTest, AggregateKeyFrameResultsEnsuresCropRegionFitsInFrame) {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(1);
|
|
auto* crop_region = key_frame_crop_results[0].mutable_region();
|
|
crop_region->set_x(0);
|
|
crop_region->set_y(0);
|
|
crop_region->set_width(10);
|
|
crop_region->set_height(10);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
|
|
EXPECT_EQ(scene_summary.crop_window_width(), kTargetWidth);
|
|
EXPECT_EQ(scene_summary.crop_window_height(), kTargetHeight);
|
|
const auto& compact_info = scene_summary.key_frame_compact_infos(0);
|
|
const float left = compact_info.center_x() - kTargetWidth / 2.0f;
|
|
const float right = compact_info.center_x() + kTargetWidth / 2.0f;
|
|
const float top = compact_info.center_y() - kTargetWidth / 2.0f;
|
|
const float bottom = compact_info.center_y() + kTargetWidth / 2.0f;
|
|
// Crop window is in the frame.
|
|
EXPECT_GE(left, 0);
|
|
EXPECT_LE(right, kOriginalWidth);
|
|
EXPECT_GE(top, 0);
|
|
EXPECT_LE(bottom, kOriginalHeight);
|
|
// Crop window covers input crop region.
|
|
EXPECT_LE(left, crop_region->x());
|
|
EXPECT_GE(right, crop_region->x() + crop_region->width());
|
|
EXPECT_LE(top, crop_region->y());
|
|
EXPECT_GE(bottom, crop_region->y() + crop_region->height());
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets centers and scores to -1.0 for key
|
|
// frames with empty regions.
|
|
TEST(UtilTest,
|
|
AggregateKeyFrameResultsSetsMinusOneForKeyFramesWithEmptyRegions) {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(1);
|
|
key_frame_crop_results[0].set_region_is_empty(true);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
|
|
const auto& compact_info = scene_summary.key_frame_compact_infos(0);
|
|
EXPECT_FLOAT_EQ(compact_info.center_x(), -1.0f);
|
|
EXPECT_FLOAT_EQ(compact_info.center_y(), -1.0f);
|
|
EXPECT_FLOAT_EQ(compact_info.score(), -1.0f);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults rejects negative center.
|
|
TEST(UtilTest, AggregateKeyFrameResultsRejectsNegativeCenter) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
auto* region = key_frame_crop_results[0].mutable_region();
|
|
*region = MakeRectFromCenterAndHalfSize(10, -1.0, 10, 10);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
const auto status = AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(), HasSubstr("Negative vertical center."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults rejects negative score.
|
|
TEST(UtilTest, AggregateKeyFrameResultsRejectsNegativeScore) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
key_frame_crop_results[0].set_region_score(-1.0);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
const auto status = AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(), HasSubstr("Negative score."));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults properly sets center ranges.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsCenterRanges) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
const std::vector<float> centers_x = {30.0, 20.0, 45.0, 3.0, 10.0};
|
|
const std::vector<float> centers_y = {10.0, 0.0, 5.0, 30.0, 20.0};
|
|
const int half_width = 25, half_height = 25;
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
auto* region = key_frame_crop_results[i].mutable_region();
|
|
*region = MakeRectFromCenterAndHalfSize(centers_x[i], centers_y[i],
|
|
half_width, half_height);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_center_min_x(), 25.0f);
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_center_max_x(), 45.0f);
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_center_min_y(), 25.0f);
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_center_max_y(), 30.0f);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults properly sets score range.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsScoreRange) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
const std::vector<float> scores = {0.9, 0.1, 1.2, 2.0, 0.5};
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
key_frame_crop_results[i].set_region_score(scores[i]);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_min_score(),
|
|
*std::min_element(scores.begin(), scores.end()));
|
|
EXPECT_FLOAT_EQ(scene_summary.key_frame_max_score(),
|
|
*std::max_element(scores.begin(), scores.end()));
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets crop window size to target size
|
|
// when the crop regions fit in target size.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsCropWindowSizeToTargetSize) {
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), GetDefaultKeyFrameCropResults(),
|
|
kOriginalWidth, kOriginalHeight, &scene_summary));
|
|
EXPECT_EQ(scene_summary.crop_window_width(), kTargetWidth);
|
|
EXPECT_EQ(scene_summary.crop_window_height(), kTargetHeight);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults properly sets crop window size when the
|
|
// crop regions exceed target size.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsCropWindowSizeExceedingTargetSize) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
key_frame_crop_results[0].mutable_region()->set_width(kTargetWidth + 1);
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
EXPECT_EQ(scene_summary.crop_window_width(), kTargetWidth + 1);
|
|
EXPECT_EQ(scene_summary.crop_window_height(), kTargetHeight);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets has salient region to true when
|
|
// there are salient regions.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsHasSalientRegionTrue) {
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), GetDefaultKeyFrameCropResults(),
|
|
kOriginalWidth, kOriginalHeight, &scene_summary));
|
|
EXPECT_TRUE(scene_summary.has_salient_region());
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets has salient region to false when
|
|
// there are no salient regions.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsHasSalientRegionFalse) {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(kNumKeyFrames);
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
key_frame_crop_results[i].set_region_is_empty(true);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
EXPECT_FALSE(scene_summary.has_salient_region());
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets has required salient region to true
|
|
// when there are required salient regions.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsHasRequiredSalientRegionTrue) {
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(
|
|
GetDefaultKeyFrameCropOptions(), GetDefaultKeyFrameCropResults(),
|
|
kOriginalWidth, kOriginalHeight, &scene_summary));
|
|
EXPECT_TRUE(scene_summary.has_required_salient_region());
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets has required salient region to
|
|
// false when there are no required salient regions.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsHasRequiredSalientRegionFalse) {
|
|
std::vector<KeyFrameCropResult> key_frame_crop_results(kNumKeyFrames);
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
key_frame_crop_results[i].set_required_region_is_empty(true);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
EXPECT_FALSE(scene_summary.has_required_salient_region());
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults sets key frame required crop region
|
|
// union.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsKeyFrameRequiredCropRegionUnion) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
*key_frame_crop_results[i].mutable_required_region() =
|
|
MakeRect(i, 0, 50, 50);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
const auto& required_crop_region_union =
|
|
scene_summary.key_frame_required_crop_region_union();
|
|
EXPECT_EQ(required_crop_region_union.x(), 0);
|
|
EXPECT_EQ(required_crop_region_union.width(), 50 + kNumKeyFrames - 1);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults properly sets frame success rate.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsFrameSuccessRate) {
|
|
const int num_success_frames = 3;
|
|
const float success_rate =
|
|
static_cast<float>(num_success_frames) / kNumKeyFrames;
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
const bool successful = i < num_success_frames ? true : false;
|
|
key_frame_crop_results[i].set_are_required_regions_covered_in_target_size(
|
|
successful);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
EXPECT_FLOAT_EQ(scene_summary.frame_success_rate(), success_rate);
|
|
}
|
|
|
|
// Checks that AggregateKeyFrameResults properly sets motion.
|
|
TEST(UtilTest, AggregateKeyFrameResultsSetsMotion) {
|
|
auto key_frame_crop_results = GetDefaultKeyFrameCropResults();
|
|
const std::vector<float> centers_x = {30.0, 55.0, 45.0, 30.0, 60.0};
|
|
const std::vector<float> centers_y = {30.0, 40.0, 50.0, 45.0, 25.0};
|
|
const float motion_x = (60.0 - 30.0) / kOriginalWidth;
|
|
const float motion_y = (50.0 - 25.0) / kOriginalHeight;
|
|
const int half_width = 25, half_height = 25;
|
|
for (int i = 0; i < kNumKeyFrames; ++i) {
|
|
auto* region = key_frame_crop_results[i].mutable_region();
|
|
*region = MakeRectFromCenterAndHalfSize(centers_x[i], centers_y[i],
|
|
half_width, half_height);
|
|
}
|
|
SceneKeyFrameCropSummary scene_summary;
|
|
|
|
MP_EXPECT_OK(AggregateKeyFrameResults(GetDefaultKeyFrameCropOptions(),
|
|
key_frame_crop_results, kOriginalWidth,
|
|
kOriginalHeight, &scene_summary));
|
|
EXPECT_FLOAT_EQ(scene_summary.horizontal_motion_amount(), motion_x);
|
|
EXPECT_FLOAT_EQ(scene_summary.vertical_motion_amount(), motion_y);
|
|
}
|
|
|
|
// Checks that ComputeSceneStaticBordersSize checks output not null.
|
|
TEST(UtilTest, ComputeSceneStaticBordersSizeChecksOutputNotNull) {
|
|
std::vector<StaticFeatures> static_features;
|
|
int bottom_border_size = 0;
|
|
const auto status = ComputeSceneStaticBordersSize(static_features, nullptr,
|
|
&bottom_border_size);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
testing::HasSubstr("Output top border size is null."));
|
|
}
|
|
// Checks that ComputeSceneStaticBordersSize returns 0 when there are no static
|
|
// borders.
|
|
TEST(UtilTest, ComputeSceneStaticBordersSizeNoBorder) {
|
|
std::vector<StaticFeatures> static_features(10);
|
|
int top_border_size = 0, bottom_border_size = 0;
|
|
MP_EXPECT_OK(ComputeSceneStaticBordersSize(static_features, &top_border_size,
|
|
&bottom_border_size));
|
|
EXPECT_EQ(top_border_size, 0);
|
|
EXPECT_EQ(bottom_border_size, 0);
|
|
}
|
|
|
|
// Checks that ComputeSceneStaticBordersSize correctly computes static border
|
|
// size.
|
|
TEST(UtilTest, ComputeSceneStaticBordersSizeHasBorders) {
|
|
const int num_frames = 6;
|
|
const std::vector<int> top_borders = {10, 9, 8, 9, 10, 5};
|
|
const std::vector<int> bottom_borders = {7, 7, 7, 7, 6, 7};
|
|
std::vector<StaticFeatures> static_features(num_frames);
|
|
for (int i = 0; i < num_frames; ++i) {
|
|
auto& features = static_features[i];
|
|
auto* top_part = features.add_border();
|
|
top_part->set_relative_position(Border::TOP);
|
|
top_part->mutable_border_position()->set_height(top_borders[i]);
|
|
auto* bottom_part = features.add_border();
|
|
bottom_part->set_relative_position(Border::BOTTOM);
|
|
bottom_part->mutable_border_position()->set_height(bottom_borders[i]);
|
|
}
|
|
int top_border_size = 0, bottom_border_size = 0;
|
|
MP_EXPECT_OK(ComputeSceneStaticBordersSize(static_features, &top_border_size,
|
|
&bottom_border_size));
|
|
EXPECT_EQ(top_border_size, 5);
|
|
EXPECT_EQ(bottom_border_size, 6);
|
|
}
|
|
|
|
// Checks that FindSolidBackgroundColor checks output not null.
|
|
TEST(UtilTest, FindSolidBackgroundColorChecksOutputNotNull) {
|
|
std::vector<StaticFeatures> static_features;
|
|
std::vector<int64> static_features_timestamps;
|
|
const double min_fraction_solid_background_color = 0.8;
|
|
bool has_solid_background_color;
|
|
PiecewiseLinearFunction l_function, a_function, b_function;
|
|
|
|
auto status =
|
|
FindSolidBackgroundColor(static_features, static_features_timestamps,
|
|
min_fraction_solid_background_color, nullptr,
|
|
&l_function, &a_function, &b_function);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(), testing::HasSubstr("Output boolean is null."));
|
|
|
|
status = FindSolidBackgroundColor(static_features, static_features_timestamps,
|
|
min_fraction_solid_background_color,
|
|
&has_solid_background_color, nullptr,
|
|
&a_function, &b_function);
|
|
EXPECT_FALSE(status.ok());
|
|
EXPECT_THAT(status.ToString(),
|
|
testing::HasSubstr("Output color l function is null."));
|
|
}
|
|
|
|
// Checks that FindSolidBackgroundColor returns true when there is solid
|
|
// background color.
|
|
TEST(UtilTest, FindSolidBackgroundColorReturnsTrue) {
|
|
std::vector<StaticFeatures> static_features(1);
|
|
auto* color = static_features[0].mutable_solid_background();
|
|
color->set_r(255);
|
|
color->set_g(255);
|
|
color->set_b(255);
|
|
std::vector<int64> static_features_timestamps(1);
|
|
static_features_timestamps[0] = 0;
|
|
const double min_fraction_solid_background_color = 0.8;
|
|
bool has_solid_background_color;
|
|
PiecewiseLinearFunction l_function, a_function, b_function;
|
|
|
|
MP_EXPECT_OK(FindSolidBackgroundColor(
|
|
static_features, static_features_timestamps,
|
|
min_fraction_solid_background_color, &has_solid_background_color,
|
|
&l_function, &a_function, &b_function));
|
|
EXPECT_TRUE(has_solid_background_color);
|
|
}
|
|
|
|
// Checks that FindSolidBackgroundColor returns false when there is no solid
|
|
// background color.
|
|
TEST(UtilTest, FindSolidBackgroundColorReturnsFalse) {
|
|
std::vector<StaticFeatures> static_features(1);
|
|
std::vector<int64> static_features_timestamps(1);
|
|
static_features_timestamps[0] = 0;
|
|
const double min_fraction_solid_background_color = 0.8;
|
|
bool has_solid_background_color;
|
|
PiecewiseLinearFunction l_function, a_function, b_function;
|
|
|
|
MP_EXPECT_OK(FindSolidBackgroundColor(
|
|
static_features, static_features_timestamps,
|
|
min_fraction_solid_background_color, &has_solid_background_color,
|
|
&l_function, &a_function, &b_function));
|
|
EXPECT_FALSE(has_solid_background_color);
|
|
}
|
|
|
|
// Checks that FindSolidBackgroundColor sets the interpolation functions.
|
|
TEST(UtilTest, FindSolidBackgroundColorSetsInterpolationFunctions) {
|
|
const uint8 rgb1[] = {255, 255, 0}; // cyan in bgr
|
|
const double lab1[] = {91.1133, -48.0938, -14.125};
|
|
const int64 time1 = 0;
|
|
const uint8 rgb2[] = {255, 0, 255}; // magenta in bgr
|
|
const double lab2[] = {60.321, 98.2344, -60.8281};
|
|
const int64 time2 = 2000;
|
|
std::vector<StaticFeatures> static_features(2);
|
|
auto* color1 = static_features[0].mutable_solid_background();
|
|
color1->set_r(rgb1[0]);
|
|
color1->set_g(rgb1[1]);
|
|
color1->set_b(rgb1[2]);
|
|
auto* color2 = static_features[1].mutable_solid_background();
|
|
color2->set_r(rgb2[0]);
|
|
color2->set_g(rgb2[1]);
|
|
color2->set_b(rgb2[2]);
|
|
std::vector<int64> static_features_timestamps(2);
|
|
static_features_timestamps[0] = time1;
|
|
static_features_timestamps[1] = time2;
|
|
const double min_fraction_solid_background_color = 0.8;
|
|
bool has_solid_background_color;
|
|
PiecewiseLinearFunction l_function, a_function, b_function;
|
|
|
|
MP_EXPECT_OK(FindSolidBackgroundColor(
|
|
static_features, static_features_timestamps,
|
|
min_fraction_solid_background_color, &has_solid_background_color,
|
|
&l_function, &a_function, &b_function));
|
|
|
|
EXPECT_TRUE(has_solid_background_color);
|
|
EXPECT_LE(std::fabs(l_function.Evaluate(time1) - lab1[0]), 1e-2f);
|
|
EXPECT_LE(std::fabs(a_function.Evaluate(time1) - lab1[1]), 1e-2f);
|
|
EXPECT_LE(std::fabs(b_function.Evaluate(time1) - lab1[2]), 1e-2f);
|
|
EXPECT_LE(std::fabs(l_function.Evaluate(time2) - lab2[0]), 1e-2f);
|
|
EXPECT_LE(std::fabs(a_function.Evaluate(time2) - lab2[1]), 1e-2f);
|
|
EXPECT_LE(std::fabs(b_function.Evaluate(time2) - lab2[2]), 1e-2f);
|
|
|
|
EXPECT_LE(std::fabs(l_function.Evaluate((time1 + time2) / 2.0) -
|
|
(lab1[0] + lab2[0]) / 2.0),
|
|
1e-2f);
|
|
EXPECT_LE(std::fabs(a_function.Evaluate((time1 + time2) / 2.0) -
|
|
(lab1[1] + lab2[1]) / 2.0),
|
|
1e-2f);
|
|
EXPECT_LE(std::fabs(b_function.Evaluate((time1 + time2) / 2.0) -
|
|
(lab1[2] + lab2[2]) / 2.0),
|
|
1e-2f);
|
|
}
|
|
|
|
TEST(UtilTest, TestAffineRetargeterPass) {
|
|
std::vector<cv::Mat> transforms;
|
|
std::vector<cv::Mat> frames;
|
|
std::vector<cv::Mat> results;
|
|
for (int i = 0; i < 5; i++) {
|
|
cv::Mat transform = cv::Mat(2, 3, CV_32FC1);
|
|
transform.at<float>(0, 0) = 1;
|
|
transform.at<float>(0, 1) = 0;
|
|
transform.at<float>(1, 0) = 0;
|
|
transform.at<float>(1, 1) = 1;
|
|
transform.at<float>(0, 2) = -i * 50;
|
|
transform.at<float>(1, 2) = 0;
|
|
transforms.push_back(transform);
|
|
cv::Mat image = cv::Mat::zeros(1080, 1920, CV_8UC3);
|
|
cv::Vec3b val;
|
|
val[0] = 255;
|
|
val[1] = 255;
|
|
val[2] = 255;
|
|
image(cv::Rect(0, 0, 395, 1080)).setTo(val);
|
|
frames.push_back(image);
|
|
|
|
cv::Mat image_out = cv::Mat::zeros(500, 1920, CV_8UC3);
|
|
results.push_back(image_out);
|
|
}
|
|
|
|
MP_ASSERT_OK(
|
|
AffineRetarget(cv::Size(500, 1080), frames, transforms, &results));
|
|
ASSERT_EQ(results.size(), 5);
|
|
for (int i = 0; i < 5; i++) {
|
|
EXPECT_GT(results[i].at<cv::Vec3b>(540, 390 - i * 50)[0], 250);
|
|
EXPECT_GT(results[i].at<cv::Vec3b>(540, 390 - i * 50)[1], 250);
|
|
EXPECT_GT(results[i].at<cv::Vec3b>(540, 390 - i * 50)[2], 250);
|
|
EXPECT_LT(results[i].at<cv::Vec3b>(540, 400 - i * 50)[0], 5);
|
|
EXPECT_LT(results[i].at<cv::Vec3b>(540, 400 - i * 50)[1], 5);
|
|
EXPECT_LT(results[i].at<cv::Vec3b>(540, 400 - i * 50)[2], 5);
|
|
}
|
|
}
|
|
|
|
TEST(UtilTest, TestAffineRetargeterFail) {
|
|
std::vector<cv::Mat> transforms;
|
|
std::vector<cv::Mat> frames;
|
|
std::vector<cv::Mat> results;
|
|
|
|
cv::Mat dummy;
|
|
transforms.push_back(dummy);
|
|
frames.push_back(dummy);
|
|
|
|
EXPECT_THAT(
|
|
AffineRetarget(cv::Size(500, 1080), frames, transforms, &results)
|
|
.ToString(),
|
|
testing::HasSubstr("Output vector cropped_frames must be populated"));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace autoflip
|
|
} // namespace mediapipe
|