Home | History | Annotate | Download | only in device3
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "Camera3-DistMapper"
     18 #define ATRACE_TAG ATRACE_TAG_CAMERA
     19 //#define LOG_NDEBUG 0
     20 
     21 #include <algorithm>
     22 #include <cmath>
     23 
     24 #include "device3/DistortionMapper.h"
     25 
     26 namespace android {
     27 
     28 namespace camera3 {
     29 
     30 /**
     31  * Metadata keys to correct when adjusting coordinates for distortion correction
     32  */
     33 
     34 // Both capture request and result
     35 constexpr std::array<uint32_t, 3> DistortionMapper::kMeteringRegionsToCorrect = {
     36     ANDROID_CONTROL_AF_REGIONS,
     37     ANDROID_CONTROL_AE_REGIONS,
     38     ANDROID_CONTROL_AWB_REGIONS
     39 };
     40 
     41 // Only capture request
     42 constexpr std::array<uint32_t, 1> DistortionMapper::kRequestRectsToCorrect = {
     43     ANDROID_SCALER_CROP_REGION,
     44 };
     45 
     46 // Only for capture result
     47 constexpr std::array<uint32_t, 1> DistortionMapper::kResultRectsToCorrect = {
     48     ANDROID_SCALER_CROP_REGION,
     49 };
     50 
     51 // Only for capture result
     52 constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = {
     53     ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points
     54     ANDROID_STATISTICS_FACE_LANDMARKS,
     55 };
     56 
     57 
     58 DistortionMapper::DistortionMapper() : mValidMapping(false), mValidGrids(false) {
     59 }
     60 
     61 bool DistortionMapper::isDistortionSupported(const CameraMetadata &result) {
     62     bool isDistortionCorrectionSupported = false;
     63     camera_metadata_ro_entry_t distortionCorrectionModes =
     64             result.find(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES);
     65     for (size_t i = 0; i < distortionCorrectionModes.count; i++) {
     66         if (distortionCorrectionModes.data.u8[i] !=
     67                 ANDROID_DISTORTION_CORRECTION_MODE_OFF) {
     68             isDistortionCorrectionSupported = true;
     69             break;
     70         }
     71     }
     72     return isDistortionCorrectionSupported;
     73 }
     74 
     75 status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) {
     76     std::lock_guard<std::mutex> lock(mMutex);
     77     camera_metadata_ro_entry_t array;
     78 
     79     array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
     80     if (array.count != 4) return BAD_VALUE;
     81 
     82     float arrayX = static_cast<float>(array.data.i32[0]);
     83     float arrayY = static_cast<float>(array.data.i32[1]);
     84     mArrayWidth = static_cast<float>(array.data.i32[2]);
     85     mArrayHeight = static_cast<float>(array.data.i32[3]);
     86 
     87     array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
     88     if (array.count != 4) return BAD_VALUE;
     89 
     90     float activeX = static_cast<float>(array.data.i32[0]);
     91     float activeY = static_cast<float>(array.data.i32[1]);
     92     mActiveWidth = static_cast<float>(array.data.i32[2]);
     93     mActiveHeight = static_cast<float>(array.data.i32[3]);
     94 
     95     mArrayDiffX = activeX - arrayX;
     96     mArrayDiffY = activeY - arrayY;
     97 
     98     return updateCalibration(deviceInfo);
     99 }
    100 
    101 bool DistortionMapper::calibrationValid() const {
    102     std::lock_guard<std::mutex> lock(mMutex);
    103 
    104     return mValidMapping;
    105 }
    106 
    107 status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) {
    108     std::lock_guard<std::mutex> lock(mMutex);
    109     status_t res;
    110 
    111     if (!mValidMapping) return OK;
    112 
    113     camera_metadata_entry_t e;
    114     e = request->find(ANDROID_DISTORTION_CORRECTION_MODE);
    115     if (e.count != 0 && e.data.u8[0] != ANDROID_DISTORTION_CORRECTION_MODE_OFF) {
    116         for (auto region : kMeteringRegionsToCorrect) {
    117             e = request->find(region);
    118             for (size_t j = 0; j < e.count; j += 5) {
    119                 int32_t weight = e.data.i32[j + 4];
    120                 if (weight == 0) {
    121                     continue;
    122                 }
    123                 res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true);
    124                 if (res != OK) return res;
    125             }
    126         }
    127         for (auto rect : kRequestRectsToCorrect) {
    128             e = request->find(rect);
    129             res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
    130             if (res != OK) return res;
    131         }
    132     }
    133 
    134     return OK;
    135 }
    136 
    137 status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) {
    138     std::lock_guard<std::mutex> lock(mMutex);
    139     status_t res;
    140 
    141     if (!mValidMapping) return OK;
    142 
    143     res = updateCalibration(*result);
    144     if (res != OK) {
    145         ALOGE("Failure to update lens calibration information");
    146         return INVALID_OPERATION;
    147     }
    148 
    149     camera_metadata_entry_t e;
    150     e = result->find(ANDROID_DISTORTION_CORRECTION_MODE);
    151     if (e.count != 0 && e.data.u8[0] != ANDROID_DISTORTION_CORRECTION_MODE_OFF) {
    152         for (auto region : kMeteringRegionsToCorrect) {
    153             e = result->find(region);
    154             for (size_t j = 0; j < e.count; j += 5) {
    155                 int32_t weight = e.data.i32[j + 4];
    156                 if (weight == 0) {
    157                     continue;
    158                 }
    159                 res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true);
    160                 if (res != OK) return res;
    161             }
    162         }
    163         for (auto rect : kResultRectsToCorrect) {
    164             e = result->find(rect);
    165             res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
    166             if (res != OK) return res;
    167         }
    168         for (auto pts : kResultPointsToCorrectNoClamp) {
    169             e = result->find(pts);
    170             res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false);
    171             if (res != OK) return res;
    172         }
    173     }
    174 
    175     return OK;
    176 }
    177 
    178 // Utility methods; not guarded by mutex
    179 
    180 status_t DistortionMapper::updateCalibration(const CameraMetadata &result) {
    181     camera_metadata_ro_entry_t calib, distortion;
    182 
    183     calib = result.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
    184     distortion = result.find(ANDROID_LENS_DISTORTION);
    185 
    186     if (calib.count != 5) return BAD_VALUE;
    187     if (distortion.count != 5) return BAD_VALUE;
    188 
    189     // Skip redoing work if no change to calibration fields
    190     if (mValidMapping &&
    191             mFx == calib.data.f[0] &&
    192             mFy == calib.data.f[1] &&
    193             mCx == calib.data.f[2] &&
    194             mCy == calib.data.f[3] &&
    195             mS == calib.data.f[4]) {
    196         bool noChange = true;
    197         for (size_t i = 0; i < distortion.count; i++) {
    198             if (mK[i] != distortion.data.f[i]) {
    199                 noChange = false;
    200                 break;
    201             }
    202         }
    203         if (noChange) return OK;
    204     }
    205 
    206     mFx = calib.data.f[0];
    207     mFy = calib.data.f[1];
    208     mCx = calib.data.f[2];
    209     mCy = calib.data.f[3];
    210     mS = calib.data.f[4];
    211 
    212     mInvFx = 1 / mFx;
    213     mInvFy = 1 / mFy;
    214 
    215     for (size_t i = 0; i < distortion.count; i++) {
    216         mK[i] = distortion.data.f[i];
    217     }
    218 
    219     mValidMapping = true;
    220     // Need to recalculate grid
    221     mValidGrids = false;
    222 
    223     return OK;
    224 }
    225 
    226 status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount,
    227         bool clamp, bool simple) {
    228     if (!mValidMapping) return INVALID_OPERATION;
    229 
    230     if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp);
    231 
    232     if (!mValidGrids) {
    233         status_t res = buildGrids();
    234         if (res != OK) return res;
    235     }
    236 
    237     for (int i = 0; i < coordCount * 2; i += 2) {
    238         const GridQuad *quad = findEnclosingQuad(coordPairs + i, mDistortedGrid);
    239         if (quad == nullptr) {
    240             ALOGE("Raw to corrected mapping failure: No quad found for (%d, %d)",
    241                     *(coordPairs + i), *(coordPairs + i + 1));
    242             return INVALID_OPERATION;
    243         }
    244         ALOGV("src xy: %d, %d, enclosing quad: (%f, %f), (%f, %f), (%f, %f), (%f, %f)",
    245                 coordPairs[i], coordPairs[i+1],
    246                 quad->coords[0], quad->coords[1],
    247                 quad->coords[2], quad->coords[3],
    248                 quad->coords[4], quad->coords[5],
    249                 quad->coords[6], quad->coords[7]);
    250 
    251         const GridQuad *corrQuad = quad->src;
    252         if (corrQuad == nullptr) {
    253             ALOGE("Raw to corrected mapping failure: No src quad found");
    254             return INVALID_OPERATION;
    255         }
    256         ALOGV("              corr quad: (%f, %f), (%f, %f), (%f, %f), (%f, %f)",
    257                 corrQuad->coords[0], corrQuad->coords[1],
    258                 corrQuad->coords[2], corrQuad->coords[3],
    259                 corrQuad->coords[4], corrQuad->coords[5],
    260                 corrQuad->coords[6], corrQuad->coords[7]);
    261 
    262         float u = calculateUorV(coordPairs + i, *quad, /*calculateU*/ true);
    263         float v = calculateUorV(coordPairs + i, *quad, /*calculateU*/ false);
    264 
    265         ALOGV("uv: %f, %f", u, v);
    266 
    267         // Interpolate along top edge of corrected quad (which are axis-aligned) for x
    268         float corrX = corrQuad->coords[0] + u * (corrQuad->coords[2] - corrQuad->coords[0]);
    269         // Interpolate along left edge of corrected quad (which are axis-aligned) for y
    270         float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]);
    271 
    272         // Clamp to within active array
    273         if (clamp) {
    274             corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
    275             corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
    276         }
    277 
    278         coordPairs[i] = static_cast<int32_t>(std::round(corrX));
    279         coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
    280     }
    281 
    282     return OK;
    283 }
    284 
    285 status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
    286         bool clamp) const {
    287     if (!mValidMapping) return INVALID_OPERATION;
    288 
    289     float scaleX = mActiveWidth / mArrayWidth;
    290     float scaleY = mActiveHeight / mArrayHeight;
    291     for (int i = 0; i < coordCount * 2; i += 2) {
    292         float x = coordPairs[i];
    293         float y = coordPairs[i + 1];
    294         float corrX = x * scaleX;
    295         float corrY = y * scaleY;
    296         if (clamp) {
    297             corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
    298             corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
    299         }
    300         coordPairs[i] = static_cast<int32_t>(std::round(corrX));
    301         coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
    302     }
    303 
    304     return OK;
    305 }
    306 
    307 status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
    308         bool simple) {
    309     if (!mValidMapping) return INVALID_OPERATION;
    310     for (int i = 0; i < rectCount * 4; i += 4) {
    311         // Map from (l, t, width, height) to (l, t, r, b)
    312         int32_t coords[4] = {
    313             rects[i],
    314             rects[i + 1],
    315             rects[i] + rects[i + 2] - 1,
    316             rects[i + 1] + rects[i + 3] - 1
    317         };
    318 
    319         mapRawToCorrected(coords, 2, clamp, simple);
    320 
    321         // Map back to (l, t, width, height)
    322         rects[i] = coords[0];
    323         rects[i + 1] = coords[1];
    324         rects[i + 2] = coords[2] - coords[0] + 1;
    325         rects[i + 3] = coords[3] - coords[1] + 1;
    326     }
    327 
    328     return OK;
    329 }
    330 
    331 status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp,
    332         bool simple) const {
    333     return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple);
    334 }
    335 
    336 template<typename T>
    337 status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp,
    338         bool simple) const {
    339     if (!mValidMapping) return INVALID_OPERATION;
    340 
    341     if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp);
    342 
    343     float activeCx = mCx - mArrayDiffX;
    344     float activeCy = mCy - mArrayDiffY;
    345     for (int i = 0; i < coordCount * 2; i += 2) {
    346         // Move to normalized space from active array space
    347         float ywi = (coordPairs[i + 1] - activeCy) * mInvFy;
    348         float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx;
    349         // Apply distortion model to calculate raw image coordinates
    350         float rSq = xwi * xwi + ywi * ywi;
    351         float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
    352         float xc = xwi * Fr + (mK[3] * 2 * xwi * ywi) + mK[4] * (rSq + 2 * xwi * xwi);
    353         float yc = ywi * Fr + (mK[4] * 2 * xwi * ywi) + mK[3] * (rSq + 2 * ywi * ywi);
    354         // Move back to image space
    355         float xr = mFx * xc + mS * yc + mCx;
    356         float yr = mFy * yc + mCy;
    357         // Clamp to within pre-correction active array
    358         if (clamp) {
    359             xr = std::min(mArrayWidth - 1, std::max(0.f, xr));
    360             yr = std::min(mArrayHeight - 1, std::max(0.f, yr));
    361         }
    362 
    363         coordPairs[i] = static_cast<T>(std::round(xr));
    364         coordPairs[i + 1] = static_cast<T>(std::round(yr));
    365     }
    366 
    367     return OK;
    368 }
    369 
    370 template<typename T>
    371 status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount,
    372         bool clamp) const {
    373     if (!mValidMapping) return INVALID_OPERATION;
    374 
    375     float scaleX = mArrayWidth / mActiveWidth;
    376     float scaleY = mArrayHeight / mActiveHeight;
    377     for (int i = 0; i < coordCount * 2; i += 2) {
    378         float x = coordPairs[i];
    379         float y = coordPairs[i + 1];
    380         float rawX = x * scaleX;
    381         float rawY = y * scaleY;
    382         if (clamp) {
    383             rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX));
    384             rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY));
    385         }
    386         coordPairs[i] = static_cast<T>(std::round(rawX));
    387         coordPairs[i + 1] = static_cast<T>(std::round(rawY));
    388     }
    389 
    390     return OK;
    391 }
    392 
    393 
    394 status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
    395         bool simple) const {
    396     if (!mValidMapping) return INVALID_OPERATION;
    397 
    398     for (int i = 0; i < rectCount * 4; i += 4) {
    399         // Map from (l, t, width, height) to (l, t, r, b)
    400         int32_t coords[4] = {
    401             rects[i],
    402             rects[i + 1],
    403             rects[i] + rects[i + 2] - 1,
    404             rects[i + 1] + rects[i + 3] - 1
    405         };
    406 
    407         mapCorrectedToRaw(coords, 2, clamp, simple);
    408 
    409         // Map back to (l, t, width, height)
    410         rects[i] = coords[0];
    411         rects[i + 1] = coords[1];
    412         rects[i + 2] = coords[2] - coords[0] + 1;
    413         rects[i + 3] = coords[3] - coords[1] + 1;
    414     }
    415 
    416     return OK;
    417 }
    418 
    419 status_t DistortionMapper::buildGrids() {
    420     if (mCorrectedGrid.size() != kGridSize * kGridSize) {
    421         mCorrectedGrid.resize(kGridSize * kGridSize);
    422         mDistortedGrid.resize(kGridSize * kGridSize);
    423     }
    424 
    425     float gridMargin = mArrayWidth * kGridMargin;
    426     float gridSpacingX = (mArrayWidth + 2 * gridMargin) / kGridSize;
    427     float gridSpacingY = (mArrayHeight + 2 * gridMargin) / kGridSize;
    428 
    429     size_t index = 0;
    430     float x = -gridMargin;
    431     for (size_t i = 0; i < kGridSize; i++, x += gridSpacingX) {
    432         float y = -gridMargin;
    433         for (size_t j = 0; j < kGridSize; j++, y += gridSpacingY, index++) {
    434             mCorrectedGrid[index].src = nullptr;
    435             mCorrectedGrid[index].coords = {
    436                 x, y,
    437                 x + gridSpacingX, y,
    438                 x + gridSpacingX, y + gridSpacingY,
    439                 x, y + gridSpacingY
    440             };
    441             mDistortedGrid[index].src = &mCorrectedGrid[index];
    442             mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
    443             status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4,
    444                     /*clamp*/false, /*simple*/false);
    445             if (res != OK) return res;
    446         }
    447     }
    448 
    449     mValidGrids = true;
    450     return OK;
    451 }
    452 
    453 const DistortionMapper::GridQuad* DistortionMapper::findEnclosingQuad(
    454         const int32_t pt[2], const std::vector<GridQuad>& grid) {
    455     const float x = pt[0];
    456     const float y = pt[1];
    457 
    458     for (const GridQuad& quad : grid) {
    459         const float &x1 = quad.coords[0];
    460         const float &y1 = quad.coords[1];
    461         const float &x2 = quad.coords[2];
    462         const float &y2 = quad.coords[3];
    463         const float &x3 = quad.coords[4];
    464         const float &y3 = quad.coords[5];
    465         const float &x4 = quad.coords[6];
    466         const float &y4 = quad.coords[7];
    467 
    468         // Point-in-quad test:
    469 
    470         // Quad has corners P1-P4; if P is within the quad, then it is on the same side of all the
    471         // edges (or on top of one of the edges or corners), traversed in a consistent direction.
    472         // This means that the cross product of edge En = Pn->P(n+1 mod 4) and line Ep = Pn->P must
    473         // have the same sign (or be zero) for all edges.
    474         // For clockwise traversal, the sign should be negative or zero for Ep x En, indicating that
    475         // En is to the left of Ep, or overlapping.
    476         float s1 = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);
    477         if (s1 > 0) continue;
    478         float s2 = (x - x2) * (y3 - y2) - (y - y2) * (x3 - x2);
    479         if (s2 > 0) continue;
    480         float s3 = (x - x3) * (y4 - y3) - (y - y3) * (x4 - x3);
    481         if (s3 > 0) continue;
    482         float s4 = (x - x4) * (y1 - y4) - (y - y4) * (x1 - x4);
    483         if (s4 > 0) continue;
    484 
    485         return &quad;
    486     }
    487     return nullptr;
    488 }
    489 
    490 float DistortionMapper::calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU) {
    491     const float x = pt[0];
    492     const float y = pt[1];
    493     const float &x1 = quad.coords[0];
    494     const float &y1 = quad.coords[1];
    495     const float &x2 = calculateU ? quad.coords[2] : quad.coords[6];
    496     const float &y2 = calculateU ? quad.coords[3] : quad.coords[7];
    497     const float &x3 = quad.coords[4];
    498     const float &y3 = quad.coords[5];
    499     const float &x4 = calculateU ? quad.coords[6] : quad.coords[2];
    500     const float &y4 = calculateU ? quad.coords[7] : quad.coords[3];
    501 
    502     float a = (x1 - x2) * (y1 - y2 + y3 - y4) - (y1 - y2) * (x1 - x2 + x3 - x4);
    503     float b = (x - x1) * (y1 - y2 + y3 - y4) + (x1 - x2) * (y4 - y1) -
    504               (y - y1) * (x1 - x2 + x3 - x4) - (y1 - y2) * (x4 - x1);
    505     float c = (x - x1) * (y4 - y1) - (y - y1) * (x4 - x1);
    506 
    507     if (a == 0) {
    508         // One solution may happen if edges are parallel
    509         float u0 = -c / b;
    510         ALOGV("u0: %.9g, b: %f, c: %f", u0, b, c);
    511         return u0;
    512     }
    513 
    514     float det = b * b - 4 * a * c;
    515     if (det < 0) {
    516         // Sanity check - should not happen if pt is within the quad
    517         ALOGE("Bad determinant! a: %f, b: %f, c: %f, det: %f", a,b,c,det);
    518         return -1;
    519     }
    520 
    521     // Select more numerically stable solution
    522     float sqdet = b > 0 ? -std::sqrt(det) : std::sqrt(det);
    523 
    524     float u1 = (-b + sqdet) / (2 * a);
    525     ALOGV("u1: %.9g", u1);
    526     if (0 - kFloatFuzz < u1 && u1 < 1 + kFloatFuzz) return u1;
    527 
    528     float u2 = c / (a * u1);
    529     ALOGV("u2: %.9g", u2);
    530     if (0 - kFloatFuzz < u2 && u2 < 1 + kFloatFuzz) return u2;
    531 
    532     // Last resort, return the smaller-magnitude solution
    533     return fabs(u1) < fabs(u2) ? u1 : u2;
    534 }
    535 
    536 } // namespace camera3
    537 
    538 } // namespace android
    539