| /* |
| * 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_TAG "Camera3-DistMapper" |
| #define ATRACE_TAG ATRACE_TAG_CAMERA |
| //#define LOG_NDEBUG 0 |
| |
| #include <algorithm> |
| #include <cmath> |
| |
| #include "device3/DistortionMapper.h" |
| #include "utils/SessionConfigurationUtilsHost.h" |
| |
| namespace android { |
| |
| namespace camera3 { |
| |
| DistortionMapper::DistortionMapper() { |
| initRemappedKeys(); |
| } |
| |
| void DistortionMapper::initRemappedKeys() { |
| mRemappedKeys.insert( |
| kMeteringRegionsToCorrect.begin(), |
| kMeteringRegionsToCorrect.end()); |
| mRemappedKeys.insert( |
| kRectsToCorrect.begin(), |
| kRectsToCorrect.end()); |
| mRemappedKeys.insert( |
| kResultPointsToCorrectNoClamp.begin(), |
| kResultPointsToCorrectNoClamp.end()); |
| mRemappedKeys.insert(ANDROID_DISTORTION_CORRECTION_MODE); |
| if (flags::concert_mode()) { |
| mRemappedKeys.insert(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION); |
| } |
| } |
| |
| bool DistortionMapper::isDistortionSupported(const CameraMetadata &deviceInfo) { |
| bool isDistortionCorrectionSupported = false; |
| camera_metadata_ro_entry_t distortionCorrectionModes = |
| deviceInfo.find(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES); |
| for (size_t i = 0; i < distortionCorrectionModes.count; i++) { |
| if (distortionCorrectionModes.data.u8[i] != |
| ANDROID_DISTORTION_CORRECTION_MODE_OFF) { |
| isDistortionCorrectionSupported = true; |
| break; |
| } |
| } |
| return isDistortionCorrectionSupported; |
| } |
| |
| status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) { |
| std::lock_guard<std::mutex> lock(mMutex); |
| status_t res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/false); |
| if (res != OK) { |
| return res; |
| } |
| |
| bool mMaxResolution = SessionConfigurationUtils::supportsUltraHighResolutionCapture(deviceInfo); |
| if (mMaxResolution) { |
| res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/true); |
| } |
| return res; |
| } |
| |
| status_t DistortionMapper::setupStaticInfoLocked(const CameraMetadata &deviceInfo, |
| bool maxResolution) { |
| DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution : |
| &mDistortionMapperInfo; |
| |
| camera_metadata_ro_entry_t array; |
| |
| array = deviceInfo.find( |
| SessionConfigurationUtils::getAppropriateModeTag( |
| ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, maxResolution)); |
| if (array.count != 4) return BAD_VALUE; |
| |
| float arrayX = static_cast<float>(array.data.i32[0]); |
| float arrayY = static_cast<float>(array.data.i32[1]); |
| mapperInfo->mArrayWidth = static_cast<float>(array.data.i32[2]); |
| mapperInfo->mArrayHeight = static_cast<float>(array.data.i32[3]); |
| |
| array = deviceInfo.find( |
| SessionConfigurationUtils::getAppropriateModeTag( |
| ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, maxResolution)); |
| if (array.count != 4) return BAD_VALUE; |
| |
| float activeX = static_cast<float>(array.data.i32[0]); |
| float activeY = static_cast<float>(array.data.i32[1]); |
| mapperInfo->mActiveWidth = static_cast<float>(array.data.i32[2]); |
| mapperInfo->mActiveHeight = static_cast<float>(array.data.i32[3]); |
| |
| mapperInfo->mArrayDiffX = activeX - arrayX; |
| mapperInfo->mArrayDiffY = activeY - arrayY; |
| |
| return updateCalibration(deviceInfo, /*isStatic*/ true, maxResolution); |
| } |
| |
| static bool doesSettingsHaveMaxResolution(const CameraMetadata *settings) { |
| if (settings == nullptr) { |
| return false; |
| } |
| // First we get the sensorPixelMode from the settings metadata. |
| camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE); |
| if (sensorPixelModeEntry.count != 0) { |
| return (sensorPixelModeEntry.data.u8[0] == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); |
| } |
| return false; |
| } |
| |
| bool DistortionMapper::calibrationValid() const { |
| std::lock_guard<std::mutex> lock(mMutex); |
| bool isValid = mDistortionMapperInfo.mValidMapping; |
| if (mMaxResolution) { |
| isValid = isValid && mDistortionMapperInfoMaximumResolution.mValidMapping; |
| } |
| return isValid; |
| } |
| |
| status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) { |
| std::lock_guard<std::mutex> lock(mMutex); |
| status_t res; |
| |
| bool maxResolution = doesSettingsHaveMaxResolution(request); |
| DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution : |
| &mDistortionMapperInfo; |
| |
| if (!mapperInfo->mValidMapping) return OK; |
| |
| camera_metadata_entry_t e; |
| e = request->find(ANDROID_DISTORTION_CORRECTION_MODE); |
| if (e.count != 0 && e.data.u8[0] != ANDROID_DISTORTION_CORRECTION_MODE_OFF) { |
| for (auto region : kMeteringRegionsToCorrect) { |
| e = request->find(region); |
| for (size_t j = 0; j < e.count; j += 5) { |
| int32_t weight = e.data.i32[j + 4]; |
| if (weight == 0) { |
| continue; |
| } |
| res = mapCorrectedToRaw(e.data.i32 + j, 2, mapperInfo, /*clamp*/true); |
| if (res != OK) return res; |
| } |
| } |
| for (auto rect : kRectsToCorrect) { |
| e = request->find(rect); |
| res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true); |
| if (res != OK) return res; |
| } |
| } |
| return OK; |
| } |
| |
| status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) { |
| std::lock_guard<std::mutex> lock(mMutex); |
| |
| bool maxResolution = doesSettingsHaveMaxResolution(result); |
| DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution : |
| &mDistortionMapperInfo; |
| status_t res; |
| |
| if (!mapperInfo->mValidMapping) return OK; |
| |
| res = updateCalibration(*result, /*isStatic*/ false, maxResolution); |
| if (res != OK) { |
| ALOGE("Failure to update lens calibration information"); |
| return INVALID_OPERATION; |
| } |
| |
| camera_metadata_entry_t e; |
| e = result->find(ANDROID_DISTORTION_CORRECTION_MODE); |
| if (e.count != 0 && e.data.u8[0] != ANDROID_DISTORTION_CORRECTION_MODE_OFF) { |
| for (auto region : kMeteringRegionsToCorrect) { |
| e = result->find(region); |
| for (size_t j = 0; j < e.count; j += 5) { |
| int32_t weight = e.data.i32[j + 4]; |
| if (weight == 0) { |
| continue; |
| } |
| res = mapRawToCorrected(e.data.i32 + j, 2, mapperInfo, /*clamp*/true); |
| if (res != OK) return res; |
| } |
| } |
| for (auto rect : kRectsToCorrect) { |
| e = result->find(rect); |
| res = mapRawRectToCorrected(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true); |
| if (res != OK) return res; |
| } |
| for (auto pts : kResultPointsToCorrectNoClamp) { |
| e = result->find(pts); |
| res = mapRawToCorrected(e.data.i32, e.count / 2, mapperInfo, /*clamp*/false); |
| if (res != OK) return res; |
| } |
| } |
| |
| return OK; |
| } |
| |
| // Utility methods; not guarded by mutex |
| |
| status_t DistortionMapper::updateCalibration(const CameraMetadata &result, bool isStatic, |
| bool maxResolution) { |
| camera_metadata_ro_entry_t calib, distortion; |
| DistortionMapperInfo *mapperInfo = |
| maxResolution ? &mDistortionMapperInfoMaximumResolution : &mDistortionMapperInfo; |
| // We only need maximum resolution version of LENS_INTRINSIC_CALIBRATION and |
| // LENS_DISTORTION since CaptureResults would still use the same key |
| // regardless of sensor pixel mode. |
| int calibrationKey = |
| SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_INTRINSIC_CALIBRATION, |
| maxResolution && isStatic); |
| int distortionKey = |
| SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_DISTORTION, |
| maxResolution && isStatic); |
| |
| calib = result.find(calibrationKey); |
| distortion = result.find(distortionKey); |
| |
| if (calib.count != 5) return BAD_VALUE; |
| if (distortion.count != 5) return BAD_VALUE; |
| |
| // Skip redoing work if no change to calibration fields |
| if (mapperInfo->mValidMapping && |
| mapperInfo->mFx == calib.data.f[0] && |
| mapperInfo->mFy == calib.data.f[1] && |
| mapperInfo->mCx == calib.data.f[2] && |
| mapperInfo->mCy == calib.data.f[3] && |
| mapperInfo->mS == calib.data.f[4]) { |
| bool noChange = true; |
| for (size_t i = 0; i < distortion.count; i++) { |
| if (mapperInfo->mK[i] != distortion.data.f[i]) { |
| noChange = false; |
| break; |
| } |
| } |
| if (noChange) return OK; |
| } |
| |
| mapperInfo->mFx = calib.data.f[0]; |
| mapperInfo->mFy = calib.data.f[1]; |
| mapperInfo->mCx = calib.data.f[2]; |
| mapperInfo->mCy = calib.data.f[3]; |
| mapperInfo->mS = calib.data.f[4]; |
| |
| mapperInfo->mInvFx = 1 / mapperInfo->mFx; |
| mapperInfo->mInvFy = 1 / mapperInfo->mFy; |
| |
| for (size_t i = 0; i < distortion.count; i++) { |
| mapperInfo->mK[i] = distortion.data.f[i]; |
| } |
| |
| mapperInfo->mValidMapping = true; |
| // Need to recalculate grid |
| mapperInfo->mValidGrids = false; |
| |
| return OK; |
| } |
| |
| status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount, |
| DistortionMapperInfo *mapperInfo, bool clamp, bool simple) { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| |
| if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, mapperInfo, clamp); |
| |
| if (!mapperInfo->mValidGrids) { |
| status_t res = buildGrids(mapperInfo); |
| if (res != OK) return res; |
| } |
| |
| for (int i = 0; i < coordCount * 2; i += 2) { |
| const GridQuad *quad = findEnclosingQuad(coordPairs + i, mapperInfo->mDistortedGrid); |
| if (quad == nullptr) { |
| ALOGE("Raw to corrected mapping failure: No quad found for (%d, %d)", |
| *(coordPairs + i), *(coordPairs + i + 1)); |
| return INVALID_OPERATION; |
| } |
| ALOGV("src xy: %d, %d, enclosing quad: (%f, %f), (%f, %f), (%f, %f), (%f, %f)", |
| coordPairs[i], coordPairs[i+1], |
| quad->coords[0], quad->coords[1], |
| quad->coords[2], quad->coords[3], |
| quad->coords[4], quad->coords[5], |
| quad->coords[6], quad->coords[7]); |
| |
| const GridQuad *corrQuad = quad->src; |
| if (corrQuad == nullptr) { |
| ALOGE("Raw to corrected mapping failure: No src quad found"); |
| return INVALID_OPERATION; |
| } |
| ALOGV(" corr quad: (%f, %f), (%f, %f), (%f, %f), (%f, %f)", |
| corrQuad->coords[0], corrQuad->coords[1], |
| corrQuad->coords[2], corrQuad->coords[3], |
| corrQuad->coords[4], corrQuad->coords[5], |
| corrQuad->coords[6], corrQuad->coords[7]); |
| |
| float u = calculateUorV(coordPairs + i, *quad, /*calculateU*/ true); |
| float v = calculateUorV(coordPairs + i, *quad, /*calculateU*/ false); |
| |
| ALOGV("uv: %f, %f", u, v); |
| |
| // Interpolate along top edge of corrected quad (which are axis-aligned) for x |
| float corrX = corrQuad->coords[0] + u * (corrQuad->coords[2] - corrQuad->coords[0]); |
| // 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]); |
| |
| // Clamp to within active array |
| if (clamp) { |
| corrX = std::min(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX)); |
| corrY = std::min(mapperInfo->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, |
| const DistortionMapperInfo *mapperInfo, bool clamp) const { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| |
| float scaleX = mapperInfo->mActiveWidth / mapperInfo->mArrayWidth; |
| float scaleY = mapperInfo->mActiveHeight / mapperInfo->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(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX)); |
| corrY = std::min(mapperInfo->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::mapRawRectToCorrected(int32_t *rects, int rectCount, |
| DistortionMapperInfo *mapperInfo, bool clamp, bool simple) { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| for (int i = 0; i < rectCount * 4; i += 4) { |
| // Map from (l, t, width, height) to (l, t, r, b) |
| int32_t coords[4] = { |
| rects[i], |
| rects[i + 1], |
| rects[i] + rects[i + 2] - 1, |
| rects[i + 1] + rects[i + 3] - 1 |
| }; |
| |
| mapRawToCorrected(coords, 2, mapperInfo, clamp, simple); |
| |
| // Map back to (l, t, width, height) |
| rects[i] = coords[0]; |
| rects[i + 1] = coords[1]; |
| rects[i + 2] = coords[2] - coords[0] + 1; |
| rects[i + 3] = coords[3] - coords[1] + 1; |
| } |
| |
| return OK; |
| } |
| |
| status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, |
| const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const { |
| return mapCorrectedToRawImpl(coordPairs, coordCount, mapperInfo, clamp, simple); |
| } |
| |
| template<typename T> |
| status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, |
| const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| |
| if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, mapperInfo, clamp); |
| |
| float activeCx = mapperInfo->mCx - mapperInfo->mArrayDiffX; |
| float activeCy = mapperInfo->mCy - mapperInfo->mArrayDiffY; |
| for (int i = 0; i < coordCount * 2; i += 2) { |
| // Move to normalized space from active array space |
| float ywi = (coordPairs[i + 1] - activeCy) * mapperInfo->mInvFy; |
| float xwi = (coordPairs[i] - activeCx - mapperInfo->mS * ywi) * mapperInfo->mInvFx; |
| // Apply distortion model to calculate raw image coordinates |
| const std::array<float, 5> &kK = mapperInfo->mK; |
| float rSq = xwi * xwi + ywi * ywi; |
| float Fr = 1.f + (kK[0] * rSq) + (kK[1] * rSq * rSq) + (kK[2] * rSq * rSq * rSq); |
| float xc = xwi * Fr + (kK[3] * 2 * xwi * ywi) + kK[4] * (rSq + 2 * xwi * xwi); |
| float yc = ywi * Fr + (kK[4] * 2 * xwi * ywi) + kK[3] * (rSq + 2 * ywi * ywi); |
| // Move back to image space |
| float xr = mapperInfo->mFx * xc + mapperInfo->mS * yc + mapperInfo->mCx; |
| float yr = mapperInfo->mFy * yc + mapperInfo->mCy; |
| // Clamp to within pre-correction active array |
| if (clamp) { |
| xr = std::min(mapperInfo->mArrayWidth - 1, std::max(0.f, xr)); |
| yr = std::min(mapperInfo->mArrayHeight - 1, std::max(0.f, yr)); |
| } |
| |
| coordPairs[i] = static_cast<T>(std::round(xr)); |
| coordPairs[i + 1] = static_cast<T>(std::round(yr)); |
| } |
| return OK; |
| } |
| |
| template<typename T> |
| status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount, |
| const DistortionMapperInfo *mapperInfo, bool clamp) const { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| |
| float scaleX = mapperInfo->mArrayWidth / mapperInfo->mActiveWidth; |
| float scaleY = mapperInfo->mArrayHeight / mapperInfo->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(mapperInfo->mArrayWidth - 1, std::max(0.f, rawX)); |
| rawY = std::min(mapperInfo->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 DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const { |
| if (!mapperInfo->mValidMapping) return INVALID_OPERATION; |
| |
| for (int i = 0; i < rectCount * 4; i += 4) { |
| // Map from (l, t, width, height) to (l, t, r, b) |
| int32_t coords[4] = { |
| rects[i], |
| rects[i + 1], |
| rects[i] + rects[i + 2] - 1, |
| rects[i + 1] + rects[i + 3] - 1 |
| }; |
| |
| mapCorrectedToRaw(coords, 2, mapperInfo, clamp, simple); |
| |
| // Map back to (l, t, width, height) |
| rects[i] = coords[0]; |
| rects[i + 1] = coords[1]; |
| rects[i + 2] = coords[2] - coords[0] + 1; |
| rects[i + 3] = coords[3] - coords[1] + 1; |
| } |
| |
| return OK; |
| } |
| |
| status_t DistortionMapper::buildGrids(DistortionMapperInfo *mapperInfo) { |
| if (mapperInfo->mCorrectedGrid.size() != kGridSize * kGridSize) { |
| mapperInfo->mCorrectedGrid.resize(kGridSize * kGridSize); |
| mapperInfo->mDistortedGrid.resize(kGridSize * kGridSize); |
| } |
| |
| float gridMargin = mapperInfo->mArrayWidth * kGridMargin; |
| float gridSpacingX = (mapperInfo->mArrayWidth + 2 * gridMargin) / kGridSize; |
| float gridSpacingY = (mapperInfo->mArrayHeight + 2 * gridMargin) / kGridSize; |
| |
| size_t index = 0; |
| float x = -gridMargin; |
| for (size_t i = 0; i < kGridSize; i++, x += gridSpacingX) { |
| float y = -gridMargin; |
| for (size_t j = 0; j < kGridSize; j++, y += gridSpacingY, index++) { |
| mapperInfo->mCorrectedGrid[index].src = nullptr; |
| mapperInfo->mCorrectedGrid[index].coords = { |
| x, y, |
| x + gridSpacingX, y, |
| x + gridSpacingX, y + gridSpacingY, |
| x, y + gridSpacingY |
| }; |
| mapperInfo->mDistortedGrid[index].src = &(mapperInfo->mCorrectedGrid[index]); |
| mapperInfo->mDistortedGrid[index].coords = mapperInfo->mCorrectedGrid[index].coords; |
| status_t res = mapCorrectedToRawImpl(mapperInfo->mDistortedGrid[index].coords.data(), 4, |
| mapperInfo, /*clamp*/false, /*simple*/false); |
| if (res != OK) return res; |
| } |
| } |
| |
| mapperInfo->mValidGrids = true; |
| return OK; |
| } |
| |
| const DistortionMapper::GridQuad* DistortionMapper::findEnclosingQuad( |
| const int32_t pt[2], const std::vector<GridQuad>& grid) { |
| const float x = pt[0]; |
| const float y = pt[1]; |
| |
| for (const GridQuad& quad : grid) { |
| const float &x1 = quad.coords[0]; |
| const float &y1 = quad.coords[1]; |
| const float &x2 = quad.coords[2]; |
| const float &y2 = quad.coords[3]; |
| const float &x3 = quad.coords[4]; |
| const float &y3 = quad.coords[5]; |
| const float &x4 = quad.coords[6]; |
| const float &y4 = quad.coords[7]; |
| |
| // Point-in-quad test: |
| |
| // Quad has corners P1-P4; if P is within the quad, then it is on the same side of all the |
| // edges (or on top of one of the edges or corners), traversed in a consistent direction. |
| // This means that the cross product of edge En = Pn->P(n+1 mod 4) and line Ep = Pn->P must |
| // have the same sign (or be zero) for all edges. |
| // For clockwise traversal, the sign should be negative or zero for Ep x En, indicating that |
| // En is to the left of Ep, or overlapping. |
| float s1 = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); |
| if (s1 > 0) continue; |
| float s2 = (x - x2) * (y3 - y2) - (y - y2) * (x3 - x2); |
| if (s2 > 0) continue; |
| float s3 = (x - x3) * (y4 - y3) - (y - y3) * (x4 - x3); |
| if (s3 > 0) continue; |
| float s4 = (x - x4) * (y1 - y4) - (y - y4) * (x1 - x4); |
| if (s4 > 0) continue; |
| |
| return &quad; |
| } |
| return nullptr; |
| } |
| |
| float DistortionMapper::calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU) { |
| const float x = pt[0]; |
| const float y = pt[1]; |
| const float &x1 = quad.coords[0]; |
| const float &y1 = quad.coords[1]; |
| const float &x2 = calculateU ? quad.coords[2] : quad.coords[6]; |
| const float &y2 = calculateU ? quad.coords[3] : quad.coords[7]; |
| const float &x3 = quad.coords[4]; |
| const float &y3 = quad.coords[5]; |
| const float &x4 = calculateU ? quad.coords[6] : quad.coords[2]; |
| const float &y4 = calculateU ? quad.coords[7] : quad.coords[3]; |
| |
| float a = (x1 - x2) * (y1 - y2 + y3 - y4) - (y1 - y2) * (x1 - x2 + x3 - x4); |
| float b = (x - x1) * (y1 - y2 + y3 - y4) + (x1 - x2) * (y4 - y1) - |
| (y - y1) * (x1 - x2 + x3 - x4) - (y1 - y2) * (x4 - x1); |
| float c = (x - x1) * (y4 - y1) - (y - y1) * (x4 - x1); |
| |
| if (a == 0) { |
| // One solution may happen if edges are parallel |
| float u0 = -c / b; |
| ALOGV("u0: %.9g, b: %f, c: %f", u0, b, c); |
| return u0; |
| } |
| |
| float det = b * b - 4 * a * c; |
| if (det < 0) { |
| // Validation check - should not happen if pt is within the quad |
| ALOGE("Bad determinant! a: %f, b: %f, c: %f, det: %f", a,b,c,det); |
| return -1; |
| } |
| |
| // Select more numerically stable solution |
| float sqdet = b > 0 ? -std::sqrt(det) : std::sqrt(det); |
| |
| float u1 = (-b + sqdet) / (2 * a); |
| ALOGV("u1: %.9g", u1); |
| if (0 - kFloatFuzz < u1 && u1 < 1 + kFloatFuzz) return u1; |
| |
| float u2 = c / (a * u1); |
| ALOGV("u2: %.9g", u2); |
| if (0 - kFloatFuzz < u2 && u2 < 1 + kFloatFuzz) return u2; |
| |
| // Last resort, return the smaller-magnitude solution |
| return fabs(u1) < fabs(u2) ? u1 : u2; |
| } |
| |
| } // namespace camera3 |
| |
| } // namespace android |