1 package com.android.rs.refocus; 2 3 import android.util.Log; 4 import java.util.ArrayList; 5 6 /** 7 * An object that contains all the parameters that are needed in refocusing 8 * filtering function, including the range of depth levels, the disc blur radius 9 * of each depth level, how the depth levels are grouped into layers, which 10 * layer is in focus. 11 * 12 * <b> Here by "depth", we mean inverse depth. Pixels with larger depth values 13 * are closer to the camera. 14 * 15 * For a layer n, its depth interval is (@code [layerInfo[n].backDepth, 16 * layerInfo[n].frontDepth]), where (@code backDepth<=frontDepth). 17 * 18 * The layers are ordered from near to far; note that near layers have larger 19 * depth values. 20 * 21 * (@code focusLayer) is the index of the layer that is in focus, that is, has 22 * zero blur. 23 * 24 * @author zhl@google.com (Li Zhang) 25 */ 26 27 public class BlurStack { 28 //private static final Log.Tag TAG = new Log.Tag("BlurStack"); 29 private static final String TAG = "BlurStack"; 30 /** 31 * The cap for disc radius of blur kernels. 32 */ 33 private static final float MAX_DISC_RADIUS = 25.0f; 34 35 /** 36 * The minimum of the interval that is used to group depth levels into 37 * blending layers based on the corresponding blur disk radius. 38 */ 39 private static final float MIN_DISK_RADIUS_STEP_SIZE = 2.0f; 40 41 /** 42 * The starting index of depth quantization level. Must be positive as zero is 43 * reserved for invalid depth. 44 */ 45 private static final int MIN_DEPTH = 1; 46 47 /** 48 * The ending index of depth quantization level. It must be a power of 2. 49 */ 50 private static final int MAX_DEPTH = 64; 51 52 /** 53 * The scale to apply to 8-bit depthmaps. 54 */ 55 private static final int DEPTH_SCALE = 256 / MAX_DEPTH; 56 57 /** 58 * For each depth value {@code d} within [MIN_DEPTH,MAX_DEPTH], its blur disc 59 * radius is saved in {@code diskRadius[d-MIN_DEPTH]}. Hence the length 60 * {@code diskRadius} is {@code MAX_DEPTH-MIN_DEPTH+1}. 61 */ 62 private float[] diskRadiusArray; 63 64 /** 65 * A set of non-overlapping layers that covers all the depth levels. The 66 * layers are ordered from front (closer to the camera) to back (farther away 67 * from the camera). 68 */ 69 private LayerInfo[] layerInfo; 70 71 /** 72 * The layer in which the focal depth belongs to. <b> For this layer, we 73 * assume that it is a single depth layer. That is, the front depth and back 74 * depth both equal to focal depth. 75 */ 76 private int focusLayer; 77 getMaxDiskRadius()78 public static float getMaxDiskRadius() { 79 return MAX_DISC_RADIUS; 80 } 81 82 /** 83 * Returns the blur disk radius of a depth level. 84 * 85 * @param depth depth level 86 * @return the blur disk radius of the depth level 87 */ getDiskRadius(int depth)88 public float getDiskRadius(int depth) { 89 return diskRadiusArray[depth - MIN_DEPTH]; 90 } 91 getNumLayers()92 public int getNumLayers() { 93 return layerInfo.length; 94 } 95 getLayerInfo(int layer)96 public LayerInfo getLayerInfo(int layer) { 97 return layerInfo[layer]; 98 } 99 100 /** 101 * Returns the number of depths in a given layer. 102 * 103 * @param layer layer index 104 * @return the number of depth levels in the layer 105 */ getNumDepths(int layer)106 public int getNumDepths(int layer) { 107 return layerInfo[layer].frontDepth - layerInfo[layer].backDepth + 1; 108 } 109 getFocusLayer()110 public int getFocusLayer() { 111 return focusLayer; 112 } 113 114 /** 115 * Returns the depth given the layer and the relative depth in the layer. 116 * 117 * @param layer the layer index 118 * @param relativeDepthInLayer the relative depth index relative to the back 119 * depth of a layer 120 * @return the depth 121 */ getDepth(int layer, int relativeDepthInLayer)122 public int getDepth(int layer, int relativeDepthInLayer) { 123 return layerInfo[layer].backDepth + relativeDepthInLayer; 124 } 125 126 /** 127 * Creates an instance of BlurStack using depth range, focal depth, desired 128 * amount of blur at infinity, and the number of blending layers. 129 * 130 * @param depthTransform an object that translates between floating depth and 131 * quantized depth. 132 * @param focusDepth3D focus depth in 3D 133 * @param depthOfField the range of depth values around focus depth 3D that 134 * has zero blur. 135 * @param blurInfinity the desired amount of blur, represented as blur radius 136 * at infinity 137 * @param numBlendingLayers the number of blending layers that group all the 138 * depth levels 139 * @return an instance of {@code BlurStack} 140 */ createFromDepthTransform( final DepthTransform depthTransform, float focusDepth3D, float depthOfField, float blurInfinity, int numBlendingLayers)141 public static BlurStack createFromDepthTransform( 142 final DepthTransform depthTransform, float focusDepth3D, 143 float depthOfField, float blurInfinity, int numBlendingLayers) { 144 BlurStack blurStack = new BlurStack(); 145 // Finds the front and back depth levels for the focus layer. 146 if (depthOfField < 0) { 147 depthOfField = -depthOfField; 148 Log.e(TAG, "Negative depth of field"); 149 } 150 int frontFocalDepth = openglDepthToStackDepth( 151 depthTransform.quantize(focusDepth3D * (1 - depthOfField))); 152 int backFocalDepth = openglDepthToStackDepth( 153 depthTransform.quantize(focusDepth3D * (1 + depthOfField))); 154 // Computes blur disk radius for all the depth levels. 155 blurStack.computeDiskRadius(depthTransform, frontFocalDepth, backFocalDepth, 156 blurInfinity); 157 158 if (numBlendingLayers >= MAX_DEPTH) { 159 blurStack.generateOneLayerForEachDepth(frontFocalDepth, backFocalDepth); 160 } else { 161 // Sets the max variation of blur disk radius in a blending layer. 162 float diskRadiusInterval = (blurStack.getDiskRadius(MIN_DEPTH) 163 + blurStack.getDiskRadius(MAX_DEPTH)) / numBlendingLayers; 164 diskRadiusInterval = 165 Math.max(diskRadiusInterval, MIN_DISK_RADIUS_STEP_SIZE); 166 // Computes {@code layerInfo, focusLayer}, assuming {@code diskRadius} 167 // have been computed. 168 blurStack.groupDepthLevelsIntoLayers(frontFocalDepth, backFocalDepth, 169 diskRadiusInterval); 170 } 171 return blurStack; 172 } 173 174 @Override toString()175 public String toString() { 176 String s = "disparity range: " + MAX_DEPTH + ", " + MIN_DEPTH + "\n"; 177 s += "focus disparity: " + layerInfo[focusLayer].frontDepth + ", " 178 + layerInfo[focusLayer].backDepth + "\n"; 179 s += "num of layers: " + getNumLayers() + "\n"; 180 s += "focus layer: " + focusLayer + "\n"; 181 182 for (int n = 0; n < layerInfo.length; ++n) { 183 int front = layerInfo[n].frontDepth; 184 int back = layerInfo[n].backDepth; 185 s += "\nlayer " + n + " num of disparities " + (front - back + 1) + "\n"; 186 187 for (int d = front; d >= back; --d) { 188 s += "layer " + n + " disparity " + d + " disk radius " 189 + getDiskRadius(d) + "\n"; 190 } 191 } 192 193 return s; 194 } 195 196 /** 197 * OpenGL depth is from 0(near) to 255(far). The depth in BlurStack is from 198 * 1(far) to MAX_DEPTH(near). Converts from openglDepth to stackDepth. 199 * 200 * @param openglDepth openGL depth. 201 * @return stackDepth stack depth. 202 */ openglDepthToStackDepth(int openglDepth)203 private static int openglDepthToStackDepth(int openglDepth) { 204 return MAX_DEPTH - (openglDepth / DEPTH_SCALE); 205 } 206 207 /** 208 * OpenGL depth is from 0(near) to 255(far). The depth in BlurStack is from 209 * 1(far) to MAX_DEPTH(near). Converts from stackDepth to openglDepth. 210 * 211 * @param stackDepth stack depth. 212 * @return openglDepth openGL depth. 213 */ stackDepthToOpenglDepth(int stackDepth)214 private static int stackDepthToOpenglDepth(int stackDepth) { 215 return (MAX_DEPTH - stackDepth) * DEPTH_SCALE; 216 } 217 218 /** 219 * A private constructor that forces users to use {@code createFromDepthRange} 220 * to construct an instance of BlurStack. 221 */ BlurStack()222 private BlurStack() {} 223 224 /** 225 * Quantizes the depth range into MAX_DEPTH levels in inverse depth space, and 226 * for each level, computes the blur disk radius. 227 * 228 * @param depthTransform an object that translates between floating depth and 229 * quantized depth. 230 * @param frontFocalDepth front focal depth level 231 * @param backFocalDepth back focal depth level 232 * @param blurInfinity the amount of desired blur represented as the blur 233 * radius at infinity 234 */ computeDiskRadius(final DepthTransform depthTransform, int frontFocalDepth, int backFocalDepth, float blurInfinity)235 private void computeDiskRadius(final DepthTransform depthTransform, 236 int frontFocalDepth, int backFocalDepth, float blurInfinity) { 237 int numLevels = MAX_DEPTH - MIN_DEPTH + 1; 238 diskRadiusArray = new float[numLevels]; 239 float frontFocalDepth3D = 240 depthTransform.reconstruct(stackDepthToOpenglDepth(frontFocalDepth)); 241 float backFocalDepth3D = 242 depthTransform.reconstruct(stackDepthToOpenglDepth(backFocalDepth)); 243 244 // Computes the blur disk radius for each depth level. 245 for (int depth = MIN_DEPTH; depth <= MAX_DEPTH; ++depth) { 246 float depth3D = 247 depthTransform.reconstruct(stackDepthToOpenglDepth(depth)); 248 float radius = 0; 249 if (depth3D < frontFocalDepth3D) { 250 radius = blurInfinity * (frontFocalDepth3D - depth3D) / depth3D; 251 } else if (depth3D > backFocalDepth3D) { 252 radius = blurInfinity * (depth3D - backFocalDepth3D) / depth3D; 253 } 254 diskRadiusArray[depth - MIN_DEPTH] = Math.min(radius, MAX_DISC_RADIUS); 255 } 256 } 257 258 /** 259 * Sets up {@code focusLayer} such that each layer contains only a single 260 * depth, except that the focal layer contains frontFocalDepth and 261 * backFocalDepth. 262 * 263 * <b> This function computes {@code layerInfo, focusLayer}. 264 * 265 * @param frontFocalDepth the front depth of focal layer. 266 * @param backFocalDepth the back depth of focal layer. 267 */ generateOneLayerForEachDepth(int frontFocalDepth, int backFocalDepth)268 private void generateOneLayerForEachDepth(int frontFocalDepth, 269 int backFocalDepth) { 270 int numLayers = 271 MAX_DEPTH - MIN_DEPTH + 1 - (frontFocalDepth - backFocalDepth); 272 layerInfo = new LayerInfo[numLayers]; 273 274 // Pushes single depth layers in front of the focal layer to layerInfo. 275 int layer = 0; 276 for (int depth = MAX_DEPTH; depth > frontFocalDepth; --depth, ++layer) { 277 layerInfo[layer] = new LayerInfo(depth); 278 } 279 280 // Pushes focal layer to layerInfo. 281 focusLayer = layer; 282 layerInfo[layer] = new LayerInfo(frontFocalDepth, backFocalDepth); 283 ++layer; 284 285 // Pushes single depth layers behind the focal layer to layerInfo. 286 for (int depth = backFocalDepth - 1; depth >= MIN_DEPTH; --depth, ++layer) { 287 layerInfo[layer] = new LayerInfo(depth); 288 } 289 } 290 291 /** 292 * Sets up {@code focusLayer} such that within each layer, the blur radius 293 * variation due to depth difference is no larger than 294 * {@code diskRadiusInterval}. 295 * 296 * <b> This function computes {@code layerInfo, focusLayer}, assuming that 297 * {@code diskRadius} have been properly initialized. 298 * 299 * @param frontFocalDepth the front depth of focal layer. 300 * @param backFocalDepth the back depth of focal layer. 301 * @diskRadiusInterval the max allowed blur disk radius difference within each 302 * layer. 303 */ groupDepthLevelsIntoLayers(int frontFocalDepth, int backFocalDepth, float diskRadiusInterval)304 private void groupDepthLevelsIntoLayers(int frontFocalDepth, 305 int backFocalDepth, float diskRadiusInterval) { 306 // Groups depth levels behind the focal depth into several layers. 307 // The blur radius difference in each layer is no larger than 308 // diskRadiusInterval. 309 ArrayList<LayerInfo> layerInfoBehindFocus = 310 groupDepthLevelsBehindFocus(backFocalDepth, diskRadiusInterval); 311 312 // Groups depth levels in front of the focal depth into several layers. 313 // The blur radius difference in each layer is no larger than {@code 314 // diskRadiusInterval}. 315 ArrayList<LayerInfo> layerInfoInFrontOfFocus = 316 groupDepthLevelsInFrontOfFocus(frontFocalDepth, diskRadiusInterval); 317 318 // Merges the two groups of layers into one stack of layers, plus the focus 319 // depth layer. 320 int numLayers = 321 layerInfoInFrontOfFocus.size() + 1 + layerInfoBehindFocus.size(); 322 layerInfo = new LayerInfo[numLayers]; 323 focusLayer = layerInfoInFrontOfFocus.size(); 324 325 // The merged layers is ordered from the front-most layer to the back-most 326 // layer. 327 for (int n = 0; n < numLayers; ++n) { 328 if (n < layerInfoInFrontOfFocus.size()) { 329 // Finds the corresponding layer index m in layerInfoInFrontOfFocus, 330 // which is ordered from focal depth to front-most. 331 int m = (layerInfoInFrontOfFocus.size() - 1) - n; 332 layerInfo[n] = layerInfoInFrontOfFocus.get(m); 333 } else if (n == layerInfoInFrontOfFocus.size()) { 334 layerInfo[n] = new LayerInfo(frontFocalDepth, backFocalDepth); 335 } else { 336 // Finds the corresponding layer index m in layerInfoBehindFocus, which 337 // is ordered from focal depth to back-most. 338 int m = n - (layerInfoInFrontOfFocus.size() + 1); 339 layerInfo[n] = layerInfoBehindFocus.get(m); 340 } 341 } 342 } 343 344 /** 345 * Groups depth levels behind the focal depth into several layers. The blur 346 * radius difference in each layer is no larger than 347 * {@code diskRadiusInterval}. 348 * 349 * @param backFocalDepth the back depth of focal layer. 350 * @param diskRadiusInterval max disk radius variation in each layer 351 * @return layerInfo layering of depth levels behind the focal depth 352 */ groupDepthLevelsBehindFocus(int backFocalDepth, float diskRadiusInterval)353 private ArrayList<LayerInfo> groupDepthLevelsBehindFocus(int backFocalDepth, 354 float diskRadiusInterval) { 355 // Initializes the layerInfo array with maximum capacity needed. 356 ArrayList<LayerInfo> layerInfo = 357 new ArrayList<LayerInfo>(diskRadiusArray.length); 358 359 if (backFocalDepth == MIN_DEPTH) { 360 return layerInfo; 361 } 362 363 // At this point, focusDepth > minDepth. 364 // Moves to the first depth behind the focus depth and initializes a layer. 365 int d = backFocalDepth - 1; 366 layerInfo.add(new LayerInfo(d)); 367 // Sets up the max radius threshold for the layer. 368 float radiusThreshold = getDiskRadius(d) + diskRadiusInterval; 369 370 // Expands the layer to include depth levels so long as the blur disk 371 // radius within the layer is not larger than radiusThreshold. 372 // Stops the expansion when current depth is already the minDepth. 373 while (d > MIN_DEPTH) { 374 // Moves to the next depth. 375 d--; 376 if (getDiskRadius(d) <= radiusThreshold) { 377 // Expands the current layer by lowering its back depth. 378 int numLayers = layerInfo.size(); 379 layerInfo.get(numLayers - 1).backDepth = d; 380 } else { 381 // Generates a new single-depth layer. 382 // Expands it in the next iteration if necessary. 383 layerInfo.add(new LayerInfo(d)); 384 radiusThreshold = getDiskRadius(d) + diskRadiusInterval; 385 } 386 } 387 return layerInfo; 388 } 389 390 /** 391 * Groups depth levels in front of the focal depth into several layers. The 392 * blur radius difference in each layer is no larger than 393 * {@code diskRadiusInterval}. 394 * 395 * @param frontFocalDepth the back depth of focal layer. 396 * @param diskRadiusInterval max disk radius variation in each layer 397 * @return layerInfo layering of depth levels behind the focal depth 398 */ groupDepthLevelsInFrontOfFocus( int frontFocalDepth, float diskRadiusInterval)399 private ArrayList<LayerInfo> groupDepthLevelsInFrontOfFocus( 400 int frontFocalDepth, float diskRadiusInterval) { 401 // Initializes the layerInfo array with maximum capacity needed. 402 ArrayList<LayerInfo> layerInfo = 403 new ArrayList<LayerInfo>(diskRadiusArray.length); 404 405 if (frontFocalDepth == MAX_DEPTH) { 406 return layerInfo; 407 } 408 409 // At this point, focusDepth < maxDepth. 410 // Moves to the first depth in front of the focus depth and initializes a 411 // layer. 412 int d = frontFocalDepth + 1; 413 layerInfo.add(new LayerInfo(d)); 414 // Sets up the max radius threshold for the layer. 415 float radiusThreshold = getDiskRadius(d) + diskRadiusInterval; 416 417 // Expands the layer to include depth levels so long as the blur disk 418 // radius within the layer is not larger than radiusThreshold. 419 // Stops the expansion when current depth is already the maxDepth. 420 while (d < MAX_DEPTH) { 421 // Moves to the next depth. 422 d++; 423 if (getDiskRadius(d) <= radiusThreshold) { 424 // Expands the current layer by increasing its front depth. 425 int numLayers = layerInfo.size(); 426 layerInfo.get(numLayers - 1).frontDepth = d; 427 } else { 428 // Generates a new single-depth layer. 429 // Expands it in the next iteration if necessary. 430 layerInfo.add(new LayerInfo(d)); 431 radiusThreshold = getDiskRadius(d) + diskRadiusInterval; 432 } 433 } 434 return layerInfo; 435 } 436 } 437