1 /* 2 * Copyright 2014 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 package android.hardware.camera2.cts; 18 19 import static junit.framework.Assert.assertEquals; 20 import static junit.framework.Assert.assertNotNull; 21 import static junit.framework.Assert.assertTrue; 22 import static junit.framework.Assert.fail; 23 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.graphics.BitmapRegionDecoder; 27 import android.graphics.ImageFormat; 28 import android.graphics.Rect; 29 import android.graphics.RectF; 30 import android.hardware.camera2.CameraCaptureSession; 31 import android.hardware.camera2.CameraCharacteristics; 32 import android.hardware.camera2.CameraDevice; 33 import android.hardware.camera2.CameraMetadata; 34 import android.hardware.camera2.CaptureRequest; 35 import android.hardware.camera2.CaptureResult; 36 import android.hardware.camera2.DngCreator; 37 import android.hardware.camera2.TotalCaptureResult; 38 import android.hardware.camera2.cts.helpers.StaticMetadata; 39 import android.hardware.camera2.cts.rs.BitmapUtils; 40 import android.hardware.camera2.cts.rs.RawConverter; 41 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase; 42 import android.hardware.camera2.params.InputConfiguration; 43 import android.location.Location; 44 import android.media.ExifInterface; 45 import android.media.Image; 46 import android.media.ImageReader; 47 import android.media.ImageWriter; 48 import android.os.ConditionVariable; 49 import android.os.SystemClock; 50 import android.util.Log; 51 import android.util.Pair; 52 import android.util.Size; 53 import android.view.Surface; 54 55 import com.android.ex.camera2.blocking.BlockingSessionCallback; 56 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.Parameterized; 60 61 import java.io.ByteArrayOutputStream; 62 import java.io.File; 63 import java.io.FileOutputStream; 64 import java.nio.ByteBuffer; 65 import java.nio.channels.FileChannel; 66 import java.text.SimpleDateFormat; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.Calendar; 70 import java.util.Collections; 71 import java.util.Date; 72 import java.util.List; 73 import java.util.TimeZone; 74 75 /** 76 * Tests for the DngCreator API. 77 */ 78 79 @RunWith(Parameterized.class) 80 public class DngCreatorTest extends Camera2AndroidTestCase { 81 private static final String TAG = "DngCreatorTest"; 82 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 83 private static final String DEBUG_DNG_FILE = "raw16.dng"; 84 private static final String TEST_DNG_FILE = "test.dng"; 85 86 private static final double IMAGE_DIFFERENCE_TOLERANCE = 65; 87 private static final int DEFAULT_PATCH_DIMEN = 512; 88 private static final int AE_TIMEOUT_MS = 3000; 89 private static final int MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE = 4; 90 private static final int MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS = 91 CAPTURE_WAIT_TIMEOUT_MS * MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE; 92 93 // Constants used for GPS testing. 94 private static final double GPS_DIFFERENCE_TOLERANCE = 0.0001; 95 private static final double GPS_LATITUDE = 37.420016; 96 private static final double GPS_LONGITUDE = -122.081987; 97 private static final String GPS_DATESTAMP = "2015:01:27"; 98 private static final String GPS_TIMESTAMP = "02:12:01"; 99 private static final Calendar GPS_CALENDAR = 100 Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); 101 102 /** Load DNG validation jni on initialization */ 103 static { 104 System.loadLibrary("ctscamera2_jni"); 105 } 106 107 static { 108 GPS_CALENDAR.set(2015, 0, 27, 2, 12, 01); 109 } 110 111 class CapturedData { 112 public Pair<List<Image>, CaptureResult> imagePair; 113 public CameraCharacteristics characteristics; 114 } 115 116 // CapturedData for maximum resolution mode, raw and jpeg don't share the same capture result 117 // since mandatory streams for maximum resolution sensor pixel mode don't guarantee more than 1 118 // stream. 119 class CapturedDataMaximumResolution { 120 public Pair<Image, CaptureResult> raw; 121 public Pair<Image, CaptureResult> jpeg; 122 public CameraCharacteristics characteristics; 123 } 124 125 class DngDebugParams { 126 String deviceId; 127 String intermediateStr; 128 CameraCharacteristics characteristics; 129 CaptureResult captureResult; 130 FileOutputStream fileStream; 131 FileChannel fileChannel; 132 Image jpeg; 133 Image raw; 134 Bitmap rawBitmap; 135 } 136 137 /** 138 * Test basic raw capture and DNG saving functionality for each of the available cameras. 139 * 140 * <p> 141 * For each camera, capture a single RAW16 image at the first capture size reported for 142 * the raw format on that device, and save that image as a DNG file. No further validation 143 * is done. 144 * </p> 145 * 146 * <p> 147 * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the 148 * raw image captured for the first reported camera device to be saved to an output file. 149 * </p> 150 */ 151 @Test testSingleImageBasic()152 public void testSingleImageBasic() throws Exception { 153 String[] cameraIdsUnderTest = getCameraIdsUnderTest(); 154 for (int i = 0; i < cameraIdsUnderTest.length; i++) { 155 String deviceId = cameraIdsUnderTest[i]; 156 ImageReader captureReader = null; 157 FileOutputStream fileStream = null; 158 ByteArrayOutputStream outputStream = null; 159 try { 160 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported( 161 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 162 Log.i(TAG, "RAW capability is not supported in camera " + cameraIdsUnderTest[i] + 163 ". Skip the test."); 164 continue; 165 } 166 167 openDevice(deviceId); 168 Size activeArraySize = mStaticInfo.getRawDimensChecked(); 169 170 // Create capture image reader 171 CameraTestUtils.SimpleImageReaderListener captureListener 172 = new CameraTestUtils.SimpleImageReaderListener(); 173 captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2, 174 captureListener); 175 Pair<Image, CaptureResult> resultPair = captureSingleRawShot(activeArraySize, 176 /*waitForAe*/false, captureReader, captureListener); 177 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics(); 178 179 // Test simple writeImage, no header checks 180 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second); 181 outputStream = new ByteArrayOutputStream(); 182 dngCreator.writeImage(outputStream, resultPair.first); 183 184 if (VERBOSE) { 185 // Write DNG to file 186 String dngFilePath = mDebugFileNameBase + "/camera_basic_" + deviceId + "_" + 187 DEBUG_DNG_FILE; 188 // Write out captured DNG file for the first camera device if setprop is enabled 189 fileStream = new FileOutputStream(dngFilePath); 190 fileStream.write(outputStream.toByteArray()); 191 fileStream.flush(); 192 fileStream.close(); 193 Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath); 194 } 195 assertTrue("Generated DNG file does not pass validation", 196 validateDngNative(outputStream.toByteArray())); 197 } finally { 198 closeDevice(deviceId); 199 closeImageReader(captureReader); 200 201 if (outputStream != null) { 202 outputStream.close(); 203 } 204 205 if (fileStream != null) { 206 fileStream.close(); 207 } 208 } 209 } 210 } 211 212 /** 213 * Test basic maximum resolution raw capture and DNG saving functionality for each of the 214 * available ultra high resolution cameras. 215 * 216 * <p> 217 * For ultra high resolution each camera, capture a single RAW16 image at the first capture size 218 * reported for the maximum resolution raw format on that device, and save that image as a DNG 219 * file. No further validation is done. 220 * </p> 221 * 222 * <p> 223 * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the 224 * raw image captured for the first reported camera device to be saved to an output file. 225 * </p> 226 */ 227 @Test testSingleImageBasicMaximumResolution()228 public void testSingleImageBasicMaximumResolution() throws Exception { 229 String[] cameraIdsUnderTest = getCameraIdsUnderTest(); 230 for (int i = 0; i < cameraIdsUnderTest.length; i++) { 231 String deviceId = cameraIdsUnderTest[i]; 232 ImageReader captureReader = null; 233 ImageReader reprocessCaptureReader = null; 234 FileOutputStream fileStream = null; 235 ByteArrayOutputStream outputStream = null; 236 try { 237 // All ultra high resolution sensors must necessarily support RAW 238 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported( 239 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) { 240 Log.i(TAG, "ULTRA_HIGH_RESOLUTION_SENSOR capability is not supported in " + 241 " camera " + cameraIdsUnderTest[i] + ". Skip the test."); 242 continue; 243 } 244 245 openDevice(deviceId); 246 Size activeArraySize = mStaticInfo.getRawDimensChecked(/*maxResolution*/true); 247 248 // Create capture image reader 249 CameraTestUtils.SimpleImageReaderListener captureReaderListener 250 = new CameraTestUtils.SimpleImageReaderListener(); 251 CameraTestUtils.SimpleImageReaderListener reprocessReaderListener 252 = new CameraTestUtils.SimpleImageReaderListener(); 253 254 captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2, 255 captureReaderListener); 256 257 reprocessCaptureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 258 2, reprocessReaderListener); 259 Pair<Image, CaptureResult> resultPair = null; 260 if (mAllStaticInfo.get(deviceId).isCapabilitySupported( 261 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) { 262 resultPair = 263 captureReprocessedRawShot(activeArraySize, captureReader, 264 reprocessCaptureReader, captureReaderListener, 265 reprocessReaderListener, /*waitForAe*/false); 266 } else { 267 resultPair = captureSingleShotMaximumResolution(activeArraySize, 268 captureReader, /*waitForAe*/false, captureReaderListener); 269 } 270 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics(); 271 272 // Test simple writeImage, no header checks 273 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second); 274 outputStream = new ByteArrayOutputStream(); 275 dngCreator.writeImage(outputStream, resultPair.first); 276 277 if (VERBOSE) { 278 // Write DNG to file 279 String dngFilePath = mDebugFileNameBase + "/camera_basic_max_resolution_" + 280 deviceId + "_" + DEBUG_DNG_FILE; 281 // Write out captured DNG file for the first camera device if setprop is enabled 282 fileStream = new FileOutputStream(dngFilePath); 283 fileStream.write(outputStream.toByteArray()); 284 fileStream.flush(); 285 fileStream.close(); 286 Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath); 287 } 288 assertTrue("Generated DNG file does not pass validation", 289 validateDngNative(outputStream.toByteArray())); 290 } finally { 291 closeDevice(deviceId); 292 closeImageReader(captureReader); 293 closeImageReader(reprocessCaptureReader); 294 295 if (outputStream != null) { 296 outputStream.close(); 297 } 298 299 if (fileStream != null) { 300 fileStream.close(); 301 } 302 } 303 } 304 } 305 306 /** 307 * Test basic raw capture and DNG saving with a thumbnail, rotation, usercomment, and GPS tags 308 * set. 309 * 310 * <p> 311 * For each camera, capture a single RAW16 image at the first capture size reported for 312 * the raw format on that device, and save that image as a DNG file. GPS information validation 313 * is done via ExifInterface. 314 * </p> 315 * 316 * <p> 317 * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the 318 * raw image captured for the first reported camera device to be saved to an output file. 319 * </p> 320 */ 321 @Test testSingleImageThumbnail()322 public void testSingleImageThumbnail() throws Exception { 323 String[] cameraIdsUnderTest = getCameraIdsUnderTest(); 324 for (int i = 0; i < cameraIdsUnderTest.length; i++) { 325 String deviceId = cameraIdsUnderTest[i]; 326 List<ImageReader> captureReaders = new ArrayList<ImageReader>(); 327 List<CameraTestUtils.SimpleImageReaderListener> captureListeners = 328 new ArrayList<CameraTestUtils.SimpleImageReaderListener>(); 329 FileOutputStream fileStream = null; 330 ByteArrayOutputStream outputStream = null; 331 try { 332 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported( 333 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 334 Log.i(TAG, "RAW capability is not supported in camera " + cameraIdsUnderTest[i] + 335 ". Skip the test."); 336 continue; 337 } 338 339 openDevice(deviceId); 340 Size activeArraySize = mStaticInfo.getRawDimensChecked(); 341 342 Size[] targetPreviewSizes = 343 mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888, 344 StaticMetadata.StreamDirection.Output); 345 // Get smallest preview size 346 Size previewSize = mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1); 347 348 // Create capture image reader 349 CameraTestUtils.SimpleImageReaderListener captureListener 350 = new CameraTestUtils.SimpleImageReaderListener(); 351 captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2, 352 captureListener)); 353 captureListeners.add(captureListener); 354 355 CameraTestUtils.SimpleImageReaderListener previewListener 356 = new CameraTestUtils.SimpleImageReaderListener(); 357 358 captureReaders.add(createImageReader(previewSize, ImageFormat.YUV_420_888, 2, 359 previewListener)); 360 captureListeners.add(previewListener); 361 362 Date beforeCaptureDate = new Date(); 363 Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize, 364 captureReaders, /*waitForAe*/false, captureListeners); 365 Date afterCaptureDate = new Date(); 366 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics(); 367 368 if (VERBOSE) { 369 Log.v(TAG, "Sensor timestamp (ms): " + 370 resultPair.second.get(CaptureResult.SENSOR_TIMESTAMP) / 1000000); 371 Log.v(TAG, "SystemClock.elapsedRealtimeNanos (ms): " + 372 SystemClock.elapsedRealtimeNanos() / 1000000); 373 Log.v(TAG, "SystemClock.uptimeMillis(): " + SystemClock.uptimeMillis()); 374 } 375 // Test simple writeImage, no header checks 376 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second); 377 Location l = new Location("test"); 378 l.reset(); 379 l.setLatitude(GPS_LATITUDE); 380 l.setLongitude(GPS_LONGITUDE); 381 l.setTime(GPS_CALENDAR.getTimeInMillis()); 382 dngCreator.setLocation(l); 383 384 dngCreator.setDescription("helloworld"); 385 dngCreator.setOrientation(ExifInterface.ORIENTATION_FLIP_VERTICAL); 386 dngCreator.setThumbnail(resultPair.first.get(1)); 387 outputStream = new ByteArrayOutputStream(); 388 dngCreator.writeImage(outputStream, resultPair.first.get(0)); 389 390 String filePath = mDebugFileNameBase + "/camera_thumb_" + deviceId + "_" + 391 DEBUG_DNG_FILE; 392 // Write out captured DNG file for the first camera device 393 fileStream = new FileOutputStream(filePath); 394 fileStream.write(outputStream.toByteArray()); 395 fileStream.flush(); 396 fileStream.close(); 397 if (VERBOSE) { 398 Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath); 399 } 400 401 assertTrue("Generated DNG file does not pass validation", 402 validateDngNative(outputStream.toByteArray())); 403 404 ExifInterface exifInterface = new ExifInterface(filePath); 405 // Verify GPS data. 406 float[] latLong = new float[2]; 407 assertTrue(exifInterface.getLatLong(latLong)); 408 assertEquals(GPS_LATITUDE, latLong[0], GPS_DIFFERENCE_TOLERANCE); 409 assertEquals(GPS_LONGITUDE, latLong[1], GPS_DIFFERENCE_TOLERANCE); 410 assertEquals(GPS_DATESTAMP, 411 exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP)); 412 assertEquals(GPS_TIMESTAMP, 413 exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP)); 414 415 // Verify the orientation. 416 assertEquals(ExifInterface.ORIENTATION_FLIP_VERTICAL, 417 exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 418 ExifInterface.ORIENTATION_UNDEFINED)); 419 420 // Verify the date/time 421 final SimpleDateFormat dngDateTimeStampFormat = 422 new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); 423 dngDateTimeStampFormat.setLenient(false); 424 425 String dateTimeString = 426 exifInterface.getAttribute(ExifInterface.TAG_DATETIME); 427 assertTrue(dateTimeString != null); 428 429 Date dateTime = dngDateTimeStampFormat.parse(dateTimeString); 430 long captureTimeMs = dateTime.getTime(); 431 432 Log.i(TAG, "DNG DateTime tag: " + dateTimeString); 433 Log.i(TAG, "Before capture time: " + beforeCaptureDate.getTime()); 434 Log.i(TAG, "Capture time: " + captureTimeMs); 435 Log.i(TAG, "After capture time: " + afterCaptureDate.getTime()); 436 437 // Offset beforeCaptureTime by 1 second to account for rounding down of 438 // DNG tag 439 long beforeCaptureTimeMs = beforeCaptureDate.getTime() - 1000; 440 long afterCaptureTimeMs = afterCaptureDate.getTime(); 441 assertTrue(captureTimeMs >= beforeCaptureTimeMs); 442 assertTrue(captureTimeMs <= afterCaptureTimeMs); 443 444 if (!VERBOSE) { 445 // Delete the captured DNG file. 446 File dngFile = new File(filePath); 447 assertTrue(dngFile.delete()); 448 } 449 } finally { 450 closeDevice(deviceId); 451 for (ImageReader r : captureReaders) { 452 closeImageReader(r); 453 } 454 455 if (outputStream != null) { 456 outputStream.close(); 457 } 458 459 if (fileStream != null) { 460 fileStream.close(); 461 } 462 } 463 } 464 } 465 466 /** 467 * Test basic maximum resolution RAW capture, and ensure that the rendered RAW output is 468 * similar to the maximum resolution JPEG created for a similar frame. 469 * 470 * Since mandatory streams for maximum resolution sensor pixel mode do not guarantee 2 maximum 471 * resolution streams we can't capture RAW + JPEG images of the same frame. Therefore, 2 472 * sessions are created, one for RAW capture and the other for JPEG capture. 473 * 474 * <p> 475 * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline 476 * similar to one in the Adobe DNG validation tool. JPEGs produced by the vendor hardware may 477 * have different tonemapping and saturation applied than the RGB bitmaps produced 478 * from this DNG rendering pipeline, and this test allows for fairly wide variations 479 * between the histograms for the RAW and JPEG buffers to avoid false positives. 480 * </p> 481 * 482 * <p> 483 * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW 484 * metadata, the DNGs and JPEGs produced here should also be manually compared using external 485 * DNG rendering tools. The DNG, rendered RGB bitmap, and JPEG buffer for this test can be 486 * dumped to the SD card for further examination by enabling the 'verbose' mode for this test 487 * using: 488 * adb shell setprop log.tag.DngCreatorTest VERBOSE 489 * </p> 490 */ 491 @Test testRaw16JpegMaximumResolutionConsistency()492 public void testRaw16JpegMaximumResolutionConsistency() throws Exception { 493 for (String deviceId : getCameraIdsUnderTest()) { 494 ImageReader rawImageReader = null; 495 ImageReader jpegImageReader = null; 496 FileOutputStream fileStream = null; 497 FileChannel fileChannel = null; 498 try { 499 // All ultra high resolution sensors must necessarily support RAW 500 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported( 501 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) { 502 Log.i(TAG, "ULTRA_HIGH_RESOLUTION_SENSOR capability is not supported in " + 503 " camera " + deviceId + ". Skip " + 504 "testRaw16JpegMaximumResolutionConsistency"); 505 continue; 506 } 507 508 CapturedDataMaximumResolution data = 509 captureRawJpegImagePairMaximumResolution(deviceId, rawImageReader, 510 jpegImageReader); 511 if (data == null) { 512 continue; 513 } 514 Image raw = data.raw.first; 515 Image jpeg = data.jpeg.first; 516 517 Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(), 518 Bitmap.Config.ARGB_8888); 519 520 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()]; 521 522 // Render RAW image to a bitmap 523 raw.getPlanes()[0].getBuffer().get(rawPlane); 524 raw.getPlanes()[0].getBuffer().rewind(); 525 526 RawConverter.convertToSRGB(raw.getWidth(), 527 raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane, 528 data.characteristics, /*captureREsult*/data.raw.second, /*offsetX*/ 0, 529 /*offsetY*/ 0, /*out*/ rawBitmap); 530 531 rawPlane = null; 532 System.gc(); // Hint to VM 533 534 if (VERBOSE) { 535 DngDebugParams params = new DngDebugParams(); 536 params.deviceId = deviceId; 537 params.characteristics = data.characteristics; 538 params.captureResult = data.raw.second; 539 params.fileStream = fileStream; 540 params.raw = raw; 541 params.jpeg = jpeg; 542 params.fileChannel = fileChannel; 543 params.rawBitmap = rawBitmap; 544 params.intermediateStr = "maximum_resolution_"; 545 546 debugDumpDng(params); 547 } 548 549 validateRawJpegImagePair(rawBitmap, jpeg, deviceId); 550 } finally { 551 closeImageReader(rawImageReader); 552 closeImageReader(jpegImageReader); 553 554 if (fileChannel != null) { 555 fileChannel.close(); 556 } 557 if (fileStream != null) { 558 fileStream.close(); 559 } 560 } 561 } 562 } 563 564 565 566 /** 567 * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG 568 * created for the same frame. 569 * 570 * <p> 571 * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline 572 * similar to one in the Adobe DNG validation tool. JPEGs produced by the vendor hardware may 573 * have different tonemapping and saturation applied than the RGB bitmaps produced 574 * from this DNG rendering pipeline, and this test allows for fairly wide variations 575 * between the histograms for the RAW and JPEG buffers to avoid false positives. 576 * </p> 577 * 578 * <p> 579 * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW 580 * metadata, the DNGs and JPEGs produced here should also be manually compared using external 581 * DNG rendering tools. The DNG, rendered RGB bitmap, and JPEG buffer for this test can be 582 * dumped to the SD card for further examination by enabling the 'verbose' mode for this test 583 * using: 584 * adb shell setprop log.tag.DngCreatorTest VERBOSE 585 * </p> 586 */ 587 @Test testRaw16JpegConsistency()588 public void testRaw16JpegConsistency() throws Exception { 589 for (String deviceId : getCameraIdsUnderTest()) { 590 List<ImageReader> captureReaders = new ArrayList<>(); 591 FileOutputStream fileStream = null; 592 FileChannel fileChannel = null; 593 try { 594 CapturedData data = captureRawJpegImagePair(deviceId, captureReaders); 595 if (data == null) { 596 continue; 597 } 598 Image raw = data.imagePair.first.get(0); 599 Image jpeg = data.imagePair.first.get(1); 600 601 Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(), 602 Bitmap.Config.ARGB_8888); 603 604 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()]; 605 606 // Render RAW image to a bitmap 607 raw.getPlanes()[0].getBuffer().get(rawPlane); 608 raw.getPlanes()[0].getBuffer().rewind(); 609 610 RawConverter.convertToSRGB(raw.getWidth(), 611 raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane, 612 data.characteristics, data.imagePair.second, /*offsetX*/ 0, /*offsetY*/ 0, 613 /*out*/ rawBitmap); 614 615 rawPlane = null; 616 System.gc(); // Hint to VM 617 618 if (VERBOSE) { 619 DngDebugParams params = new DngDebugParams(); 620 params.deviceId = deviceId; 621 params.characteristics = data.characteristics; 622 params.captureResult = data.imagePair.second; 623 params.fileStream = fileStream; 624 params.raw = raw; 625 params.jpeg = jpeg; 626 params.fileChannel = fileChannel; 627 params.rawBitmap = rawBitmap; 628 params.intermediateStr = ""; 629 630 debugDumpDng(params); 631 } 632 633 validateRawJpegImagePair(rawBitmap, jpeg, deviceId); 634 } finally { 635 for (ImageReader r : captureReaders) { 636 closeImageReader(r); 637 } 638 639 if (fileChannel != null) { 640 fileChannel.close(); 641 } 642 643 if (fileStream != null) { 644 fileStream.close(); 645 } 646 } 647 } 648 } 649 650 /** 651 * Test basic DNG creation, ensure that the DNG image can be rendered by BitmapFactory. 652 */ 653 @Test testDngRenderingByBitmapFactor()654 public void testDngRenderingByBitmapFactor() throws Exception { 655 for (String deviceId : getCameraIdsUnderTest()) { 656 List<ImageReader> captureReaders = new ArrayList<>(); 657 658 CapturedData data = captureRawJpegImagePair(deviceId, captureReaders); 659 if (data == null) { 660 continue; 661 } 662 Image raw = data.imagePair.first.get(0); 663 Image jpeg = data.imagePair.first.get(1); 664 665 // Generate DNG file 666 DngCreator dngCreator = new DngCreator(data.characteristics, data.imagePair.second); 667 668 // Write DNG to file 669 String dngFilePath = mDebugFileNameBase + "/camera_" + 670 deviceId + "_" + TEST_DNG_FILE; 671 672 // Write out captured DNG file for the first camera device if setprop is enabled 673 try (FileOutputStream fileStream = new FileOutputStream(dngFilePath)) { 674 dngCreator.writeImage(fileStream, raw); 675 676 // Render the DNG file using BitmapFactory. 677 Bitmap rawBitmap = BitmapFactory.decodeFile(dngFilePath); 678 assertNotNull(rawBitmap); 679 680 validateRawJpegImagePair(rawBitmap, jpeg, deviceId); 681 } finally { 682 for (ImageReader r : captureReaders) { 683 closeImageReader(r); 684 } 685 686 System.gc(); // Hint to VM 687 } 688 } 689 } 690 691 /* 692 * Create RAW + JPEG image pair with characteristics info. 693 */ captureRawJpegImagePair(String deviceId, List<ImageReader> captureReaders)694 private CapturedData captureRawJpegImagePair(String deviceId, List<ImageReader> captureReaders) 695 throws Exception { 696 CapturedData data = new CapturedData(); 697 List<CameraTestUtils.SimpleImageReaderListener> captureListeners = new ArrayList<>(); 698 try { 699 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported( 700 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 701 Log.i(TAG, "RAW capability is not supported in camera " + deviceId 702 + ". Skip the test."); 703 return null; 704 } 705 706 openDevice(deviceId); 707 Size activeArraySize = mStaticInfo.getRawDimensChecked(); 708 709 // Get largest jpeg size 710 Size[] targetJpegSizes = mStaticInfo.getAvailableSizesForFormatChecked( 711 ImageFormat.JPEG, StaticMetadata.StreamDirection.Output); 712 713 Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes), 714 new CameraTestUtils.SizeComparator()); 715 716 // Create raw image reader and capture listener 717 CameraTestUtils.SimpleImageReaderListener rawListener = 718 new CameraTestUtils.SimpleImageReaderListener(); 719 captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2, 720 rawListener)); 721 captureListeners.add(rawListener); 722 723 724 // Create jpeg image reader and capture listener 725 CameraTestUtils.SimpleImageReaderListener jpegListener = 726 new CameraTestUtils.SimpleImageReaderListener(); 727 captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2, 728 jpegListener)); 729 captureListeners.add(jpegListener); 730 731 data.imagePair = captureSingleRawShot(activeArraySize, 732 captureReaders, /*waitForAe*/ true, captureListeners); 733 data.characteristics = mStaticInfo.getCharacteristics(); 734 735 Image raw = data.imagePair.first.get(0); 736 Size rawBitmapSize = new Size(raw.getWidth(), raw.getHeight()); 737 assertTrue("Raw bitmap size must be equal to either pre-correction active array" + 738 " size or pixel array size.", rawBitmapSize.equals(activeArraySize)); 739 740 return data; 741 } finally { 742 closeDevice(deviceId); 743 } 744 } 745 debugDumpDng(DngDebugParams params)746 private void debugDumpDng(DngDebugParams params) throws Exception { 747 // Generate DNG file 748 DngCreator dngCreator = 749 new DngCreator(params.characteristics, params.captureResult); 750 751 // Write DNG to file 752 String dngFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr + 753 params.deviceId + "_" + DEBUG_DNG_FILE; 754 // Write out captured DNG file for the first camera device if setprop is enabled 755 params.fileStream = new FileOutputStream(dngFilePath); 756 dngCreator.writeImage(params.fileStream, params.raw); 757 params.fileStream.flush(); 758 params.fileStream.close(); 759 Log.v(TAG, "Test DNG file for camera " + params.deviceId + " saved to " + dngFilePath); 760 761 // Write JPEG to file 762 String jpegFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr + 763 params.deviceId + "_jpeg.jpg"; 764 // Write out captured DNG file for the first camera device if setprop is enabled 765 params.fileChannel = new FileOutputStream(jpegFilePath).getChannel(); 766 ByteBuffer jPlane = params.jpeg.getPlanes()[0].getBuffer(); 767 params.fileChannel.write(jPlane); 768 params.fileChannel.close(); 769 jPlane.rewind(); 770 Log.v(TAG, "Test JPEG file for camera " + params.deviceId + " saved to " + 771 jpegFilePath); 772 773 // Write jpeg generated from demosaiced RAW frame to file 774 String rawFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr + 775 params.deviceId + "_raw.jpg"; 776 // Write out captured DNG file for the first camera device if setprop is enabled 777 params.fileStream = new FileOutputStream(rawFilePath); 778 params.rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, params.fileStream); 779 params.fileStream.flush(); 780 params.fileStream.close(); 781 Log.v(TAG, "Test converted RAW file for camera " + params.deviceId + " saved to " + 782 rawFilePath); 783 } 784 785 /* 786 * Create RAW + JPEG image pair with characteristics info. Assumes the device supports the RAW 787 * capability. 788 */ captureRawJpegImagePairMaximumResolution(String deviceId, ImageReader rawCaptureReader, ImageReader jpegCaptureReader)789 private CapturedDataMaximumResolution captureRawJpegImagePairMaximumResolution(String deviceId, 790 ImageReader rawCaptureReader, ImageReader jpegCaptureReader) 791 throws Exception { 792 CapturedDataMaximumResolution data = new CapturedDataMaximumResolution(); 793 try { 794 795 openDevice(deviceId); 796 Size activeArraySize = mStaticInfo.getRawDimensChecked(/*maxResolution*/true); 797 798 // Get largest jpeg size 799 Size[] targetJpegSizes = mStaticInfo.getAvailableSizesForFormatChecked( 800 ImageFormat.JPEG, StaticMetadata.StreamDirection.Output, /*fastSizes*/ true, 801 /*slowSizes*/ true, /*maxResolution*/true); 802 803 Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes), 804 new CameraTestUtils.SizeComparator()); 805 806 // Create raw image reader and capture listener 807 CameraTestUtils.SimpleImageReaderListener rawCaptureReaderListener = 808 new CameraTestUtils.SimpleImageReaderListener(); 809 rawCaptureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2, 810 rawCaptureReaderListener); 811 812 // Create jpeg image reader and capture listener 813 CameraTestUtils.SimpleImageReaderListener jpegCaptureListener = 814 new CameraTestUtils.SimpleImageReaderListener(); 815 jpegCaptureReader = createImageReader(largestJpegSize, ImageFormat.JPEG, 2, 816 jpegCaptureListener); 817 818 Pair<Image, CaptureResult> jpegResultPair = 819 captureSingleShotMaximumResolution(activeArraySize, 820 jpegCaptureReader, /*waitForAe*/true, jpegCaptureListener); 821 data.jpeg = jpegResultPair; 822 data.characteristics = mStaticInfo.getCharacteristics(); 823 // Create capture image reader 824 CameraTestUtils.SimpleImageReaderListener outputRawCaptureReaderListener 825 = new CameraTestUtils.SimpleImageReaderListener(); 826 CameraTestUtils.SimpleImageReaderListener reprocessReaderListener 827 = new CameraTestUtils.SimpleImageReaderListener(); 828 829 ImageReader outputRawCaptureReader = createImageReader(activeArraySize, 830 ImageFormat.RAW_SENSOR, 2, outputRawCaptureReaderListener); 831 Pair<Image, CaptureResult> rawResultPair = null; 832 if (mAllStaticInfo.get(deviceId).isCapabilitySupported( 833 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) { 834 rawResultPair = 835 captureReprocessedRawShot(activeArraySize, outputRawCaptureReader, 836 rawCaptureReader, outputRawCaptureReaderListener, 837 reprocessReaderListener, /*waitForAe*/ true); 838 } else { 839 rawResultPair = captureSingleShotMaximumResolution(activeArraySize, 840 rawCaptureReader, /*waitForAe*/true, rawCaptureReaderListener); 841 } 842 data.raw = rawResultPair; 843 Size rawBitmapSize = 844 new Size(rawResultPair.first.getWidth(), rawResultPair.first.getHeight()); 845 assertTrue("Raw bitmap size must be equal to either pre-correction active array" + 846 " size or pixel array size.", rawBitmapSize.equals(activeArraySize)); 847 848 return data; 849 } finally { 850 closeDevice(deviceId); 851 } 852 } 853 854 /* 855 * Verify the image pair by comparing the center patch. 856 */ validateRawJpegImagePair(Bitmap rawBitmap, Image jpeg, String deviceId)857 private void validateRawJpegImagePair(Bitmap rawBitmap, Image jpeg, String deviceId) 858 throws Exception { 859 // Decompress JPEG image to a bitmap 860 byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg); 861 862 // Get JPEG dimensions without decoding 863 BitmapFactory.Options opt0 = new BitmapFactory.Options(); 864 opt0.inJustDecodeBounds = true; 865 BitmapFactory.decodeByteArray(compressedJpegData, /*offset*/0, 866 compressedJpegData.length, /*inout*/opt0); 867 Rect jpegDimens = new Rect(0, 0, opt0.outWidth, opt0.outHeight); 868 869 // Find square center patch from JPEG and RAW bitmaps 870 RectF jpegRect = new RectF(jpegDimens); 871 RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight()); 872 int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN, 873 jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()), 874 rawBitmap.getHeight()); 875 876 RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen); 877 jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(), 878 jpegRect.centerY() - jpegIntermediate.centerY()); 879 880 RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen); 881 rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(), 882 rawRect.centerY() - rawIntermediate.centerY()); 883 Rect jpegFinal = new Rect(); 884 jpegIntermediate.roundOut(jpegFinal); 885 Rect rawFinal = new Rect(); 886 rawIntermediate.roundOut(rawFinal); 887 888 // Get RAW center patch, and free up rest of RAW image 889 Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top, 890 rawFinal.width(), rawFinal.height()); 891 rawBitmap.recycle(); 892 rawBitmap = null; 893 System.gc(); // Hint to VM 894 895 BitmapFactory.Options opt = new BitmapFactory.Options(); 896 opt.inPreferredConfig = Bitmap.Config.ARGB_8888; 897 Bitmap jpegPatch = BitmapRegionDecoder.newInstance(compressedJpegData, 898 /*offset*/0, compressedJpegData.length, /*isShareable*/true). 899 decodeRegion(jpegFinal, opt); 900 901 // Compare center patch from JPEG and rendered RAW bitmap 902 double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch); 903 if (difference > IMAGE_DIFFERENCE_TOLERANCE) { 904 FileOutputStream fileStream = null; 905 try { 906 // Write JPEG patch to file 907 String jpegFilePath = mDebugFileNameBase + "/camera_" + deviceId + 908 "_jpeg_patch.jpg"; 909 fileStream = new FileOutputStream(jpegFilePath); 910 jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream); 911 fileStream.flush(); 912 fileStream.close(); 913 Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " + 914 jpegFilePath); 915 916 // Write RAW patch to file 917 String rawFilePath = mDebugFileNameBase + "/camera_" + deviceId + 918 "_raw_patch.jpg"; 919 fileStream = new FileOutputStream(rawFilePath); 920 rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream); 921 fileStream.flush(); 922 fileStream.close(); 923 Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " + 924 rawFilePath); 925 926 fail("Camera " + deviceId + ": RAW and JPEG image at for the same " + 927 "frame are not similar, center patches have difference metric of " + 928 difference); 929 } finally { 930 if (fileStream != null) { 931 fileStream.close(); 932 } 933 } 934 } 935 } 936 captureSingleRawShot(Size s, boolean waitForAe, ImageReader captureReader, CameraTestUtils.SimpleImageReaderListener captureListener)937 private Pair<Image, CaptureResult> captureSingleRawShot(Size s, boolean waitForAe, 938 ImageReader captureReader, 939 CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception { 940 List<ImageReader> readers = new ArrayList<ImageReader>(); 941 readers.add(captureReader); 942 List<CameraTestUtils.SimpleImageReaderListener> listeners = 943 new ArrayList<CameraTestUtils.SimpleImageReaderListener>(); 944 listeners.add(captureListener); 945 Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, waitForAe, 946 listeners); 947 return new Pair<Image, CaptureResult>(res.first.get(0), res.second); 948 } 949 captureSingleRawShot(Size s, List<ImageReader> captureReaders, boolean waitForAe, List<CameraTestUtils.SimpleImageReaderListener> captureListeners)950 private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, 951 List<ImageReader> captureReaders, boolean waitForAe, 952 List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception { 953 return captureRawShots(s, captureReaders, waitForAe, captureListeners, 1, 954 /*maxResolution*/false).get(0); 955 } 956 captureSingleShotMaximumResolution(Size s, ImageReader captureReader, boolean waitForAe, CameraTestUtils.SimpleImageReaderListener captureListener)957 private Pair<Image, CaptureResult> captureSingleShotMaximumResolution(Size s, 958 ImageReader captureReader, boolean waitForAe, 959 CameraTestUtils.SimpleImageReaderListener captureListener) 960 throws Exception { 961 List<ImageReader> readers = new ArrayList<ImageReader>(); 962 readers.add(captureReader); 963 List<CameraTestUtils.SimpleImageReaderListener> listeners = 964 new ArrayList<CameraTestUtils.SimpleImageReaderListener>(); 965 listeners.add(captureListener); 966 Pair<List<Image>, CaptureResult> res = captureRawShots(s, readers, waitForAe, 967 listeners, /*numShots*/ 1, /*maxResolution*/ true).get(0); 968 return new Pair<Image, CaptureResult>(res.first.get(0), res.second); 969 } 970 captureReprocessedRawShot(Size sz, ImageReader inputReader, ImageReader reprocessOutputReader, CameraTestUtils.SimpleImageReaderListener inputReaderListener, CameraTestUtils.SimpleImageReaderListener reprocessReaderListener, boolean waitForAe)971 private Pair<Image, CaptureResult> captureReprocessedRawShot(Size sz, 972 ImageReader inputReader, 973 ImageReader reprocessOutputReader, 974 CameraTestUtils.SimpleImageReaderListener inputReaderListener, 975 CameraTestUtils.SimpleImageReaderListener reprocessReaderListener, 976 boolean waitForAe) throws Exception { 977 978 InputConfiguration inputConfig = 979 new InputConfiguration(sz.getWidth(), sz.getHeight(), ImageFormat.RAW_SENSOR); 980 CameraTestUtils.SimpleCaptureCallback inputCaptureListener = 981 new CameraTestUtils.SimpleCaptureCallback(); 982 CameraTestUtils.SimpleCaptureCallback reprocessOutputCaptureListener = 983 new CameraTestUtils.SimpleCaptureCallback(); 984 985 inputReader.setOnImageAvailableListener(inputReaderListener, mHandler); 986 reprocessOutputReader.setOnImageAvailableListener(reprocessReaderListener, mHandler); 987 988 ArrayList<Surface> outputSurfaces = new ArrayList<Surface>(); 989 outputSurfaces.add(inputReader.getSurface()); 990 outputSurfaces.add(reprocessOutputReader.getSurface()); 991 BlockingSessionCallback sessionListener = new BlockingSessionCallback(); 992 ImageReader previewReader = null; 993 if (waitForAe) { 994 // Also setup a small YUV output for AE metering if needed 995 Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null : 996 mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1); 997 assertNotNull("Must support at least one small YUV size.", yuvSize); 998 previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888, 999 /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener()); 1000 outputSurfaces.add(previewReader.getSurface()); 1001 } 1002 1003 createReprocessableSession(inputConfig, outputSurfaces); 1004 1005 if (waitForAe) { 1006 CaptureRequest.Builder precaptureRequest = 1007 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 1008 assertNotNull("Fail to get captureRequest", precaptureRequest); 1009 precaptureRequest.addTarget(previewReader.getSurface()); 1010 precaptureRequest.set(CaptureRequest.CONTROL_MODE, 1011 CaptureRequest.CONTROL_MODE_AUTO); 1012 precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE, 1013 CaptureRequest.CONTROL_AE_MODE_ON); 1014 1015 final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false); 1016 CameraCaptureSession.CaptureCallback captureCallback = 1017 new CameraCaptureSession.CaptureCallback() { 1018 @Override 1019 public void onCaptureProgressed(CameraCaptureSession session, 1020 CaptureRequest request, CaptureResult partialResult) { 1021 Integer aeState = partialResult.get(CaptureResult.CONTROL_AE_STATE); 1022 if (aeState != null && 1023 (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED || 1024 aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED)) { 1025 waitForAeCondition.open(); 1026 } 1027 } 1028 1029 @Override 1030 public void onCaptureCompleted(CameraCaptureSession session, 1031 CaptureRequest request, TotalCaptureResult result) { 1032 int aeState = result.get(CaptureResult.CONTROL_AE_STATE); 1033 if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED || 1034 aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 1035 waitForAeCondition.open(); 1036 } 1037 } 1038 }; 1039 1040 startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler); 1041 1042 precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1043 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1044 startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler); 1045 assertTrue("Timeout out waiting for AE to converge", 1046 waitForAeCondition.block(AE_TIMEOUT_MS)); 1047 } 1048 ImageWriter inputWriter = 1049 ImageWriter.newInstance(mCameraSession.getInputSurface(), 1); 1050 // Prepare a request for reprocess input 1051 CaptureRequest.Builder builder = mCamera.createCaptureRequest( 1052 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); 1053 builder.addTarget(inputReader.getSurface()); 1054 // This is a max resolution capture 1055 builder.set(CaptureRequest.SENSOR_PIXEL_MODE, 1056 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); 1057 CaptureRequest inputRequest = builder.build(); 1058 mCameraSession.capture(inputRequest, inputCaptureListener, mHandler); 1059 List<CaptureRequest> reprocessCaptureRequests = new ArrayList<>(); 1060 1061 TotalCaptureResult inputResult = 1062 inputCaptureListener.getTotalCaptureResult( 1063 MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS); 1064 builder = mCamera.createReprocessCaptureRequest(inputResult); 1065 inputWriter.queueInputImage(inputReaderListener.getImage( 1066 MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS)); 1067 builder.addTarget(reprocessOutputReader.getSurface()); 1068 reprocessCaptureRequests.add(builder.build()); 1069 mCameraSession.captureBurst(reprocessCaptureRequests, reprocessOutputCaptureListener, 1070 mHandler); 1071 TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult( 1072 CAPTURE_WAIT_TIMEOUT_MS); 1073 return new Pair<Image, CaptureResult>(reprocessReaderListener.getImage( 1074 MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS), result); 1075 } 1076 1077 /** 1078 * Capture raw images. 1079 * 1080 * <p>Capture raw images for a given size.</p> 1081 * 1082 * @param sz The size of the raw image to capture. Must be one of the available sizes for this 1083 * device. 1084 * 1085 * @param captureReaders The image readers which are associated with the targets for this 1086 * capture. 1087 * 1088 * @param waitForAe Whether we should wait for AE to converge before capturing outputs for 1089 * the captureReaders targets 1090 * 1091 * @param captureListeners ImageReader listeners which wait on the captured images to be 1092 * available. 1093 * 1094 * @param numShots The number of shots to be captured 1095 * 1096 * @param maxResolution Whether the target in captureReaders are max resolution captures. If 1097 * this is set to true, captureReaders.size() must be == 1 ( in order to 1098 * satisfy mandatory streams for maximum resolution sensor pixel mode). 1099 * 1100 * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for 1101 * each capture. 1102 */ captureRawShots(Size sz, List<ImageReader> captureReaders, boolean waitForAe, List<CameraTestUtils.SimpleImageReaderListener> captureListeners, int numShots, boolean maxResolution)1103 private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size sz, 1104 List<ImageReader> captureReaders, boolean waitForAe, 1105 List<CameraTestUtils.SimpleImageReaderListener> captureListeners, 1106 int numShots, boolean maxResolution) throws Exception { 1107 if (VERBOSE) { 1108 Log.v(TAG, "captureSingleRawShot - Capturing raw image."); 1109 } 1110 1111 int timeoutScale = maxResolution ? MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE : 1; 1112 Size[] targetCaptureSizes = 1113 mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR, 1114 StaticMetadata.StreamDirection.Output, /*fastSizes*/ true, 1115 /*slowSizes*/ true, maxResolution); 1116 1117 if (maxResolution) { 1118 assertTrue("Maximum number of maximum resolution targets for a session should be 1 as" + 1119 " per the mandatory streams guarantee", captureReaders.size() == 1); 1120 } 1121 1122 // Validate size 1123 boolean validSize = false; 1124 for (int i = 0; i < targetCaptureSizes.length; ++i) { 1125 if (targetCaptureSizes[i].equals(sz)) { 1126 validSize = true; 1127 break; 1128 } 1129 } 1130 assertTrue("Capture size is supported.", validSize); 1131 1132 // Capture images. 1133 final List<Surface> outputSurfaces = new ArrayList<Surface>(); 1134 for (ImageReader captureReader : captureReaders) { 1135 Surface captureSurface = captureReader.getSurface(); 1136 outputSurfaces.add(captureSurface); 1137 } 1138 1139 // Set up still capture template targeting JPEG/RAW outputs 1140 CaptureRequest.Builder request = 1141 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 1142 request.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, 1143 CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF); 1144 assertNotNull("Fail to get captureRequest", request); 1145 for (Surface surface : outputSurfaces) { 1146 request.addTarget(surface); 1147 } 1148 1149 ImageReader previewReader = null; 1150 if (waitForAe) { 1151 // Also setup a small YUV output for AE metering if needed 1152 Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null : 1153 mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1); 1154 assertNotNull("Must support at least one small YUV size.", yuvSize); 1155 previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888, 1156 /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener()); 1157 outputSurfaces.add(previewReader.getSurface()); 1158 } 1159 1160 createSession(outputSurfaces); 1161 1162 if (waitForAe) { 1163 CaptureRequest.Builder precaptureRequest = 1164 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 1165 assertNotNull("Fail to get captureRequest", precaptureRequest); 1166 precaptureRequest.addTarget(previewReader.getSurface()); 1167 precaptureRequest.set(CaptureRequest.CONTROL_MODE, 1168 CaptureRequest.CONTROL_MODE_AUTO); 1169 precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE, 1170 CaptureRequest.CONTROL_AE_MODE_ON); 1171 1172 final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false); 1173 CameraCaptureSession.CaptureCallback captureCallback = 1174 new CameraCaptureSession.CaptureCallback() { 1175 @Override 1176 public void onCaptureProgressed(CameraCaptureSession session, 1177 CaptureRequest request, CaptureResult partialResult) { 1178 Integer aeState = partialResult.get(CaptureResult.CONTROL_AE_STATE); 1179 if (aeState != null && 1180 (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED || 1181 aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED)) { 1182 waitForAeCondition.open(); 1183 } 1184 } 1185 1186 @Override 1187 public void onCaptureCompleted(CameraCaptureSession session, 1188 CaptureRequest request, TotalCaptureResult result) { 1189 int aeState = result.get(CaptureResult.CONTROL_AE_STATE); 1190 if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED || 1191 aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 1192 waitForAeCondition.open(); 1193 } 1194 } 1195 }; 1196 startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler); 1197 1198 precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1199 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1200 startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler); 1201 assertTrue("Timeout out waiting for AE to converge", 1202 waitForAeCondition.block(AE_TIMEOUT_MS)); 1203 } 1204 1205 request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 1206 CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON); 1207 if (maxResolution) { 1208 request.set(CaptureRequest.SENSOR_PIXEL_MODE, 1209 CaptureRequest.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); 1210 } 1211 CameraTestUtils.SimpleCaptureCallback resultListener = 1212 new CameraTestUtils.SimpleCaptureCallback(); 1213 1214 CaptureRequest request1 = request.build(); 1215 for (int i = 0; i < numShots; i++) { 1216 startCapture(request1, /*repeating*/false, resultListener, mHandler); 1217 } 1218 List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>(); 1219 for (int i = 0; i < numShots; i++) { 1220 // Verify capture result and images 1221 CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS); 1222 1223 List<Image> resultImages = new ArrayList<Image>(); 1224 for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) { 1225 Image captureImage = 1226 captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS * timeoutScale); 1227 1228 /*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(), 1229 ImageFormat.RAW_SENSOR, null);*/ 1230 resultImages.add(captureImage); 1231 } 1232 ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result)); 1233 } 1234 // Stop capture, delete the streams. 1235 stopCapture(/*fast*/false); 1236 1237 return ret; 1238 } 1239 1240 /** 1241 * Use the DNG SDK to validate a DNG file stored in the buffer. 1242 * 1243 * Returns false if the DNG has validation errors. Validation warnings/errors 1244 * will be printed to logcat. 1245 */ validateDngNative(byte[] dngBuffer)1246 private static native boolean validateDngNative(byte[] dngBuffer); 1247 } 1248