blob: 2162514d14f20dd41630fe42e9239bf2255c2420 [file] [log] [blame]
/*
* 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 <array>
#include <random>
#include <dlfcn.h>
#include <gtest/gtest.h>
#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<camera3::process_depth_photo_frame> (
dlsym(*libHandle, kDepthPhotoProcessFunction));
ASSERT_NE(*processFrameFunc, nullptr);
}
}
void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif,
bool switchDimensions, std::vector<uint8_t> *colorJpegBuffer /*out*/) {
ASSERT_NE(colorJpegBuffer, nullptr);
std::array<uint8_t, kTestBufferNV12Size> colorSourceBuffer;
std::default_random_engine gen(kSeed);
std::uniform_int_distribution<int> 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<const unsigned char*> (colorSourceBuffer.data()), width, height,
jpegQuality, orientationValue));
} else {
ASSERT_TRUE(jpegCompressor.compress(
reinterpret_cast<const unsigned char*> (colorSourceBuffer.data()), width, height,
jpegQuality));
}
*colorJpegBuffer = std::move(jpegCompressor.getCompressedData());
ASSERT_FALSE(colorJpegBuffer->empty());
}
void generateDepth16Buffer(std::array<uint16_t, kTestBufferDepthSize> *depth16Buffer /*out*/) {
ASSERT_NE(depth16Buffer, nullptr);
std::default_random_engine gen(kSeed+1);
std::uniform_int_distribution<int> 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<uint8_t> colorJpegBuffer;
generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED,
/*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer);
std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
generateDepth16Buffer(&depth16Buffer);
std::vector<uint8_t> 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<const char*> (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<uint8_t> colorJpegBuffer;
generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED,
/*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer);
std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
generateDepth16Buffer(&depth16Buffer);
DepthPhotoInputFrame inputFrame;
inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (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<uint8_t> 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<uint8_t> 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<uint16_t, kTestBufferDepthSize> depth16Buffer;
generateDepth16Buffer(&depth16Buffer);
DepthPhotoInputFrame inputFrame;
inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (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<uint8_t> 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<uint8_t> 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<uint16_t, kTestBufferDepthSize> depth16Buffer;
generateDepth16Buffer(&depth16Buffer);
DepthPhotoInputFrame inputFrame;
inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (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<uint8_t> 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);
}