1 /* 2 * Copyright (C) 2011 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 com.android.cts.verifier.camera.analyzer; 18 19 import android.graphics.Bitmap; 20 import android.graphics.BitmapFactory; 21 import android.graphics.ImageFormat; 22 import android.hardware.Camera; 23 import android.util.Log; 24 import android.widget.ImageView; 25 import java.io.FileOutputStream; 26 import java.io.FileNotFoundException; 27 import java.io.IOException; 28 29 import java.util.List; 30 31 /** 32 * Implements a test to verify whether the correlated color temperatures (CTT) 33 * of the supported white balance modes are inside the range camera 34 * manufacturers generally agree on. 35 * 36 * The test assumes that the Daylight white balance mode has a CCT of 5200K, 37 * which is widely agreed in industry and academics. It then use this as a 38 * benchmark and compare images taken with other white balance settings. 39 * Using the pixel values of the grey squares on the color checker, the CCT 40 * of other white balance modes can be computed. The reference ranges were 41 * summarized with the help of online resources. For the Auto mode, the 42 * reference CCT is computed as the CCT that will keep the grey squares appear 43 * grey in the result image. 44 */ 45 public class WhiteBalanceTest extends CameraTests { 46 47 private static final String TAG = "WhiteBalanceTest"; 48 49 /** Current white balance mode. */ 50 private String mWhiteBalance; 51 /** Array to store the reference CCT's of each mode. */ 52 private int[][] mReferenceTemperature; 53 /** List of supported white balance mode on a device. */ 54 private List<String> mWhiteBalanceList; 55 /** The index of the white balance mode "Auto". */ 56 private int mAutoId; 57 58 /** Debug results in text. */ 59 private String mDebugText; 60 /** Memory address of the native test handler instance. */ 61 private long mTestHandler; 62 /** Thread lock. */ 63 private final Object mProcessingImage = new Object(); 64 /** Test result to show. */ 65 private int[] mTestResults; 66 /** Number of test. */ 67 private int mNumTests; 68 /** Camera Parameters. */ 69 private Camera.Parameters mParams; 70 /** Singleton test instance. */ 71 private static WhiteBalanceTest singletonTest = null; 72 /** Boolean to check whehter daylight wb has been recorded. */ 73 private boolean mHasDaylight = false; 74 75 /** 76 * Constructs a <code>WhiteBalanceTest</code> instance with a given 77 * Camera pointer. 78 */ WhiteBalanceTest()79 private WhiteBalanceTest() { 80 super(); 81 } 82 updateCamera()83 public void updateCamera() { 84 mAutoId = 0; 85 mHasDaylight = false; 86 mParams = mTestCamera.getParameters(); 87 mWhiteBalanceList = mParams.getSupportedWhiteBalance(); 88 mNumTests = mWhiteBalanceList.size() + 1; 89 mTestResults = new int[mNumTests]; 90 for (int i = 0; i < mNumTests; ++i) { 91 mTestResults[i] = CameraTests.CAMERA_TEST_NOT_RUN; 92 } 93 94 if (mWhiteBalanceList != null) { 95 mReferenceTemperature = new int[mWhiteBalanceList.size()][2]; 96 97 // Sets the reference CCT of the supported white balance modes 98 for (int i = 0; i < mWhiteBalanceList.size(); i++) { 99 setReferenceTemperature(i, mWhiteBalanceList.get(i)); 100 if (mWhiteBalanceList.get(i).equals("auto")) { 101 mAutoId = i; 102 } 103 } 104 } 105 } 106 getSingletonTest()107 public static synchronized WhiteBalanceTest getSingletonTest() { 108 if (singletonTest == null) { 109 Log.v(TAG, "Creating a new WhiteBalanceTest instance"); 110 singletonTest = new WhiteBalanceTest(); 111 singletonTest.initializeTest(); 112 } 113 return singletonTest; 114 } 115 initializeTest()116 private void initializeTest() { 117 mDebugText = new String(); 118 // Creates a native white balance test handler. 119 // mTestHandler stores the memory address of this instance. 120 mTestHandler = createWhiteBalanceTest(); 121 } 122 initializeWhiteBalanceTest()123 private void initializeWhiteBalanceTest() { 124 mWhiteBalance = "daylight"; 125 takePicture(mWhiteBalance); 126 int colorTemperature = processWhiteBalanceTest(mTestHandler); 127 128 setReferenceTemperature(mAutoId, colorTemperature); 129 mHasDaylight = true; 130 } 131 takePicture(String whiteBalance)132 private void takePicture(String whiteBalance) { 133 mParams.setWhiteBalance(whiteBalance); 134 mTestCamera.setParameters(mParams); 135 136 try{ 137 Log.v(TAG, "Waiting for white balance to adjust"); 138 Thread.sleep(4000); 139 Log.v(TAG, "END Waiting"); 140 } catch (InterruptedException e) {} 141 142 mTestCamera.takePicture(null, null, null, mTestJpegListener); 143 144 // Thread locks until image capture is done 145 synchronized (mProcessingImage) { 146 try{ 147 Log.v(TAG, "Start waiting for Image"); 148 mProcessingImage.wait(); 149 } catch (InterruptedException e) { 150 Log.v(TAG, "Callback wait fails!"); 151 } 152 } 153 } 154 155 /** 156 * Runs the white balance camera test instance. 157 */ 158 @Override run(int index)159 public synchronized void run(int index) { 160 Log.v(TAG, "WhiteBalanceTest thread started!"); 161 162 if (!mHasDaylight) { 163 initializeWhiteBalanceTest(); 164 } 165 166 if (index != 0) { 167 // Retrieves the list of supported white balance mode. 168 mParams = mTestCamera.getParameters(); 169 170 int i = index - 1; 171 mWhiteBalance = mWhiteBalanceList.get(i); 172 173 Log.v(TAG, "Current white balance is " + mWhiteBalance); 174 175 takePicture(mWhiteBalance); 176 177 // Processes the white balance test data in the native code. 178 // Returns an array of CCT of each white balance modes, given the CCT 179 // of the "Daylight" mode is 5200K. 180 int colorTemperature = 0; 181 182 Log.v(TAG, "Finished taking picture, ready to process"); 183 colorTemperature = processWhiteBalanceTest(mTestHandler); 184 185 // Records the index of the "Auto" white balance mode 186 if (mWhiteBalance.equals("daylight")) { 187 setReferenceTemperature(mAutoId, colorTemperature); 188 prepareDebugText(5200, index); 189 // Computes the reference CCT range of the "Auto" mode. Assuming that 190 // all grey squares on the color checker should be imaged as grey under 191 // a CCT, this CCT is used as a middle point to provide a range. 192 //setReferenceTemperature(mAutoId, colorTemperature[mWhiteBalanceList.size()]); 193 } else { 194 // Prepares the debug output. 195 prepareDebugText(colorTemperature, index); 196 } 197 } else { 198 for (int i = 0; i < mWhiteBalanceList.size(); i++) { 199 run(i + 1); 200 } 201 } 202 203 mParams.setWhiteBalance("auto"); 204 mTestCamera.setParameters(mParams); 205 } 206 207 /** 208 * Prepares the debug results in HTML text. For each white balance mode, 209 * the CCT will be printed in green if it is in the reference range and 210 * red otherwise. The reference CCT range is also printed below, with 211 * green meaning the CCT range is satisfied and red otherwise. 212 * 213 * @param colorTemperature the CCT of the supported white balance modes 214 */ prepareDebugText(int colorTemperature, int index)215 private void prepareDebugText(int colorTemperature, int index) { 216 mDebugText += String.format("CCT Ref is %d, %d, and CCT is %d", 217 mReferenceTemperature[index - 1][0], 218 mReferenceTemperature[index - 1][1], 219 colorTemperature); 220 Log.v(TAG, String.format("CCT Ref is %d, %d, and CCT is %d", 221 mReferenceTemperature[index - 1][0], 222 mReferenceTemperature[index - 1][1], 223 colorTemperature)); 224 if ((colorTemperature >= mReferenceTemperature[index - 1][0]) && 225 (colorTemperature <= mReferenceTemperature[index - 1][1])) { 226 mTestResults[index] = CameraTests.CAMERA_TEST_SUCCESS; 227 } else { 228 mTestResults[index] = CameraTests.CAMERA_TEST_FAILURE; 229 } 230 231 } 232 233 @Override getDebugText()234 public String getDebugText() { 235 return mDebugText; 236 } 237 238 @Override getResultText()239 public String getResultText() { 240 return mDebugText; 241 } 242 243 @Override getTestName()244 public String getTestName() { 245 return "White Balance Test: \n"; 246 } 247 248 @Override getTestName(int index)249 public String getTestName(int index) { 250 if (index != 0){ 251 return String.format("%s mode test", mWhiteBalanceList.get(index - 1)); 252 } else { 253 return "Run all tests"; 254 } 255 } 256 257 @Override getResult(int index)258 public int getResult(int index) { 259 return mTestResults[index]; 260 } 261 262 @Override getNumTests()263 public int getNumTests() { 264 return mNumTests; 265 } 266 267 /** 268 * Sets the reference temperatures for the white balance modes of 269 * incandescent, fluorescent, warm-fluorescent, daylight, cloudy, shade 270 * and twilight. These are the currently supported White balance mode 271 * listed on the Android camera API. 272 * 273 * The reference range are summarized based on the published settings of 274 * Canon, Nikon and the references from Wikipedia on color temperature. 275 * 276 * @param i the index of the current white balance mode 277 * @param referenceWhiteBalance the name of the white balance mode. 278 */ setReferenceTemperature(int i, String referenceWhiteBalance)279 private void setReferenceTemperature(int i, String referenceWhiteBalance) { 280 if (referenceWhiteBalance.equals("incandescent")) { 281 mReferenceTemperature[i][0] = 2500; 282 mReferenceTemperature[i][1] = 3500; 283 } else if (referenceWhiteBalance.equals("fluorescent")) { 284 mReferenceTemperature[i][0] = 3000; 285 mReferenceTemperature[i][1] = 6500; 286 } else if (referenceWhiteBalance.equals("warm-fluorescent")) { 287 mReferenceTemperature[i][0] = 2500; 288 mReferenceTemperature[i][1] = 3000; 289 } else if (referenceWhiteBalance.equals("daylight")) { 290 mReferenceTemperature[i][0] = 5000; 291 mReferenceTemperature[i][1] = 5400; 292 } else if (referenceWhiteBalance.equals("cloudy-daylight")) { 293 mReferenceTemperature[i][0] = 5500; 294 mReferenceTemperature[i][1] = 7500; 295 } else if (referenceWhiteBalance.equals("shade")) { 296 mReferenceTemperature[i][0] = 6800; 297 mReferenceTemperature[i][1] = 8000; 298 } else if (referenceWhiteBalance.equals("twilight")) { 299 mReferenceTemperature[i][0] = 10000; 300 mReferenceTemperature[i][1] = 14000; 301 } 302 } 303 304 /** Sets a reference range of CCT based on a given middle point CCT. 305 * The rerence range is from -10% of the CCT value to +10%. 306 * 307 * @param i the index of the current white balance mode 308 * @param t the middle point CCT. 309 */ setReferenceTemperature(int i, int t)310 private void setReferenceTemperature(int i, int t) { 311 mReferenceTemperature[i][0] = (int)((double)t * 0.9); 312 mReferenceTemperature[i][1] = (int)((double)t * 1.1); 313 } 314 315 private Camera.PictureCallback mTestJpegListener = new Camera.PictureCallback() { 316 public void onPictureTaken(byte[] data, Camera mCamera) { 317 Log.v(TAG, "Shutter pressed down!"); 318 Bitmap inputImage; 319 try { 320 FileOutputStream outStream = new FileOutputStream( 321 String.format("/sdcard/wb%d.jpg", System.currentTimeMillis())); 322 outStream.write(data); 323 outStream.close(); 324 } catch (FileNotFoundException e) { 325 } catch (IOException e) {} 326 // Decodes the camera data to Bitmap and creates a native image 327 // class with the Bitmap. 328 inputImage = BitmapFactory.decodeByteArray(data, 0, data.length); 329 long bufferAddress = findNative(inputImage); 330 Log.v(TAG, "findNative method finishes"); 331 332 // Cleans up the Bitmap memory space. 333 inputImage.recycle(); 334 data = null; 335 inputImage = null; 336 System.gc(); 337 338 // Adds the data from the current image base class to the native 339 // white balance test handler. 340 createWhiteBalanceClass(bufferAddress, mTestHandler, 341 getCheckerCenter(), getCheckerRadius(), mWhiteBalance); 342 343 mCamera.startPreview(); 344 345 // Notifies the thread lock that the image capture is finished 346 synchronized (mProcessingImage) { 347 mProcessingImage.notifyAll(); 348 } 349 } 350 }; 351 352 /** 353 * Creates a native white balance test handler. 354 * 355 * @return the memory address of the test handler 356 */ createWhiteBalanceTest()357 private native long createWhiteBalanceTest(); 358 359 /** 360 * Adds the data of interest from the image class pointed by 361 * <code>bufferAddress</code> to the handler class pointed by 362 * <code>handlerAddress</code> by using the color checker coordinates. 363 * Also sets the white balance of this test. 364 * 365 * @param bufferAddress the memory address of the native image class 366 * containing the current camera captured image 367 * @param handlerAddress the memory address of the native white balance 368 * test handler instance 369 * @param checkerCenterAddress the memory address of the color checker 370 * center coordinates 371 * @param checkerRadiusAddress the memory address of the color checker 372 * radius 373 * @param whiteBalance the white balance mode used for shooting the image 374 */ createWhiteBalanceClass(long bufferAddress, long handlerAddress, long checkerCenterAddress, long checkerRadiusAddress, String whiteBalance)375 private native void createWhiteBalanceClass(long bufferAddress, long handlerAddress, 376 long checkerCenterAddress, 377 long checkerRadiusAddress, 378 String whiteBalance); 379 380 /** 381 * Processes the white balance test in the native code. This is executed 382 * after the images are taken with all possible white balance modes. It 383 * uses the "Daylight" white balance mode as reference and computes the 384 * CCT of other white balance modes. 385 * 386 * @param handlerAddress the memory address of the native white balance 387 * test handler instance. 388 * @param colorTemperature the array to store the computed CCT of all white 389 * balance modes. 390 */ processWhiteBalanceTest(long handlerAddress)391 private native int processWhiteBalanceTest(long handlerAddress); 392 getAutoTemperature(long handlerAddress)393 private native int getAutoTemperature(long handlerAddress); 394 395 static { 396 System.loadLibrary("cameraanalyzer"); 397 } 398 } 399