1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_NDEBUG 0
18 #define LOG_TAG "DepthProcessorTest"
19
20 #include <array>
21 #include <random>
22
23 #include <dlfcn.h>
24 #include <gtest/gtest.h>
25
26 #include "../common/DepthPhotoProcessor.h"
27 #include "../utils/ExifUtils.h"
28 #include "NV12Compressor.h"
29
30 using namespace android;
31 using namespace android::camera3;
32
33 static const size_t kTestBufferWidth = 640;
34 static const size_t kTestBufferHeight = 480;
35 static const size_t kTestBufferNV12Size ((((kTestBufferWidth) * (kTestBufferHeight)) * 3) / 2);
36 static const size_t kTestBufferDepthSize (kTestBufferWidth * kTestBufferHeight);
37 static const size_t kSeed = 1234;
38
linkToDepthPhotoLibrary(void ** libHandle,process_depth_photo_frame * processFrameFunc)39 void linkToDepthPhotoLibrary(void **libHandle /*out*/,
40 process_depth_photo_frame *processFrameFunc /*out*/) {
41 ASSERT_NE(libHandle, nullptr);
42 ASSERT_NE(processFrameFunc, nullptr);
43
44 *libHandle = dlopen(kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL);
45 if (*libHandle != nullptr) {
46 *processFrameFunc = reinterpret_cast<camera3::process_depth_photo_frame> (
47 dlsym(*libHandle, kDepthPhotoProcessFunction));
48 ASSERT_NE(*processFrameFunc, nullptr);
49 }
50 }
51
generateColorJpegBuffer(int jpegQuality,ExifOrientation orientationValue,bool includeExif,bool switchDimensions,std::vector<uint8_t> * colorJpegBuffer)52 void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif,
53 bool switchDimensions, std::vector<uint8_t> *colorJpegBuffer /*out*/) {
54 ASSERT_NE(colorJpegBuffer, nullptr);
55
56 std::array<uint8_t, kTestBufferNV12Size> colorSourceBuffer;
57 std::default_random_engine gen(kSeed);
58 std::uniform_int_distribution<int> uniDist(0, UINT8_MAX - 1);
59 for (size_t i = 0; i < colorSourceBuffer.size(); i++) {
60 colorSourceBuffer[i] = uniDist(gen);
61 }
62
63 size_t width = kTestBufferWidth;
64 size_t height = kTestBufferHeight;
65 if (switchDimensions) {
66 width = kTestBufferHeight;
67 height = kTestBufferWidth;
68 }
69
70 NV12Compressor jpegCompressor;
71 if (includeExif) {
72 ASSERT_TRUE(jpegCompressor.compressWithExifOrientation(
73 reinterpret_cast<const unsigned char*> (colorSourceBuffer.data()), width, height,
74 jpegQuality, orientationValue));
75 } else {
76 ASSERT_TRUE(jpegCompressor.compress(
77 reinterpret_cast<const unsigned char*> (colorSourceBuffer.data()), width, height,
78 jpegQuality));
79 }
80
81 *colorJpegBuffer = std::move(jpegCompressor.getCompressedData());
82 ASSERT_FALSE(colorJpegBuffer->empty());
83 }
84
generateDepth16Buffer(std::array<uint16_t,kTestBufferDepthSize> * depth16Buffer)85 void generateDepth16Buffer(std::array<uint16_t, kTestBufferDepthSize> *depth16Buffer /*out*/) {
86 ASSERT_NE(depth16Buffer, nullptr);
87 std::default_random_engine gen(kSeed+1);
88 std::uniform_int_distribution<int> uniDist(0, UINT16_MAX - 1);
89 for (size_t i = 0; i < depth16Buffer->size(); i++) {
90 (*depth16Buffer)[i] = uniDist(gen);
91 }
92 }
93
TEST(DepthProcessorTest,LinkToLibray)94 TEST(DepthProcessorTest, LinkToLibray) {
95 void *libHandle;
96 process_depth_photo_frame processFunc;
97 linkToDepthPhotoLibrary(&libHandle, &processFunc);
98 if (libHandle != nullptr) {
99 dlclose(libHandle);
100 }
101 }
102
TEST(DepthProcessorTest,BadInput)103 TEST(DepthProcessorTest, BadInput) {
104 void *libHandle;
105 int jpegQuality = 95;
106
107 process_depth_photo_frame processFunc;
108 linkToDepthPhotoLibrary(&libHandle, &processFunc);
109 if (libHandle == nullptr) {
110 // Depth library no present, nothing more to test.
111 return;
112 }
113
114 DepthPhotoInputFrame inputFrame;
115 // Worst case both depth and confidence maps have the same size as the main color image.
116 inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
117
118 std::vector<uint8_t> colorJpegBuffer;
119 generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED,
120 /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer);
121
122 std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
123 generateDepth16Buffer(&depth16Buffer);
124
125 std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
126 size_t actualDepthPhotoSize = 0;
127
128 inputFrame.mMainJpegWidth = kTestBufferWidth;
129 inputFrame.mMainJpegHeight = kTestBufferHeight;
130 inputFrame.mJpegQuality = jpegQuality;
131 ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
132 &actualDepthPhotoSize), 0);
133
134 inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (colorJpegBuffer.data());
135 inputFrame.mMainJpegSize = colorJpegBuffer.size();
136 ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
137 &actualDepthPhotoSize), 0);
138
139 inputFrame.mDepthMapBuffer = depth16Buffer.data();
140 inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth;
141 inputFrame.mDepthMapHeight = kTestBufferHeight;
142 ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), nullptr,
143 &actualDepthPhotoSize), 0);
144
145 ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), nullptr),
146 0);
147
148 dlclose(libHandle);
149 }
150
TEST(DepthProcessorTest,BasicDepthPhotoValidation)151 TEST(DepthProcessorTest, BasicDepthPhotoValidation) {
152 void *libHandle;
153 int jpegQuality = 95;
154
155 process_depth_photo_frame processFunc;
156 linkToDepthPhotoLibrary(&libHandle, &processFunc);
157 if (libHandle == nullptr) {
158 // Depth library no present, nothing more to test.
159 return;
160 }
161
162 std::vector<uint8_t> colorJpegBuffer;
163 generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED,
164 /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer);
165
166 std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
167 generateDepth16Buffer(&depth16Buffer);
168
169 DepthPhotoInputFrame inputFrame;
170 inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (colorJpegBuffer.data());
171 inputFrame.mMainJpegSize = colorJpegBuffer.size();
172 // Worst case both depth and confidence maps have the same size as the main color image.
173 inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
174 inputFrame.mMainJpegWidth = kTestBufferWidth;
175 inputFrame.mMainJpegHeight = kTestBufferHeight;
176 inputFrame.mJpegQuality = jpegQuality;
177 inputFrame.mDepthMapBuffer = depth16Buffer.data();
178 inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth;
179 inputFrame.mDepthMapHeight = kTestBufferHeight;
180
181 std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
182 size_t actualDepthPhotoSize = 0;
183 ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
184 &actualDepthPhotoSize), 0);
185 ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize));
186
187 // The final depth photo must consist of three jpeg images:
188 // - the main color image
189 // - the depth map image
190 // - the confidence map image
191 size_t mainJpegSize = 0;
192 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize,
193 &mainJpegSize), OK);
194 ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize));
195 size_t depthMapSize = 0;
196 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize,
197 actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK);
198 ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize)));
199
200 dlclose(libHandle);
201 }
202
TEST(DepthProcessorTest,TestDepthPhotoExifOrientation)203 TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) {
204 void *libHandle;
205 int jpegQuality = 95;
206
207 process_depth_photo_frame processFunc;
208 linkToDepthPhotoLibrary(&libHandle, &processFunc);
209 if (libHandle == nullptr) {
210 // Depth library no present, nothing more to test.
211 return;
212 }
213
214 ExifOrientation exifOrientations[] = { ExifOrientation::ORIENTATION_UNDEFINED,
215 ExifOrientation::ORIENTATION_0_DEGREES, ExifOrientation::ORIENTATION_90_DEGREES,
216 ExifOrientation::ORIENTATION_180_DEGREES, ExifOrientation::ORIENTATION_270_DEGREES };
217 for (auto exifOrientation : exifOrientations) {
218 std::vector<uint8_t> colorJpegBuffer;
219 generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true,
220 /*switchDimensions*/ false, &colorJpegBuffer);
221 if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) {
222 auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
223 ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(),
224 colorJpegBuffer.size(), &jpegExifOrientation), OK);
225 ASSERT_EQ(exifOrientation, jpegExifOrientation);
226 }
227
228 std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
229 generateDepth16Buffer(&depth16Buffer);
230
231 DepthPhotoInputFrame inputFrame;
232 inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (colorJpegBuffer.data());
233 inputFrame.mMainJpegSize = colorJpegBuffer.size();
234 // Worst case both depth and confidence maps have the same size as the main color image.
235 inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
236 inputFrame.mMainJpegWidth = kTestBufferWidth;
237 inputFrame.mMainJpegHeight = kTestBufferHeight;
238 inputFrame.mJpegQuality = jpegQuality;
239 inputFrame.mDepthMapBuffer = depth16Buffer.data();
240 inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth;
241 inputFrame.mDepthMapHeight = kTestBufferHeight;
242
243 std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
244 size_t actualDepthPhotoSize = 0;
245 ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
246 &actualDepthPhotoSize), 0);
247 ASSERT_TRUE((actualDepthPhotoSize > 0) &&
248 (depthPhotoBuffer.size() >= actualDepthPhotoSize));
249
250 size_t mainJpegSize = 0;
251 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize,
252 &mainJpegSize), OK);
253 ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize));
254 size_t depthMapSize = 0;
255 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize,
256 actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK);
257 ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize)));
258 size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize);
259
260 //Depth and confidence images must have the same EXIF orientation as the source
261 auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
262 ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize,
263 depthMapSize, &depthJpegExifOrientation), OK);
264 if (exifOrientation == ORIENTATION_UNDEFINED) {
265 // In case of undefined or missing EXIF orientation, always expect 0 degrees in the
266 // depth map.
267 ASSERT_EQ(depthJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES);
268 } else {
269 ASSERT_EQ(depthJpegExifOrientation, exifOrientation);
270 }
271
272 auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
273 ASSERT_EQ(NV12Compressor::getExifOrientation(
274 depthPhotoBuffer.data() + mainJpegSize + depthMapSize,
275 confidenceMapSize, &confidenceJpegExifOrientation), OK);
276 if (exifOrientation == ORIENTATION_UNDEFINED) {
277 // In case of undefined or missing EXIF orientation, always expect 0 degrees in the
278 // confidence map.
279 ASSERT_EQ(confidenceJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES);
280 } else {
281 ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation);
282 }
283 }
284
285 dlclose(libHandle);
286 }
287
TEST(DepthProcessorTest,TestDephtPhotoPhysicalRotation)288 TEST(DepthProcessorTest, TestDephtPhotoPhysicalRotation) {
289 void *libHandle;
290 int jpegQuality = 95;
291
292 process_depth_photo_frame processFunc;
293 linkToDepthPhotoLibrary(&libHandle, &processFunc);
294 if (libHandle == nullptr) {
295 // Depth library no present, nothing more to test.
296 return;
297 }
298
299 // In case of physical rotation, the EXIF orientation must always be 0.
300 auto exifOrientation = ExifOrientation::ORIENTATION_0_DEGREES;
301 DepthPhotoOrientation depthOrientations[] = {
302 DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES,
303 DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES,
304 DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES,
305 DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES };
306 for (auto depthOrientation : depthOrientations) {
307 std::vector<uint8_t> colorJpegBuffer;
308 bool switchDimensions = false;
309 size_t expectedWidth = kTestBufferWidth;
310 size_t expectedHeight = kTestBufferHeight;
311 if ((depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES) ||
312 (depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES)) {
313 switchDimensions = true;
314 expectedWidth = kTestBufferHeight;
315 expectedHeight = kTestBufferWidth;
316 }
317 generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true,
318 switchDimensions, &colorJpegBuffer);
319 auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
320 ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), colorJpegBuffer.size(),
321 &jpegExifOrientation), OK);
322 ASSERT_EQ(exifOrientation, jpegExifOrientation);
323
324 std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
325 generateDepth16Buffer(&depth16Buffer);
326
327 DepthPhotoInputFrame inputFrame;
328 inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (colorJpegBuffer.data());
329 inputFrame.mMainJpegSize = colorJpegBuffer.size();
330 // Worst case both depth and confidence maps have the same size as the main color image.
331 inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
332 inputFrame.mMainJpegWidth = kTestBufferWidth;
333 inputFrame.mMainJpegHeight = kTestBufferHeight;
334 inputFrame.mJpegQuality = jpegQuality;
335 inputFrame.mDepthMapBuffer = depth16Buffer.data();
336 inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth;
337 inputFrame.mDepthMapHeight = kTestBufferHeight;
338 inputFrame.mOrientation = depthOrientation;
339
340 std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
341 size_t actualDepthPhotoSize = 0;
342 ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
343 &actualDepthPhotoSize), 0);
344 ASSERT_TRUE((actualDepthPhotoSize > 0) &&
345 (depthPhotoBuffer.size() >= actualDepthPhotoSize));
346
347 size_t mainJpegSize = 0;
348 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize,
349 &mainJpegSize), OK);
350 ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize));
351 size_t depthMapSize = 0;
352 ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize,
353 actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK);
354 ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize)));
355 size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize);
356
357 //Depth and confidence images must have the same EXIF orientation as the source
358 auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
359 ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize,
360 depthMapSize, &depthJpegExifOrientation), OK);
361 ASSERT_EQ(depthJpegExifOrientation, exifOrientation);
362 size_t depthMapWidth, depthMapHeight;
363 ASSERT_EQ(NV12Compressor::getJpegImageDimensions(depthPhotoBuffer.data() + mainJpegSize,
364 depthMapSize, &depthMapWidth, &depthMapHeight), OK);
365 ASSERT_EQ(depthMapWidth, expectedWidth);
366 ASSERT_EQ(depthMapHeight, expectedHeight);
367
368 auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED;
369 ASSERT_EQ(NV12Compressor::getExifOrientation(
370 depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize,
371 &confidenceJpegExifOrientation), OK);
372 ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation);
373 size_t confidenceMapWidth, confidenceMapHeight;
374 ASSERT_EQ(NV12Compressor::getJpegImageDimensions(
375 depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize,
376 &confidenceMapWidth, &confidenceMapHeight), OK);
377 ASSERT_EQ(confidenceMapWidth, expectedWidth);
378 ASSERT_EQ(confidenceMapHeight, expectedHeight);
379 }
380
381 dlclose(libHandle);
382 }
383