Loading services/camera/libcameraservice/device3/DistortionMapper.cpp +102 −41 Original line number Original line Diff line number Diff line Loading @@ -49,7 +49,7 @@ constexpr std::array<uint32_t, 1> DistortionMapper::kResultRectsToCorrect = { }; }; // Only for capture result // Only for capture result constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrect = { constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = { ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points ANDROID_STATISTICS_FACE_LANDMARKS, ANDROID_STATISTICS_FACE_LANDMARKS, }; }; Loading Loading @@ -79,12 +79,21 @@ status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) { array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); if (array.count != 4) return BAD_VALUE; if (array.count != 4) return BAD_VALUE; mArrayWidth = array.data.i32[2]; float arrayX = static_cast<float>(array.data.i32[0]); mArrayHeight = array.data.i32[3]; float arrayY = static_cast<float>(array.data.i32[1]); mArrayWidth = static_cast<float>(array.data.i32[2]); mArrayHeight = static_cast<float>(array.data.i32[3]); array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); mActiveWidth = array.data.i32[2]; if (array.count != 4) return BAD_VALUE; mActiveHeight = array.data.i32[3]; float activeX = static_cast<float>(array.data.i32[0]); float activeY = static_cast<float>(array.data.i32[1]); mActiveWidth = static_cast<float>(array.data.i32[2]); mActiveHeight = static_cast<float>(array.data.i32[3]); mArrayDiffX = activeX - arrayX; mArrayDiffY = activeY - arrayY; return updateCalibration(deviceInfo); return updateCalibration(deviceInfo); } } Loading @@ -111,22 +120,13 @@ status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) { if (weight == 0) { if (weight == 0) { continue; continue; } } res = mapCorrectedToRaw(e.data.i32 + j, 2); res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; for (size_t k = 0; k < 4; k+=2) { int32_t& x = e.data.i32[j + k]; int32_t& y = e.data.i32[j + k + 1]; // Clamp to within active array x = std::max(0, x); x = std::min(mActiveWidth - 1, x); y = std::max(0, y); y = std::min(mActiveHeight - 1, y); } } } } } for (auto rect : kRequestRectsToCorrect) { for (auto rect : kRequestRectsToCorrect) { e = request->find(rect); e = request->find(rect); res = mapCorrectedRectToRaw(e.data.i32, e.count / 4); res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; } } } } Loading Loading @@ -156,27 +156,18 @@ status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) { if (weight == 0) { if (weight == 0) { continue; continue; } } res = mapRawToCorrected(e.data.i32 + j, 2); res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; for (size_t k = 0; k < 4; k+=2) { int32_t& x = e.data.i32[j + k]; int32_t& y = e.data.i32[j + k + 1]; // Clamp to within active array x = std::max(0, x); x = std::min(mActiveWidth - 1, x); y = std::max(0, y); y = std::min(mActiveHeight - 1, y); } } } } } for (auto rect : kResultRectsToCorrect) { for (auto rect : kResultRectsToCorrect) { e = result->find(rect); e = result->find(rect); res = mapRawRectToCorrected(e.data.i32, e.count / 4); res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; } } for (auto pts : kResultPointsToCorrect) { for (auto pts : kResultPointsToCorrectNoClamp) { e = result->find(pts); e = result->find(pts); res = mapRawToCorrected(e.data.i32, e.count / 2); res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false); if (res != OK) return res; if (res != OK) return res; } } } } Loading Loading @@ -232,9 +223,12 @@ status_t DistortionMapper::updateCalibration(const CameraMetadata &result) { return OK; return OK; } } status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount) { status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp, bool simple) { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp); if (!mValidGrids) { if (!mValidGrids) { status_t res = buildGrids(); status_t res = buildGrids(); if (res != OK) return res; if (res != OK) return res; Loading Loading @@ -275,6 +269,34 @@ status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount // Interpolate along left edge of corrected quad (which are axis-aligned) for y // Interpolate along left edge of corrected quad (which are axis-aligned) for y float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]); float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]); // Clamp to within active array if (clamp) { corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX)); corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY)); } coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); } return OK; } status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const { if (!mValidMapping) return INVALID_OPERATION; float scaleX = mActiveWidth / mArrayWidth; float scaleY = mActiveHeight / mArrayHeight; for (int i = 0; i < coordCount * 2; i += 2) { float x = coordPairs[i]; float y = coordPairs[i + 1]; float corrX = x * scaleX; float corrY = y * scaleY; if (clamp) { corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX)); corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY)); } coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); } } Loading @@ -282,7 +304,8 @@ status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount return OK; return OK; } } status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) { status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp, bool simple) { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; for (int i = 0; i < rectCount * 4; i += 4) { for (int i = 0; i < rectCount * 4; i += 4) { // Map from (l, t, width, height) to (l, t, r, b) // Map from (l, t, width, height) to (l, t, r, b) Loading @@ -293,7 +316,7 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) rects[i + 1] + rects[i + 3] rects[i + 1] + rects[i + 3] }; }; mapRawToCorrected(coords, 2); mapRawToCorrected(coords, 2, clamp, simple); // Map back to (l, t, width, height) // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i] = coords[0]; Loading @@ -305,14 +328,24 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) return OK; return OK; } } status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp, bool simple) const { return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple); } template<typename T> template<typename T> status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) const { status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp, bool simple) const { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp); float activeCx = mCx - mArrayDiffX; float activeCy = mCy - mArrayDiffY; for (int i = 0; i < coordCount * 2; i += 2) { for (int i = 0; i < coordCount * 2; i += 2) { // Move to normalized space // Move to normalized space from active array space float ywi = (coordPairs[i + 1] - mCy) * mInvFy; float ywi = (coordPairs[i + 1] - activeCy) * mInvFy; float xwi = (coordPairs[i] - mCx - mS * ywi) * mInvFx; float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx; // Apply distortion model to calculate raw image coordinates // Apply distortion model to calculate raw image coordinates float rSq = xwi * xwi + ywi * ywi; float rSq = xwi * xwi + ywi * ywi; float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq); float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq); Loading @@ -321,6 +354,11 @@ status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) cons // Move back to image space // Move back to image space float xr = mFx * xc + mS * yc + mCx; float xr = mFx * xc + mS * yc + mCx; float yr = mFy * yc + mCy; float yr = mFy * yc + mCy; // Clamp to within pre-correction active array if (clamp) { xr = std::min(mArrayWidth - 1, std::max(0.f, xr)); yr = std::min(mArrayHeight - 1, std::max(0.f, yr)); } coordPairs[i] = static_cast<T>(std::round(xr)); coordPairs[i] = static_cast<T>(std::round(xr)); coordPairs[i + 1] = static_cast<T>(std::round(yr)); coordPairs[i + 1] = static_cast<T>(std::round(yr)); Loading @@ -329,10 +367,32 @@ status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) cons return OK; return OK; } } template status_t DistortionMapper::mapCorrectedToRaw(int32_t*, int) const; template<typename T> template status_t DistortionMapper::mapCorrectedToRaw(float*, int) const; status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount, bool clamp) const { if (!mValidMapping) return INVALID_OPERATION; float scaleX = mArrayWidth / mActiveWidth; float scaleY = mArrayHeight / mActiveHeight; for (int i = 0; i < coordCount * 2; i += 2) { float x = coordPairs[i]; float y = coordPairs[i + 1]; float rawX = x * scaleX; float rawY = y * scaleY; if (clamp) { rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX)); rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY)); } coordPairs[i] = static_cast<T>(std::round(rawX)); coordPairs[i + 1] = static_cast<T>(std::round(rawY)); } return OK; } status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) const { status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp, bool simple) const { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; for (int i = 0; i < rectCount * 4; i += 4) { for (int i = 0; i < rectCount * 4; i += 4) { Loading @@ -344,7 +404,7 @@ status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) rects[i + 1] + rects[i + 3] rects[i + 1] + rects[i + 3] }; }; mapCorrectedToRaw(coords, 2); mapCorrectedToRaw(coords, 2, clamp, simple); // Map back to (l, t, width, height) // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i] = coords[0]; Loading Loading @@ -380,7 +440,8 @@ status_t DistortionMapper::buildGrids() { }; }; mDistortedGrid[index].src = &mCorrectedGrid[index]; mDistortedGrid[index].src = &mCorrectedGrid[index]; mDistortedGrid[index].coords = mCorrectedGrid[index].coords; mDistortedGrid[index].coords = mCorrectedGrid[index].coords; status_t res = mapCorrectedToRaw(mDistortedGrid[index].coords.data(), 4); status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4, /*clamp*/false, /*simple*/false); if (res != OK) return res; if (res != OK) return res; } } } } Loading services/camera/libcameraservice/device3/DistortionMapper.h +32 −9 Original line number Original line Diff line number Diff line Loading @@ -73,8 +73,11 @@ class DistortionMapper { * * * coordPairs: A pointer to an array of consecutive (x,y) points * coordPairs: A pointer to an array of consecutive (x,y) points * coordCount: Number of (x,y) pairs to transform * coordCount: Number of (x,y) pairs to transform * clamp: Whether to clamp the result to the bounds of the active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount); status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp, bool simple = true); /** /** * Transform from distorted (original) to corrected (warped) coordinates. * Transform from distorted (original) to corrected (warped) coordinates. Loading @@ -82,8 +85,11 @@ class DistortionMapper { * * * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rectCount: Number of rectangles to transform * rectCount: Number of rectangles to transform * clamp: Whether to clamp the result to the bounds of the active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapRawRectToCorrected(int32_t *rects, int rectCount); status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp, bool simple = true); /** /** * Transform from corrected (warped) to distorted (original) coordinates. * Transform from corrected (warped) to distorted (original) coordinates. Loading @@ -91,9 +97,11 @@ class DistortionMapper { * * * coordPairs: A pointer to an array of consecutive (x,y) points * coordPairs: A pointer to an array of consecutive (x,y) points * coordCount: Number of (x,y) pairs to transform * coordCount: Number of (x,y) pairs to transform * clamp: Whether to clamp the result to the bounds of the precorrection active array * simple: Whether to do complex correction or just a simple linear map */ */ template<typename T> status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp, status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const; bool simple = true) const; /** /** * Transform from corrected (warped) to distorted (original) coordinates. * Transform from corrected (warped) to distorted (original) coordinates. Loading @@ -101,8 +109,11 @@ class DistortionMapper { * * * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rectCount: Number of rectangles to transform * rectCount: Number of rectangles to transform * clamp: Whether to clamp the result to the bounds of the precorrection active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const; status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp, bool simple = true) const; struct GridQuad { struct GridQuad { // Source grid quad, or null // Source grid quad, or null Loading Loading @@ -150,8 +161,18 @@ class DistortionMapper { // Only capture result // Only capture result static const std::array<uint32_t, 1> kResultRectsToCorrect; static const std::array<uint32_t, 1> kResultRectsToCorrect; // Only for capture results // Only for capture results; don't clamp static const std::array<uint32_t, 2> kResultPointsToCorrect; static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp; // Single implementation for various mapCorrectedToRaw methods template<typename T> status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const; // Simple linear interpolation option template<typename T> status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const; status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const; // Utility to create reverse mapping grids // Utility to create reverse mapping grids status_t buildGrids(); status_t buildGrids(); Loading @@ -168,9 +189,11 @@ class DistortionMapper { float mK[5]; float mK[5]; // pre-correction active array dimensions // pre-correction active array dimensions int mArrayWidth, mArrayHeight; float mArrayWidth, mArrayHeight; // active array dimensions // active array dimensions int mActiveWidth, mActiveHeight; float mActiveWidth, mActiveHeight; // corner offsets between pre-correction and active arrays float mArrayDiffX, mArrayDiffY; std::vector<GridQuad> mCorrectedGrid; std::vector<GridQuad> mCorrectedGrid; std::vector<GridQuad> mDistortedGrid; std::vector<GridQuad> mDistortedGrid; Loading services/camera/libcameraservice/tests/DistortionMapperTest.cpp +101 −27 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ using namespace android::camera3; int32_t testActiveArray[] = {100, 100, 1000, 750}; int32_t testActiveArray[] = {100, 100, 1000, 750}; int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770}; float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f }; float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f }; Loading @@ -45,14 +46,19 @@ std::array<int32_t, 12> basicCoords = { }; }; void setupTestMapper(DistortionMapper *m, float distortion[5]) { void setupTestMapper(DistortionMapper *m, float distortion[5], float intrinsics[5], int32_t activeArray[4], int32_t preCorrectionActiveArray[4]) { CameraMetadata deviceInfo; CameraMetadata deviceInfo; deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, testActiveArray, 4); preCorrectionActiveArray, 4); deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArray, 4); deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, testICal, 5); intrinsics, 5); deviceInfo.update(ANDROID_LENS_DISTORTION, deviceInfo.update(ANDROID_LENS_DISTORTION, distortion, 5); distortion, 5); Loading Loading @@ -89,6 +95,9 @@ TEST(DistortionMapperTest, Initialization) { ASSERT_FALSE(m.calibrationValid()); ASSERT_FALSE(m.calibrationValid()); deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, testPreCorrActiveArray, 4); deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, testActiveArray, 4); testActiveArray, 4); deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, Loading Loading @@ -118,17 +127,19 @@ TEST(DistortionMapperTest, IdentityTransform) { status_t res; status_t res; DistortionMapper m; DistortionMapper m; setupTestMapper(&m, identityDistortion); setupTestMapper(&m, identityDistortion, testICal, /*activeArray*/ testActiveArray, /*preCorrectionActiveArray*/ testActiveArray); auto coords = basicCoords; auto coords = basicCoords; res = m.mapCorrectedToRaw(coords.data(), 5); res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < coords.size(); i++) { for (size_t i = 0; i < coords.size(); i++) { EXPECT_EQ(coords[i], basicCoords[i]); EXPECT_EQ(coords[i], basicCoords[i]); } } res = m.mapRawToCorrected(coords.data(), 5); res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < coords.size(); i++) { for (size_t i = 0; i < coords.size(); i++) { Loading @@ -137,18 +148,18 @@ TEST(DistortionMapperTest, IdentityTransform) { std::array<int32_t, 8> rects = { std::array<int32_t, 8> rects = { 0, 0, 100, 100, 0, 0, 100, 100, testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100 testActiveArray[2] - 101, testActiveArray[3] - 101, 100, 100 }; }; auto rectsOrig = rects; auto rectsOrig = rects; res = m.mapCorrectedRectToRaw(rects.data(), 2); res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < rects.size(); i++) { for (size_t i = 0; i < rects.size(); i++) { EXPECT_EQ(rects[i], rectsOrig[i]); EXPECT_EQ(rects[i], rectsOrig[i]); } } res = m.mapRawRectToCorrected(rects.data(), 2); res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < rects.size(); i++) { for (size_t i = 0; i < rects.size(); i++) { Loading @@ -156,23 +167,39 @@ TEST(DistortionMapperTest, IdentityTransform) { } } } } TEST(DistortionMapperTest, LargeTransform) { TEST(DistortionMapperTest, SimpleTransform) { status_t res; DistortionMapper m; setupTestMapper(&m, identityDistortion, testICal, /*activeArray*/ testActiveArray, /*preCorrectionActiveArray*/ testPreCorrActiveArray); auto coords = basicCoords; res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true); ASSERT_EQ(res, OK); ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0); ASSERT_EQ(coords[2], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[3], 0); ASSERT_EQ(coords[4], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[5], testPreCorrActiveArray[3] - 1); ASSERT_EQ(coords[6], 0); ASSERT_EQ(coords[7], testPreCorrActiveArray[3] - 1); ASSERT_EQ(coords[8], testPreCorrActiveArray[2] / 2); ASSERT_EQ(coords[9], testPreCorrActiveArray[3] / 2); } void RandomTransformTest(::testing::Test *test, int32_t* activeArray, DistortionMapper &m, bool clamp, bool simple) { status_t res; status_t res; constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed constexpr int bucketsPerPixel = 3; // Histogram granularity constexpr int bucketsPerPixel = 3; // Histogram granularity unsigned int seed = 1234; // Ensure repeatability for debugging unsigned int seed = 1234; // Ensure repeatability for debugging const size_t coordCount = 1e6; // Number of random test points const size_t coordCount = 1e5; // 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::default_random_engine gen(seed); std::uniform_int_distribution<int> x_dist(0, testActiveArray[2] - 1); std::uniform_int_distribution<int> x_dist(0, activeArray[2] - 1); std::uniform_int_distribution<int> y_dist(0, testActiveArray[3] - 1); std::uniform_int_distribution<int> y_dist(0, activeArray[3] - 1); std::vector<int32_t> randCoords(coordCount * 2); std::vector<int32_t> randCoords(coordCount * 2); Loading @@ -186,12 +213,12 @@ TEST(DistortionMapperTest, LargeTransform) { auto origCoords = randCoords; auto origCoords = randCoords; base::Timer correctedToRawTimer; base::Timer correctedToRawTimer; res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2); res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple); auto correctedToRawDurationMs = correctedToRawTimer.duration(); auto correctedToRawDurationMs = correctedToRawTimer.duration(); EXPECT_EQ(res, OK); EXPECT_EQ(res, OK); base::Timer rawToCorrectedTimer; base::Timer rawToCorrectedTimer; res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2); res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple); auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration(); auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration(); EXPECT_EQ(res, OK); EXPECT_EQ(res, OK); Loading @@ -202,9 +229,9 @@ TEST(DistortionMapperTest, LargeTransform) { (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>( (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>( rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count(); rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count(); RecordProperty("CorrectedToRawDurationPerCoordUs", test->RecordProperty("CorrectedToRawDurationPerCoordUs", base::StringPrintf("%f", correctedToRawDurationPerCoordUs)); base::StringPrintf("%f", correctedToRawDurationPerCoordUs)); RecordProperty("RawToCorrectedDurationPerCoordUs", test->RecordProperty("RawToCorrectedDurationPerCoordUs", base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs)); base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs)); // Calculate mapping errors after round trip // Calculate mapping errors after round trip Loading Loading @@ -239,17 +266,61 @@ TEST(DistortionMapperTest, LargeTransform) { } } float rmsError = std::sqrt(totalErrorSq / randCoords.size()); float rmsError = std::sqrt(totalErrorSq / randCoords.size()); RecordProperty("RmsError", base::StringPrintf("%f", rmsError)); test->RecordProperty("RmsError", base::StringPrintf("%f", rmsError)); for (size_t i = 0; i < histogram.size(); i++) { for (size_t i = 0; i < histogram.size(); i++) { std::string label = base::StringPrintf("HistogramBin[%f,%f)", std::string label = base::StringPrintf("HistogramBin[%f,%f)", (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel); (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel); RecordProperty(label, histogram[i]); test->RecordProperty(label, histogram[i]); } } RecordProperty("HistogramOutOfRange", outOfHistogram); test->RecordProperty("HistogramOutOfRange", outOfHistogram); } // Test a realistic distortion function with matching calibration values, enforcing // clamping. TEST(DistortionMapperTest, DISABLED_SmallTransform) { int32_t activeArray[] = {0, 8, 3278, 2450}; int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464}; float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431}; float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000}; DistortionMapper m; setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray); RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/false); } // Test a realistic distortion function with matching calibration values, enforcing // clamping, but using the simple linear transform TEST(DistortionMapperTest, SmallSimpleTransform) { int32_t activeArray[] = {0, 8, 3278, 2450}; int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464}; float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431}; float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000}; DistortionMapper m; setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray); RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/true); } // Test a very large distortion function; the regions aren't valid for such a big transform, // so disable clamping. This test is just to verify round-trip math accuracy for big transforms TEST(DistortionMapperTest, LargeTransform) { float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01}; DistortionMapper m; setupTestMapper(&m, bigDistortion, testICal, /*activeArray*/testActiveArray, /*preCorrectionActiveArray*/testPreCorrActiveArray); RandomTransformTest(this, testActiveArray, m, /*clamp*/false, /*simple*/false); } } // Compare against values calculated by OpenCV // Compare against values calculated by OpenCV // undistortPoints() method, which is the same as mapRawToCorrected // undistortPoints() method, which is the same as mapRawToCorrected // Ignore clamping // See script DistortionMapperComp.py // See script DistortionMapperComp.py #include "DistortionMapperTest_OpenCvData.h" #include "DistortionMapperTest_OpenCvData.h" Loading @@ -262,11 +333,14 @@ TEST(DistortionMapperTest, CompareToOpenCV) { const int32_t maxSqError = 2; const int32_t maxSqError = 2; DistortionMapper m; DistortionMapper m; setupTestMapper(&m, bigDistortion); setupTestMapper(&m, bigDistortion, testICal, /*activeArray*/testActiveArray, /*preCorrectionActiveArray*/testActiveArray); using namespace openCvData; using namespace openCvData; res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2); res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false, /*simple*/false); for (size_t i = 0; i < rawCoords.size(); i+=2) { for (size_t i = 0; i < rawCoords.size(); i+=2) { int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) + int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) + Loading Loading
services/camera/libcameraservice/device3/DistortionMapper.cpp +102 −41 Original line number Original line Diff line number Diff line Loading @@ -49,7 +49,7 @@ constexpr std::array<uint32_t, 1> DistortionMapper::kResultRectsToCorrect = { }; }; // Only for capture result // Only for capture result constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrect = { constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = { ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points ANDROID_STATISTICS_FACE_LANDMARKS, ANDROID_STATISTICS_FACE_LANDMARKS, }; }; Loading Loading @@ -79,12 +79,21 @@ status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) { array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); if (array.count != 4) return BAD_VALUE; if (array.count != 4) return BAD_VALUE; mArrayWidth = array.data.i32[2]; float arrayX = static_cast<float>(array.data.i32[0]); mArrayHeight = array.data.i32[3]; float arrayY = static_cast<float>(array.data.i32[1]); mArrayWidth = static_cast<float>(array.data.i32[2]); mArrayHeight = static_cast<float>(array.data.i32[3]); array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); mActiveWidth = array.data.i32[2]; if (array.count != 4) return BAD_VALUE; mActiveHeight = array.data.i32[3]; float activeX = static_cast<float>(array.data.i32[0]); float activeY = static_cast<float>(array.data.i32[1]); mActiveWidth = static_cast<float>(array.data.i32[2]); mActiveHeight = static_cast<float>(array.data.i32[3]); mArrayDiffX = activeX - arrayX; mArrayDiffY = activeY - arrayY; return updateCalibration(deviceInfo); return updateCalibration(deviceInfo); } } Loading @@ -111,22 +120,13 @@ status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) { if (weight == 0) { if (weight == 0) { continue; continue; } } res = mapCorrectedToRaw(e.data.i32 + j, 2); res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; for (size_t k = 0; k < 4; k+=2) { int32_t& x = e.data.i32[j + k]; int32_t& y = e.data.i32[j + k + 1]; // Clamp to within active array x = std::max(0, x); x = std::min(mActiveWidth - 1, x); y = std::max(0, y); y = std::min(mActiveHeight - 1, y); } } } } } for (auto rect : kRequestRectsToCorrect) { for (auto rect : kRequestRectsToCorrect) { e = request->find(rect); e = request->find(rect); res = mapCorrectedRectToRaw(e.data.i32, e.count / 4); res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; } } } } Loading Loading @@ -156,27 +156,18 @@ status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) { if (weight == 0) { if (weight == 0) { continue; continue; } } res = mapRawToCorrected(e.data.i32 + j, 2); res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; for (size_t k = 0; k < 4; k+=2) { int32_t& x = e.data.i32[j + k]; int32_t& y = e.data.i32[j + k + 1]; // Clamp to within active array x = std::max(0, x); x = std::min(mActiveWidth - 1, x); y = std::max(0, y); y = std::min(mActiveHeight - 1, y); } } } } } for (auto rect : kResultRectsToCorrect) { for (auto rect : kResultRectsToCorrect) { e = result->find(rect); e = result->find(rect); res = mapRawRectToCorrected(e.data.i32, e.count / 4); res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true); if (res != OK) return res; if (res != OK) return res; } } for (auto pts : kResultPointsToCorrect) { for (auto pts : kResultPointsToCorrectNoClamp) { e = result->find(pts); e = result->find(pts); res = mapRawToCorrected(e.data.i32, e.count / 2); res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false); if (res != OK) return res; if (res != OK) return res; } } } } Loading Loading @@ -232,9 +223,12 @@ status_t DistortionMapper::updateCalibration(const CameraMetadata &result) { return OK; return OK; } } status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount) { status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp, bool simple) { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp); if (!mValidGrids) { if (!mValidGrids) { status_t res = buildGrids(); status_t res = buildGrids(); if (res != OK) return res; if (res != OK) return res; Loading Loading @@ -275,6 +269,34 @@ status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount // Interpolate along left edge of corrected quad (which are axis-aligned) for y // Interpolate along left edge of corrected quad (which are axis-aligned) for y float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]); float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]); // Clamp to within active array if (clamp) { corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX)); corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY)); } coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); } return OK; } status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const { if (!mValidMapping) return INVALID_OPERATION; float scaleX = mActiveWidth / mArrayWidth; float scaleY = mActiveHeight / mArrayHeight; for (int i = 0; i < coordCount * 2; i += 2) { float x = coordPairs[i]; float y = coordPairs[i + 1]; float corrX = x * scaleX; float corrY = y * scaleY; if (clamp) { corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX)); corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY)); } coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i] = static_cast<int32_t>(std::round(corrX)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY)); } } Loading @@ -282,7 +304,8 @@ status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount return OK; return OK; } } status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) { status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp, bool simple) { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; for (int i = 0; i < rectCount * 4; i += 4) { for (int i = 0; i < rectCount * 4; i += 4) { // Map from (l, t, width, height) to (l, t, r, b) // Map from (l, t, width, height) to (l, t, r, b) Loading @@ -293,7 +316,7 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) rects[i + 1] + rects[i + 3] rects[i + 1] + rects[i + 3] }; }; mapRawToCorrected(coords, 2); mapRawToCorrected(coords, 2, clamp, simple); // Map back to (l, t, width, height) // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i] = coords[0]; Loading @@ -305,14 +328,24 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) return OK; return OK; } } status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp, bool simple) const { return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple); } template<typename T> template<typename T> status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) const { status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp, bool simple) const { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp); float activeCx = mCx - mArrayDiffX; float activeCy = mCy - mArrayDiffY; for (int i = 0; i < coordCount * 2; i += 2) { for (int i = 0; i < coordCount * 2; i += 2) { // Move to normalized space // Move to normalized space from active array space float ywi = (coordPairs[i + 1] - mCy) * mInvFy; float ywi = (coordPairs[i + 1] - activeCy) * mInvFy; float xwi = (coordPairs[i] - mCx - mS * ywi) * mInvFx; float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx; // Apply distortion model to calculate raw image coordinates // Apply distortion model to calculate raw image coordinates float rSq = xwi * xwi + ywi * ywi; float rSq = xwi * xwi + ywi * ywi; float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq); float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq); Loading @@ -321,6 +354,11 @@ status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) cons // Move back to image space // Move back to image space float xr = mFx * xc + mS * yc + mCx; float xr = mFx * xc + mS * yc + mCx; float yr = mFy * yc + mCy; float yr = mFy * yc + mCy; // Clamp to within pre-correction active array if (clamp) { xr = std::min(mArrayWidth - 1, std::max(0.f, xr)); yr = std::min(mArrayHeight - 1, std::max(0.f, yr)); } coordPairs[i] = static_cast<T>(std::round(xr)); coordPairs[i] = static_cast<T>(std::round(xr)); coordPairs[i + 1] = static_cast<T>(std::round(yr)); coordPairs[i + 1] = static_cast<T>(std::round(yr)); Loading @@ -329,10 +367,32 @@ status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) cons return OK; return OK; } } template status_t DistortionMapper::mapCorrectedToRaw(int32_t*, int) const; template<typename T> template status_t DistortionMapper::mapCorrectedToRaw(float*, int) const; status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount, bool clamp) const { if (!mValidMapping) return INVALID_OPERATION; float scaleX = mArrayWidth / mActiveWidth; float scaleY = mArrayHeight / mActiveHeight; for (int i = 0; i < coordCount * 2; i += 2) { float x = coordPairs[i]; float y = coordPairs[i + 1]; float rawX = x * scaleX; float rawY = y * scaleY; if (clamp) { rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX)); rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY)); } coordPairs[i] = static_cast<T>(std::round(rawX)); coordPairs[i + 1] = static_cast<T>(std::round(rawY)); } return OK; } status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) const { status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp, bool simple) const { if (!mValidMapping) return INVALID_OPERATION; if (!mValidMapping) return INVALID_OPERATION; for (int i = 0; i < rectCount * 4; i += 4) { for (int i = 0; i < rectCount * 4; i += 4) { Loading @@ -344,7 +404,7 @@ status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) rects[i + 1] + rects[i + 3] rects[i + 1] + rects[i + 3] }; }; mapCorrectedToRaw(coords, 2); mapCorrectedToRaw(coords, 2, clamp, simple); // Map back to (l, t, width, height) // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i] = coords[0]; Loading Loading @@ -380,7 +440,8 @@ status_t DistortionMapper::buildGrids() { }; }; mDistortedGrid[index].src = &mCorrectedGrid[index]; mDistortedGrid[index].src = &mCorrectedGrid[index]; mDistortedGrid[index].coords = mCorrectedGrid[index].coords; mDistortedGrid[index].coords = mCorrectedGrid[index].coords; status_t res = mapCorrectedToRaw(mDistortedGrid[index].coords.data(), 4); status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4, /*clamp*/false, /*simple*/false); if (res != OK) return res; if (res != OK) return res; } } } } Loading
services/camera/libcameraservice/device3/DistortionMapper.h +32 −9 Original line number Original line Diff line number Diff line Loading @@ -73,8 +73,11 @@ class DistortionMapper { * * * coordPairs: A pointer to an array of consecutive (x,y) points * coordPairs: A pointer to an array of consecutive (x,y) points * coordCount: Number of (x,y) pairs to transform * coordCount: Number of (x,y) pairs to transform * clamp: Whether to clamp the result to the bounds of the active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount); status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp, bool simple = true); /** /** * Transform from distorted (original) to corrected (warped) coordinates. * Transform from distorted (original) to corrected (warped) coordinates. Loading @@ -82,8 +85,11 @@ class DistortionMapper { * * * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rectCount: Number of rectangles to transform * rectCount: Number of rectangles to transform * clamp: Whether to clamp the result to the bounds of the active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapRawRectToCorrected(int32_t *rects, int rectCount); status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp, bool simple = true); /** /** * Transform from corrected (warped) to distorted (original) coordinates. * Transform from corrected (warped) to distorted (original) coordinates. Loading @@ -91,9 +97,11 @@ class DistortionMapper { * * * coordPairs: A pointer to an array of consecutive (x,y) points * coordPairs: A pointer to an array of consecutive (x,y) points * coordCount: Number of (x,y) pairs to transform * coordCount: Number of (x,y) pairs to transform * clamp: Whether to clamp the result to the bounds of the precorrection active array * simple: Whether to do complex correction or just a simple linear map */ */ template<typename T> status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp, status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const; bool simple = true) const; /** /** * Transform from corrected (warped) to distorted (original) coordinates. * Transform from corrected (warped) to distorted (original) coordinates. Loading @@ -101,8 +109,11 @@ class DistortionMapper { * * * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rects: A pointer to an array of consecutive (x,y, w, h) rectangles * rectCount: Number of rectangles to transform * rectCount: Number of rectangles to transform * clamp: Whether to clamp the result to the bounds of the precorrection active array * simple: Whether to do complex correction or just a simple linear map */ */ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const; status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp, bool simple = true) const; struct GridQuad { struct GridQuad { // Source grid quad, or null // Source grid quad, or null Loading Loading @@ -150,8 +161,18 @@ class DistortionMapper { // Only capture result // Only capture result static const std::array<uint32_t, 1> kResultRectsToCorrect; static const std::array<uint32_t, 1> kResultRectsToCorrect; // Only for capture results // Only for capture results; don't clamp static const std::array<uint32_t, 2> kResultPointsToCorrect; static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp; // Single implementation for various mapCorrectedToRaw methods template<typename T> status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const; // Simple linear interpolation option template<typename T> status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const; status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const; // Utility to create reverse mapping grids // Utility to create reverse mapping grids status_t buildGrids(); status_t buildGrids(); Loading @@ -168,9 +189,11 @@ class DistortionMapper { float mK[5]; float mK[5]; // pre-correction active array dimensions // pre-correction active array dimensions int mArrayWidth, mArrayHeight; float mArrayWidth, mArrayHeight; // active array dimensions // active array dimensions int mActiveWidth, mActiveHeight; float mActiveWidth, mActiveHeight; // corner offsets between pre-correction and active arrays float mArrayDiffX, mArrayDiffY; std::vector<GridQuad> mCorrectedGrid; std::vector<GridQuad> mCorrectedGrid; std::vector<GridQuad> mDistortedGrid; std::vector<GridQuad> mDistortedGrid; Loading
services/camera/libcameraservice/tests/DistortionMapperTest.cpp +101 −27 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ using namespace android::camera3; int32_t testActiveArray[] = {100, 100, 1000, 750}; int32_t testActiveArray[] = {100, 100, 1000, 750}; int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770}; float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f }; float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f }; Loading @@ -45,14 +46,19 @@ std::array<int32_t, 12> basicCoords = { }; }; void setupTestMapper(DistortionMapper *m, float distortion[5]) { void setupTestMapper(DistortionMapper *m, float distortion[5], float intrinsics[5], int32_t activeArray[4], int32_t preCorrectionActiveArray[4]) { CameraMetadata deviceInfo; CameraMetadata deviceInfo; deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, testActiveArray, 4); preCorrectionActiveArray, 4); deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArray, 4); deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, testICal, 5); intrinsics, 5); deviceInfo.update(ANDROID_LENS_DISTORTION, deviceInfo.update(ANDROID_LENS_DISTORTION, distortion, 5); distortion, 5); Loading Loading @@ -89,6 +95,9 @@ TEST(DistortionMapperTest, Initialization) { ASSERT_FALSE(m.calibrationValid()); ASSERT_FALSE(m.calibrationValid()); deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, testPreCorrActiveArray, 4); deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, testActiveArray, 4); testActiveArray, 4); deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION, Loading Loading @@ -118,17 +127,19 @@ TEST(DistortionMapperTest, IdentityTransform) { status_t res; status_t res; DistortionMapper m; DistortionMapper m; setupTestMapper(&m, identityDistortion); setupTestMapper(&m, identityDistortion, testICal, /*activeArray*/ testActiveArray, /*preCorrectionActiveArray*/ testActiveArray); auto coords = basicCoords; auto coords = basicCoords; res = m.mapCorrectedToRaw(coords.data(), 5); res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < coords.size(); i++) { for (size_t i = 0; i < coords.size(); i++) { EXPECT_EQ(coords[i], basicCoords[i]); EXPECT_EQ(coords[i], basicCoords[i]); } } res = m.mapRawToCorrected(coords.data(), 5); res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < coords.size(); i++) { for (size_t i = 0; i < coords.size(); i++) { Loading @@ -137,18 +148,18 @@ TEST(DistortionMapperTest, IdentityTransform) { std::array<int32_t, 8> rects = { std::array<int32_t, 8> rects = { 0, 0, 100, 100, 0, 0, 100, 100, testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100 testActiveArray[2] - 101, testActiveArray[3] - 101, 100, 100 }; }; auto rectsOrig = rects; auto rectsOrig = rects; res = m.mapCorrectedRectToRaw(rects.data(), 2); res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < rects.size(); i++) { for (size_t i = 0; i < rects.size(); i++) { EXPECT_EQ(rects[i], rectsOrig[i]); EXPECT_EQ(rects[i], rectsOrig[i]); } } res = m.mapRawRectToCorrected(rects.data(), 2); res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true); ASSERT_EQ(res, OK); ASSERT_EQ(res, OK); for (size_t i = 0; i < rects.size(); i++) { for (size_t i = 0; i < rects.size(); i++) { Loading @@ -156,23 +167,39 @@ TEST(DistortionMapperTest, IdentityTransform) { } } } } TEST(DistortionMapperTest, LargeTransform) { TEST(DistortionMapperTest, SimpleTransform) { status_t res; DistortionMapper m; setupTestMapper(&m, identityDistortion, testICal, /*activeArray*/ testActiveArray, /*preCorrectionActiveArray*/ testPreCorrActiveArray); auto coords = basicCoords; res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true); ASSERT_EQ(res, OK); ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0); ASSERT_EQ(coords[2], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[3], 0); ASSERT_EQ(coords[4], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[5], testPreCorrActiveArray[3] - 1); ASSERT_EQ(coords[6], 0); ASSERT_EQ(coords[7], testPreCorrActiveArray[3] - 1); ASSERT_EQ(coords[8], testPreCorrActiveArray[2] / 2); ASSERT_EQ(coords[9], testPreCorrActiveArray[3] / 2); } void RandomTransformTest(::testing::Test *test, int32_t* activeArray, DistortionMapper &m, bool clamp, bool simple) { status_t res; status_t res; constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed constexpr int bucketsPerPixel = 3; // Histogram granularity constexpr int bucketsPerPixel = 3; // Histogram granularity unsigned int seed = 1234; // Ensure repeatability for debugging unsigned int seed = 1234; // Ensure repeatability for debugging const size_t coordCount = 1e6; // Number of random test points const size_t coordCount = 1e5; // 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::default_random_engine gen(seed); std::uniform_int_distribution<int> x_dist(0, testActiveArray[2] - 1); std::uniform_int_distribution<int> x_dist(0, activeArray[2] - 1); std::uniform_int_distribution<int> y_dist(0, testActiveArray[3] - 1); std::uniform_int_distribution<int> y_dist(0, activeArray[3] - 1); std::vector<int32_t> randCoords(coordCount * 2); std::vector<int32_t> randCoords(coordCount * 2); Loading @@ -186,12 +213,12 @@ TEST(DistortionMapperTest, LargeTransform) { auto origCoords = randCoords; auto origCoords = randCoords; base::Timer correctedToRawTimer; base::Timer correctedToRawTimer; res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2); res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple); auto correctedToRawDurationMs = correctedToRawTimer.duration(); auto correctedToRawDurationMs = correctedToRawTimer.duration(); EXPECT_EQ(res, OK); EXPECT_EQ(res, OK); base::Timer rawToCorrectedTimer; base::Timer rawToCorrectedTimer; res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2); res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple); auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration(); auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration(); EXPECT_EQ(res, OK); EXPECT_EQ(res, OK); Loading @@ -202,9 +229,9 @@ TEST(DistortionMapperTest, LargeTransform) { (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>( (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>( rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count(); rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count(); RecordProperty("CorrectedToRawDurationPerCoordUs", test->RecordProperty("CorrectedToRawDurationPerCoordUs", base::StringPrintf("%f", correctedToRawDurationPerCoordUs)); base::StringPrintf("%f", correctedToRawDurationPerCoordUs)); RecordProperty("RawToCorrectedDurationPerCoordUs", test->RecordProperty("RawToCorrectedDurationPerCoordUs", base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs)); base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs)); // Calculate mapping errors after round trip // Calculate mapping errors after round trip Loading Loading @@ -239,17 +266,61 @@ TEST(DistortionMapperTest, LargeTransform) { } } float rmsError = std::sqrt(totalErrorSq / randCoords.size()); float rmsError = std::sqrt(totalErrorSq / randCoords.size()); RecordProperty("RmsError", base::StringPrintf("%f", rmsError)); test->RecordProperty("RmsError", base::StringPrintf("%f", rmsError)); for (size_t i = 0; i < histogram.size(); i++) { for (size_t i = 0; i < histogram.size(); i++) { std::string label = base::StringPrintf("HistogramBin[%f,%f)", std::string label = base::StringPrintf("HistogramBin[%f,%f)", (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel); (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel); RecordProperty(label, histogram[i]); test->RecordProperty(label, histogram[i]); } } RecordProperty("HistogramOutOfRange", outOfHistogram); test->RecordProperty("HistogramOutOfRange", outOfHistogram); } // Test a realistic distortion function with matching calibration values, enforcing // clamping. TEST(DistortionMapperTest, DISABLED_SmallTransform) { int32_t activeArray[] = {0, 8, 3278, 2450}; int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464}; float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431}; float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000}; DistortionMapper m; setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray); RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/false); } // Test a realistic distortion function with matching calibration values, enforcing // clamping, but using the simple linear transform TEST(DistortionMapperTest, SmallSimpleTransform) { int32_t activeArray[] = {0, 8, 3278, 2450}; int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464}; float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431}; float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000}; DistortionMapper m; setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray); RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/true); } // Test a very large distortion function; the regions aren't valid for such a big transform, // so disable clamping. This test is just to verify round-trip math accuracy for big transforms TEST(DistortionMapperTest, LargeTransform) { float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01}; DistortionMapper m; setupTestMapper(&m, bigDistortion, testICal, /*activeArray*/testActiveArray, /*preCorrectionActiveArray*/testPreCorrActiveArray); RandomTransformTest(this, testActiveArray, m, /*clamp*/false, /*simple*/false); } } // Compare against values calculated by OpenCV // Compare against values calculated by OpenCV // undistortPoints() method, which is the same as mapRawToCorrected // undistortPoints() method, which is the same as mapRawToCorrected // Ignore clamping // See script DistortionMapperComp.py // See script DistortionMapperComp.py #include "DistortionMapperTest_OpenCvData.h" #include "DistortionMapperTest_OpenCvData.h" Loading @@ -262,11 +333,14 @@ TEST(DistortionMapperTest, CompareToOpenCV) { const int32_t maxSqError = 2; const int32_t maxSqError = 2; DistortionMapper m; DistortionMapper m; setupTestMapper(&m, bigDistortion); setupTestMapper(&m, bigDistortion, testICal, /*activeArray*/testActiveArray, /*preCorrectionActiveArray*/testActiveArray); using namespace openCvData; using namespace openCvData; res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2); res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false, /*simple*/false); for (size_t i = 0; i < rawCoords.size(); i+=2) { for (size_t i = 0; i < rawCoords.size(); i+=2) { int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) + int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) + Loading