Merge "Collected Patches from Ittiam" into pi-dev
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 1a56edc..7b3b81d 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1023,7 +1023,9 @@
break;
}
}
- mLastTrack->meta.setInt32(kKeyCryptoMode, mode);
+ if (mode != kCryptoModeUnencrypted) {
+ mLastTrack->meta.setInt32(kKeyCryptoMode, mode);
+ }
break;
}
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 9bdf895..52791b9 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -57,7 +57,7 @@
const char *language,
uint32_t flags);
- bool getActiveURI(AString *uri) const;
+ bool getActiveURI(AString *uri, const char *baseURL) const;
void pickRandomMediaItems();
status_t selectTrack(size_t index, bool select);
@@ -76,6 +76,7 @@
AString mURI;
AString mLanguage;
uint32_t mFlags;
+ AString makeURL(const char *baseURL) const;
};
Type mType;
@@ -228,12 +229,12 @@
return format;
}
-bool M3UParser::MediaGroup::getActiveURI(AString *uri) const {
+bool M3UParser::MediaGroup::getActiveURI(AString *uri, const char *baseURL) const {
for (size_t i = 0; i < mMediaItems.size(); ++i) {
if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) {
const Media &item = mMediaItems.itemAt(i);
- *uri = item.mURI;
+ *uri = item.makeURL(baseURL);
return true;
}
}
@@ -322,7 +323,7 @@
}
if (uri) {
- *uri = mItems.itemAt(index).mURI;
+ *uri = mItems.itemAt(index).makeURL(mBaseURI.c_str());
}
if (meta) {
@@ -428,7 +429,7 @@
AString groupID;
if (!meta->findString(key, &groupID)) {
if (uri != NULL) {
- *uri = mItems.itemAt(index).mURI;
+ *uri = mItems.itemAt(index).makeURL(mBaseURI.c_str());
}
AString codecs;
@@ -459,7 +460,7 @@
// don't care about the active URI (or if there is an active one)
if (uri != NULL) {
sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
- if (!group->getActiveURI(uri)) {
+ if (!group->getActiveURI(uri, mBaseURI.c_str())) {
return false;
}
@@ -548,6 +549,18 @@
return true;
}
+AString M3UParser::Item::makeURL(const char *baseURL) const {
+ AString out;
+ CHECK(MakeURL(baseURL, mURI.c_str(), &out));
+ return out;
+}
+
+AString M3UParser::MediaGroup::Media::makeURL(const char *baseURL) const {
+ AString out;
+ CHECK(MakeURL(baseURL, mURI.c_str(), &out));
+ return out;
+}
+
status_t M3UParser::parse(const void *_data, size_t size) {
int32_t lineNo = 0;
@@ -678,7 +691,7 @@
mItems.push();
Item *item = &mItems.editItemAt(mItems.size() - 1);
- CHECK(MakeURL(mBaseURI.c_str(), line.c_str(), &item->mURI));
+ item->mURI = line;
item->mMeta = itemMeta;
@@ -1190,9 +1203,7 @@
AString tmp(val, 1, val.size() - 2);
- if (!MakeURL(mBaseURI.c_str(), tmp.c_str(), &groupURI)) {
- ALOGI("Failed to make absolute URI from '%s'.", tmp.c_str());
- }
+ groupURI = tmp;
haveGroupURI = true;
}
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index fa648ed..c85335a 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -64,6 +64,7 @@
struct Item {
AString mURI;
sp<AMessage> mMeta;
+ AString makeURL(const char *baseURL) const;
};
status_t mInitCheck;
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index f615500..a0a62f4 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -607,6 +607,9 @@
// UCS-2
// API wants number of characters, not number of bytes...
int len = n / 2;
+ if (len == 0) {
+ return;
+ }
const char16_t *framedata = (const char16_t *) (frameData + 1);
char16_t *framedatacopy = NULL;
if (*framedata == 0xfffe) {
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index cc67834..7d2c2dd 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1077,7 +1077,8 @@
}
case OMXBuffer::kBufferTypeSharedMem: {
- if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer) {
+ if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
+ && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
break;
}
return useBuffer_l(portIndex, omxBuffer.mMem, NULL, buffer);
@@ -1092,7 +1093,8 @@
case OMXBuffer::kBufferTypeHidlMemory: {
if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
- && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
+ && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
+ && mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
break;
}
sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
@@ -1107,7 +1109,8 @@
break;
}
- ALOGE("b/77486542");
+ ALOGE("b/77486542 : bufferType = %d vs. portMode = %d",
+ omxBuffer.mBufferType, mPortMode[portIndex]);
android_errorWriteLog(0x534e4554, "77486542");
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 7b86180..96261ab 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -51,6 +51,7 @@
device3/StatusTracker.cpp \
device3/Camera3BufferManager.cpp \
device3/Camera3StreamSplitter.cpp \
+ device3/DistortionMapper.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
utils/AutoConditionLock.cpp \
@@ -96,3 +97,8 @@
LOCAL_MODULE:= libcameraservice
include $(BUILD_SHARED_LIBRARY)
+
+# Build tests too
+
+include $(LOCAL_PATH)/tests/Android.mk
+
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 187bea9..0c738e7 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -197,7 +197,6 @@
faceRects[i*4 + 2], scalerCrop);
face.rect[3] = l.mParameters.arrayYToNormalizedWithCrop(
faceRects[i*4 + 3], scalerCrop);
-
face.score = faceScores[i];
if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) {
face.id = faceIds[i];
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 4a58620..d66dec4 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -954,13 +954,24 @@
const uint8_t *caps = availableCapabilities.data.u8;
for (size_t i = 0; i < availableCapabilities.count; i++) {
if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING ==
- caps[i]) {
+ caps[i]) {
isZslReprocessPresent = true;
break;
}
}
}
+ isDistortionCorrectionSupported = false;
+ camera_metadata_ro_entry_t distortionCorrectionModes =
+ staticInfo(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;
+ }
+ }
+
if (isDeviceZslSupported || slowJpegMode ||
property_get_bool("camera.disable_zsl_mode", false)) {
ALOGI("Camera %d: Disabling ZSL mode", cameraId);
@@ -1222,7 +1233,7 @@
const uint8_t *caps = availableCapabilities.data.u8;
for (size_t i = 0; i < availableCapabilities.count; i++) {
if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING ==
- caps[i]) {
+ caps[i]) {
isZslReprocessPresent = true;
break;
}
@@ -2097,15 +2108,24 @@
if (intent.count == 0) return BAD_VALUE;
+ uint8_t distortionMode = ANDROID_DISTORTION_CORRECTION_MODE_OFF;
if (intent.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE) {
res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
fastInfo.bestStillCaptureFpsRange, 2);
+ distortionMode = ANDROID_DISTORTION_CORRECTION_MODE_HIGH_QUALITY;
} else {
res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
previewFpsRange, 2);
+ distortionMode = ANDROID_DISTORTION_CORRECTION_MODE_FAST;
}
if (res != OK) return res;
+ if (isDistortionCorrectionSupported) {
+ res = request->update(ANDROID_DISTORTION_CORRECTION_MODE,
+ &distortionMode, 1);
+ if (res != OK) return res;
+ }
+
if (autoWhiteBalanceLockAvailable) {
uint8_t reqWbLock = autoWhiteBalanceLock ?
ANDROID_CONTROL_AWB_LOCK_ON : ANDROID_CONTROL_AWB_LOCK_OFF;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index e35b7a4..97f8ea7 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -177,6 +177,8 @@
bool isZslReprocessPresent;
// Whether the device supports enableZsl.
bool isDeviceZslSupported;
+ // Whether the device supports geometric distortion correction
+ bool isDistortionCorrectionSupported;
// Overall camera state
enum State {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index d9bcba3..543914e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -249,6 +249,14 @@
}
}
+ if (DistortionMapper::isDistortionSupported(mDeviceInfo)) {
+ res = mDistortionMapper.setupStaticInfo(mDeviceInfo);
+ if (res != OK) {
+ SET_ERR_L("Unable to read necessary calibration fields for distortion correction");
+ return res;
+ }
+ }
+
return OK;
}
@@ -2983,6 +2991,14 @@
}
}
+ // Fix up some result metadata to account for HAL-level distortion correction
+ status_t res = mDistortionMapper.correctCaptureResult(&captureResult.mMetadata);
+ if (res != OK) {
+ SET_ERR("Unable to correct capture result metadata for frame %d: %s (%d)",
+ frameNumber, strerror(res), res);
+ return;
+ }
+
mTagMonitor.monitorMetadata(TagMonitor::RESULT,
frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
@@ -4705,13 +4721,13 @@
// Insert any queued triggers (before metadata is locked)
status_t res = insertTriggers(captureRequest);
-
if (res < 0) {
SET_ERR("RequestThread: Unable to insert triggers "
"(capture request %d, HAL device: %s (%d)",
halRequest->frame_number, strerror(-res), res);
return INVALID_OPERATION;
}
+
int triggerCount = res;
bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
mPrevTriggers = triggerCount;
@@ -4731,6 +4747,21 @@
return INVALID_OPERATION;
}
+ {
+ // Correct metadata regions for distortion correction if enabled
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ res = parent->mDistortionMapper.correctCaptureRequest(
+ &(captureRequest->mSettingsList.begin()->metadata));
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to correct capture requests "
+ "for lens distortion for request %d: %s (%d)",
+ halRequest->frame_number, strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
+ }
+
/**
* The request should be presorted so accesses in HAL
* are O(logn). Sidenote, sorting a sorted metadata is nop.
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 35f799d..d8fe19f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -43,6 +43,7 @@
#include "common/CameraDeviceBase.h"
#include "device3/StatusTracker.h"
#include "device3/Camera3BufferManager.h"
+#include "device3/DistortionMapper.h"
#include "utils/TagMonitor.h"
#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
@@ -1179,6 +1180,12 @@
/**** End scope for mInFlightLock ****/
+ /**
+ * Distortion correction support
+ */
+
+ camera3::DistortionMapper mDistortionMapper;
+
// Debug tracker for metadata tag value changes
// - Enabled with the -m <taglist> option to dumpsys, such as
// dumpsys -m android.control.aeState,android.control.aeMode
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
new file mode 100644
index 0000000..9229079
--- /dev/null
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -0,0 +1,445 @@
+/*
+ * 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 <cmath>
+
+#include "device3/DistortionMapper.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Metadata keys to correct when adjusting coordinates for distortion correction
+ */
+
+// Both capture request and result
+constexpr std::array<uint32_t, 3> DistortionMapper::kMeteringRegionsToCorrect = {
+ ANDROID_CONTROL_AF_REGIONS,
+ ANDROID_CONTROL_AE_REGIONS,
+ ANDROID_CONTROL_AWB_REGIONS
+};
+
+// Only capture request
+constexpr std::array<uint32_t, 1> DistortionMapper::kRequestRectsToCorrect = {
+ ANDROID_SCALER_CROP_REGION,
+};
+
+// Only for capture result
+constexpr std::array<uint32_t, 2> DistortionMapper::kResultRectsToCorrect = {
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_STATISTICS_FACE_RECTANGLES
+};
+
+// Only for capture result
+constexpr std::array<uint32_t, 1> DistortionMapper::kResultPointsToCorrect = {
+ ANDROID_STATISTICS_FACE_LANDMARKS,
+};
+
+
+DistortionMapper::DistortionMapper() : mValidMapping(false), mValidGrids(false) {
+}
+
+bool DistortionMapper::isDistortionSupported(const CameraMetadata &result) {
+ bool isDistortionCorrectionSupported = false;
+ camera_metadata_ro_entry_t distortionCorrectionModes =
+ result.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);
+ camera_metadata_ro_entry_t array;
+
+ array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ if (array.count != 4) return BAD_VALUE;
+
+ mArrayWidth = array.data.i32[2];
+ mArrayHeight = array.data.i32[3];
+
+ return updateCalibration(deviceInfo);
+}
+
+bool DistortionMapper::calibrationValid() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ return mValidMapping;
+}
+
+status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ status_t res;
+
+ if (!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) {
+ res = mapCorrectedToRaw(e.data.i32 + j, 2);
+ if (res != OK) return res;
+ }
+ }
+ for (auto rect : kRequestRectsToCorrect) {
+ e = request->find(rect);
+ res = mapCorrectedRectToRaw(e.data.i32, e.count / 4);
+ if (res != OK) return res;
+ }
+ }
+
+ return OK;
+}
+
+status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ status_t res;
+
+ if (!mValidMapping) return OK;
+
+ res = updateCalibration(*result);
+ 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) {
+ res = mapRawToCorrected(e.data.i32 + j, 2);
+ if (res != OK) return res;
+ }
+ }
+ for (auto rect : kResultRectsToCorrect) {
+ e = result->find(rect);
+ res = mapRawRectToCorrected(e.data.i32, e.count / 4);
+ if (res != OK) return res;
+ }
+ for (auto pts : kResultPointsToCorrect) {
+ e = result->find(pts);
+ res = mapRawToCorrected(e.data.i32, e.count / 2);
+ if (res != OK) return res;
+ }
+ }
+
+ return OK;
+}
+
+// Utility methods; not guarded by mutex
+
+status_t DistortionMapper::updateCalibration(const CameraMetadata &result) {
+ camera_metadata_ro_entry_t calib, distortion;
+
+ calib = result.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
+ distortion = result.find(ANDROID_LENS_DISTORTION);
+
+ if (calib.count != 5) return BAD_VALUE;
+ if (distortion.count != 5) return BAD_VALUE;
+
+ // Skip redoing work if no change to calibration fields
+ if (mValidMapping &&
+ mFx == calib.data.f[0] &&
+ mFy == calib.data.f[1] &&
+ mCx == calib.data.f[2] &&
+ mCy == calib.data.f[3] &&
+ mS == calib.data.f[4]) {
+ bool noChange = true;
+ for (size_t i = 0; i < distortion.count; i++) {
+ if (mK[i] != distortion.data.f[i]) {
+ noChange = false;
+ break;
+ }
+ }
+ if (noChange) return OK;
+ }
+
+ mFx = calib.data.f[0];
+ mFy = calib.data.f[1];
+ mCx = calib.data.f[2];
+ mCy = calib.data.f[3];
+ mS = calib.data.f[4];
+
+ mInvFx = 1 / mFx;
+ mInvFy = 1 / mFy;
+
+ for (size_t i = 0; i < distortion.count; i++) {
+ mK[i] = distortion.data.f[i];
+ }
+
+ mValidMapping = true;
+ // Need to recalculate grid
+ mValidGrids = false;
+
+ return OK;
+}
+
+status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount) {
+ if (!mValidMapping) return INVALID_OPERATION;
+
+ if (!mValidGrids) {
+ status_t res = buildGrids();
+ if (res != OK) return res;
+ }
+
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ const GridQuad *quad = findEnclosingQuad(coordPairs + i, mDistortedGrid);
+ if (quad == nullptr) {
+ ALOGE("Raw to corrected mapping failure: No quad found");
+ 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]);
+
+ 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) {
+ if (!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],
+ rects[i + 1] + rects[i + 3]
+ };
+
+ mapRawToCorrected(coords, 2);
+
+ // Map back to (l, t, width, height)
+ rects[i] = coords[0];
+ rects[i + 1] = coords[1];
+ rects[i + 2] = coords[2] - coords[0];
+ rects[i + 3] = coords[3] - coords[1];
+ }
+
+ return OK;
+}
+
+template<typename T>
+status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) const {
+ if (!mValidMapping) return INVALID_OPERATION;
+
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ // Move to normalized space
+ float ywi = (coordPairs[i + 1] - mCy) * mInvFy;
+ float xwi = (coordPairs[i] - mCx - mS * ywi) * mInvFx;
+ // Apply distortion model to calculate raw image coordinates
+ float rSq = xwi * xwi + ywi * ywi;
+ float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
+ float xc = xwi * Fr + (mK[3] * 2 * xwi * ywi) + mK[4] * (rSq + 2 * xwi * xwi);
+ float yc = ywi * Fr + (mK[4] * 2 * xwi * ywi) + mK[3] * (rSq + 2 * ywi * ywi);
+ // Move back to image space
+ float xr = mFx * xc + mS * yc + mCx;
+ float yr = mFy * yc + mCy;
+
+ coordPairs[i] = static_cast<T>(std::round(xr));
+ coordPairs[i + 1] = static_cast<T>(std::round(yr));
+ }
+
+ return OK;
+}
+
+template status_t DistortionMapper::mapCorrectedToRaw(int32_t*, int) const;
+template status_t DistortionMapper::mapCorrectedToRaw(float*, int) const;
+
+status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) const {
+ if (!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],
+ rects[i + 1] + rects[i + 3]
+ };
+
+ mapCorrectedToRaw(coords, 2);
+
+ // Map back to (l, t, width, height)
+ rects[i] = coords[0];
+ rects[i + 1] = coords[1];
+ rects[i + 2] = coords[2] - coords[0];
+ rects[i + 3] = coords[3] - coords[1];
+ }
+
+ return OK;
+}
+
+status_t DistortionMapper::buildGrids() {
+ if (mCorrectedGrid.size() != kGridSize * kGridSize) {
+ mCorrectedGrid.resize(kGridSize * kGridSize);
+ mDistortedGrid.resize(kGridSize * kGridSize);
+ }
+
+ float gridMargin = mArrayWidth * kGridMargin;
+ float gridSpacingX = (mArrayWidth + 2 * gridMargin) / kGridSize;
+ float gridSpacingY = (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++) {
+ mCorrectedGrid[index].src = nullptr;
+ mCorrectedGrid[index].coords = {
+ x, y,
+ x + gridSpacingX, y,
+ x + gridSpacingX, y + gridSpacingY,
+ x, y + gridSpacingY
+ };
+ mDistortedGrid[index].src = &mCorrectedGrid[index];
+ mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
+ status_t res = mapCorrectedToRaw(mDistortedGrid[index].coords.data(), 4);
+ if (res != OK) return res;
+ }
+ }
+
+ 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) {
+ // Sanity 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
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
new file mode 100644
index 0000000..c6d715b
--- /dev/null
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_DISTORTIONMAPPER_H
+#define ANDROID_SERVERS_DISTORTIONMAPPER_H
+
+#include <utils/Errors.h>
+#include <array>
+#include <mutex>
+
+#include "camera/CameraMetadata.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Utilities to transform between raw (distorted) and warped (corrected) coordinate systems
+ * for cameras that support geometric distortion
+ */
+class DistortionMapper {
+ public:
+ DistortionMapper();
+
+ /**
+ * Check whether distortion correction is supported by the camera HAL
+ */
+ static bool isDistortionSupported(const CameraMetadata &deviceInfo);
+
+ /**
+ * Update static lens calibration info from camera characteristics
+ */
+ status_t setupStaticInfo(const CameraMetadata &deviceInfo);
+
+ /**
+ * Return whether distortion correction can be applied currently
+ */
+ bool calibrationValid() const;
+
+ /**
+ * Correct capture request if distortion correction is enabled
+ */
+ status_t correctCaptureRequest(CameraMetadata *request);
+
+ /**
+ * Correct capture result if distortion correction is enabled
+ */
+ status_t correctCaptureResult(CameraMetadata *request);
+
+
+ public: // Visible for testing. Not guarded by mutex; do not use concurrently
+ /**
+ * Update lens calibration from capture results or equivalent
+ */
+ status_t updateCalibration(const CameraMetadata &result);
+
+ /**
+ * Transform from distorted (original) to corrected (warped) coordinates.
+ * Coordinates are transformed in-place
+ *
+ * coordPairs: A pointer to an array of consecutive (x,y) points
+ * coordCount: Number of (x,y) pairs to transform
+ */
+ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount);
+
+ /**
+ * Transform from distorted (original) to corrected (warped) coordinates.
+ * Coordinates are transformed in-place
+ *
+ * rects: A pointer to an array of consecutive (x,y, w, h) rectangles
+ * rectCount: Number of rectangles to transform
+ */
+ status_t mapRawRectToCorrected(int32_t *rects, int rectCount);
+
+ /**
+ * Transform from corrected (warped) to distorted (original) coordinates.
+ * Coordinates are transformed in-place
+ *
+ * coordPairs: A pointer to an array of consecutive (x,y) points
+ * coordCount: Number of (x,y) pairs to transform
+ */
+ template<typename T>
+ status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const;
+
+ /**
+ * Transform from corrected (warped) to distorted (original) coordinates.
+ * Coordinates are transformed in-place
+ *
+ * rects: A pointer to an array of consecutive (x,y, w, h) rectangles
+ * rectCount: Number of rectangles to transform
+ */
+ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const;
+
+ struct GridQuad {
+ // Source grid quad, or null
+ const GridQuad *src;
+ // x,y coordinates of corners, in
+ // clockwise order
+ std::array<float, 8> coords;
+ };
+
+ // Find which grid quad encloses the point; returns null if none do
+ static const GridQuad* findEnclosingQuad(
+ const int32_t pt[2], const std::vector<GridQuad>& grid);
+
+ // Calculate 'horizontal' interpolation coordinate for the point and the quad
+ // Assumes the point P is within the quad Q.
+ // Given quad with points P1-P4, and edges E12-E41, and considering the edge segments as
+ // functions of U: E12(u), where E12(0) = P1 and E12(1) = P2, then we want to find a u
+ // such that the edge E12(u) -> E43(u) contains point P.
+ // This can be determined by checking if the cross product of vector [E12(u)-E43(u)] and
+ // vector [E12(u)-P] is zero. Solving the equation
+ // [E12(u)-E43(u)] x [E12(u)-P] = 0 gives a quadratic equation in u; the solution in the range
+ // 0 to 1 is the one chosen.
+ // If calculateU is true, then an interpolation coordinate for edges E12 and E43 is found;
+ // if it is false, then an interpolation coordinate for edges E14 and E23 is found.
+ static float calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU);
+
+ private:
+ mutable std::mutex mMutex;
+
+ // Number of quads in each dimension of the mapping grids
+ constexpr static size_t kGridSize = 15;
+ // Margin to expand the grid by to ensure it doesn't clip the domain
+ constexpr static float kGridMargin = 0.05f;
+ // Fuzziness for float inequality tests
+ constexpr static float kFloatFuzz = 1e-4;
+
+ // Metadata key lists to correct
+
+ // Both capture request and result
+ static const std::array<uint32_t, 3> kMeteringRegionsToCorrect;
+
+ // Only capture request
+ static const std::array<uint32_t, 1> kRequestRectsToCorrect;
+
+ // Only capture result
+ static const std::array<uint32_t, 2> kResultRectsToCorrect;
+
+ // Only for capture results
+ static const std::array<uint32_t, 1> kResultPointsToCorrect;
+
+ // Utility to create reverse mapping grids
+ status_t buildGrids();
+
+
+ bool mValidMapping;
+ bool mValidGrids;
+
+ // intrisic parameters, in pixels
+ float mFx, mFy, mCx, mCy, mS;
+ // pre-calculated inverses for speed
+ float mInvFx, mInvFy;
+ // radial/tangential distortion parameters
+ float mK[5];
+
+ // pre-correction active array dimensions
+ int mArrayWidth, mArrayHeight;
+
+ std::vector<GridQuad> mCorrectedGrid;
+ std::vector<GridQuad> mDistortedGrid;
+
+}; // class DistortionMapper
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index 37a05c2..f77069c 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES:= $(call all-cpp-files-under, .)
LOCAL_SHARED_LIBRARIES := \
+ libbase \
libcutils \
libcameraservice \
libhidlbase \
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index c1d6e85..ef93d9a 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -190,6 +190,7 @@
hardware::camera::common::V1_0::CameraDeviceStatus) override {}
void onTorchStatusChanged(const String8 &,
hardware::camera::common::V1_0::TorchModeStatus) override {}
+ void onNewProviderRegistered() override {}
};
TEST(CameraProviderManagerTest, InitializeTest) {
diff --git a/services/camera/libcameraservice/tests/DistortionMapperComp.py b/services/camera/libcameraservice/tests/DistortionMapperComp.py
new file mode 100644
index 0000000..dea36a7
--- /dev/null
+++ b/services/camera/libcameraservice/tests/DistortionMapperComp.py
@@ -0,0 +1,47 @@
+# Calculates comparison output values for DistortionMapperTest.cpp:CompareToOpenCV
+#
+# Assumes a python that has numpy and cv2 (OpenCV) available
+
+import numpy as np
+import cv2
+
+Fx = 1000
+Fy = 1000
+Cx = 500
+Cy = 500
+# s = 0 - not supported by OpenCV
+
+K = np.array([[Fx, 0, Cx],[0, Fy, Cy],[0, 0, 1]])
+
+# Order is k1, k2, t1, t2, k3
+dist = np.array([0.1, -0.003, 0.02, 0.01, 0.004])
+
+np.random.seed(1234)
+
+activeArray = np.array([[1000, 750]])
+
+rawCoords = np.floor(np.random.rand(1000,2) * activeArray)
+
+# OpenCV needs either row count or col count = 1 for some reason
+rawCoords2 = rawCoords.reshape(-1, 1, 2)
+
+# P is the output camera matrix, K is the input; use the same for both
+expCoords = cv2.undistortPoints(rawCoords2, K, dist, P = K)
+
+with open('DistortionMapperTest_OpenCvData.h','w') as f:
+ f.write('// Generated by DistortionMapperComp.py\n');
+ f.write('// for use by DistortionMapperTest.cpp\n\n');
+
+ f.write('namespace openCvData {\n')
+ f.write('std::array<int32_t, %d> rawCoords = {\n' % (rawCoords.shape[0] * rawCoords.shape[1]))
+ for i in range(rawCoords.shape[0]):
+ f.write(' %d, %d,\n' % (rawCoords[i][0], rawCoords[i][1]))
+ f.write('};\n')
+
+ f.write('std::array<int32_t, %d> expCoords = {\n' % (expCoords.shape[0] * expCoords.shape[2]))
+ for i in range(expCoords.shape[0]):
+ f.write(' %d, %d,\n' % (expCoords[i][0][0], expCoords[i][0][1]))
+ f.write('};\n')
+ f.write('} // namespace openCvData\n')
+
+print "DistortionMapperTest_OpenCvData.h generated"
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
new file mode 100644
index 0000000..b489931
--- /dev/null
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "DistortionMapperTest"
+
+#include <random>
+
+#include <gtest/gtest.h>
+#include <android-base/stringprintf.h>
+#include <android-base/chrono_utils.h>
+
+#include "../device3/DistortionMapper.h"
+
+using namespace android;
+using namespace android::camera3;
+
+
+int32_t testActiveArray[] = {100, 100, 1000, 750};
+
+float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f };
+
+float identityDistortion[] = { 0.f, 0.f, 0.f, 0.f, 0.f};
+
+std::array<int32_t, 12> basicCoords = {
+ 0, 0,
+ testActiveArray[2] - 1, 0,
+ testActiveArray[2] - 1, testActiveArray[3] - 1,
+ 0, testActiveArray[3] - 1,
+ testActiveArray[2] / 2, testActiveArray[3] / 2,
+ 251, 403 // A particularly bad coordinate for current grid count/array size
+};
+
+
+void setupTestMapper(DistortionMapper *m, float distortion[5]) {
+ CameraMetadata deviceInfo;
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ testActiveArray, 4);
+
+ deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
+ testICal, 5);
+
+ deviceInfo.update(ANDROID_LENS_DISTORTION,
+ distortion, 5);
+
+ m->setupStaticInfo(deviceInfo);
+}
+
+TEST(DistortionMapperTest, Initialization) {
+ CameraMetadata deviceInfo;
+
+ ASSERT_FALSE(DistortionMapper::isDistortionSupported(deviceInfo));
+
+ uint8_t distortionModes[] =
+ {ANDROID_DISTORTION_CORRECTION_MODE_OFF,
+ ANDROID_DISTORTION_CORRECTION_MODE_FAST,
+ ANDROID_DISTORTION_CORRECTION_MODE_HIGH_QUALITY};
+
+ deviceInfo.update(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES,
+ distortionModes, 1);
+
+ ASSERT_FALSE(DistortionMapper::isDistortionSupported(deviceInfo));
+
+ deviceInfo.update(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES,
+ distortionModes, 3);
+
+ ASSERT_TRUE(DistortionMapper::isDistortionSupported(deviceInfo));
+
+ DistortionMapper m;
+
+ ASSERT_FALSE(m.calibrationValid());
+
+ ASSERT_NE(m.setupStaticInfo(deviceInfo), OK);
+
+ ASSERT_FALSE(m.calibrationValid());
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ testActiveArray, 4);
+
+ deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
+ testICal, 5);
+
+ deviceInfo.update(ANDROID_LENS_DISTORTION,
+ identityDistortion, 5);
+
+ ASSERT_EQ(m.setupStaticInfo(deviceInfo), OK);
+
+ ASSERT_TRUE(m.calibrationValid());
+
+ CameraMetadata captureResult;
+
+ ASSERT_NE(m.updateCalibration(captureResult), OK);
+
+ captureResult.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
+ testICal, 5);
+ captureResult.update(ANDROID_LENS_DISTORTION,
+ identityDistortion, 5);
+
+ ASSERT_EQ(m.updateCalibration(captureResult), OK);
+
+}
+
+TEST(DistortionMapperTest, IdentityTransform) {
+ status_t res;
+
+ DistortionMapper m;
+ setupTestMapper(&m, identityDistortion);
+
+ auto coords = basicCoords;
+ res = m.mapCorrectedToRaw(coords.data(), 5);
+ ASSERT_EQ(res, OK);
+
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_EQ(coords[i], basicCoords[i]);
+ }
+
+ res = m.mapRawToCorrected(coords.data(), 5);
+ ASSERT_EQ(res, OK);
+
+ for (size_t i = 0; i < coords.size(); i++) {
+ EXPECT_EQ(coords[i], basicCoords[i]);
+ }
+
+ std::array<int32_t, 8> rects = {
+ 0, 0, 100, 100,
+ testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100
+ };
+
+ auto rectsOrig = rects;
+ res = m.mapCorrectedRectToRaw(rects.data(), 2);
+ ASSERT_EQ(res, OK);
+
+ for (size_t i = 0; i < rects.size(); i++) {
+ EXPECT_EQ(rects[i], rectsOrig[i]);
+ }
+
+ res = m.mapRawRectToCorrected(rects.data(), 2);
+ ASSERT_EQ(res, OK);
+
+ for (size_t i = 0; i < rects.size(); i++) {
+ EXPECT_EQ(rects[i], rectsOrig[i]);
+ }
+}
+
+TEST(DistortionMapperTest, LargeTransform) {
+ status_t res;
+ constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed
+ constexpr int bucketsPerPixel = 3; // Histogram granularity
+
+ unsigned int seed = 1234; // Ensure repeatability for debugging
+ const size_t coordCount = 1e6; // Number of random test points
+
+ float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
+
+ DistortionMapper m;
+ setupTestMapper(&m, bigDistortion);
+
+ std::default_random_engine gen(seed);
+
+ std::uniform_int_distribution<int> x_dist(0, testActiveArray[2] - 1);
+ std::uniform_int_distribution<int> y_dist(0, testActiveArray[3] - 1);
+
+ std::vector<int32_t> randCoords(coordCount * 2);
+
+ for (size_t i = 0; i < randCoords.size(); i += 2) {
+ randCoords[i] = x_dist(gen);
+ randCoords[i + 1] = y_dist(gen);
+ }
+
+ randCoords.insert(randCoords.end(), basicCoords.begin(), basicCoords.end());
+
+ auto origCoords = randCoords;
+
+ base::Timer correctedToRawTimer;
+ res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2);
+ auto correctedToRawDurationMs = correctedToRawTimer.duration();
+ EXPECT_EQ(res, OK);
+
+ base::Timer rawToCorrectedTimer;
+ res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2);
+ auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration();
+ EXPECT_EQ(res, OK);
+
+ float correctedToRawDurationPerCoordUs =
+ (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(
+ correctedToRawDurationMs) / (randCoords.size() / 2) ).count();
+ float rawToCorrectedDurationPerCoordUs =
+ (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(
+ rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count();
+
+ RecordProperty("CorrectedToRawDurationPerCoordUs",
+ base::StringPrintf("%f", correctedToRawDurationPerCoordUs));
+ RecordProperty("RawToCorrectedDurationPerCoordUs",
+ base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs));
+
+ // Calculate mapping errors after round trip
+ float totalErrorSq = 0;
+ // Basic histogram; buckets go from [N to N+1)
+ std::array<int, maxAllowedPixelError * bucketsPerPixel> histogram = {0};
+ int outOfHistogram = 0;
+
+ for (size_t i = 0; i < randCoords.size(); i += 2) {
+ int xOrig = origCoords[i];
+ int yOrig = origCoords[i + 1];
+ int xMapped = randCoords[i];
+ int yMapped = randCoords[i + 1];
+
+ float errorSq = (xMapped - xOrig) * (xMapped - xOrig) +
+ (yMapped - yOrig) * (yMapped - yOrig);
+
+ EXPECT_LE(errorSq, maxAllowedPixelError * maxAllowedPixelError) << "( " <<
+ xOrig << "," << yOrig << ") -> (" << xMapped << "," << yMapped << ")";
+
+ // Note: Integer coordinates, so histogram will be clumpy; error distances can only be of
+ // form sqrt(X^2+Y^2) where X, Y are integers, so:
+ // 0, 1, sqrt(2), 2, sqrt(5), sqrt(8), 3, sqrt(10), sqrt(13), 4 ...
+ totalErrorSq += errorSq;
+ float errorDist = std::sqrt(errorSq);
+ if (errorDist < maxAllowedPixelError) {
+ int histBucket = static_cast<int>(errorDist * bucketsPerPixel); // rounds down
+ histogram[histBucket]++;
+ } else {
+ outOfHistogram++;
+ }
+ }
+
+ float rmsError = std::sqrt(totalErrorSq / randCoords.size());
+ RecordProperty("RmsError", base::StringPrintf("%f", rmsError));
+ for (size_t i = 0; i < histogram.size(); i++) {
+ std::string label = base::StringPrintf("HistogramBin[%f,%f)",
+ (float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel);
+ RecordProperty(label, histogram[i]);
+ }
+ RecordProperty("HistogramOutOfRange", outOfHistogram);
+}
+
+// Compare against values calculated by OpenCV
+// undistortPoints() method, which is the same as mapRawToCorrected
+// See script DistortionMapperComp.py
+#include "DistortionMapperTest_OpenCvData.h"
+
+TEST(DistortionMapperTest, CompareToOpenCV) {
+ status_t res;
+
+ float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
+
+ // Expect to match within sqrt(2) radius pixels
+ const int32_t maxSqError = 2;
+
+ DistortionMapper m;
+ setupTestMapper(&m, bigDistortion);
+
+ using namespace openCvData;
+
+ res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2);
+
+ for (size_t i = 0; i < rawCoords.size(); i+=2) {
+ int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) +
+ (rawCoords[i + 1] - expCoords[i + 1]) * (rawCoords[i + 1] - expCoords[i + 1]);
+ EXPECT_LE(dist, maxSqError)
+ << "(" << rawCoords[i] << ", " << rawCoords[i + 1] << ") != ("
+ << expCoords[i] << ", " << expCoords[i + 1] << ")";
+ }
+}
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h b/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h
new file mode 100644
index 0000000..f996bd5
--- /dev/null
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest_OpenCvData.h
@@ -0,0 +1,2009 @@
+// Generated by DistortionMapperComp.py
+// for use by DistortionMapperTest.cpp
+
+namespace openCvData {
+std::array<int32_t, 2000> rawCoords = {
+ 191, 466,
+ 437, 589,
+ 779, 204,
+ 276, 601,
+ 958, 656,
+ 357, 375,
+ 683, 534,
+ 370, 420,
+ 503, 10,
+ 772, 661,
+ 364, 461,
+ 75, 276,
+ 933, 488,
+ 397, 591,
+ 316, 426,
+ 869, 327,
+ 802, 107,
+ 704, 528,
+ 218, 693,
+ 442, 681,
+ 59, 138,
+ 47, 506,
+ 594, 399,
+ 43, 421,
+ 329, 377,
+ 111, 455,
+ 565, 5,
+ 617, 684,
+ 790, 744,
+ 958, 593,
+ 285, 468,
+ 478, 146,
+ 382, 40,
+ 451, 736,
+ 123, 89,
+ 738, 440,
+ 471, 80,
+ 229, 674,
+ 416, 401,
+ 6, 225,
+ 436, 459,
+ 918, 469,
+ 705, 112,
+ 746, 623,
+ 633, 328,
+ 152, 426,
+ 528, 713,
+ 480, 376,
+ 536, 614,
+ 57, 502,
+ 767, 531,
+ 796, 418,
+ 965, 110,
+ 29, 445,
+ 114, 713,
+ 325, 145,
+ 457, 690,
+ 879, 189,
+ 348, 136,
+ 901, 529,
+ 726, 675,
+ 779, 449,
+ 291, 113,
+ 335, 493,
+ 73, 41,
+ 323, 442,
+ 853, 215,
+ 173, 100,
+ 994, 134,
+ 317, 426,
+ 9, 675,
+ 977, 417,
+ 84, 249,
+ 728, 106,
+ 552, 204,
+ 974, 500,
+ 255, 81,
+ 776, 586,
+ 761, 685,
+ 658, 426,
+ 201, 523,
+ 952, 667,
+ 993, 614,
+ 545, 338,
+ 890, 729,
+ 593, 274,
+ 323, 653,
+ 215, 551,
+ 365, 601,
+ 782, 526,
+ 622, 370,
+ 840, 534,
+ 443, 23,
+ 363, 548,
+ 475, 258,
+ 640, 94,
+ 171, 552,
+ 127, 277,
+ 604, 77,
+ 802, 709,
+ 979, 660,
+ 627, 697,
+ 724, 537,
+ 41, 329,
+ 282, 251,
+ 83, 570,
+ 509, 495,
+ 630, 278,
+ 446, 311,
+ 480, 737,
+ 373, 9,
+ 921, 654,
+ 351, 472,
+ 357, 159,
+ 223, 314,
+ 72, 488,
+ 755, 699,
+ 376, 222,
+ 371, 620,
+ 901, 320,
+ 2, 31,
+ 141, 448,
+ 106, 222,
+ 58, 465,
+ 22, 641,
+ 306, 569,
+ 563, 28,
+ 565, 494,
+ 183, 598,
+ 612, 416,
+ 629, 514,
+ 240, 590,
+ 856, 545,
+ 692, 354,
+ 856, 498,
+ 333, 388,
+ 394, 23,
+ 576, 637,
+ 951, 512,
+ 76, 307,
+ 41, 130,
+ 565, 129,
+ 566, 385,
+ 868, 540,
+ 903, 208,
+ 256, 524,
+ 901, 737,
+ 640, 247,
+ 606, 616,
+ 627, 88,
+ 285, 740,
+ 431, 430,
+ 527, 148,
+ 165, 375,
+ 990, 295,
+ 714, 131,
+ 70, 118,
+ 161, 212,
+ 536, 579,
+ 880, 478,
+ 852, 448,
+ 693, 402,
+ 889, 39,
+ 783, 108,
+ 58, 44,
+ 51, 386,
+ 404, 749,
+ 108, 245,
+ 997, 300,
+ 883, 426,
+ 953, 664,
+ 779, 23,
+ 962, 389,
+ 207, 656,
+ 224, 103,
+ 725, 730,
+ 535, 333,
+ 22, 454,
+ 964, 725,
+ 930, 138,
+ 622, 309,
+ 363, 27,
+ 868, 504,
+ 87, 665,
+ 782, 237,
+ 818, 380,
+ 21, 325,
+ 446, 179,
+ 830, 558,
+ 586, 369,
+ 487, 200,
+ 605, 565,
+ 270, 391,
+ 98, 535,
+ 884, 425,
+ 994, 134,
+ 12, 342,
+ 931, 634,
+ 473, 676,
+ 225, 228,
+ 714, 543,
+ 18, 214,
+ 580, 698,
+ 338, 90,
+ 516, 524,
+ 298, 646,
+ 905, 576,
+ 261, 703,
+ 938, 558,
+ 910, 177,
+ 494, 607,
+ 954, 478,
+ 910, 519,
+ 42, 625,
+ 369, 702,
+ 483, 93,
+ 964, 12,
+ 676, 105,
+ 155, 487,
+ 981, 521,
+ 761, 318,
+ 138, 162,
+ 764, 40,
+ 490, 135,
+ 630, 413,
+ 567, 613,
+ 938, 144,
+ 712, 523,
+ 258, 686,
+ 532, 418,
+ 322, 253,
+ 332, 734,
+ 203, 500,
+ 574, 38,
+ 542, 155,
+ 91, 652,
+ 27, 726,
+ 327, 307,
+ 135, 95,
+ 413, 463,
+ 132, 730,
+ 180, 570,
+ 482, 576,
+ 302, 11,
+ 463, 527,
+ 66, 501,
+ 345, 443,
+ 229, 200,
+ 932, 619,
+ 145, 485,
+ 883, 556,
+ 515, 101,
+ 39, 625,
+ 117, 392,
+ 873, 29,
+ 692, 357,
+ 169, 47,
+ 32, 181,
+ 112, 303,
+ 42, 694,
+ 935, 607,
+ 188, 440,
+ 903, 725,
+ 67, 238,
+ 696, 480,
+ 632, 621,
+ 713, 251,
+ 167, 573,
+ 359, 66,
+ 729, 660,
+ 41, 131,
+ 420, 255,
+ 44, 84,
+ 438, 53,
+ 816, 649,
+ 131, 144,
+ 437, 728,
+ 773, 98,
+ 927, 620,
+ 392, 105,
+ 52, 69,
+ 886, 126,
+ 362, 490,
+ 604, 296,
+ 374, 47,
+ 428, 539,
+ 768, 145,
+ 862, 21,
+ 902, 177,
+ 128, 238,
+ 848, 624,
+ 345, 179,
+ 535, 203,
+ 34, 470,
+ 520, 31,
+ 757, 741,
+ 801, 257,
+ 335, 263,
+ 442, 434,
+ 617, 132,
+ 864, 532,
+ 170, 641,
+ 19, 481,
+ 162, 193,
+ 342, 4,
+ 438, 597,
+ 675, 408,
+ 564, 10,
+ 608, 741,
+ 224, 440,
+ 835, 594,
+ 329, 267,
+ 960, 167,
+ 211, 115,
+ 495, 195,
+ 555, 54,
+ 378, 345,
+ 778, 540,
+ 231, 18,
+ 257, 307,
+ 8, 353,
+ 852, 692,
+ 231, 743,
+ 5, 251,
+ 789, 73,
+ 442, 285,
+ 832, 533,
+ 355, 18,
+ 693, 315,
+ 858, 431,
+ 940, 660,
+ 458, 12,
+ 68, 240,
+ 592, 457,
+ 512, 108,
+ 876, 553,
+ 373, 621,
+ 90, 48,
+ 505, 700,
+ 148, 427,
+ 59, 126,
+ 69, 679,
+ 447, 79,
+ 665, 376,
+ 409, 545,
+ 172, 288,
+ 267, 231,
+ 177, 361,
+ 629, 44,
+ 196, 209,
+ 707, 245,
+ 425, 528,
+ 159, 329,
+ 629, 693,
+ 356, 614,
+ 640, 536,
+ 738, 728,
+ 246, 31,
+ 247, 33,
+ 705, 626,
+ 934, 353,
+ 512, 197,
+ 98, 599,
+ 142, 604,
+ 879, 374,
+ 914, 309,
+ 200, 482,
+ 399, 460,
+ 437, 444,
+ 951, 414,
+ 903, 103,
+ 1, 459,
+ 541, 585,
+ 821, 715,
+ 609, 217,
+ 826, 282,
+ 609, 465,
+ 441, 149,
+ 443, 693,
+ 671, 61,
+ 572, 90,
+ 999, 748,
+ 694, 280,
+ 401, 693,
+ 244, 498,
+ 481, 26,
+ 110, 671,
+ 892, 686,
+ 307, 223,
+ 659, 446,
+ 984, 461,
+ 154, 623,
+ 815, 69,
+ 887, 12,
+ 863, 674,
+ 850, 489,
+ 328, 409,
+ 237, 653,
+ 176, 277,
+ 759, 229,
+ 616, 164,
+ 872, 485,
+ 473, 175,
+ 833, 73,
+ 205, 176,
+ 596, 471,
+ 982, 132,
+ 799, 116,
+ 360, 716,
+ 130, 204,
+ 809, 724,
+ 92, 437,
+ 405, 674,
+ 84, 135,
+ 50, 225,
+ 190, 6,
+ 127, 84,
+ 730, 179,
+ 901, 246,
+ 874, 177,
+ 378, 406,
+ 515, 310,
+ 388, 255,
+ 402, 342,
+ 382, 493,
+ 881, 429,
+ 428, 193,
+ 865, 129,
+ 579, 545,
+ 730, 302,
+ 117, 572,
+ 533, 541,
+ 597, 317,
+ 933, 745,
+ 547, 547,
+ 27, 647,
+ 998, 243,
+ 850, 458,
+ 441, 395,
+ 779, 188,
+ 462, 635,
+ 678, 275,
+ 137, 302,
+ 931, 504,
+ 419, 426,
+ 41, 746,
+ 619, 584,
+ 931, 256,
+ 811, 654,
+ 665, 441,
+ 893, 336,
+ 244, 610,
+ 697, 307,
+ 5, 715,
+ 605, 4,
+ 498, 448,
+ 300, 346,
+ 495, 439,
+ 869, 624,
+ 778, 411,
+ 613, 550,
+ 566, 581,
+ 986, 591,
+ 614, 118,
+ 476, 212,
+ 153, 582,
+ 58, 59,
+ 664, 392,
+ 446, 230,
+ 17, 220,
+ 267, 27,
+ 804, 250,
+ 891, 607,
+ 964, 718,
+ 591, 233,
+ 918, 37,
+ 225, 272,
+ 412, 708,
+ 579, 140,
+ 596, 700,
+ 134, 736,
+ 153, 615,
+ 677, 303,
+ 217, 580,
+ 847, 397,
+ 409, 13,
+ 148, 603,
+ 419, 254,
+ 297, 538,
+ 997, 413,
+ 889, 126,
+ 883, 527,
+ 422, 647,
+ 235, 422,
+ 26, 285,
+ 361, 68,
+ 45, 256,
+ 355, 746,
+ 944, 98,
+ 518, 357,
+ 401, 697,
+ 515, 607,
+ 881, 572,
+ 464, 55,
+ 470, 150,
+ 208, 133,
+ 354, 683,
+ 433, 133,
+ 752, 37,
+ 82, 28,
+ 465, 452,
+ 181, 389,
+ 710, 693,
+ 529, 728,
+ 547, 4,
+ 472, 391,
+ 152, 490,
+ 130, 340,
+ 982, 99,
+ 60, 50,
+ 96, 614,
+ 629, 587,
+ 77, 728,
+ 882, 472,
+ 929, 298,
+ 488, 514,
+ 281, 507,
+ 495, 593,
+ 218, 559,
+ 454, 306,
+ 922, 113,
+ 130, 286,
+ 541, 708,
+ 323, 73,
+ 947, 642,
+ 26, 88,
+ 829, 103,
+ 569, 358,
+ 306, 42,
+ 936, 678,
+ 722, 490,
+ 392, 730,
+ 711, 369,
+ 326, 86,
+ 972, 205,
+ 187, 161,
+ 760, 708,
+ 501, 496,
+ 347, 96,
+ 681, 293,
+ 26, 375,
+ 528, 167,
+ 1, 334,
+ 505, 60,
+ 822, 180,
+ 9, 168,
+ 84, 619,
+ 714, 183,
+ 63, 320,
+ 706, 538,
+ 193, 555,
+ 956, 386,
+ 430, 17,
+ 507, 514,
+ 138, 504,
+ 39, 323,
+ 854, 316,
+ 88, 42,
+ 103, 363,
+ 674, 68,
+ 832, 582,
+ 61, 241,
+ 377, 376,
+ 449, 350,
+ 104, 280,
+ 21, 336,
+ 893, 581,
+ 249, 548,
+ 315, 372,
+ 50, 436,
+ 282, 220,
+ 126, 669,
+ 451, 488,
+ 809, 212,
+ 273, 289,
+ 421, 699,
+ 867, 333,
+ 29, 80,
+ 196, 178,
+ 824, 672,
+ 27, 429,
+ 805, 315,
+ 525, 214,
+ 658, 67,
+ 822, 605,
+ 191, 478,
+ 832, 352,
+ 580, 81,
+ 462, 664,
+ 464, 349,
+ 196, 29,
+ 615, 423,
+ 108, 556,
+ 183, 261,
+ 480, 482,
+ 621, 570,
+ 286, 369,
+ 681, 382,
+ 768, 224,
+ 546, 183,
+ 443, 607,
+ 103, 172,
+ 791, 424,
+ 827, 731,
+ 965, 712,
+ 551, 69,
+ 740, 423,
+ 745, 341,
+ 155, 746,
+ 889, 602,
+ 411, 159,
+ 294, 467,
+ 248, 599,
+ 18, 360,
+ 734, 512,
+ 421, 519,
+ 367, 174,
+ 785, 545,
+ 706, 23,
+ 239, 278,
+ 581, 65,
+ 232, 609,
+ 752, 603,
+ 294, 585,
+ 224, 217,
+ 848, 558,
+ 332, 425,
+ 699, 68,
+ 53, 647,
+ 629, 652,
+ 87, 649,
+ 41, 718,
+ 227, 563,
+ 400, 302,
+ 253, 380,
+ 184, 42,
+ 366, 539,
+ 474, 691,
+ 170, 538,
+ 869, 96,
+ 974, 565,
+ 916, 28,
+ 285, 617,
+ 274, 38,
+ 147, 12,
+ 782, 261,
+ 749, 41,
+ 78, 592,
+ 370, 83,
+ 405, 488,
+ 436, 151,
+ 443, 556,
+ 96, 383,
+ 843, 745,
+ 630, 214,
+ 126, 10,
+ 338, 363,
+ 546, 27,
+ 61, 17,
+ 507, 199,
+ 445, 730,
+ 797, 213,
+ 555, 148,
+ 790, 65,
+ 837, 180,
+ 434, 320,
+ 102, 681,
+ 149, 680,
+ 10, 130,
+ 839, 232,
+ 848, 683,
+ 899, 650,
+ 837, 190,
+ 843, 463,
+ 984, 457,
+ 651, 490,
+ 552, 139,
+ 980, 71,
+ 748, 393,
+ 290, 171,
+ 503, 698,
+ 574, 742,
+ 429, 312,
+ 627, 680,
+ 69, 412,
+ 154, 538,
+ 135, 3,
+ 537, 12,
+ 535, 34,
+ 153, 632,
+ 797, 227,
+ 398, 336,
+ 20, 463,
+ 804, 175,
+ 400, 369,
+ 501, 250,
+ 105, 480,
+ 151, 146,
+ 57, 686,
+ 830, 119,
+ 867, 380,
+ 128, 84,
+ 222, 667,
+ 450, 522,
+ 390, 466,
+ 716, 375,
+ 760, 624,
+ 559, 407,
+ 587, 18,
+ 989, 53,
+ 817, 102,
+ 153, 269,
+ 253, 164,
+ 563, 360,
+ 93, 385,
+ 197, 360,
+ 277, 7,
+ 887, 280,
+ 416, 658,
+ 760, 411,
+ 902, 690,
+ 465, 424,
+ 28, 105,
+ 399, 620,
+ 455, 520,
+ 637, 491,
+ 769, 0,
+ 300, 521,
+ 90, 392,
+ 894, 722,
+ 705, 573,
+ 344, 188,
+ 667, 111,
+ 470, 16,
+ 759, 154,
+ 840, 581,
+ 176, 663,
+ 93, 151,
+ 372, 130,
+ 345, 425,
+ 156, 581,
+ 33, 8,
+ 320, 395,
+ 629, 661,
+ 641, 17,
+ 695, 663,
+ 751, 197,
+ 507, 93,
+ 608, 519,
+ 77, 303,
+ 513, 605,
+ 98, 354,
+ 567, 401,
+ 184, 440,
+ 785, 748,
+ 52, 32,
+ 528, 452,
+ 82, 532,
+ 116, 147,
+ 779, 341,
+ 308, 275,
+ 763, 135,
+ 137, 375,
+ 14, 260,
+ 337, 378,
+ 492, 262,
+ 202, 119,
+ 561, 334,
+ 855, 683,
+ 876, 724,
+ 202, 544,
+ 571, 437,
+ 456, 436,
+ 67, 4,
+ 468, 592,
+ 922, 540,
+ 125, 539,
+ 615, 290,
+ 785, 76,
+ 402, 556,
+ 12, 696,
+ 460, 52,
+ 909, 92,
+ 894, 153,
+ 931, 373,
+ 360, 120,
+ 726, 626,
+ 318, 733,
+ 472, 424,
+ 146, 74,
+ 86, 564,
+ 742, 236,
+ 845, 400,
+ 832, 139,
+ 275, 437,
+ 929, 42,
+ 818, 123,
+ 439, 274,
+ 65, 590,
+ 512, 132,
+ 520, 443,
+ 444, 107,
+ 961, 313,
+ 130, 488,
+ 587, 191,
+ 287, 603,
+ 56, 208,
+ 936, 628,
+ 908, 445,
+ 773, 258,
+ 383, 283,
+ 425, 530,
+ 244, 133,
+ 216, 543,
+ 631, 595,
+ 785, 108,
+ 87, 192,
+ 640, 427,
+ 889, 688,
+ 152, 89,
+ 10, 209,
+ 122, 343,
+ 188, 5,
+ 896, 748,
+ 806, 22,
+ 535, 457,
+ 851, 307,
+ 261, 566,
+ 791, 590,
+ 947, 300,
+ 658, 394,
+ 418, 305,
+ 371, 632,
+ 470, 438,
+ 165, 410,
+ 538, 380,
+ 643, 408,
+ 318, 591,
+ 564, 311,
+ 327, 690,
+ 930, 8,
+ 93, 100,
+ 627, 196,
+ 582, 416,
+ 200, 492,
+ 943, 267,
+ 31, 355,
+ 67, 374,
+ 692, 57,
+ 229, 373,
+ 542, 371,
+ 801, 230,
+ 114, 420,
+ 769, 326,
+ 83, 448,
+ 846, 137,
+ 912, 77,
+ 126, 3,
+ 784, 420,
+ 660, 391,
+ 795, 188,
+ 530, 42,
+ 137, 106,
+ 663, 80,
+ 757, 340,
+ 694, 267,
+ 768, 612,
+ 926, 155,
+ 600, 25,
+ 292, 31,
+ 97, 225,
+ 60, 437,
+ 724, 563,
+ 698, 85,
+ 286, 196,
+ 66, 1,
+ 269, 25,
+ 467, 405,
+ 204, 171,
+ 653, 14,
+ 299, 360,
+ 521, 719,
+ 760, 602,
+ 329, 282,
+ 687, 530,
+ 110, 200,
+ 30, 300,
+ 6, 501,
+ 868, 281,
+ 281, 76,
+ 805, 363,
+ 876, 114,
+ 219, 549,
+ 65, 611,
+ 859, 23,
+ 66, 354,
+ 205, 169,
+ 434, 174,
+ 828, 668,
+ 814, 720,
+ 663, 34,
+ 875, 707,
+ 969, 561,
+ 932, 66,
+ 834, 548,
+ 961, 86,
+ 263, 148,
+ 145, 202,
+ 83, 146,
+ 947, 727,
+ 3, 138,
+ 927, 514,
+ 814, 742,
+ 80, 430,
+ 866, 184,
+ 593, 731,
+ 193, 219,
+ 496, 490,
+ 606, 530,
+ 314, 334,
+ 301, 327,
+ 50, 715,
+ 178, 57,
+ 936, 626,
+ 972, 617,
+ 33, 427,
+ 147, 435,
+ 83, 341,
+ 859, 244,
+ 337, 688,
+ 637, 124,
+ 874, 71,
+ 590, 474,
+ 332, 120,
+ 640, 290,
+ 816, 171,
+ 665, 431,
+ 79, 31,
+ 857, 110,
+ 103, 79,
+ 293, 397,
+ 866, 651,
+ 356, 73,
+ 438, 710,
+ 41, 233,
+ 782, 596,
+ 852, 407,
+ 590, 104,
+ 34, 116,
+ 756, 276,
+ 282, 181,
+ 871, 275,
+ 888, 712,
+ 872, 279,
+ 645, 324,
+ 730, 524,
+ 430, 302,
+ 601, 486,
+ 114, 529,
+ 359, 317,
+ 313, 426,
+ 33, 732,
+ 970, 211,
+ 657, 582,
+ 945, 501,
+ 450, 630,
+ 822, 697,
+ 702, 600,
+ 958, 289,
+ 732, 96,
+ 205, 662,
+ 695, 533,
+ 369, 433,
+ 83, 445,
+ 176, 315,
+ 239, 95,
+ 895, 682,
+ 628, 118,
+ 730, 741,
+ 779, 734,
+ 804, 314,
+ 465, 567,
+ 810, 106,
+ 81, 268,
+ 968, 518,
+ 22, 159,
+ 726, 504,
+ 38, 269,
+ 751, 649,
+ 954, 659,
+};
+std::array<int32_t, 2000> expCoords = {
+ 190, 464,
+ 437, 588,
+ 774, 203,
+ 276, 599,
+ 939, 646,
+ 356, 373,
+ 681, 533,
+ 369, 419,
+ 500, 7,
+ 765, 655,
+ 363, 460,
+ 75, 272,
+ 920, 484,
+ 397, 590,
+ 315, 424,
+ 861, 326,
+ 795, 107,
+ 701, 526,
+ 220, 688,
+ 442, 678,
+ 59, 134,
+ 50, 501,
+ 593, 398,
+ 44, 417,
+ 327, 375,
+ 111, 452,
+ 562, 3,
+ 614, 680,
+ 780, 734,
+ 941, 586,
+ 284, 467,
+ 476, 142,
+ 379, 36,
+ 451, 731,
+ 122, 85,
+ 735, 439,
+ 469, 76,
+ 231, 670,
+ 415, 400,
+ 8, 221,
+ 435, 458,
+ 906, 466,
+ 701, 111,
+ 741, 619,
+ 632, 327,
+ 151, 423,
+ 527, 709,
+ 479, 375,
+ 535, 612,
+ 59, 498,
+ 762, 529,
+ 791, 417,
+ 948, 113,
+ 31, 441,
+ 119, 705,
+ 323, 141,
+ 457, 687,
+ 869, 189,
+ 346, 132,
+ 890, 525,
+ 721, 670,
+ 775, 448,
+ 288, 108,
+ 334, 492,
+ 74, 38,
+ 322, 441,
+ 845, 215,
+ 171, 96,
+ 975, 137,
+ 316, 425,
+ 17, 665,
+ 961, 414,
+ 83, 245,
+ 723, 105,
+ 551, 201,
+ 958, 495,
+ 253, 77,
+ 770, 583,
+ 754, 679,
+ 657, 425,
+ 201, 521,
+ 934, 657,
+ 973, 605,
+ 544, 336,
+ 875, 717,
+ 592, 272,
+ 323, 650,
+ 215, 549,
+ 365, 600,
+ 777, 524,
+ 621, 369,
+ 832, 531,
+ 440, 19,
+ 362, 547,
+ 474, 255,
+ 637, 92,
+ 171, 549,
+ 126, 273,
+ 601, 74,
+ 792, 701,
+ 959, 649,
+ 624, 692,
+ 721, 535,
+ 42, 325,
+ 280, 247,
+ 86, 565,
+ 508, 494,
+ 629, 276,
+ 445, 309,
+ 479, 732,
+ 370, 5,
+ 905, 645,
+ 350, 471,
+ 355, 155,
+ 221, 311,
+ 73, 484,
+ 748, 692,
+ 374, 218,
+ 371, 618,
+ 891, 319,
+ 6, 29,
+ 141, 445,
+ 105, 218,
+ 60, 461,
+ 28, 633,
+ 306, 567,
+ 560, 25,
+ 564, 493,
+ 184, 595,
+ 611, 415,
+ 628, 513,
+ 240, 587,
+ 847, 541,
+ 690, 353,
+ 848, 495,
+ 331, 386,
+ 391, 19,
+ 575, 635,
+ 936, 507,
+ 76, 303,
+ 42, 126,
+ 563, 126,
+ 565, 384,
+ 858, 536,
+ 892, 209,
+ 255, 522,
+ 884, 724,
+ 639, 245,
+ 604, 614,
+ 624, 86,
+ 287, 734,
+ 430, 429,
+ 525, 145,
+ 164, 372,
+ 974, 295,
+ 710, 130,
+ 70, 114,
+ 159, 208,
+ 535, 578,
+ 871, 475,
+ 844, 446,
+ 691, 401,
+ 876, 43,
+ 777, 108,
+ 59, 41,
+ 52, 382,
+ 404, 744,
+ 107, 241,
+ 980, 300,
+ 874, 424,
+ 935, 654,
+ 771, 24,
+ 948, 387,
+ 209, 652,
+ 222, 99,
+ 718, 722,
+ 534, 331,
+ 25, 449,
+ 943, 711,
+ 916, 140,
+ 621, 307,
+ 360, 23,
+ 859, 501,
+ 92, 658,
+ 777, 236,
+ 812, 379,
+ 22, 321,
+ 444, 175,
+ 822, 554,
+ 585, 368,
+ 486, 197,
+ 604, 564,
+ 268, 389,
+ 100, 531,
+ 875, 423,
+ 975, 137,
+ 14, 338,
+ 915, 626,
+ 472, 673,
+ 223, 224,
+ 711, 541,
+ 19, 210,
+ 578, 694,
+ 336, 85,
+ 515, 523,
+ 298, 643,
+ 892, 570,
+ 263, 698,
+ 923, 552,
+ 898, 178,
+ 493, 606,
+ 940, 474,
+ 898, 515,
+ 47, 618,
+ 369, 698,
+ 481, 89,
+ 945, 18,
+ 673, 103,
+ 155, 484,
+ 964, 516,
+ 757, 317,
+ 136, 158,
+ 757, 41,
+ 488, 131,
+ 629, 412,
+ 566, 611,
+ 924, 146,
+ 709, 521,
+ 259, 682,
+ 531, 417,
+ 320, 250,
+ 333, 729,
+ 202, 498,
+ 571, 35,
+ 540, 152,
+ 95, 645,
+ 36, 715,
+ 325, 304,
+ 134, 91,
+ 412, 462,
+ 137, 722,
+ 181, 567,
+ 481, 575,
+ 300, 7,
+ 462, 526,
+ 68, 497,
+ 344, 442,
+ 227, 196,
+ 917, 611,
+ 145, 482,
+ 872, 551,
+ 513, 97,
+ 44, 618,
+ 116, 389,
+ 861, 32,
+ 690, 356,
+ 168, 43,
+ 33, 177,
+ 111, 299,
+ 49, 685,
+ 920, 600,
+ 187, 438,
+ 887, 713,
+ 67, 234,
+ 694, 479,
+ 630, 619,
+ 710, 250,
+ 168, 570,
+ 356, 62,
+ 724, 655,
+ 42, 127,
+ 418, 252,
+ 45, 81,
+ 436, 49,
+ 807, 643,
+ 130, 140,
+ 437, 723,
+ 767, 98,
+ 912, 612,
+ 390, 101,
+ 53, 66,
+ 875, 128,
+ 361, 489,
+ 603, 294,
+ 371, 43,
+ 427, 538,
+ 763, 144,
+ 850, 24,
+ 891, 178,
+ 126, 234,
+ 838, 618,
+ 343, 175,
+ 534, 200,
+ 36, 466,
+ 517, 28,
+ 749, 732,
+ 796, 256,
+ 333, 260,
+ 441, 433,
+ 615, 129,
+ 855, 528,
+ 172, 636,
+ 22, 476,
+ 160, 189,
+ 339, 0,
+ 438, 596,
+ 673, 407,
+ 561, 8,
+ 605, 735,
+ 223, 438,
+ 826, 589,
+ 327, 264,
+ 945, 169,
+ 209, 111,
+ 494, 192,
+ 552, 51,
+ 377, 343,
+ 773, 537,
+ 229, 14,
+ 255, 304,
+ 10, 349,
+ 840, 683,
+ 234, 736,
+ 7, 247,
+ 782, 73,
+ 441, 282,
+ 824, 530,
+ 352, 14,
+ 691, 314,
+ 850, 429,
+ 923, 650,
+ 455, 8,
+ 68, 236,
+ 591, 456,
+ 510, 104,
+ 866, 549,
+ 373, 619,
+ 90, 45,
+ 504, 696,
+ 147, 424,
+ 59, 122,
+ 75, 671,
+ 445, 75,
+ 664, 375,
+ 408, 544,
+ 170, 284,
+ 265, 227,
+ 175, 358,
+ 625, 42,
+ 194, 205,
+ 704, 244,
+ 424, 527,
+ 157, 326,
+ 626, 689,
+ 356, 612,
+ 638, 535,
+ 731, 720,
+ 244, 27,
+ 245, 29,
+ 701, 623,
+ 922, 352,
+ 511, 194,
+ 101, 594,
+ 144, 600,
+ 870, 373,
+ 903, 308,
+ 199, 480,
+ 398, 459,
+ 436, 443,
+ 938, 412,
+ 891, 105,
+ 4, 454,
+ 540, 584,
+ 810, 706,
+ 608, 215,
+ 820, 281,
+ 608, 464,
+ 439, 145,
+ 443, 690,
+ 667, 60,
+ 570, 87,
+ 974, 731,
+ 692, 279,
+ 401, 690,
+ 243, 496,
+ 478, 22,
+ 114, 664,
+ 878, 676,
+ 305, 219,
+ 658, 445,
+ 967, 457,
+ 156, 619,
+ 807, 70,
+ 873, 16,
+ 851, 666,
+ 842, 486,
+ 327, 407,
+ 238, 649,
+ 174, 273,
+ 755, 228,
+ 614, 161,
+ 863, 482,
+ 471, 171,
+ 824, 74,
+ 203, 172,
+ 595, 470,
+ 964, 135,
+ 792, 116,
+ 361, 712,
+ 128, 200,
+ 799, 715,
+ 92, 434,
+ 405, 671,
+ 84, 131,
+ 50, 221,
+ 189, 3,
+ 126, 80,
+ 726, 178,
+ 891, 246,
+ 865, 178,
+ 377, 405,
+ 514, 308,
+ 386, 252,
+ 401, 340,
+ 381, 492,
+ 872, 427,
+ 426, 189,
+ 855, 130,
+ 578, 544,
+ 727, 301,
+ 119, 568,
+ 532, 540,
+ 596, 315,
+ 914, 731,
+ 546, 546,
+ 33, 639,
+ 981, 244,
+ 842, 456,
+ 440, 394,
+ 774, 187,
+ 462, 633,
+ 676, 273,
+ 135, 298,
+ 918, 500,
+ 418, 425,
+ 50, 734,
+ 617, 582,
+ 919, 256,
+ 802, 648,
+ 664, 440,
+ 884, 335,
+ 244, 607,
+ 695, 306,
+ 14, 704,
+ 601, 2,
+ 497, 447,
+ 298, 344,
+ 494, 438,
+ 858, 618,
+ 774, 410,
+ 612, 549,
+ 565, 580,
+ 967, 583,
+ 612, 115,
+ 475, 209,
+ 154, 578,
+ 59, 56,
+ 663, 391,
+ 444, 227,
+ 18, 216,
+ 265, 23,
+ 799, 249,
+ 879, 601,
+ 943, 704,
+ 590, 231,
+ 903, 41,
+ 223, 268,
+ 412, 704,
+ 577, 137,
+ 594, 696,
+ 139, 727,
+ 155, 611,
+ 675, 302,
+ 217, 577,
+ 840, 396,
+ 406, 9,
+ 150, 599,
+ 417, 251,
+ 296, 537,
+ 980, 410,
+ 878, 128,
+ 873, 523,
+ 422, 645,
+ 234, 420,
+ 27, 281,
+ 358, 64,
+ 45, 252,
+ 356, 740,
+ 929, 101,
+ 517, 356,
+ 401, 694,
+ 514, 606,
+ 870, 567,
+ 462, 51,
+ 468, 146,
+ 206, 129,
+ 354, 680,
+ 431, 129,
+ 745, 37,
+ 83, 25,
+ 464, 451,
+ 180, 386,
+ 705, 687,
+ 528, 723,
+ 544, 1,
+ 471, 390,
+ 152, 487,
+ 129, 337,
+ 964, 103,
+ 61, 47,
+ 99, 609,
+ 627, 585,
+ 84, 718,
+ 873, 469,
+ 917, 298,
+ 488, 513,
+ 280, 506,
+ 494, 592,
+ 218, 557,
+ 453, 304,
+ 908, 115,
+ 129, 282,
+ 540, 704,
+ 320, 69,
+ 930, 633,
+ 28, 85,
+ 821, 104,
+ 568, 357,
+ 304, 38,
+ 919, 667,
+ 719, 489,
+ 392, 725,
+ 709, 368,
+ 323, 81,
+ 956, 206,
+ 185, 157,
+ 753, 701,
+ 500, 495,
+ 345, 92,
+ 679, 292,
+ 27, 371,
+ 526, 164,
+ 3, 330,
+ 503, 56,
+ 815, 180,
+ 11, 164,
+ 88, 613,
+ 711, 182,
+ 63, 316,
+ 703, 536,
+ 193, 552,
+ 942, 384,
+ 427, 13,
+ 506, 513,
+ 138, 501,
+ 40, 319,
+ 847, 315,
+ 88, 39,
+ 102, 360,
+ 670, 66,
+ 824, 578,
+ 61, 237,
+ 376, 374,
+ 448, 348,
+ 103, 276,
+ 22, 332,
+ 881, 576,
+ 249, 546,
+ 313, 370,
+ 51, 432,
+ 280, 216,
+ 130, 663,
+ 450, 487,
+ 803, 211,
+ 271, 286,
+ 421, 696,
+ 859, 332,
+ 31, 77,
+ 194, 174,
+ 814, 665,
+ 29, 425,
+ 800, 314,
+ 524, 211,
+ 654, 65,
+ 814, 600,
+ 190, 476,
+ 826, 351,
+ 577, 78,
+ 462, 662,
+ 463, 347,
+ 194, 25,
+ 614, 422,
+ 110, 552,
+ 181, 257,
+ 479, 481,
+ 619, 569,
+ 284, 367,
+ 679, 381,
+ 764, 223,
+ 545, 180,
+ 443, 606,
+ 102, 168,
+ 786, 423,
+ 816, 721,
+ 944, 699,
+ 548, 66,
+ 737, 422,
+ 742, 340,
+ 160, 737,
+ 877, 596,
+ 409, 155,
+ 293, 466,
+ 248, 596,
+ 20, 356,
+ 731, 510,
+ 420, 518,
+ 365, 170,
+ 779, 542,
+ 700, 23,
+ 237, 275,
+ 578, 62,
+ 233, 606,
+ 747, 599,
+ 294, 583,
+ 222, 213,
+ 839, 554,
+ 331, 424,
+ 694, 67,
+ 58, 640,
+ 627, 649,
+ 91, 642,
+ 49, 708,
+ 227, 561,
+ 398, 299,
+ 251, 378,
+ 182, 38,
+ 365, 538,
+ 473, 688,
+ 170, 535,
+ 858, 98,
+ 956, 558,
+ 901, 33,
+ 285, 615,
+ 272, 34,
+ 146, 9,
+ 778, 260,
+ 743, 41,
+ 81, 587,
+ 368, 79,
+ 404, 487,
+ 434, 147,
+ 443, 555,
+ 96, 380,
+ 830, 734,
+ 628, 212,
+ 126, 7,
+ 336, 361,
+ 543, 24,
+ 62, 15,
+ 506, 196,
+ 445, 725,
+ 792, 212,
+ 553, 145,
+ 783, 66,
+ 829, 180,
+ 433, 318,
+ 107, 674,
+ 152, 674,
+ 12, 127,
+ 832, 232,
+ 837, 675,
+ 885, 642,
+ 830, 190,
+ 836, 461,
+ 967, 453,
+ 650, 489,
+ 550, 136,
+ 961, 76,
+ 745, 392,
+ 288, 167,
+ 502, 694,
+ 572, 736,
+ 428, 310,
+ 624, 676,
+ 70, 408,
+ 155, 535,
+ 135, 0,
+ 534, 9,
+ 532, 31,
+ 155, 627,
+ 792, 226,
+ 397, 334,
+ 23, 458,
+ 798, 175,
+ 399, 367,
+ 500, 247,
+ 106, 477,
+ 149, 142,
+ 63, 677,
+ 822, 120,
+ 859, 379,
+ 127, 80,
+ 224, 663,
+ 449, 521,
+ 389, 465,
+ 714, 374,
+ 754, 620,
+ 558, 406,
+ 584, 16,
+ 969, 59,
+ 809, 103,
+ 151, 265,
+ 251, 160,
+ 562, 359,
+ 93, 382,
+ 195, 357,
+ 275, 3,
+ 878, 280,
+ 416, 656,
+ 756, 410,
+ 887, 680,
+ 464, 423,
+ 30, 102,
+ 399, 618,
+ 454, 519,
+ 636, 490,
+ 761, 2,
+ 299, 520,
+ 90, 388,
+ 878, 710,
+ 702, 571,
+ 342, 184,
+ 664, 109,
+ 467, 12,
+ 754, 153,
+ 831, 577,
+ 178, 658,
+ 92, 147,
+ 370, 126,
+ 344, 424,
+ 157, 577,
+ 36, 7,
+ 318, 393,
+ 626, 658,
+ 637, 16,
+ 691, 659,
+ 747, 196,
+ 505, 89,
+ 607, 518,
+ 77, 299,
+ 512, 604,
+ 97, 350,
+ 566, 400,
+ 183, 438,
+ 775, 738,
+ 54, 30,
+ 527, 451,
+ 84, 528,
+ 115, 143,
+ 775, 340,
+ 306, 272,
+ 758, 134,
+ 136, 372,
+ 15, 256,
+ 335, 376,
+ 491, 259,
+ 200, 115,
+ 560, 332,
+ 843, 675,
+ 862, 713,
+ 202, 541,
+ 570, 436,
+ 455, 435,
+ 68, 2,
+ 468, 591,
+ 909, 535,
+ 126, 535,
+ 614, 288,
+ 778, 76,
+ 402, 555,
+ 20, 686,
+ 458, 48,
+ 896, 95,
+ 883, 154,
+ 919, 372,
+ 358, 116,
+ 721, 622,
+ 319, 728,
+ 471, 423,
+ 145, 70,
+ 88, 559,
+ 739, 235,
+ 838, 399,
+ 824, 139,
+ 274, 435,
+ 913, 46,
+ 810, 123,
+ 438, 271,
+ 69, 584,
+ 510, 128,
+ 519, 442,
+ 442, 103,
+ 947, 312,
+ 130, 485,
+ 585, 188,
+ 287, 601,
+ 56, 204,
+ 920, 620,
+ 897, 443,
+ 769, 257,
+ 381, 280,
+ 424, 529,
+ 242, 129,
+ 216, 541,
+ 629, 593,
+ 779, 108,
+ 86, 188,
+ 639, 426,
+ 875, 678,
+ 151, 85,
+ 12, 205,
+ 121, 340,
+ 187, 2,
+ 879, 735,
+ 797, 24,
+ 534, 456,
+ 844, 306,
+ 261, 564,
+ 784, 586,
+ 934, 300,
+ 657, 393,
+ 417, 303,
+ 371, 630,
+ 469, 437,
+ 164, 407,
+ 537, 379,
+ 642, 407,
+ 318, 589,
+ 563, 309,
+ 328, 686,
+ 913, 14,
+ 93, 96,
+ 625, 194,
+ 581, 415,
+ 199, 490,
+ 930, 267,
+ 32, 351,
+ 67, 370,
+ 687, 56,
+ 227, 370,
+ 541, 370,
+ 796, 229,
+ 114, 417,
+ 765, 325,
+ 84, 444,
+ 837, 138,
+ 898, 80,
+ 126, 0,
+ 780, 419,
+ 659, 390,
+ 789, 187,
+ 527, 39,
+ 136, 102,
+ 659, 78,
+ 754, 339,
+ 692, 266,
+ 762, 608,
+ 913, 157,
+ 597, 23,
+ 290, 27,
+ 96, 221,
+ 61, 433,
+ 720, 561,
+ 694, 84,
+ 284, 192,
+ 68, 0,
+ 267, 21,
+ 466, 404,
+ 202, 167,
+ 649, 13,
+ 297, 358,
+ 520, 715,
+ 755, 598,
+ 327, 279,
+ 685, 529,
+ 109, 196,
+ 31, 296,
+ 10, 496,
+ 860, 280,
+ 279, 72,
+ 800, 362,
+ 865, 116,
+ 219, 547,
+ 69, 605,
+ 847, 26,
+ 66, 350,
+ 203, 165,
+ 432, 170,
+ 818, 661,
+ 804, 711,
+ 659, 33,
+ 861, 697,
+ 952, 555,
+ 917, 70,
+ 826, 544,
+ 944, 90,
+ 260, 144,
+ 143, 198,
+ 83, 142,
+ 927, 713,
+ 5, 135,
+ 914, 510,
+ 803, 732,
+ 81, 426,
+ 857, 184,
+ 590, 726,
+ 191, 215,
+ 495, 489,
+ 605, 529,
+ 312, 332,
+ 299, 324,
+ 57, 705,
+ 176, 53,
+ 920, 618,
+ 953, 608,
+ 35, 423,
+ 146, 432,
+ 83, 337,
+ 851, 244,
+ 338, 684,
+ 634, 122,
+ 863, 73,
+ 589, 473,
+ 330, 116,
+ 639, 288,
+ 809, 171,
+ 664, 430,
+ 80, 28,
+ 847, 111,
+ 103, 75,
+ 291, 395,
+ 854, 644,
+ 353, 69,
+ 438, 706,
+ 41, 229,
+ 776, 592,
+ 845, 405,
+ 588, 101,
+ 35, 113,
+ 752, 275,
+ 280, 177,
+ 863, 275,
+ 873, 701,
+ 864, 279,
+ 644, 323,
+ 727, 522,
+ 429, 300,
+ 600, 485,
+ 115, 525,
+ 357, 315,
+ 312, 424,
+ 42, 721,
+ 955, 212,
+ 655, 580,
+ 931, 497,
+ 450, 628,
+ 812, 689,
+ 698, 597,
+ 944, 289,
+ 727, 95,
+ 207, 657,
+ 692, 531,
+ 368, 432,
+ 84, 441,
+ 174, 312,
+ 237, 91,
+ 881, 672,
+ 625, 116,
+ 723, 733,
+ 770, 725,
+ 799, 313,
+ 465, 566,
+ 803, 106,
+ 80, 264,
+ 952, 513,
+ 23, 155,
+ 723, 502,
+ 39, 265,
+ 745, 644,
+ 936, 649,
+};
+} // namespace openCvData
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index d505cfe..7c6527f 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -44,7 +44,7 @@
include $(BUILD_EXECUTABLE)
# service seccomp filter
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86))
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86 x86_64))
include $(CLEAR_VARS)
LOCAL_MODULE := mediaextractor.policy
LOCAL_MODULE_CLASS := ETC
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
new file mode 100755
index 0000000..63c7780
--- /dev/null
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
@@ -0,0 +1,57 @@
+# Organized by frequency of systemcall - in descending order for
+# best performance.
+ioctl: 1
+futex: 1
+prctl: 1
+write: 1
+getpriority: 1
+close: 1
+dup: 1
+munmap: 1
+mmap: 1
+madvise: 1
+openat: 1
+clock_gettime: 1
+writev: 1
+brk: 1
+mprotect: 1
+read: 1
+lseek: 1
+clone: 1
+getuid: 1
+setpriority: 1
+sigaltstack: 1
+newfstatat: 1
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+faccessat: 1
+sched_setscheduler: 1
+getrlimit: 1
+nanosleep: 1
+
+# for FileSource
+readlinkat: 1
+
+# for attaching to debuggerd on process crash
+tgkill: 1
+socket: arg0 == 1
+connect: 1
+fcntl: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
+getdents64: 1
+pipe2: 1
+ppoll: 1
+
+# Required by AddressSanitizer
+gettid: 1
+sched_yield: 1
+getpid: 1
+gettid: 1