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