You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
15 KiB
403 lines
15 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_NDEBUG 0
|
|
#define LOG_TAG "RotateAndCropMapperTest"
|
|
|
|
#include <functional>
|
|
#include <random>
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
|
|
#include "../device3/RotateAndCropMapper.h"
|
|
|
|
namespace rotateAndCropMapperTest {
|
|
|
|
using namespace android;
|
|
using namespace android::camera3;
|
|
|
|
using ::testing::ElementsAreArray;
|
|
using ::testing::Each;
|
|
using ::testing::AllOf;
|
|
using ::testing::Ge;
|
|
using ::testing::Le;
|
|
|
|
#define EXPECT_EQUAL_WITHIN_N(vec, array, N, msg) \
|
|
{ \
|
|
for (size_t i = 0; i < vec.size(); i++) { \
|
|
EXPECT_THAT(vec[i] - array[i], AllOf(Ge(-N), Le(N))) << msg " failed at index:" << i; \
|
|
} \
|
|
}
|
|
|
|
int32_t testActiveArray[] = {100, 100, 4000, 3000};
|
|
|
|
std::vector<uint8_t> basicModes = {
|
|
ANDROID_SCALER_ROTATE_AND_CROP_NONE,
|
|
ANDROID_SCALER_ROTATE_AND_CROP_90,
|
|
ANDROID_SCALER_ROTATE_AND_CROP_AUTO
|
|
};
|
|
|
|
CameraMetadata setupDeviceInfo(int32_t activeArray[4], std::vector<uint8_t> availableCropModes ) {
|
|
CameraMetadata deviceInfo;
|
|
|
|
deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
|
|
activeArray, 4);
|
|
|
|
deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
|
|
availableCropModes.data(), availableCropModes.size());
|
|
|
|
return deviceInfo;
|
|
}
|
|
|
|
TEST(RotationMapperTest, Initialization) {
|
|
CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
|
|
{ANDROID_SCALER_ROTATE_AND_CROP_NONE});
|
|
|
|
ASSERT_FALSE(RotateAndCropMapper::isNeeded(&deviceInfo));
|
|
|
|
deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
|
|
basicModes.data(), 3);
|
|
|
|
ASSERT_TRUE(RotateAndCropMapper::isNeeded(&deviceInfo));
|
|
}
|
|
|
|
TEST(RotationMapperTest, IdentityTransform) {
|
|
status_t res;
|
|
|
|
CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
|
|
basicModes);
|
|
|
|
RotateAndCropMapper mapper(&deviceInfo);
|
|
|
|
CameraMetadata request;
|
|
uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
|
|
auto full_crop = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3]};
|
|
auto full_region = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3], 1};
|
|
request.update(ANDROID_SCALER_ROTATE_AND_CROP,
|
|
&mode, 1);
|
|
request.update(ANDROID_SCALER_CROP_REGION,
|
|
full_crop.data(), full_crop.size());
|
|
request.update(ANDROID_CONTROL_AE_REGIONS,
|
|
full_region.data(), full_region.size());
|
|
|
|
// Map to HAL
|
|
|
|
res = mapper.updateCaptureRequest(&request);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
|
|
EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
|
|
|
|
e = request.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
|
|
|
|
// Add fields in HAL
|
|
|
|
CameraMetadata result(request);
|
|
|
|
auto face = std::vector<int32_t> {300,300,500,500};
|
|
result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
|
|
face.data(), face.size());
|
|
|
|
// Map to app
|
|
|
|
res = mapper.updateCaptureResult(&result);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
e = result.find(ANDROID_CONTROL_AE_REGIONS);
|
|
EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
|
|
|
|
e = result.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
|
|
|
|
e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
|
|
EXPECT_THAT(face, ElementsAreArray(e.data.i32, e.count));
|
|
}
|
|
|
|
TEST(RotationMapperTest, Transform90) {
|
|
status_t res;
|
|
|
|
CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
|
|
basicModes);
|
|
|
|
RotateAndCropMapper mapper(&deviceInfo);
|
|
|
|
CameraMetadata request;
|
|
uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_90;
|
|
auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
|
|
auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
|
|
request.update(ANDROID_SCALER_ROTATE_AND_CROP,
|
|
&mode, 1);
|
|
request.update(ANDROID_SCALER_CROP_REGION,
|
|
full_crop.data(), full_crop.size());
|
|
request.update(ANDROID_CONTROL_AE_REGIONS,
|
|
full_region.data(), full_region.size());
|
|
|
|
// Map to HAL
|
|
|
|
res = mapper.updateCaptureRequest(&request);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
|
|
float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
|
|
int32_t rw = full_crop[3] / aspectRatio;
|
|
int32_t rh = full_crop[3];
|
|
auto rotated_region = std::vector<int32_t> {
|
|
full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
|
|
full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
|
|
1
|
|
};
|
|
EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated AE region isn't right";
|
|
|
|
e = request.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated crop region isn't right";
|
|
|
|
// Add fields in HAL
|
|
|
|
CameraMetadata result(request);
|
|
|
|
auto face = std::vector<int32_t> {
|
|
rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
|
|
rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
|
|
result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
|
|
face.data(), face.size());
|
|
|
|
auto landmarks = std::vector<int32_t> {
|
|
rotated_region[0], rotated_region[1],
|
|
rotated_region[2], rotated_region[3],
|
|
rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
|
|
rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
|
|
rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
|
|
};
|
|
result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
|
|
landmarks.data(), landmarks.size());
|
|
|
|
// Map to app
|
|
|
|
res = mapper.updateCaptureResult(&result);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
// Round-trip results can't be exact since we've gone from a large int range -> small int range
|
|
// and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
|
|
e = result.find(ANDROID_CONTROL_AE_REGIONS);
|
|
EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
|
|
|
|
e = result.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
|
|
|
|
auto full_face = std::vector<int32_t> {
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
|
|
EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
|
|
|
|
auto full_landmarks = std::vector<int32_t> {
|
|
full_crop[0], full_crop[1] + full_crop[3],
|
|
full_crop[0] + full_crop[2], full_crop[1],
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
|
|
full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
|
|
EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
|
|
}
|
|
|
|
TEST(RotationMapperTest, Transform270) {
|
|
status_t res;
|
|
|
|
CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
|
|
basicModes);
|
|
|
|
RotateAndCropMapper mapper(&deviceInfo);
|
|
|
|
CameraMetadata request;
|
|
uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_270;
|
|
auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
|
|
auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
|
|
request.update(ANDROID_SCALER_ROTATE_AND_CROP,
|
|
&mode, 1);
|
|
request.update(ANDROID_SCALER_CROP_REGION,
|
|
full_crop.data(), full_crop.size());
|
|
request.update(ANDROID_CONTROL_AE_REGIONS,
|
|
full_region.data(), full_region.size());
|
|
|
|
// Map to HAL
|
|
|
|
res = mapper.updateCaptureRequest(&request);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
|
|
float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
|
|
int32_t rw = full_crop[3] / aspectRatio;
|
|
int32_t rh = full_crop[3];
|
|
auto rotated_region = std::vector<int32_t> {
|
|
full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
|
|
full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
|
|
1
|
|
};
|
|
EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated AE region isn't right";
|
|
|
|
e = request.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated crop region isn't right";
|
|
|
|
// Add fields in HAL
|
|
|
|
CameraMetadata result(request);
|
|
|
|
auto face = std::vector<int32_t> {
|
|
rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
|
|
rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
|
|
result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
|
|
face.data(), face.size());
|
|
|
|
auto landmarks = std::vector<int32_t> {
|
|
rotated_region[0], rotated_region[1],
|
|
rotated_region[2], rotated_region[3],
|
|
rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
|
|
rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
|
|
rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
|
|
};
|
|
result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
|
|
landmarks.data(), landmarks.size());
|
|
|
|
// Map to app
|
|
|
|
res = mapper.updateCaptureResult(&result);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
// Round-trip results can't be exact since we've gone from a large int range -> small int range
|
|
// and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
|
|
|
|
e = result.find(ANDROID_CONTROL_AE_REGIONS);
|
|
EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
|
|
|
|
e = result.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
|
|
|
|
auto full_face = std::vector<int32_t> {
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
|
|
EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
|
|
|
|
auto full_landmarks = std::vector<int32_t> {
|
|
full_crop[0] + full_crop[2], full_crop[1],
|
|
full_crop[0], full_crop[1] + full_crop[3],
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4,
|
|
full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
|
|
EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
|
|
}
|
|
|
|
TEST(RotationMapperTest, Transform180) {
|
|
status_t res;
|
|
|
|
CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
|
|
basicModes);
|
|
|
|
RotateAndCropMapper mapper(&deviceInfo);
|
|
|
|
CameraMetadata request;
|
|
uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_180;
|
|
auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
|
|
auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
|
|
request.update(ANDROID_SCALER_ROTATE_AND_CROP,
|
|
&mode, 1);
|
|
request.update(ANDROID_SCALER_CROP_REGION,
|
|
full_crop.data(), full_crop.size());
|
|
request.update(ANDROID_CONTROL_AE_REGIONS,
|
|
full_region.data(), full_region.size());
|
|
|
|
// Map to HAL
|
|
|
|
res = mapper.updateCaptureRequest(&request);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
|
|
auto rotated_region = full_region;
|
|
EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated AE region isn't right";
|
|
|
|
e = request.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Rotated crop region isn't right";
|
|
|
|
// Add fields in HAL
|
|
|
|
CameraMetadata result(request);
|
|
|
|
float rw = full_region[2] - full_region[0];
|
|
float rh = full_region[3] - full_region[1];
|
|
auto face = std::vector<int32_t> {
|
|
rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
|
|
rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
|
|
};
|
|
result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
|
|
face.data(), face.size());
|
|
|
|
auto landmarks = std::vector<int32_t> {
|
|
rotated_region[0], rotated_region[1],
|
|
rotated_region[2], rotated_region[3],
|
|
rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
|
|
rotated_region[0] + (int)(rw / 2), rotated_region[1] + (int)(rh / 2),
|
|
rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
|
|
};
|
|
result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
|
|
landmarks.data(), landmarks.size());
|
|
|
|
// Map to app
|
|
|
|
res = mapper.updateCaptureResult(&result);
|
|
ASSERT_TRUE(res == OK);
|
|
|
|
e = result.find(ANDROID_CONTROL_AE_REGIONS);
|
|
EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Round-tripped AE region isn't right";
|
|
|
|
e = result.find(ANDROID_SCALER_CROP_REGION);
|
|
EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
|
|
<< "Round-tripped crop region isn't right";
|
|
|
|
auto full_face = std::vector<int32_t> {
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
|
|
EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
|
|
|
|
auto full_landmarks = std::vector<int32_t> {
|
|
full_crop[0] + full_crop[2], full_crop[1] + full_crop[3],
|
|
full_crop[0], full_crop[1],
|
|
full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
|
|
full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
|
|
full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4
|
|
};
|
|
e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
|
|
EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
|
|
}
|
|
|
|
|
|
} // namespace rotateAndCropMapperTest
|