blob: 1bfa03faca50ba46fb937c780df28c76c92c8144 [file] [log] [blame]
Shuzhen Wangdbdf72b2019-11-13 11:22:12 -08001/*
2 * Copyright (C) 2019 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 "ZoomRatioMapperTest"
19
20#include <gtest/gtest.h>
21#include <utils/Errors.h>
22
23#include "../device3/ZoomRatioMapper.h"
24
25using namespace std;
26using namespace android;
27using namespace android::camera3;
28
29constexpr int kMaxAllowedPixelError = 1;
30constexpr float kMaxAllowedRatioError = 0.1;
31
32constexpr int32_t testActiveArraySize[] = {100, 100, 1024, 768};
33constexpr int32_t testPreCorrActiveArraySize[] = {90, 90, 1044, 788};
34
35constexpr int32_t testDefaultCropSize[][4] = {
36 {0, 0, 1024, 768}, // active array default crop
37 {0, 0, 1044, 788}, // preCorrection active array default crop
38};
39
40constexpr int32_t test2xCropRegion[][4] = {
41 {256, 192, 512, 384}, // active array 2x zoom crop
42 {261, 197, 522, 394}, // preCorrection active array default crop
43};
44
45constexpr int32_t testLetterBoxSize[][4] = {
46 {0, 96, 1024, 576}, // active array 2x zoom crop
47 {0, 106, 1024, 576}, // preCorrection active array default crop
48};
49
50status_t setupTestMapper(ZoomRatioMapper *m, float maxDigitalZoom,
51 const int32_t activeArray[4], const int32_t preCorrectArray[4],
52 bool hasZoomRatioRange, float zoomRatioRange[2],
53 bool usePreCorrectArray) {
54 CameraMetadata deviceInfo;
55
56 deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArray, 4);
57 deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectArray, 4);
58 deviceInfo.update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, &maxDigitalZoom, 1);
59 if (hasZoomRatioRange) {
60 deviceInfo.update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange, 2);
61 }
62
63 bool supportNativeZoomRatio;
64 status_t res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
65 if (res != OK) {
66 return res;
67 }
68
69 return m->initZoomRatioTags(&deviceInfo, hasZoomRatioRange, usePreCorrectArray);
70}
71
72TEST(ZoomRatioTest, Initialization) {
73 CameraMetadata deviceInfo;
74 status_t res;
75 camera_metadata_entry_t entry;
76
77 deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
78 testPreCorrActiveArraySize, 4);
79 deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, testActiveArraySize, 4);
80
81 // Test initialization from devices not supporting zoomRange
82 float maxDigitalZoom = 4.0f;
83 ZoomRatioMapper mapperNoZoomRange;
84 deviceInfo.update(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, &maxDigitalZoom, 1);
85 bool supportNativeZoomRatio;
86 res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
87 ASSERT_EQ(res, OK);
88 ASSERT_EQ(supportNativeZoomRatio, false);
89 res = mapperNoZoomRange.initZoomRatioTags(&deviceInfo,
90 supportNativeZoomRatio, true/*usePreCorrectArray*/);
91 ASSERT_EQ(res, OK);
92 res = mapperNoZoomRange.initZoomRatioTags(&deviceInfo,
93 supportNativeZoomRatio, false/*usePreCorrectArray*/);
94 ASSERT_EQ(res, OK);
95
96 entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
97 ASSERT_EQ(entry.count, 2U);
98 ASSERT_EQ(entry.data.f[0], 1.0);
99 ASSERT_EQ(entry.data.f[1], maxDigitalZoom);
100
101 entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
102 ASSERT_GT(entry.count, 0U);
103 ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
104 ANDROID_CONTROL_ZOOM_RATIO_RANGE), entry.data.i32 + entry.count);
105
106 entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
107 ASSERT_GT(entry.count, 0U);
108 ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
109 ANDROID_CONTROL_ZOOM_RATIO), entry.data.i32 + entry.count);
110
111 entry = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
112 ASSERT_GT(entry.count, 0U);
113 ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
114 ANDROID_CONTROL_ZOOM_RATIO), entry.data.i32 + entry.count);
115
116 // Test initialization from devices supporting zoomRange
117 float ratioRange[2] = {0.2f, maxDigitalZoom};
118 deviceInfo.update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, ratioRange, 2);
119 res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
120 ASSERT_EQ(res, OK);
121 ASSERT_EQ(supportNativeZoomRatio, true);
122 ZoomRatioMapper mapperWithZoomRange;
123 res = mapperWithZoomRange.initZoomRatioTags(&deviceInfo,
124 supportNativeZoomRatio, true/*usePreCorrectArray*/);
125 ASSERT_EQ(res, OK);
126 res = mapperWithZoomRange.initZoomRatioTags(&deviceInfo,
127 supportNativeZoomRatio, false/*usePreCorrectArray*/);
128 ASSERT_EQ(res, OK);
129
130 entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
131 ASSERT_EQ(entry.count, 2U);
132 ASSERT_EQ(entry.data.f[0], ratioRange[0]);
133 ASSERT_EQ(entry.data.f[1], ratioRange[1]);
134
135 // Test default zoom ratio in template
136 CameraMetadata requestTemplate;
137 res = ZoomRatioMapper::initZoomRatioInTemplate(&requestTemplate);
138 ASSERT_EQ(res, OK);
139 entry = requestTemplate.find(ANDROID_CONTROL_ZOOM_RATIO);
140 ASSERT_EQ(entry.count, 1U);
141 ASSERT_EQ(entry.data.f[0], 1.0f);
142
143 float customRatio = 0.5f;
144 res = requestTemplate.update(ANDROID_CONTROL_ZOOM_RATIO, &customRatio, 1);
145 ASSERT_EQ(res, OK);
146 res = ZoomRatioMapper::initZoomRatioInTemplate(&requestTemplate);
147 ASSERT_EQ(res, OK);
148 entry = requestTemplate.find(ANDROID_CONTROL_ZOOM_RATIO);
149 ASSERT_EQ(entry.count, 1U);
150 ASSERT_EQ(entry.data.f[0], customRatio);
151}
152
153void subScaleCoordinatesTest(bool usePreCorrectArray) {
154 ZoomRatioMapper mapper;
155 float maxDigitalZoom = 4.0f;
156 float zoomRatioRange[2];
157 ASSERT_EQ(OK, setupTestMapper(&mapper, maxDigitalZoom,
158 testActiveArraySize, testPreCorrActiveArraySize,
159 false/*hasZoomRatioRange*/, zoomRatioRange,
160 usePreCorrectArray));
161
162 size_t index = 0;
163 int32_t width = testActiveArraySize[2];
164 int32_t height = testActiveArraySize[3];
165 if (usePreCorrectArray) {
166 index = 1;
167 width = testPreCorrActiveArraySize[2];
168 height = testPreCorrActiveArraySize[3];
169 }
170
171 std::array<int32_t, 16> originalCoords = {
172 0, 0, // top-left
173 width, 0, // top-right
174 0, height, // bottom-left
175 width, height, // bottom-right
176 width / 2, height / 2, // center
177 width / 4, height / 4, // top-left after 2x
178 width / 3, height * 2 / 3, // bottom-left after 3x zoom
179 width * 7 / 8, height / 2, // middle-right after 1.33x zoom
180 };
181
182 // Verify 1.0x zoom doesn't change the coordinates
183 auto coords = originalCoords;
184 mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, ZoomRatioMapper::ClampOff);
185 for (size_t i = 0; i < coords.size(); i++) {
186 EXPECT_EQ(coords[i], originalCoords[i]);
187 }
188
189 // Verify 2.0x zoom work as expected (no clamping)
190 std::array<float, 16> expected2xCoords = {
191 - width / 2.0f, - height / 2.0f,// top-left
192 width * 3 / 2.0f, - height / 2.0f, // top-right
193 - width / 2.0f, height * 3 / 2.0f, // bottom-left
194 width * 3 / 2.0f, height * 3 / 2.0f, // bottom-right
195 width / 2.0f, height / 2.0f, // center
196 0, 0, // top-left after 2x
197 width / 6.0f, height - height / 6.0f, // bottom-left after 3x zoom
198 width + width / 4.0f, height / 2.0f, // middle-right after 1.33x zoom
199 };
200 coords = originalCoords;
201 mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampOff);
202 for (size_t i = 0; i < coords.size(); i++) {
203 EXPECT_LE(std::abs(coords[i] - expected2xCoords[i]), kMaxAllowedPixelError);
204 }
205
206 // Verify 2.0x zoom work as expected (with inclusive clamping)
207 std::array<float, 16> expected2xCoordsClampedInc = {
208 0, 0, // top-left
209 static_cast<float>(width) - 1, 0, // top-right
210 0, static_cast<float>(height) - 1, // bottom-left
211 static_cast<float>(width) - 1, static_cast<float>(height) - 1, // bottom-right
212 width / 2.0f, height / 2.0f, // center
213 0, 0, // top-left after 2x
214 width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
215 static_cast<float>(width) - 1, height / 2.0f, // middle-right after 1.33x zoom
216 };
217 coords = originalCoords;
218 mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampInclusive);
219 for (size_t i = 0; i < coords.size(); i++) {
220 EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedInc[i]), kMaxAllowedPixelError);
221 }
222
223 // Verify 2.0x zoom work as expected (with exclusive clamping)
224 std::array<float, 16> expected2xCoordsClampedExc = {
225 0, 0, // top-left
226 static_cast<float>(width), 0, // top-right
227 0, static_cast<float>(height), // bottom-left
228 static_cast<float>(width), static_cast<float>(height), // bottom-right
229 width / 2.0f, height / 2.0f, // center
230 0, 0, // top-left after 2x
231 width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
232 static_cast<float>(width), height / 2.0f, // middle-right after 1.33x zoom
233 };
234 coords = originalCoords;
235 mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampExclusive);
236 for (size_t i = 0; i < coords.size(); i++) {
237 EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedExc[i]), kMaxAllowedPixelError);
238 }
239
240 // Verify 0.33x zoom work as expected
241 std::array<float, 16> expectedZoomOutCoords = {
242 width / 3.0f, height / 3.0f, // top-left
243 width * 2 / 3.0f, height / 3.0f, // top-right
244 width / 3.0f, height * 2 / 3.0f, // bottom-left
245 width * 2 / 3.0f, height * 2 / 3.0f, // bottom-right
246 width / 2.0f, height / 2.0f, // center
247 width * 5 / 12.0f, height * 5 / 12.0f, // top-left after 2x
248 width * 4 / 9.0f, height * 5 / 9.0f, // bottom-left after 3x zoom-in
249 width * 5 / 8.0f, height / 2.0f, // middle-right after 1.33x zoom-in
250 };
251 coords = originalCoords;
252 mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, ZoomRatioMapper::ClampOff);
253 for (size_t i = 0; i < coords.size(); i++) {
254 EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
255 }
256}
257
258TEST(ZoomRatioTest, scaleCoordinatesTest) {
259 subScaleCoordinatesTest(false/*usePreCorrectArray*/);
260 subScaleCoordinatesTest(true/*usePreCorrectArray*/);
261}
262
263void subCropOverMaxDigitalZoomTest(bool usePreCorrectArray) {
264 status_t res;
265 ZoomRatioMapper mapper;
266 float noZoomRatioRange[2];
267 res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
268 testActiveArraySize, testPreCorrActiveArraySize,
269 false/*hasZoomRatioRange*/, noZoomRatioRange,
270 usePreCorrectArray);
271 ASSERT_EQ(res, OK);
272
273 CameraMetadata metadata;
274 camera_metadata_entry_t entry;
275
276 size_t index = usePreCorrectArray ? 1 : 0;
277 metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
278 res = mapper.updateCaptureRequest(&metadata);
279 ASSERT_EQ(res, OK);
280 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
281 ASSERT_EQ(entry.count, 4U);
282 for (int i = 0; i < 4; i ++) {
283 EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
284 }
285
286 metadata.update(ANDROID_SCALER_CROP_REGION, test2xCropRegion[index], 4);
287 res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
288 ASSERT_EQ(res, OK);
289 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
290 ASSERT_EQ(entry.count, 4U);
291 for (int i = 0; i < 4; i ++) {
292 EXPECT_EQ(entry.data.i32[i], test2xCropRegion[index][i]);
293 }
294 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
295 ASSERT_TRUE(entry.count == 0 || (entry.count == 1 && entry.data.f[0] == 1.0f));
296}
297
298TEST(ZoomRatioTest, CropOverMaxDigitalZoomTest) {
299 subCropOverMaxDigitalZoomTest(false/*usePreCorrectArray*/);
300 subCropOverMaxDigitalZoomTest(true/*usePreCorrectArray*/);
301}
302
303void subCropOverZoomRangeTest(bool usePreCorrectArray) {
304 status_t res;
305 ZoomRatioMapper mapper;
306 float zoomRatioRange[2] = {0.5f, 4.0f};
307 res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
308 testActiveArraySize, testPreCorrActiveArraySize,
309 true/*hasZoomRatioRange*/, zoomRatioRange,
310 usePreCorrectArray);
311 ASSERT_EQ(res, OK);
312
313 CameraMetadata metadata;
314 camera_metadata_entry_t entry;
315
316 size_t index = usePreCorrectArray ? 1 : 0;
317
318 // 2x zoom crop region, zoomRatio is 1.0f
319 metadata.update(ANDROID_SCALER_CROP_REGION, test2xCropRegion[index], 4);
320 res = mapper.updateCaptureRequest(&metadata);
321 ASSERT_EQ(res, OK);
322 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
323 ASSERT_EQ(entry.count, 4U);
324 for (int i = 0; i < 4; i++) {
325 EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
326 }
327 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
328 EXPECT_NEAR(entry.data.f[0], 2.0f, kMaxAllowedRatioError);
329
330 res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
331 ASSERT_EQ(res, OK);
332 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
333 EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
334 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
335 ASSERT_EQ(entry.count, 4U);
336 for (int i = 0; i < 4; i++) {
337 EXPECT_EQ(entry.data.i32[i], test2xCropRegion[index][i]);
338 }
339
340 // Letter boxing crop region, zoomRatio is 1.0
341 float zoomRatio = 1.0f;
342 metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
343 metadata.update(ANDROID_SCALER_CROP_REGION, testLetterBoxSize[index], 4);
344 res = mapper.updateCaptureRequest(&metadata);
345 ASSERT_EQ(res, OK);
346 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
347 ASSERT_EQ(entry.count, 4U);
348 for (int i = 0; i < 4; i++) {
349 EXPECT_EQ(entry.data.i32[i], testLetterBoxSize[index][i]);
350 }
351 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
352 EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
353
354 res = mapper.updateCaptureResult(&metadata, true/*requestedZoomRatioIs1*/);
355 ASSERT_EQ(res, OK);
356 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
357 ASSERT_EQ(entry.count, 4U);
358 for (int i = 0; i < 4; i++) {
359 EXPECT_EQ(entry.data.i32[i], testLetterBoxSize[index][i]);
360 }
361 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
362 EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
363}
364
365TEST(ZoomRatioTest, CropOverZoomRangeTest) {
366 subCropOverZoomRangeTest(false/*usePreCorrectArray*/);
367 subCropOverZoomRangeTest(true/*usePreCorrectArray*/);
368}
369
370void subZoomOverMaxDigitalZoomTest(bool usePreCorrectArray) {
371 status_t res;
372 ZoomRatioMapper mapper;
373 float noZoomRatioRange[2];
374 res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
375 testActiveArraySize, testPreCorrActiveArraySize,
376 false/*hasZoomRatioRange*/, noZoomRatioRange,
377 usePreCorrectArray);
378 ASSERT_EQ(res, OK);
379
380 CameraMetadata metadata;
381 float zoomRatio = 3.0f;
382 camera_metadata_entry_t entry;
383
384 size_t index = usePreCorrectArray ? 1 : 0;
385
386 // Full active array crop, zoomRatio is 3.0f
387 metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
388 metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
389 res = mapper.updateCaptureRequest(&metadata);
390 ASSERT_EQ(res, OK);
391 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
392 ASSERT_EQ(entry.count, 4U);
393 std::array<float, 4> expectedCrop = {
394 testDefaultCropSize[index][2] / 3.0f, /*x*/
395 testDefaultCropSize[index][3] / 3.0f, /*y*/
396 testDefaultCropSize[index][2] / 3.0f, /*width*/
397 testDefaultCropSize[index][3] / 3.0f, /*height*/
398 };
399 for (int i = 0; i < 4; i++) {
400 EXPECT_LE(std::abs(entry.data.i32[i] - expectedCrop[i]), kMaxAllowedPixelError);
401 }
402
403 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
404 if (entry.count == 1) {
405 EXPECT_NEAR(entry.data.f[0], 1.0f, kMaxAllowedRatioError);
406 }
407}
408
409TEST(ZoomRatioTest, ZoomOverMaxDigitalZoomTest) {
410 subZoomOverMaxDigitalZoomTest(false/*usePreCorrectArray*/);
411 subZoomOverMaxDigitalZoomTest(true/*usePreCorrectArray*/);
412}
413
414void subZoomOverZoomRangeTest(bool usePreCorrectArray) {
415 status_t res;
416 ZoomRatioMapper mapper;
417 float zoomRatioRange[2] = {1.0f, 4.0f};
418 res = setupTestMapper(&mapper, 4.0/*maxDigitalZoom*/,
419 testActiveArraySize, testPreCorrActiveArraySize,
420 true/*hasZoomRatioRange*/, zoomRatioRange,
421 usePreCorrectArray);
422 ASSERT_EQ(res, OK);
423
424 CameraMetadata metadata;
425 float zoomRatio = 3.0f;
426 camera_metadata_entry_t entry;
427 size_t index = usePreCorrectArray ? 1 : 0;
428
429 // Full active array crop, zoomRatio is 3.0f
430 metadata.update(ANDROID_SCALER_CROP_REGION, testDefaultCropSize[index], 4);
431 metadata.update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
432 res = mapper.updateCaptureRequest(&metadata);
433 ASSERT_EQ(res, OK);
434 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
435 ASSERT_EQ(entry.count, 4U);
436 for (int i = 0; i < 4; i ++) {
437 EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
438 }
439 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
440 ASSERT_EQ(entry.data.f[0], zoomRatio);
441
442 res = mapper.updateCaptureResult(&metadata, false/*requestedZoomRatioIs1*/);
443 ASSERT_EQ(res, OK);
444 entry = metadata.find(ANDROID_SCALER_CROP_REGION);
445 ASSERT_EQ(entry.count, 4U);
446 for (int i = 0; i < 4; i ++) {
447 EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
448 }
449 entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
450 ASSERT_EQ(entry.data.f[0], zoomRatio);
451}
452
453TEST(ZoomRatioTest, ZoomOverZoomRangeTest) {
454 subZoomOverZoomRangeTest(false/*usePreCorrectArray*/);
455 subZoomOverZoomRangeTest(true/*usePreCorrectArray*/);
456}