1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_NDEBUG 0
18 #define LOG_TAG "RotateAndCropMapperTest"
19 
20 #include <functional>
21 #include <random>
22 
23 #include <gtest/gtest.h>
24 #include <gmock/gmock.h>
25 
26 #include "../device3/RotateAndCropMapper.h"
27 
28 namespace rotateAndCropMapperTest {
29 
30 using namespace android;
31 using namespace android::camera3;
32 
33 using ::testing::ElementsAreArray;
34 using ::testing::Each;
35 using ::testing::AllOf;
36 using ::testing::Ge;
37 using ::testing::Le;
38 
39 #define EXPECT_EQUAL_WITHIN_N(vec, array, N, msg)   \
40 { \
41     for (size_t i = 0; i < vec.size(); i++) { \
42         EXPECT_THAT(vec[i] - array[i], AllOf(Ge(-N), Le(N))) << msg " failed at index:" << i; \
43     } \
44 }
45 
46 int32_t testActiveArray[] = {100, 100, 4000, 3000};
47 
48 std::vector<uint8_t> basicModes = {
49     ANDROID_SCALER_ROTATE_AND_CROP_NONE,
50     ANDROID_SCALER_ROTATE_AND_CROP_90,
51     ANDROID_SCALER_ROTATE_AND_CROP_AUTO
52 };
53 
setupDeviceInfo(int32_t activeArray[4],std::vector<uint8_t> availableCropModes)54 CameraMetadata setupDeviceInfo(int32_t activeArray[4], std::vector<uint8_t> availableCropModes ) {
55     CameraMetadata deviceInfo;
56 
57     deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
58             activeArray, 4);
59 
60     deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
61             availableCropModes.data(), availableCropModes.size());
62 
63     return deviceInfo;
64 }
65 
TEST(RotationMapperTest,Initialization)66 TEST(RotationMapperTest, Initialization) {
67     CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
68             {ANDROID_SCALER_ROTATE_AND_CROP_NONE});
69 
70     ASSERT_FALSE(RotateAndCropMapper::isNeeded(&deviceInfo));
71 
72     deviceInfo.update(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
73             basicModes.data(), 3);
74 
75     ASSERT_TRUE(RotateAndCropMapper::isNeeded(&deviceInfo));
76 }
77 
TEST(RotationMapperTest,IdentityTransform)78 TEST(RotationMapperTest, IdentityTransform) {
79     status_t res;
80 
81     CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
82             basicModes);
83 
84     RotateAndCropMapper mapper(&deviceInfo);
85 
86     CameraMetadata request;
87     uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
88     auto full_crop = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3]};
89     auto full_region = std::vector<int32_t>{0,0, testActiveArray[2], testActiveArray[3], 1};
90     request.update(ANDROID_SCALER_ROTATE_AND_CROP,
91             &mode, 1);
92     request.update(ANDROID_SCALER_CROP_REGION,
93             full_crop.data(), full_crop.size());
94     request.update(ANDROID_CONTROL_AE_REGIONS,
95             full_region.data(), full_region.size());
96 
97     // Map to HAL
98 
99     res = mapper.updateCaptureRequest(&request);
100     ASSERT_TRUE(res == OK);
101 
102     auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
103     EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
104 
105     e = request.find(ANDROID_SCALER_CROP_REGION);
106     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
107 
108     // Add fields in HAL
109 
110     CameraMetadata result(request);
111 
112     auto face = std::vector<int32_t> {300,300,500,500};
113     result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
114             face.data(), face.size());
115 
116     // Map to app
117 
118     res = mapper.updateCaptureResult(&result);
119     ASSERT_TRUE(res == OK);
120 
121     e = result.find(ANDROID_CONTROL_AE_REGIONS);
122     EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count));
123 
124     e = result.find(ANDROID_SCALER_CROP_REGION);
125     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count));
126 
127     e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
128     EXPECT_THAT(face, ElementsAreArray(e.data.i32, e.count));
129 }
130 
TEST(RotationMapperTest,Transform90)131 TEST(RotationMapperTest, Transform90) {
132     status_t res;
133 
134     CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
135             basicModes);
136 
137     RotateAndCropMapper mapper(&deviceInfo);
138 
139     CameraMetadata request;
140     uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_90;
141     auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
142     auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
143     request.update(ANDROID_SCALER_ROTATE_AND_CROP,
144             &mode, 1);
145     request.update(ANDROID_SCALER_CROP_REGION,
146             full_crop.data(), full_crop.size());
147     request.update(ANDROID_CONTROL_AE_REGIONS,
148             full_region.data(), full_region.size());
149 
150     // Map to HAL
151 
152     res = mapper.updateCaptureRequest(&request);
153     ASSERT_TRUE(res == OK);
154 
155     auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
156     float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
157     int32_t rw = full_crop[3] / aspectRatio;
158     int32_t rh = full_crop[3];
159     auto rotated_region = std::vector<int32_t> {
160         full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
161         full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
162         1
163     };
164     EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
165             << "Rotated AE region isn't right";
166 
167     e = request.find(ANDROID_SCALER_CROP_REGION);
168     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
169             << "Rotated crop region isn't right";
170 
171     // Add fields in HAL
172 
173     CameraMetadata result(request);
174 
175     auto face = std::vector<int32_t> {
176         rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
177         rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
178     result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
179             face.data(), face.size());
180 
181     auto landmarks = std::vector<int32_t> {
182         rotated_region[0], rotated_region[1],
183         rotated_region[2], rotated_region[3],
184         rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
185         rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
186         rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
187     };
188     result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
189             landmarks.data(), landmarks.size());
190 
191     // Map to app
192 
193     res = mapper.updateCaptureResult(&result);
194     ASSERT_TRUE(res == OK);
195 
196     // Round-trip results can't be exact since we've gone from a large int range -> small int range
197     // and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
198     e = result.find(ANDROID_CONTROL_AE_REGIONS);
199     EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
200 
201     e = result.find(ANDROID_SCALER_CROP_REGION);
202     EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
203 
204     auto full_face = std::vector<int32_t> {
205         full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
206         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
207     };
208     e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
209     EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
210 
211     auto full_landmarks = std::vector<int32_t> {
212         full_crop[0], full_crop[1] + full_crop[3],
213         full_crop[0] + full_crop[2], full_crop[1],
214         full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
215         full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
216         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4
217     };
218     e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
219     EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
220 }
221 
TEST(RotationMapperTest,Transform270)222 TEST(RotationMapperTest, Transform270) {
223     status_t res;
224 
225     CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
226             basicModes);
227 
228     RotateAndCropMapper mapper(&deviceInfo);
229 
230     CameraMetadata request;
231     uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_270;
232     auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
233     auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
234     request.update(ANDROID_SCALER_ROTATE_AND_CROP,
235             &mode, 1);
236     request.update(ANDROID_SCALER_CROP_REGION,
237             full_crop.data(), full_crop.size());
238     request.update(ANDROID_CONTROL_AE_REGIONS,
239             full_region.data(), full_region.size());
240 
241     // Map to HAL
242 
243     res = mapper.updateCaptureRequest(&request);
244     ASSERT_TRUE(res == OK);
245 
246     auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
247     float aspectRatio = static_cast<float>(full_crop[2]) / full_crop[3];
248     int32_t rw = full_crop[3] / aspectRatio;
249     int32_t rh = full_crop[3];
250     auto rotated_region = std::vector<int32_t> {
251         full_crop[0] + (full_crop[2] - rw) / 2, full_crop[1],
252         full_crop[0] + (full_crop[2] + rw) / 2, full_crop[1] + full_crop[3],
253         1
254     };
255     EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
256             << "Rotated AE region isn't right";
257 
258     e = request.find(ANDROID_SCALER_CROP_REGION);
259     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
260             << "Rotated crop region isn't right";
261 
262     // Add fields in HAL
263 
264     CameraMetadata result(request);
265 
266     auto face = std::vector<int32_t> {
267         rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
268         rotated_region[2] - rw / 4, rotated_region[3] - rh / 4};
269     result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
270             face.data(), face.size());
271 
272     auto landmarks = std::vector<int32_t> {
273         rotated_region[0], rotated_region[1],
274         rotated_region[2], rotated_region[3],
275         rotated_region[0] + rw / 4, rotated_region[1] + rh / 4,
276         rotated_region[0] + rw / 2, rotated_region[1] + rh / 2,
277         rotated_region[2] - rw / 4, rotated_region[3] - rh / 4
278     };
279     result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
280             landmarks.data(), landmarks.size());
281 
282     // Map to app
283 
284     res = mapper.updateCaptureResult(&result);
285     ASSERT_TRUE(res == OK);
286 
287     // Round-trip results can't be exact since we've gone from a large int range -> small int range
288     // and back, leading to quantization. For 4/3 aspect ratio, no more than +-1 error expected
289 
290     e = result.find(ANDROID_CONTROL_AE_REGIONS);
291     EXPECT_EQUAL_WITHIN_N(full_region, e.data.i32, 1, "Round-tripped AE region isn't right");
292 
293     e = result.find(ANDROID_SCALER_CROP_REGION);
294     EXPECT_EQUAL_WITHIN_N(full_crop, e.data.i32, 1, "Round-tripped crop region isn't right");
295 
296     auto full_face = std::vector<int32_t> {
297         full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
298         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
299     };
300     e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
301     EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
302 
303     auto full_landmarks = std::vector<int32_t> {
304         full_crop[0] + full_crop[2], full_crop[1],
305         full_crop[0], full_crop[1] + full_crop[3],
306         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + full_crop[3]/4,
307         full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
308         full_crop[0] + full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
309     };
310     e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
311     EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
312 }
313 
TEST(RotationMapperTest,Transform180)314 TEST(RotationMapperTest, Transform180) {
315     status_t res;
316 
317     CameraMetadata deviceInfo = setupDeviceInfo(testActiveArray,
318             basicModes);
319 
320     RotateAndCropMapper mapper(&deviceInfo);
321 
322     CameraMetadata request;
323     uint8_t mode = ANDROID_SCALER_ROTATE_AND_CROP_180;
324     auto full_crop = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3]};
325     auto full_region = std::vector<int32_t> {0,0, testActiveArray[2], testActiveArray[3], 1};
326     request.update(ANDROID_SCALER_ROTATE_AND_CROP,
327             &mode, 1);
328     request.update(ANDROID_SCALER_CROP_REGION,
329             full_crop.data(), full_crop.size());
330     request.update(ANDROID_CONTROL_AE_REGIONS,
331             full_region.data(), full_region.size());
332 
333     // Map to HAL
334 
335     res = mapper.updateCaptureRequest(&request);
336     ASSERT_TRUE(res == OK);
337 
338     auto e = request.find(ANDROID_CONTROL_AE_REGIONS);
339     auto rotated_region = full_region;
340     EXPECT_THAT(rotated_region, ElementsAreArray(e.data.i32, e.count))
341             << "Rotated AE region isn't right";
342 
343     e = request.find(ANDROID_SCALER_CROP_REGION);
344     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
345             << "Rotated crop region isn't right";
346 
347     // Add fields in HAL
348 
349     CameraMetadata result(request);
350 
351     float rw = full_region[2] - full_region[0];
352     float rh = full_region[3] - full_region[1];
353     auto face = std::vector<int32_t> {
354         rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
355         rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
356     };
357     result.update(ANDROID_STATISTICS_FACE_RECTANGLES,
358             face.data(), face.size());
359 
360     auto landmarks = std::vector<int32_t> {
361         rotated_region[0], rotated_region[1],
362         rotated_region[2], rotated_region[3],
363         rotated_region[0] + (int)(rw / 4), rotated_region[1] + (int)(rh / 4),
364         rotated_region[0] + (int)(rw / 2), rotated_region[1] + (int)(rh / 2),
365         rotated_region[2] - (int)(rw / 4), rotated_region[3] - (int)(rh / 4)
366     };
367     result.update(ANDROID_STATISTICS_FACE_LANDMARKS,
368             landmarks.data(), landmarks.size());
369 
370     // Map to app
371 
372     res = mapper.updateCaptureResult(&result);
373     ASSERT_TRUE(res == OK);
374 
375     e = result.find(ANDROID_CONTROL_AE_REGIONS);
376     EXPECT_THAT(full_region, ElementsAreArray(e.data.i32, e.count))
377             << "Round-tripped AE region isn't right";
378 
379     e = result.find(ANDROID_SCALER_CROP_REGION);
380     EXPECT_THAT(full_crop, ElementsAreArray(e.data.i32, e.count))
381             << "Round-tripped crop region isn't right";
382 
383     auto full_face = std::vector<int32_t> {
384         full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4,
385         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4
386     };
387     e = result.find(ANDROID_STATISTICS_FACE_RECTANGLES);
388     EXPECT_EQUAL_WITHIN_N(full_face, e.data.i32, 1, "App-side face rectangle isn't right");
389 
390     auto full_landmarks = std::vector<int32_t> {
391         full_crop[0] + full_crop[2], full_crop[1] + full_crop[3],
392         full_crop[0], full_crop[1],
393         full_crop[0] + 3*full_crop[2]/4, full_crop[1] + 3*full_crop[3]/4,
394         full_crop[0] + full_crop[2]/2, full_crop[1] + full_crop[3]/2,
395         full_crop[0] + full_crop[2]/4, full_crop[1] + full_crop[3]/4
396     };
397     e = result.find(ANDROID_STATISTICS_FACE_LANDMARKS);
398     EXPECT_EQUAL_WITHIN_N(full_landmarks, e.data.i32, 1, "App-side face landmarks aren't right");
399 }
400 
401 
402 } // namespace rotateAndCropMapperTest
403