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