1package com.android.rs.refocus;
2
3import android.util.Log;
4import 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
27public 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
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   */
88  public float getDiskRadius(int depth) {
89    return diskRadiusArray[depth - MIN_DEPTH];
90  }
91
92  public int getNumLayers() {
93    return layerInfo.length;
94  }
95
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   */
106  public int getNumDepths(int layer) {
107    return layerInfo[layer].frontDepth - layerInfo[layer].backDepth + 1;
108  }
109
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   */
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   */
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
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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