1 /* 2 * Copyright (C) 2013 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.its; 18 19 import android.content.Context; 20 import android.graphics.ImageFormat; 21 import android.hardware.camera2.CameraDevice; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.CaptureResult; 25 import android.hardware.camera2.params.MeteringRectangle; 26 import android.hardware.camera2.params.StreamConfigurationMap; 27 import android.media.Image; 28 import android.media.Image.Plane; 29 import android.net.Uri; 30 import android.os.Environment; 31 import android.util.Log; 32 import android.util.Size; 33 34 import org.json.JSONArray; 35 import org.json.JSONObject; 36 37 import java.nio.ByteBuffer; 38 import java.nio.charset.Charset; 39 import java.util.ArrayList; 40 import java.util.List; 41 42 public class ItsUtils { 43 public static final String TAG = ItsUtils.class.getSimpleName(); 44 jsonToByteBuffer(JSONObject jsonObj)45 public static ByteBuffer jsonToByteBuffer(JSONObject jsonObj) { 46 return ByteBuffer.wrap(jsonObj.toString().getBytes(Charset.defaultCharset())); 47 } 48 getJsonWeightedRectsFromArray( JSONArray a, boolean normalized, int width, int height)49 public static MeteringRectangle[] getJsonWeightedRectsFromArray( 50 JSONArray a, boolean normalized, int width, int height) 51 throws ItsException { 52 try { 53 // Returns [x0,y0,x1,y1,wgt, x0,y0,x1,y1,wgt, x0,y0,x1,y1,wgt, ...] 54 assert(a.length() % 5 == 0); 55 MeteringRectangle[] ma = new MeteringRectangle[a.length() / 5]; 56 for (int i = 0; i < a.length(); i += 5) { 57 int x,y,w,h; 58 if (normalized) { 59 x = (int)Math.floor(a.getDouble(i+0) * width + 0.5f); 60 y = (int)Math.floor(a.getDouble(i+1) * height + 0.5f); 61 w = (int)Math.floor(a.getDouble(i+2) * width + 0.5f); 62 h = (int)Math.floor(a.getDouble(i+3) * height + 0.5f); 63 } else { 64 x = a.getInt(i+0); 65 y = a.getInt(i+1); 66 w = a.getInt(i+2); 67 h = a.getInt(i+3); 68 } 69 x = Math.max(x, 0); 70 y = Math.max(y, 0); 71 w = Math.min(w, width-x); 72 h = Math.min(h, height-y); 73 int wgt = a.getInt(i+4); 74 ma[i/5] = new MeteringRectangle(x,y,w,h,wgt); 75 } 76 return ma; 77 } catch (org.json.JSONException e) { 78 throw new ItsException("JSON error: ", e); 79 } 80 } 81 getOutputSpecs(JSONObject jsonObjTop)82 public static JSONArray getOutputSpecs(JSONObject jsonObjTop) 83 throws ItsException { 84 try { 85 if (jsonObjTop.has("outputSurfaces")) { 86 return jsonObjTop.getJSONArray("outputSurfaces"); 87 } 88 return null; 89 } catch (org.json.JSONException e) { 90 throw new ItsException("JSON error: ", e); 91 } 92 } 93 getRaw16OutputSizes(CameraCharacteristics ccs)94 public static Size[] getRaw16OutputSizes(CameraCharacteristics ccs) 95 throws ItsException { 96 return getOutputSizes(ccs, ImageFormat.RAW_SENSOR); 97 } 98 getRaw10OutputSizes(CameraCharacteristics ccs)99 public static Size[] getRaw10OutputSizes(CameraCharacteristics ccs) 100 throws ItsException { 101 return getOutputSizes(ccs, ImageFormat.RAW10); 102 } 103 getRaw12OutputSizes(CameraCharacteristics ccs)104 public static Size[] getRaw12OutputSizes(CameraCharacteristics ccs) 105 throws ItsException { 106 return getOutputSizes(ccs, ImageFormat.RAW12); 107 } 108 getJpegOutputSizes(CameraCharacteristics ccs)109 public static Size[] getJpegOutputSizes(CameraCharacteristics ccs) 110 throws ItsException { 111 return getOutputSizes(ccs, ImageFormat.JPEG); 112 } 113 getYuvOutputSizes(CameraCharacteristics ccs)114 public static Size[] getYuvOutputSizes(CameraCharacteristics ccs) 115 throws ItsException { 116 return getOutputSizes(ccs, ImageFormat.YUV_420_888); 117 } 118 getMaxOutputSize(CameraCharacteristics ccs, int format)119 public static Size getMaxOutputSize(CameraCharacteristics ccs, int format) 120 throws ItsException { 121 return getMaxSize(getOutputSizes(ccs, format)); 122 } 123 getOutputSizes(CameraCharacteristics ccs, int format)124 private static Size[] getOutputSizes(CameraCharacteristics ccs, int format) 125 throws ItsException { 126 StreamConfigurationMap configMap = ccs.get( 127 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 128 if (configMap == null) { 129 throw new ItsException("Failed to get stream config"); 130 } 131 Size[] normalSizes = configMap.getOutputSizes(format); 132 Size[] slowSizes = configMap.getHighResolutionOutputSizes(format); 133 Size[] allSizes = null; 134 if (normalSizes != null && slowSizes != null) { 135 allSizes = new Size[normalSizes.length + slowSizes.length]; 136 System.arraycopy(normalSizes, 0, allSizes, 0, 137 normalSizes.length); 138 System.arraycopy(slowSizes, 0, allSizes, normalSizes.length, 139 slowSizes.length); 140 } else if (normalSizes != null) { 141 allSizes = normalSizes; 142 } else if (slowSizes != null) { 143 allSizes = slowSizes; 144 } 145 return allSizes; 146 } 147 getMaxSize(Size[] sizes)148 public static Size getMaxSize(Size[] sizes) { 149 if (sizes == null || sizes.length == 0) { 150 throw new IllegalArgumentException("sizes was empty"); 151 } 152 153 Size maxSize = sizes[0]; 154 for (int i = 1; i < sizes.length; i++) { 155 if (sizes[i].getWidth() * sizes[i].getHeight() > 156 maxSize.getWidth() * maxSize.getHeight()) { 157 maxSize = sizes[i]; 158 } 159 } 160 161 return maxSize; 162 } 163 getDataFromImage(Image image)164 public static byte[] getDataFromImage(Image image) 165 throws ItsException { 166 int format = image.getFormat(); 167 int width = image.getWidth(); 168 int height = image.getHeight(); 169 byte[] data = null; 170 171 // Read image data 172 Plane[] planes = image.getPlanes(); 173 174 // Check image validity 175 if (!checkAndroidImageFormat(image)) { 176 throw new ItsException( 177 "Invalid image format passed to getDataFromImage: " + image.getFormat()); 178 } 179 180 if (format == ImageFormat.JPEG) { 181 // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer. 182 ByteBuffer buffer = planes[0].getBuffer(); 183 data = new byte[buffer.capacity()]; 184 buffer.get(data); 185 return data; 186 } else if (format == ImageFormat.YUV_420_888 || format == ImageFormat.RAW_SENSOR 187 || format == ImageFormat.RAW10 || format == ImageFormat.RAW12) { 188 int offset = 0; 189 data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8]; 190 int maxRowSize = planes[0].getRowStride(); 191 for (int i = 0; i < planes.length; i++) { 192 if (maxRowSize < planes[i].getRowStride()) { 193 maxRowSize = planes[i].getRowStride(); 194 } 195 } 196 byte[] rowData = new byte[maxRowSize]; 197 for (int i = 0; i < planes.length; i++) { 198 ByteBuffer buffer = planes[i].getBuffer(); 199 int rowStride = planes[i].getRowStride(); 200 int pixelStride = planes[i].getPixelStride(); 201 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8; 202 Logt.i(TAG, String.format( 203 "Reading image: fmt %d, plane %d, w %d, h %d, rowStride %d, pixStride %d", 204 format, i, width, height, rowStride, pixelStride)); 205 // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling. 206 int w = (i == 0) ? width : width / 2; 207 int h = (i == 0) ? height : height / 2; 208 for (int row = 0; row < h; row++) { 209 if (pixelStride == bytesPerPixel) { 210 // Special case: optimized read of the entire row 211 int length = w * bytesPerPixel; 212 buffer.get(data, offset, length); 213 // Advance buffer the remainder of the row stride 214 if (row < h - 1) { 215 buffer.position(buffer.position() + rowStride - length); 216 } 217 offset += length; 218 } else { 219 // Generic case: should work for any pixelStride but slower. 220 // Use intermediate buffer to avoid read byte-by-byte from 221 // DirectByteBuffer, which is very bad for performance. 222 // Also need avoid access out of bound by only reading the available 223 // bytes in the bytebuffer. 224 int readSize = rowStride; 225 if (buffer.remaining() < readSize) { 226 readSize = buffer.remaining(); 227 } 228 buffer.get(rowData, 0, readSize); 229 if (pixelStride >= 1) { 230 for (int col = 0; col < w; col++) { 231 data[offset++] = rowData[col * pixelStride]; 232 } 233 } else { 234 // PixelStride of 0 can mean pixel isn't a multiple of 8 bits, for 235 // example with RAW10. Just copy the buffer, dropping any padding at 236 // the end of the row. 237 int length = (w * ImageFormat.getBitsPerPixel(format)) / 8; 238 System.arraycopy(rowData,0,data,offset,length); 239 offset += length; 240 } 241 } 242 } 243 } 244 Logt.i(TAG, String.format("Done reading image, format %d", format)); 245 return data; 246 } else { 247 throw new ItsException("Unsupported image format: " + format); 248 } 249 } 250 checkAndroidImageFormat(Image image)251 private static boolean checkAndroidImageFormat(Image image) { 252 int format = image.getFormat(); 253 Plane[] planes = image.getPlanes(); 254 switch (format) { 255 case ImageFormat.YUV_420_888: 256 case ImageFormat.NV21: 257 case ImageFormat.YV12: 258 return 3 == planes.length; 259 case ImageFormat.RAW_SENSOR: 260 case ImageFormat.RAW10: 261 case ImageFormat.RAW12: 262 case ImageFormat.JPEG: 263 return 1 == planes.length; 264 default: 265 return false; 266 } 267 } 268 } 269