Home | History | Annotate | Download | only in test
      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 (at) 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 
     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