diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 5697a8739a..96261ab0ef 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -97,3 +97,8 @@ LOCAL_CFLAGS += -Wall -Wextra -Werror LOCAL_MODULE:= libcameraservice include $(BUILD_SHARED_LIBRARY) + +# Build tests too + +include $(LOCAL_PATH)/tests/Android.mk + diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp index 82923bd307..9229079717 100644 --- a/services/camera/libcameraservice/device3/DistortionMapper.cpp +++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp @@ -195,6 +195,7 @@ status_t DistortionMapper::updateCalibration(const CameraMetadata &result) { } mValidMapping = true; + // Need to recalculate grid mValidGrids = false; return OK; diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk index 37a05c2746..f77069c32f 100644 --- a/services/camera/libcameraservice/tests/Android.mk +++ b/services/camera/libcameraservice/tests/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(call all-cpp-files-under, .) LOCAL_SHARED_LIBRARIES := \ + libbase \ libcutils \ libcameraservice \ libhidlbase \ diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp index c1d6e850c9..ef93d9a9ca 100644 --- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp +++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp @@ -190,6 +190,7 @@ struct TestStatusListener : public CameraProviderManager::StatusListener { hardware::camera::common::V1_0::CameraDeviceStatus) override {} void onTorchStatusChanged(const String8 &, hardware::camera::common::V1_0::TorchModeStatus) override {} + void onNewProviderRegistered() override {} }; TEST(CameraProviderManagerTest, InitializeTest) { diff --git a/services/camera/libcameraservice/tests/DistortionMapperComp.py b/services/camera/libcameraservice/tests/DistortionMapperComp.py new file mode 100644 index 0000000000..dea36a7b3e --- /dev/null +++ b/services/camera/libcameraservice/tests/DistortionMapperComp.py @@ -0,0 +1,47 @@ +# Calculates comparison output values for DistortionMapperTest.cpp:CompareToOpenCV +# +# Assumes a python that has numpy and cv2 (OpenCV) available + +import numpy as np +import cv2 + +Fx = 1000 +Fy = 1000 +Cx = 500 +Cy = 500 +# s = 0 - not supported by OpenCV + +K = np.array([[Fx, 0, Cx],[0, Fy, Cy],[0, 0, 1]]) + +# Order is k1, k2, t1, t2, k3 +dist = np.array([0.1, -0.003, 0.02, 0.01, 0.004]) + +np.random.seed(1234) + +activeArray = np.array([[1000, 750]]) + +rawCoords = np.floor(np.random.rand(1000,2) * activeArray) + +# OpenCV needs either row count or col count = 1 for some reason +rawCoords2 = rawCoords.reshape(-1, 1, 2) + +# P is the output camera matrix, K is the input; use the same for both +expCoords = cv2.undistortPoints(rawCoords2, K, dist, P = K) + +with open('DistortionMapperTest_OpenCvData.h','w') as f: + f.write('// Generated by DistortionMapperComp.py\n'); + f.write('// for use by DistortionMapperTest.cpp\n\n'); + + f.write('namespace openCvData {\n') + f.write('std::array rawCoords = {\n' % (rawCoords.shape[0] * rawCoords.shape[1])) + for i in range(rawCoords.shape[0]): + f.write(' %d, %d,\n' % (rawCoords[i][0], rawCoords[i][1])) + f.write('};\n') + + f.write('std::array expCoords = {\n' % (expCoords.shape[0] * expCoords.shape[2])) + for i in range(expCoords.shape[0]): + f.write(' %d, %d,\n' % (expCoords[i][0][0], expCoords[i][0][1])) + f.write('};\n') + f.write('} // namespace openCvData\n') + +print "DistortionMapperTest_OpenCvData.h generated" diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp new file mode 100644 index 0000000000..b489931b9f --- /dev/null +++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2018 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 "DistortionMapperTest" + +#include + +#include +#include +#include + +#include "../device3/DistortionMapper.h" + +using namespace android; +using namespace android::camera3; + + +int32_t testActiveArray[] = {100, 100, 1000, 750}; + +float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f }; + +float identityDistortion[] = { 0.f, 0.f, 0.f, 0.f, 0.f}; + +std::array basicCoords = { + 0, 0, + testActiveArray[2] - 1, 0, + testActiveArray[2] - 1, testActiveArray[3] - 1, + 0, testActiveArray[3] - 1, + testActiveArray[2] / 2, testActiveArray[3] / 2, + 251, 403 // A particularly bad coordinate for current grid count/array size +}; + + +void setupTestMapper(DistortionMapper *m, float distortion[5]) { + CameraMetadata deviceInfo; + + deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, + testActiveArray, 4); + + deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, + testICal, 5); + + deviceInfo.update(ANDROID_LENS_DISTORTION, + distortion, 5); + + m->setupStaticInfo(deviceInfo); +} + +TEST(DistortionMapperTest, Initialization) { + CameraMetadata deviceInfo; + + ASSERT_FALSE(DistortionMapper::isDistortionSupported(deviceInfo)); + + uint8_t distortionModes[] = + {ANDROID_DISTORTION_CORRECTION_MODE_OFF, + ANDROID_DISTORTION_CORRECTION_MODE_FAST, + ANDROID_DISTORTION_CORRECTION_MODE_HIGH_QUALITY}; + + deviceInfo.update(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES, + distortionModes, 1); + + ASSERT_FALSE(DistortionMapper::isDistortionSupported(deviceInfo)); + + deviceInfo.update(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES, + distortionModes, 3); + + ASSERT_TRUE(DistortionMapper::isDistortionSupported(deviceInfo)); + + DistortionMapper m; + + ASSERT_FALSE(m.calibrationValid()); + + ASSERT_NE(m.setupStaticInfo(deviceInfo), OK); + + ASSERT_FALSE(m.calibrationValid()); + + deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, + testActiveArray, 4); + + deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, + testICal, 5); + + deviceInfo.update(ANDROID_LENS_DISTORTION, + identityDistortion, 5); + + ASSERT_EQ(m.setupStaticInfo(deviceInfo), OK); + + ASSERT_TRUE(m.calibrationValid()); + + CameraMetadata captureResult; + + ASSERT_NE(m.updateCalibration(captureResult), OK); + + captureResult.update(ANDROID_LENS_INTRINSIC_CALIBRATION, + testICal, 5); + captureResult.update(ANDROID_LENS_DISTORTION, + identityDistortion, 5); + + ASSERT_EQ(m.updateCalibration(captureResult), OK); + +} + +TEST(DistortionMapperTest, IdentityTransform) { + status_t res; + + DistortionMapper m; + setupTestMapper(&m, identityDistortion); + + auto coords = basicCoords; + res = m.mapCorrectedToRaw(coords.data(), 5); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < coords.size(); i++) { + EXPECT_EQ(coords[i], basicCoords[i]); + } + + res = m.mapRawToCorrected(coords.data(), 5); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < coords.size(); i++) { + EXPECT_EQ(coords[i], basicCoords[i]); + } + + std::array rects = { + 0, 0, 100, 100, + testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100 + }; + + auto rectsOrig = rects; + res = m.mapCorrectedRectToRaw(rects.data(), 2); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < rects.size(); i++) { + EXPECT_EQ(rects[i], rectsOrig[i]); + } + + res = m.mapRawRectToCorrected(rects.data(), 2); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < rects.size(); i++) { + EXPECT_EQ(rects[i], rectsOrig[i]); + } +} + +TEST(DistortionMapperTest, LargeTransform) { + status_t res; + constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed + constexpr int bucketsPerPixel = 3; // Histogram granularity + + unsigned int seed = 1234; // Ensure repeatability for debugging + const size_t coordCount = 1e6; // Number of random test points + + float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01}; + + DistortionMapper m; + setupTestMapper(&m, bigDistortion); + + std::default_random_engine gen(seed); + + std::uniform_int_distribution x_dist(0, testActiveArray[2] - 1); + std::uniform_int_distribution y_dist(0, testActiveArray[3] - 1); + + std::vector randCoords(coordCount * 2); + + for (size_t i = 0; i < randCoords.size(); i += 2) { + randCoords[i] = x_dist(gen); + randCoords[i + 1] = y_dist(gen); + } + + randCoords.insert(randCoords.end(), basicCoords.begin(), basicCoords.end()); + + auto origCoords = randCoords; + + base::Timer correctedToRawTimer; + res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2); + auto correctedToRawDurationMs = correctedToRawTimer.duration(); + EXPECT_EQ(res, OK); + + base::Timer rawToCorrectedTimer; + res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2); + auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration(); + EXPECT_EQ(res, OK); + + float correctedToRawDurationPerCoordUs = + (std::chrono::duration_cast>( + correctedToRawDurationMs) / (randCoords.size() / 2) ).count(); + float rawToCorrectedDurationPerCoordUs = + (std::chrono::duration_cast>( + rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count(); + + RecordProperty("CorrectedToRawDurationPerCoordUs", + base::StringPrintf("%f", correctedToRawDurationPerCoordUs)); + RecordProperty("RawToCorrectedDurationPerCoordUs", + base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs)); + + // Calculate mapping errors after round trip + float totalErrorSq = 0; + // Basic histogram; buckets go from [N to N+1) + std::array histogram = {0}; + int outOfHistogram = 0; + + for (size_t i = 0; i < randCoords.size(); i += 2) { + int xOrig = origCoords[i]; + int yOrig = origCoords[i + 1]; + int xMapped = randCoords[i]; + int yMapped = randCoords[i + 1]; + + float errorSq = (xMapped - xOrig) * (xMapped - xOrig) + + (yMapped - yOrig) * (yMapped - yOrig); + + EXPECT_LE(errorSq, maxAllowedPixelError * maxAllowedPixelError) << "( " << + xOrig << "," << yOrig << ") -> (" << xMapped << "," << yMapped << ")"; + + // Note: Integer coordinates, so histogram will be clumpy; error distances can only be of + // form sqrt(X^2+Y^2) where X, Y are integers, so: + // 0, 1, sqrt(2), 2, sqrt(5), sqrt(8), 3, sqrt(10), sqrt(13), 4 ... + totalErrorSq += errorSq; + float errorDist = std::sqrt(errorSq); + if (errorDist < maxAllowedPixelError) { + int histBucket = static_cast(errorDist * bucketsPerPixel); // rounds down + histogram[histBucket]++; + } else { + outOfHistogram++; + } + } + + float rmsError = std::sqrt(totalErrorSq / randCoords.size()); + RecordProperty("RmsError", base::StringPrintf("%f", rmsError)); + for (size_t i = 0; i < histogram.size(); i++) { + std::string label = base::StringPrintf("HistogramBin[%f,%f)", + (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel); + RecordProperty(label, histogram[i]); + } + RecordProperty("HistogramOutOfRange", outOfHistogram); +} + +// Compare against values calculated by OpenCV +// undistortPoints() method, which is the same as mapRawToCorrected +// See script DistortionMapperComp.py +#include "DistortionMapperTest_OpenCvData.h" + +TEST(DistortionMapperTest, CompareToOpenCV) { + status_t res; + + float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01}; + + // Expect to match within sqrt(2) radius pixels + const int32_t maxSqError = 2; + + DistortionMapper m; + setupTestMapper(&m, bigDistortion); + + using namespace openCvData; + + res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2); + + for (size_t i = 0; i < rawCoords.size(); i+=2) { + int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) + + (rawCoords[i + 1] - expCoords[i + 1]) * (rawCoords[i + 1] - expCoords[i + 1]); + EXPECT_LE(dist, maxSqError) + << "(" << rawCoords[i] << ", " << rawCoords[i + 1] << ") != (" + << expCoords[i] << ", " << expCoords[i + 1] << ")"; + } +} diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h b/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h new file mode 100644 index 0000000000..f996bd5c3e --- /dev/null +++ b/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h @@ -0,0 +1,2009 @@ +// Generated by DistortionMapperComp.py +// for use by DistortionMapperTest.cpp + +namespace openCvData { +std::array rawCoords = { + 191, 466, + 437, 589, + 779, 204, + 276, 601, + 958, 656, + 357, 375, + 683, 534, + 370, 420, + 503, 10, + 772, 661, + 364, 461, + 75, 276, + 933, 488, + 397, 591, + 316, 426, + 869, 327, + 802, 107, + 704, 528, + 218, 693, + 442, 681, + 59, 138, + 47, 506, + 594, 399, + 43, 421, + 329, 377, + 111, 455, + 565, 5, + 617, 684, + 790, 744, + 958, 593, + 285, 468, + 478, 146, + 382, 40, + 451, 736, + 123, 89, + 738, 440, + 471, 80, + 229, 674, + 416, 401, + 6, 225, + 436, 459, + 918, 469, + 705, 112, + 746, 623, + 633, 328, + 152, 426, + 528, 713, + 480, 376, + 536, 614, + 57, 502, + 767, 531, + 796, 418, + 965, 110, + 29, 445, + 114, 713, + 325, 145, + 457, 690, + 879, 189, + 348, 136, + 901, 529, + 726, 675, + 779, 449, + 291, 113, + 335, 493, + 73, 41, + 323, 442, + 853, 215, + 173, 100, + 994, 134, + 317, 426, + 9, 675, + 977, 417, + 84, 249, + 728, 106, + 552, 204, + 974, 500, + 255, 81, + 776, 586, + 761, 685, + 658, 426, + 201, 523, + 952, 667, + 993, 614, + 545, 338, + 890, 729, + 593, 274, + 323, 653, + 215, 551, + 365, 601, + 782, 526, + 622, 370, + 840, 534, + 443, 23, + 363, 548, + 475, 258, + 640, 94, + 171, 552, + 127, 277, + 604, 77, + 802, 709, + 979, 660, + 627, 697, + 724, 537, + 41, 329, + 282, 251, + 83, 570, + 509, 495, + 630, 278, + 446, 311, + 480, 737, + 373, 9, + 921, 654, + 351, 472, + 357, 159, + 223, 314, + 72, 488, + 755, 699, + 376, 222, + 371, 620, + 901, 320, + 2, 31, + 141, 448, + 106, 222, + 58, 465, + 22, 641, + 306, 569, + 563, 28, + 565, 494, + 183, 598, + 612, 416, + 629, 514, + 240, 590, + 856, 545, + 692, 354, + 856, 498, + 333, 388, + 394, 23, + 576, 637, + 951, 512, + 76, 307, + 41, 130, + 565, 129, + 566, 385, + 868, 540, + 903, 208, + 256, 524, + 901, 737, + 640, 247, + 606, 616, + 627, 88, + 285, 740, + 431, 430, + 527, 148, + 165, 375, + 990, 295, + 714, 131, + 70, 118, + 161, 212, + 536, 579, + 880, 478, + 852, 448, + 693, 402, + 889, 39, + 783, 108, + 58, 44, + 51, 386, + 404, 749, + 108, 245, + 997, 300, + 883, 426, + 953, 664, + 779, 23, + 962, 389, + 207, 656, + 224, 103, + 725, 730, + 535, 333, + 22, 454, + 964, 725, + 930, 138, + 622, 309, + 363, 27, + 868, 504, + 87, 665, + 782, 237, + 818, 380, + 21, 325, + 446, 179, + 830, 558, + 586, 369, + 487, 200, + 605, 565, + 270, 391, + 98, 535, + 884, 425, + 994, 134, + 12, 342, + 931, 634, + 473, 676, + 225, 228, + 714, 543, + 18, 214, + 580, 698, + 338, 90, + 516, 524, + 298, 646, + 905, 576, + 261, 703, + 938, 558, + 910, 177, + 494, 607, + 954, 478, + 910, 519, + 42, 625, + 369, 702, + 483, 93, + 964, 12, + 676, 105, + 155, 487, + 981, 521, + 761, 318, + 138, 162, + 764, 40, + 490, 135, + 630, 413, + 567, 613, + 938, 144, + 712, 523, + 258, 686, + 532, 418, + 322, 253, + 332, 734, + 203, 500, + 574, 38, + 542, 155, + 91, 652, + 27, 726, + 327, 307, + 135, 95, + 413, 463, + 132, 730, + 180, 570, + 482, 576, + 302, 11, + 463, 527, + 66, 501, + 345, 443, + 229, 200, + 932, 619, + 145, 485, + 883, 556, + 515, 101, + 39, 625, + 117, 392, + 873, 29, + 692, 357, + 169, 47, + 32, 181, + 112, 303, + 42, 694, + 935, 607, + 188, 440, + 903, 725, + 67, 238, + 696, 480, + 632, 621, + 713, 251, + 167, 573, + 359, 66, + 729, 660, + 41, 131, + 420, 255, + 44, 84, + 438, 53, + 816, 649, + 131, 144, + 437, 728, + 773, 98, + 927, 620, + 392, 105, + 52, 69, + 886, 126, + 362, 490, + 604, 296, + 374, 47, + 428, 539, + 768, 145, + 862, 21, + 902, 177, + 128, 238, + 848, 624, + 345, 179, + 535, 203, + 34, 470, + 520, 31, + 757, 741, + 801, 257, + 335, 263, + 442, 434, + 617, 132, + 864, 532, + 170, 641, + 19, 481, + 162, 193, + 342, 4, + 438, 597, + 675, 408, + 564, 10, + 608, 741, + 224, 440, + 835, 594, + 329, 267, + 960, 167, + 211, 115, + 495, 195, + 555, 54, + 378, 345, + 778, 540, + 231, 18, + 257, 307, + 8, 353, + 852, 692, + 231, 743, + 5, 251, + 789, 73, + 442, 285, + 832, 533, + 355, 18, + 693, 315, + 858, 431, + 940, 660, + 458, 12, + 68, 240, + 592, 457, + 512, 108, + 876, 553, + 373, 621, + 90, 48, + 505, 700, + 148, 427, + 59, 126, + 69, 679, + 447, 79, + 665, 376, + 409, 545, + 172, 288, + 267, 231, + 177, 361, + 629, 44, + 196, 209, + 707, 245, + 425, 528, + 159, 329, + 629, 693, + 356, 614, + 640, 536, + 738, 728, + 246, 31, + 247, 33, + 705, 626, + 934, 353, + 512, 197, + 98, 599, + 142, 604, + 879, 374, + 914, 309, + 200, 482, + 399, 460, + 437, 444, + 951, 414, + 903, 103, + 1, 459, + 541, 585, + 821, 715, + 609, 217, + 826, 282, + 609, 465, + 441, 149, + 443, 693, + 671, 61, + 572, 90, + 999, 748, + 694, 280, + 401, 693, + 244, 498, + 481, 26, + 110, 671, + 892, 686, + 307, 223, + 659, 446, + 984, 461, + 154, 623, + 815, 69, + 887, 12, + 863, 674, + 850, 489, + 328, 409, + 237, 653, + 176, 277, + 759, 229, + 616, 164, + 872, 485, + 473, 175, + 833, 73, + 205, 176, + 596, 471, + 982, 132, + 799, 116, + 360, 716, + 130, 204, + 809, 724, + 92, 437, + 405, 674, + 84, 135, + 50, 225, + 190, 6, + 127, 84, + 730, 179, + 901, 246, + 874, 177, + 378, 406, + 515, 310, + 388, 255, + 402, 342, + 382, 493, + 881, 429, + 428, 193, + 865, 129, + 579, 545, + 730, 302, + 117, 572, + 533, 541, + 597, 317, + 933, 745, + 547, 547, + 27, 647, + 998, 243, + 850, 458, + 441, 395, + 779, 188, + 462, 635, + 678, 275, + 137, 302, + 931, 504, + 419, 426, + 41, 746, + 619, 584, + 931, 256, + 811, 654, + 665, 441, + 893, 336, + 244, 610, + 697, 307, + 5, 715, + 605, 4, + 498, 448, + 300, 346, + 495, 439, + 869, 624, + 778, 411, + 613, 550, + 566, 581, + 986, 591, + 614, 118, + 476, 212, + 153, 582, + 58, 59, + 664, 392, + 446, 230, + 17, 220, + 267, 27, + 804, 250, + 891, 607, + 964, 718, + 591, 233, + 918, 37, + 225, 272, + 412, 708, + 579, 140, + 596, 700, + 134, 736, + 153, 615, + 677, 303, + 217, 580, + 847, 397, + 409, 13, + 148, 603, + 419, 254, + 297, 538, + 997, 413, + 889, 126, + 883, 527, + 422, 647, + 235, 422, + 26, 285, + 361, 68, + 45, 256, + 355, 746, + 944, 98, + 518, 357, + 401, 697, + 515, 607, + 881, 572, + 464, 55, + 470, 150, + 208, 133, + 354, 683, + 433, 133, + 752, 37, + 82, 28, + 465, 452, + 181, 389, + 710, 693, + 529, 728, + 547, 4, + 472, 391, + 152, 490, + 130, 340, + 982, 99, + 60, 50, + 96, 614, + 629, 587, + 77, 728, + 882, 472, + 929, 298, + 488, 514, + 281, 507, + 495, 593, + 218, 559, + 454, 306, + 922, 113, + 130, 286, + 541, 708, + 323, 73, + 947, 642, + 26, 88, + 829, 103, + 569, 358, + 306, 42, + 936, 678, + 722, 490, + 392, 730, + 711, 369, + 326, 86, + 972, 205, + 187, 161, + 760, 708, + 501, 496, + 347, 96, + 681, 293, + 26, 375, + 528, 167, + 1, 334, + 505, 60, + 822, 180, + 9, 168, + 84, 619, + 714, 183, + 63, 320, + 706, 538, + 193, 555, + 956, 386, + 430, 17, + 507, 514, + 138, 504, + 39, 323, + 854, 316, + 88, 42, + 103, 363, + 674, 68, + 832, 582, + 61, 241, + 377, 376, + 449, 350, + 104, 280, + 21, 336, + 893, 581, + 249, 548, + 315, 372, + 50, 436, + 282, 220, + 126, 669, + 451, 488, + 809, 212, + 273, 289, + 421, 699, + 867, 333, + 29, 80, + 196, 178, + 824, 672, + 27, 429, + 805, 315, + 525, 214, + 658, 67, + 822, 605, + 191, 478, + 832, 352, + 580, 81, + 462, 664, + 464, 349, + 196, 29, + 615, 423, + 108, 556, + 183, 261, + 480, 482, + 621, 570, + 286, 369, + 681, 382, + 768, 224, + 546, 183, + 443, 607, + 103, 172, + 791, 424, + 827, 731, + 965, 712, + 551, 69, + 740, 423, + 745, 341, + 155, 746, + 889, 602, + 411, 159, + 294, 467, + 248, 599, + 18, 360, + 734, 512, + 421, 519, + 367, 174, + 785, 545, + 706, 23, + 239, 278, + 581, 65, + 232, 609, + 752, 603, + 294, 585, + 224, 217, + 848, 558, + 332, 425, + 699, 68, + 53, 647, + 629, 652, + 87, 649, + 41, 718, + 227, 563, + 400, 302, + 253, 380, + 184, 42, + 366, 539, + 474, 691, + 170, 538, + 869, 96, + 974, 565, + 916, 28, + 285, 617, + 274, 38, + 147, 12, + 782, 261, + 749, 41, + 78, 592, + 370, 83, + 405, 488, + 436, 151, + 443, 556, + 96, 383, + 843, 745, + 630, 214, + 126, 10, + 338, 363, + 546, 27, + 61, 17, + 507, 199, + 445, 730, + 797, 213, + 555, 148, + 790, 65, + 837, 180, + 434, 320, + 102, 681, + 149, 680, + 10, 130, + 839, 232, + 848, 683, + 899, 650, + 837, 190, + 843, 463, + 984, 457, + 651, 490, + 552, 139, + 980, 71, + 748, 393, + 290, 171, + 503, 698, + 574, 742, + 429, 312, + 627, 680, + 69, 412, + 154, 538, + 135, 3, + 537, 12, + 535, 34, + 153, 632, + 797, 227, + 398, 336, + 20, 463, + 804, 175, + 400, 369, + 501, 250, + 105, 480, + 151, 146, + 57, 686, + 830, 119, + 867, 380, + 128, 84, + 222, 667, + 450, 522, + 390, 466, + 716, 375, + 760, 624, + 559, 407, + 587, 18, + 989, 53, + 817, 102, + 153, 269, + 253, 164, + 563, 360, + 93, 385, + 197, 360, + 277, 7, + 887, 280, + 416, 658, + 760, 411, + 902, 690, + 465, 424, + 28, 105, + 399, 620, + 455, 520, + 637, 491, + 769, 0, + 300, 521, + 90, 392, + 894, 722, + 705, 573, + 344, 188, + 667, 111, + 470, 16, + 759, 154, + 840, 581, + 176, 663, + 93, 151, + 372, 130, + 345, 425, + 156, 581, + 33, 8, + 320, 395, + 629, 661, + 641, 17, + 695, 663, + 751, 197, + 507, 93, + 608, 519, + 77, 303, + 513, 605, + 98, 354, + 567, 401, + 184, 440, + 785, 748, + 52, 32, + 528, 452, + 82, 532, + 116, 147, + 779, 341, + 308, 275, + 763, 135, + 137, 375, + 14, 260, + 337, 378, + 492, 262, + 202, 119, + 561, 334, + 855, 683, + 876, 724, + 202, 544, + 571, 437, + 456, 436, + 67, 4, + 468, 592, + 922, 540, + 125, 539, + 615, 290, + 785, 76, + 402, 556, + 12, 696, + 460, 52, + 909, 92, + 894, 153, + 931, 373, + 360, 120, + 726, 626, + 318, 733, + 472, 424, + 146, 74, + 86, 564, + 742, 236, + 845, 400, + 832, 139, + 275, 437, + 929, 42, + 818, 123, + 439, 274, + 65, 590, + 512, 132, + 520, 443, + 444, 107, + 961, 313, + 130, 488, + 587, 191, + 287, 603, + 56, 208, + 936, 628, + 908, 445, + 773, 258, + 383, 283, + 425, 530, + 244, 133, + 216, 543, + 631, 595, + 785, 108, + 87, 192, + 640, 427, + 889, 688, + 152, 89, + 10, 209, + 122, 343, + 188, 5, + 896, 748, + 806, 22, + 535, 457, + 851, 307, + 261, 566, + 791, 590, + 947, 300, + 658, 394, + 418, 305, + 371, 632, + 470, 438, + 165, 410, + 538, 380, + 643, 408, + 318, 591, + 564, 311, + 327, 690, + 930, 8, + 93, 100, + 627, 196, + 582, 416, + 200, 492, + 943, 267, + 31, 355, + 67, 374, + 692, 57, + 229, 373, + 542, 371, + 801, 230, + 114, 420, + 769, 326, + 83, 448, + 846, 137, + 912, 77, + 126, 3, + 784, 420, + 660, 391, + 795, 188, + 530, 42, + 137, 106, + 663, 80, + 757, 340, + 694, 267, + 768, 612, + 926, 155, + 600, 25, + 292, 31, + 97, 225, + 60, 437, + 724, 563, + 698, 85, + 286, 196, + 66, 1, + 269, 25, + 467, 405, + 204, 171, + 653, 14, + 299, 360, + 521, 719, + 760, 602, + 329, 282, + 687, 530, + 110, 200, + 30, 300, + 6, 501, + 868, 281, + 281, 76, + 805, 363, + 876, 114, + 219, 549, + 65, 611, + 859, 23, + 66, 354, + 205, 169, + 434, 174, + 828, 668, + 814, 720, + 663, 34, + 875, 707, + 969, 561, + 932, 66, + 834, 548, + 961, 86, + 263, 148, + 145, 202, + 83, 146, + 947, 727, + 3, 138, + 927, 514, + 814, 742, + 80, 430, + 866, 184, + 593, 731, + 193, 219, + 496, 490, + 606, 530, + 314, 334, + 301, 327, + 50, 715, + 178, 57, + 936, 626, + 972, 617, + 33, 427, + 147, 435, + 83, 341, + 859, 244, + 337, 688, + 637, 124, + 874, 71, + 590, 474, + 332, 120, + 640, 290, + 816, 171, + 665, 431, + 79, 31, + 857, 110, + 103, 79, + 293, 397, + 866, 651, + 356, 73, + 438, 710, + 41, 233, + 782, 596, + 852, 407, + 590, 104, + 34, 116, + 756, 276, + 282, 181, + 871, 275, + 888, 712, + 872, 279, + 645, 324, + 730, 524, + 430, 302, + 601, 486, + 114, 529, + 359, 317, + 313, 426, + 33, 732, + 970, 211, + 657, 582, + 945, 501, + 450, 630, + 822, 697, + 702, 600, + 958, 289, + 732, 96, + 205, 662, + 695, 533, + 369, 433, + 83, 445, + 176, 315, + 239, 95, + 895, 682, + 628, 118, + 730, 741, + 779, 734, + 804, 314, + 465, 567, + 810, 106, + 81, 268, + 968, 518, + 22, 159, + 726, 504, + 38, 269, + 751, 649, + 954, 659, +}; +std::array expCoords = { + 190, 464, + 437, 588, + 774, 203, + 276, 599, + 939, 646, + 356, 373, + 681, 533, + 369, 419, + 500, 7, + 765, 655, + 363, 460, + 75, 272, + 920, 484, + 397, 590, + 315, 424, + 861, 326, + 795, 107, + 701, 526, + 220, 688, + 442, 678, + 59, 134, + 50, 501, + 593, 398, + 44, 417, + 327, 375, + 111, 452, + 562, 3, + 614, 680, + 780, 734, + 941, 586, + 284, 467, + 476, 142, + 379, 36, + 451, 731, + 122, 85, + 735, 439, + 469, 76, + 231, 670, + 415, 400, + 8, 221, + 435, 458, + 906, 466, + 701, 111, + 741, 619, + 632, 327, + 151, 423, + 527, 709, + 479, 375, + 535, 612, + 59, 498, + 762, 529, + 791, 417, + 948, 113, + 31, 441, + 119, 705, + 323, 141, + 457, 687, + 869, 189, + 346, 132, + 890, 525, + 721, 670, + 775, 448, + 288, 108, + 334, 492, + 74, 38, + 322, 441, + 845, 215, + 171, 96, + 975, 137, + 316, 425, + 17, 665, + 961, 414, + 83, 245, + 723, 105, + 551, 201, + 958, 495, + 253, 77, + 770, 583, + 754, 679, + 657, 425, + 201, 521, + 934, 657, + 973, 605, + 544, 336, + 875, 717, + 592, 272, + 323, 650, + 215, 549, + 365, 600, + 777, 524, + 621, 369, + 832, 531, + 440, 19, + 362, 547, + 474, 255, + 637, 92, + 171, 549, + 126, 273, + 601, 74, + 792, 701, + 959, 649, + 624, 692, + 721, 535, + 42, 325, + 280, 247, + 86, 565, + 508, 494, + 629, 276, + 445, 309, + 479, 732, + 370, 5, + 905, 645, + 350, 471, + 355, 155, + 221, 311, + 73, 484, + 748, 692, + 374, 218, + 371, 618, + 891, 319, + 6, 29, + 141, 445, + 105, 218, + 60, 461, + 28, 633, + 306, 567, + 560, 25, + 564, 493, + 184, 595, + 611, 415, + 628, 513, + 240, 587, + 847, 541, + 690, 353, + 848, 495, + 331, 386, + 391, 19, + 575, 635, + 936, 507, + 76, 303, + 42, 126, + 563, 126, + 565, 384, + 858, 536, + 892, 209, + 255, 522, + 884, 724, + 639, 245, + 604, 614, + 624, 86, + 287, 734, + 430, 429, + 525, 145, + 164, 372, + 974, 295, + 710, 130, + 70, 114, + 159, 208, + 535, 578, + 871, 475, + 844, 446, + 691, 401, + 876, 43, + 777, 108, + 59, 41, + 52, 382, + 404, 744, + 107, 241, + 980, 300, + 874, 424, + 935, 654, + 771, 24, + 948, 387, + 209, 652, + 222, 99, + 718, 722, + 534, 331, + 25, 449, + 943, 711, + 916, 140, + 621, 307, + 360, 23, + 859, 501, + 92, 658, + 777, 236, + 812, 379, + 22, 321, + 444, 175, + 822, 554, + 585, 368, + 486, 197, + 604, 564, + 268, 389, + 100, 531, + 875, 423, + 975, 137, + 14, 338, + 915, 626, + 472, 673, + 223, 224, + 711, 541, + 19, 210, + 578, 694, + 336, 85, + 515, 523, + 298, 643, + 892, 570, + 263, 698, + 923, 552, + 898, 178, + 493, 606, + 940, 474, + 898, 515, + 47, 618, + 369, 698, + 481, 89, + 945, 18, + 673, 103, + 155, 484, + 964, 516, + 757, 317, + 136, 158, + 757, 41, + 488, 131, + 629, 412, + 566, 611, + 924, 146, + 709, 521, + 259, 682, + 531, 417, + 320, 250, + 333, 729, + 202, 498, + 571, 35, + 540, 152, + 95, 645, + 36, 715, + 325, 304, + 134, 91, + 412, 462, + 137, 722, + 181, 567, + 481, 575, + 300, 7, + 462, 526, + 68, 497, + 344, 442, + 227, 196, + 917, 611, + 145, 482, + 872, 551, + 513, 97, + 44, 618, + 116, 389, + 861, 32, + 690, 356, + 168, 43, + 33, 177, + 111, 299, + 49, 685, + 920, 600, + 187, 438, + 887, 713, + 67, 234, + 694, 479, + 630, 619, + 710, 250, + 168, 570, + 356, 62, + 724, 655, + 42, 127, + 418, 252, + 45, 81, + 436, 49, + 807, 643, + 130, 140, + 437, 723, + 767, 98, + 912, 612, + 390, 101, + 53, 66, + 875, 128, + 361, 489, + 603, 294, + 371, 43, + 427, 538, + 763, 144, + 850, 24, + 891, 178, + 126, 234, + 838, 618, + 343, 175, + 534, 200, + 36, 466, + 517, 28, + 749, 732, + 796, 256, + 333, 260, + 441, 433, + 615, 129, + 855, 528, + 172, 636, + 22, 476, + 160, 189, + 339, 0, + 438, 596, + 673, 407, + 561, 8, + 605, 735, + 223, 438, + 826, 589, + 327, 264, + 945, 169, + 209, 111, + 494, 192, + 552, 51, + 377, 343, + 773, 537, + 229, 14, + 255, 304, + 10, 349, + 840, 683, + 234, 736, + 7, 247, + 782, 73, + 441, 282, + 824, 530, + 352, 14, + 691, 314, + 850, 429, + 923, 650, + 455, 8, + 68, 236, + 591, 456, + 510, 104, + 866, 549, + 373, 619, + 90, 45, + 504, 696, + 147, 424, + 59, 122, + 75, 671, + 445, 75, + 664, 375, + 408, 544, + 170, 284, + 265, 227, + 175, 358, + 625, 42, + 194, 205, + 704, 244, + 424, 527, + 157, 326, + 626, 689, + 356, 612, + 638, 535, + 731, 720, + 244, 27, + 245, 29, + 701, 623, + 922, 352, + 511, 194, + 101, 594, + 144, 600, + 870, 373, + 903, 308, + 199, 480, + 398, 459, + 436, 443, + 938, 412, + 891, 105, + 4, 454, + 540, 584, + 810, 706, + 608, 215, + 820, 281, + 608, 464, + 439, 145, + 443, 690, + 667, 60, + 570, 87, + 974, 731, + 692, 279, + 401, 690, + 243, 496, + 478, 22, + 114, 664, + 878, 676, + 305, 219, + 658, 445, + 967, 457, + 156, 619, + 807, 70, + 873, 16, + 851, 666, + 842, 486, + 327, 407, + 238, 649, + 174, 273, + 755, 228, + 614, 161, + 863, 482, + 471, 171, + 824, 74, + 203, 172, + 595, 470, + 964, 135, + 792, 116, + 361, 712, + 128, 200, + 799, 715, + 92, 434, + 405, 671, + 84, 131, + 50, 221, + 189, 3, + 126, 80, + 726, 178, + 891, 246, + 865, 178, + 377, 405, + 514, 308, + 386, 252, + 401, 340, + 381, 492, + 872, 427, + 426, 189, + 855, 130, + 578, 544, + 727, 301, + 119, 568, + 532, 540, + 596, 315, + 914, 731, + 546, 546, + 33, 639, + 981, 244, + 842, 456, + 440, 394, + 774, 187, + 462, 633, + 676, 273, + 135, 298, + 918, 500, + 418, 425, + 50, 734, + 617, 582, + 919, 256, + 802, 648, + 664, 440, + 884, 335, + 244, 607, + 695, 306, + 14, 704, + 601, 2, + 497, 447, + 298, 344, + 494, 438, + 858, 618, + 774, 410, + 612, 549, + 565, 580, + 967, 583, + 612, 115, + 475, 209, + 154, 578, + 59, 56, + 663, 391, + 444, 227, + 18, 216, + 265, 23, + 799, 249, + 879, 601, + 943, 704, + 590, 231, + 903, 41, + 223, 268, + 412, 704, + 577, 137, + 594, 696, + 139, 727, + 155, 611, + 675, 302, + 217, 577, + 840, 396, + 406, 9, + 150, 599, + 417, 251, + 296, 537, + 980, 410, + 878, 128, + 873, 523, + 422, 645, + 234, 420, + 27, 281, + 358, 64, + 45, 252, + 356, 740, + 929, 101, + 517, 356, + 401, 694, + 514, 606, + 870, 567, + 462, 51, + 468, 146, + 206, 129, + 354, 680, + 431, 129, + 745, 37, + 83, 25, + 464, 451, + 180, 386, + 705, 687, + 528, 723, + 544, 1, + 471, 390, + 152, 487, + 129, 337, + 964, 103, + 61, 47, + 99, 609, + 627, 585, + 84, 718, + 873, 469, + 917, 298, + 488, 513, + 280, 506, + 494, 592, + 218, 557, + 453, 304, + 908, 115, + 129, 282, + 540, 704, + 320, 69, + 930, 633, + 28, 85, + 821, 104, + 568, 357, + 304, 38, + 919, 667, + 719, 489, + 392, 725, + 709, 368, + 323, 81, + 956, 206, + 185, 157, + 753, 701, + 500, 495, + 345, 92, + 679, 292, + 27, 371, + 526, 164, + 3, 330, + 503, 56, + 815, 180, + 11, 164, + 88, 613, + 711, 182, + 63, 316, + 703, 536, + 193, 552, + 942, 384, + 427, 13, + 506, 513, + 138, 501, + 40, 319, + 847, 315, + 88, 39, + 102, 360, + 670, 66, + 824, 578, + 61, 237, + 376, 374, + 448, 348, + 103, 276, + 22, 332, + 881, 576, + 249, 546, + 313, 370, + 51, 432, + 280, 216, + 130, 663, + 450, 487, + 803, 211, + 271, 286, + 421, 696, + 859, 332, + 31, 77, + 194, 174, + 814, 665, + 29, 425, + 800, 314, + 524, 211, + 654, 65, + 814, 600, + 190, 476, + 826, 351, + 577, 78, + 462, 662, + 463, 347, + 194, 25, + 614, 422, + 110, 552, + 181, 257, + 479, 481, + 619, 569, + 284, 367, + 679, 381, + 764, 223, + 545, 180, + 443, 606, + 102, 168, + 786, 423, + 816, 721, + 944, 699, + 548, 66, + 737, 422, + 742, 340, + 160, 737, + 877, 596, + 409, 155, + 293, 466, + 248, 596, + 20, 356, + 731, 510, + 420, 518, + 365, 170, + 779, 542, + 700, 23, + 237, 275, + 578, 62, + 233, 606, + 747, 599, + 294, 583, + 222, 213, + 839, 554, + 331, 424, + 694, 67, + 58, 640, + 627, 649, + 91, 642, + 49, 708, + 227, 561, + 398, 299, + 251, 378, + 182, 38, + 365, 538, + 473, 688, + 170, 535, + 858, 98, + 956, 558, + 901, 33, + 285, 615, + 272, 34, + 146, 9, + 778, 260, + 743, 41, + 81, 587, + 368, 79, + 404, 487, + 434, 147, + 443, 555, + 96, 380, + 830, 734, + 628, 212, + 126, 7, + 336, 361, + 543, 24, + 62, 15, + 506, 196, + 445, 725, + 792, 212, + 553, 145, + 783, 66, + 829, 180, + 433, 318, + 107, 674, + 152, 674, + 12, 127, + 832, 232, + 837, 675, + 885, 642, + 830, 190, + 836, 461, + 967, 453, + 650, 489, + 550, 136, + 961, 76, + 745, 392, + 288, 167, + 502, 694, + 572, 736, + 428, 310, + 624, 676, + 70, 408, + 155, 535, + 135, 0, + 534, 9, + 532, 31, + 155, 627, + 792, 226, + 397, 334, + 23, 458, + 798, 175, + 399, 367, + 500, 247, + 106, 477, + 149, 142, + 63, 677, + 822, 120, + 859, 379, + 127, 80, + 224, 663, + 449, 521, + 389, 465, + 714, 374, + 754, 620, + 558, 406, + 584, 16, + 969, 59, + 809, 103, + 151, 265, + 251, 160, + 562, 359, + 93, 382, + 195, 357, + 275, 3, + 878, 280, + 416, 656, + 756, 410, + 887, 680, + 464, 423, + 30, 102, + 399, 618, + 454, 519, + 636, 490, + 761, 2, + 299, 520, + 90, 388, + 878, 710, + 702, 571, + 342, 184, + 664, 109, + 467, 12, + 754, 153, + 831, 577, + 178, 658, + 92, 147, + 370, 126, + 344, 424, + 157, 577, + 36, 7, + 318, 393, + 626, 658, + 637, 16, + 691, 659, + 747, 196, + 505, 89, + 607, 518, + 77, 299, + 512, 604, + 97, 350, + 566, 400, + 183, 438, + 775, 738, + 54, 30, + 527, 451, + 84, 528, + 115, 143, + 775, 340, + 306, 272, + 758, 134, + 136, 372, + 15, 256, + 335, 376, + 491, 259, + 200, 115, + 560, 332, + 843, 675, + 862, 713, + 202, 541, + 570, 436, + 455, 435, + 68, 2, + 468, 591, + 909, 535, + 126, 535, + 614, 288, + 778, 76, + 402, 555, + 20, 686, + 458, 48, + 896, 95, + 883, 154, + 919, 372, + 358, 116, + 721, 622, + 319, 728, + 471, 423, + 145, 70, + 88, 559, + 739, 235, + 838, 399, + 824, 139, + 274, 435, + 913, 46, + 810, 123, + 438, 271, + 69, 584, + 510, 128, + 519, 442, + 442, 103, + 947, 312, + 130, 485, + 585, 188, + 287, 601, + 56, 204, + 920, 620, + 897, 443, + 769, 257, + 381, 280, + 424, 529, + 242, 129, + 216, 541, + 629, 593, + 779, 108, + 86, 188, + 639, 426, + 875, 678, + 151, 85, + 12, 205, + 121, 340, + 187, 2, + 879, 735, + 797, 24, + 534, 456, + 844, 306, + 261, 564, + 784, 586, + 934, 300, + 657, 393, + 417, 303, + 371, 630, + 469, 437, + 164, 407, + 537, 379, + 642, 407, + 318, 589, + 563, 309, + 328, 686, + 913, 14, + 93, 96, + 625, 194, + 581, 415, + 199, 490, + 930, 267, + 32, 351, + 67, 370, + 687, 56, + 227, 370, + 541, 370, + 796, 229, + 114, 417, + 765, 325, + 84, 444, + 837, 138, + 898, 80, + 126, 0, + 780, 419, + 659, 390, + 789, 187, + 527, 39, + 136, 102, + 659, 78, + 754, 339, + 692, 266, + 762, 608, + 913, 157, + 597, 23, + 290, 27, + 96, 221, + 61, 433, + 720, 561, + 694, 84, + 284, 192, + 68, 0, + 267, 21, + 466, 404, + 202, 167, + 649, 13, + 297, 358, + 520, 715, + 755, 598, + 327, 279, + 685, 529, + 109, 196, + 31, 296, + 10, 496, + 860, 280, + 279, 72, + 800, 362, + 865, 116, + 219, 547, + 69, 605, + 847, 26, + 66, 350, + 203, 165, + 432, 170, + 818, 661, + 804, 711, + 659, 33, + 861, 697, + 952, 555, + 917, 70, + 826, 544, + 944, 90, + 260, 144, + 143, 198, + 83, 142, + 927, 713, + 5, 135, + 914, 510, + 803, 732, + 81, 426, + 857, 184, + 590, 726, + 191, 215, + 495, 489, + 605, 529, + 312, 332, + 299, 324, + 57, 705, + 176, 53, + 920, 618, + 953, 608, + 35, 423, + 146, 432, + 83, 337, + 851, 244, + 338, 684, + 634, 122, + 863, 73, + 589, 473, + 330, 116, + 639, 288, + 809, 171, + 664, 430, + 80, 28, + 847, 111, + 103, 75, + 291, 395, + 854, 644, + 353, 69, + 438, 706, + 41, 229, + 776, 592, + 845, 405, + 588, 101, + 35, 113, + 752, 275, + 280, 177, + 863, 275, + 873, 701, + 864, 279, + 644, 323, + 727, 522, + 429, 300, + 600, 485, + 115, 525, + 357, 315, + 312, 424, + 42, 721, + 955, 212, + 655, 580, + 931, 497, + 450, 628, + 812, 689, + 698, 597, + 944, 289, + 727, 95, + 207, 657, + 692, 531, + 368, 432, + 84, 441, + 174, 312, + 237, 91, + 881, 672, + 625, 116, + 723, 733, + 770, 725, + 799, 313, + 465, 566, + 803, 106, + 80, 264, + 952, 513, + 23, 155, + 723, 502, + 39, 265, + 745, 644, + 936, 649, +}; +} // namespace openCvData