/* * Copyright (C) 2019 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 "DepthProcessorTest" #include #include #include #include #include "../common/DepthPhotoProcessor.h" #include "../utils/ExifUtils.h" #include "NV12Compressor.h" using namespace android; using namespace android::camera3; static const size_t kTestBufferWidth = 640; static const size_t kTestBufferHeight = 480; static const size_t kTestBufferNV12Size ((((kTestBufferWidth) * (kTestBufferHeight)) * 3) / 2); static const size_t kTestBufferDepthSize (kTestBufferWidth * kTestBufferHeight); static const size_t kSeed = 1234; void linkToDepthPhotoLibrary(void **libHandle /*out*/, process_depth_photo_frame *processFrameFunc /*out*/) { ASSERT_NE(libHandle, nullptr); ASSERT_NE(processFrameFunc, nullptr); *libHandle = dlopen(kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL); if (*libHandle != nullptr) { *processFrameFunc = reinterpret_cast ( dlsym(*libHandle, kDepthPhotoProcessFunction)); ASSERT_NE(*processFrameFunc, nullptr); } } void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif, bool switchDimensions, std::vector *colorJpegBuffer /*out*/) { ASSERT_NE(colorJpegBuffer, nullptr); std::array colorSourceBuffer; std::default_random_engine gen(kSeed); std::uniform_int_distribution uniDist(0, UINT8_MAX - 1); for (size_t i = 0; i < colorSourceBuffer.size(); i++) { colorSourceBuffer[i] = uniDist(gen); } size_t width = kTestBufferWidth; size_t height = kTestBufferHeight; if (switchDimensions) { width = kTestBufferHeight; height = kTestBufferWidth; } NV12Compressor jpegCompressor; if (includeExif) { ASSERT_TRUE(jpegCompressor.compressWithExifOrientation( reinterpret_cast (colorSourceBuffer.data()), width, height, jpegQuality, orientationValue)); } else { ASSERT_TRUE(jpegCompressor.compress( reinterpret_cast (colorSourceBuffer.data()), width, height, jpegQuality)); } *colorJpegBuffer = std::move(jpegCompressor.getCompressedData()); ASSERT_FALSE(colorJpegBuffer->empty()); } void generateDepth16Buffer(std::array *depth16Buffer /*out*/) { ASSERT_NE(depth16Buffer, nullptr); std::default_random_engine gen(kSeed+1); std::uniform_int_distribution uniDist(0, UINT16_MAX - 1); for (size_t i = 0; i < depth16Buffer->size(); i++) { (*depth16Buffer)[i] = uniDist(gen); } } TEST(DepthProcessorTest, LinkToLibray) { void *libHandle; process_depth_photo_frame processFunc; linkToDepthPhotoLibrary(&libHandle, &processFunc); if (libHandle != nullptr) { dlclose(libHandle); } } TEST(DepthProcessorTest, BadInput) { void *libHandle; int jpegQuality = 95; process_depth_photo_frame processFunc; linkToDepthPhotoLibrary(&libHandle, &processFunc); if (libHandle == nullptr) { // Depth library no present, nothing more to test. return; } DepthPhotoInputFrame inputFrame; // Worst case both depth and confidence maps have the same size as the main color image. inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); size_t actualDepthPhotoSize = 0; inputFrame.mMainJpegWidth = kTestBufferWidth; inputFrame.mMainJpegHeight = kTestBufferHeight; inputFrame.mJpegQuality = jpegQuality; ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), &actualDepthPhotoSize), 0); inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); inputFrame.mMainJpegSize = colorJpegBuffer.size(); ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), &actualDepthPhotoSize), 0); inputFrame.mDepthMapBuffer = depth16Buffer.data(); inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; inputFrame.mDepthMapHeight = kTestBufferHeight; ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), nullptr, &actualDepthPhotoSize), 0); ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), nullptr), 0); dlclose(libHandle); } TEST(DepthProcessorTest, BasicDepthPhotoValidation) { void *libHandle; int jpegQuality = 95; process_depth_photo_frame processFunc; linkToDepthPhotoLibrary(&libHandle, &processFunc); if (libHandle == nullptr) { // Depth library no present, nothing more to test. return; } std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); DepthPhotoInputFrame inputFrame; inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); inputFrame.mMainJpegSize = colorJpegBuffer.size(); // Worst case both depth and confidence maps have the same size as the main color image. inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; inputFrame.mMainJpegWidth = kTestBufferWidth; inputFrame.mMainJpegHeight = kTestBufferHeight; inputFrame.mJpegQuality = jpegQuality; inputFrame.mDepthMapBuffer = depth16Buffer.data(); inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; inputFrame.mDepthMapHeight = kTestBufferHeight; std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); size_t actualDepthPhotoSize = 0; ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), &actualDepthPhotoSize), 0); ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize)); // The final depth photo must consist of three jpeg images: // - the main color image // - the depth map image // - the confidence map image size_t mainJpegSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, &mainJpegSize), OK); ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); size_t depthMapSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); dlclose(libHandle); } TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { void *libHandle; int jpegQuality = 95; process_depth_photo_frame processFunc; linkToDepthPhotoLibrary(&libHandle, &processFunc); if (libHandle == nullptr) { // Depth library no present, nothing more to test. return; } ExifOrientation exifOrientations[] = { ExifOrientation::ORIENTATION_UNDEFINED, ExifOrientation::ORIENTATION_0_DEGREES, ExifOrientation::ORIENTATION_90_DEGREES, ExifOrientation::ORIENTATION_180_DEGREES, ExifOrientation::ORIENTATION_270_DEGREES }; for (auto exifOrientation : exifOrientations) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, /*switchDimensions*/ false, &colorJpegBuffer); if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), colorJpegBuffer.size(), &jpegExifOrientation), OK); ASSERT_EQ(exifOrientation, jpegExifOrientation); } std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); DepthPhotoInputFrame inputFrame; inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); inputFrame.mMainJpegSize = colorJpegBuffer.size(); // Worst case both depth and confidence maps have the same size as the main color image. inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; inputFrame.mMainJpegWidth = kTestBufferWidth; inputFrame.mMainJpegHeight = kTestBufferHeight; inputFrame.mJpegQuality = jpegQuality; inputFrame.mDepthMapBuffer = depth16Buffer.data(); inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; inputFrame.mDepthMapHeight = kTestBufferHeight; std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); size_t actualDepthPhotoSize = 0; ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), &actualDepthPhotoSize), 0); ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize)); size_t mainJpegSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, &mainJpegSize), OK); ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); size_t depthMapSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize); //Depth and confidence images must have the same EXIF orientation as the source auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, depthMapSize, &depthJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the // depth map. ASSERT_EQ(depthJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES); } else { ASSERT_EQ(depthJpegExifOrientation, exifOrientation); } auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation( depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, &confidenceJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the // confidence map. ASSERT_EQ(confidenceJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES); } else { ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation); } } dlclose(libHandle); } TEST(DepthProcessorTest, TestDephtPhotoPhysicalRotation) { void *libHandle; int jpegQuality = 95; process_depth_photo_frame processFunc; linkToDepthPhotoLibrary(&libHandle, &processFunc); if (libHandle == nullptr) { // Depth library no present, nothing more to test. return; } // In case of physical rotation, the EXIF orientation must always be 0. auto exifOrientation = ExifOrientation::ORIENTATION_0_DEGREES; DepthPhotoOrientation depthOrientations[] = { DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES, DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES, DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES, DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES }; for (auto depthOrientation : depthOrientations) { std::vector colorJpegBuffer; bool switchDimensions = false; size_t expectedWidth = kTestBufferWidth; size_t expectedHeight = kTestBufferHeight; if ((depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES) || (depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES)) { switchDimensions = true; expectedWidth = kTestBufferHeight; expectedHeight = kTestBufferWidth; } generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, switchDimensions, &colorJpegBuffer); auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), colorJpegBuffer.size(), &jpegExifOrientation), OK); ASSERT_EQ(exifOrientation, jpegExifOrientation); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); DepthPhotoInputFrame inputFrame; inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); inputFrame.mMainJpegSize = colorJpegBuffer.size(); // Worst case both depth and confidence maps have the same size as the main color image. inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; inputFrame.mMainJpegWidth = kTestBufferWidth; inputFrame.mMainJpegHeight = kTestBufferHeight; inputFrame.mJpegQuality = jpegQuality; inputFrame.mDepthMapBuffer = depth16Buffer.data(); inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; inputFrame.mDepthMapHeight = kTestBufferHeight; inputFrame.mOrientation = depthOrientation; std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); size_t actualDepthPhotoSize = 0; ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), &actualDepthPhotoSize), 0); ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize)); size_t mainJpegSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, &mainJpegSize), OK); ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); size_t depthMapSize = 0; ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize); //Depth and confidence images must have the same EXIF orientation as the source auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, depthMapSize, &depthJpegExifOrientation), OK); ASSERT_EQ(depthJpegExifOrientation, exifOrientation); size_t depthMapWidth, depthMapHeight; ASSERT_EQ(NV12Compressor::getJpegImageDimensions(depthPhotoBuffer.data() + mainJpegSize, depthMapSize, &depthMapWidth, &depthMapHeight), OK); ASSERT_EQ(depthMapWidth, expectedWidth); ASSERT_EQ(depthMapHeight, expectedHeight); auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation( depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, &confidenceJpegExifOrientation), OK); ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation); size_t confidenceMapWidth, confidenceMapHeight; ASSERT_EQ(NV12Compressor::getJpegImageDimensions( depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, &confidenceMapWidth, &confidenceMapHeight), OK); ASSERT_EQ(confidenceMapWidth, expectedWidth); ASSERT_EQ(confidenceMapHeight, expectedHeight); } dlclose(libHandle); }