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
17package com.android.rs.refocus;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.net.Uri;
23import com.android.rs.refocus.image.RangeInverseDepthTransform;
24import com.android.rs.refocus.image.RangeLinearDepthTransform;
25import java.io.FileNotFoundException;
26import java.io.IOException;
27import java.io.InputStream;
28
29public class DepthImage {
30    private final String mFormat;
31    private final double mFar;
32    private final double mNear;
33    private final Bitmap mDepthBitmap;
34    private final double mBlurAtInfinity;
35    private final double mFocalDistance;
36    private final double mDepthOfField;
37    private final double mFocalPointX;
38    private final double mFocalPointY;
39    private final DepthTransform mDepthTransform;
40
41    public DepthImage(String format, double far, double near,
42                      Bitmap depthBitmap, double blurAtInfinity,
43                      double focalDistance, double depthOfField,
44                      double focalPointX, double focalPointY,
45                      DepthTransform depthTransform) {
46        mFormat = format;
47        mFar = far;
48        mNear = near;
49        mDepthBitmap = depthBitmap;
50        mBlurAtInfinity = blurAtInfinity;
51        mFocalDistance = focalDistance;
52        mDepthOfField = depthOfField;
53        mFocalPointX = focalPointX;
54        mFocalPointY = focalPointY;
55        mDepthTransform = depthTransform;
56    }
57
58    public static DepthImage createFromXMPMetadata(Context context, Uri image)
59            throws IOException {
60        InputStream input = context.getContentResolver().openInputStream(image);
61        XmpDepthDecode decode = new XmpDepthDecode(input);
62        return new DepthImage(decode.getFormat(), decode.getFar(),
63                              decode.getNear(), decode.getDepthBitmap(),
64                              decode.getBlurAtInfinity(),
65                              decode.getFocalDistance(),
66                              decode.getDepthOfField(),
67                              decode.getFocalPointX(),
68                              decode.getFocalPointY(),
69                              decode.getDepthTransform());
70    }
71
72    public static DepthImage createFromDepthmap(Context context, Uri uriDepthmap)
73            throws IOException {
74        Bitmap bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uriDepthmap));
75        if (bitmap == null) {
76            throw new FileNotFoundException(uriDepthmap.toString());
77        }
78
79        double near = 12.0;
80        double far = 120.0;
81        DepthTransform transform = new RangeInverseDepthTransform((float)near, (float)far);
82        return new DepthImage(RangeInverseDepthTransform.FORMAT,
83                              far,
84                              near,
85                              bitmap, // depthmap
86                              5.0,    // blur at ininity
87                              15.0,   // focal distance
88                              0.1,    // depth of field
89                              0.5,    // x of focal point
90                              0.5,    // y of focla point
91                              transform);
92    }
93
94    private static class MinMax {
95        public float min;
96        public float max;
97
98        public MinMax(float min, float max) {
99            this.min = min;
100            this.max = max;
101        }
102
103        public static MinMax create(float min, float max) {
104            return new MinMax(min, max);
105        }
106    }
107
108    private static MinMax findMinAndMax(final float[] array) {
109        float min = array[0];
110        float max = min;
111        for (int i = 1; i < array.length; i++) {
112            final float x = array[i];
113            if (x < min) {
114                min = x;
115            } else if (x > max) {
116                max = x;
117            }
118        }
119        return MinMax.create(min, max);
120    }
121
122    public static DepthImage createFromPFM(Context context, Uri uriPFM)
123            throws IOException {
124        PortableFloatMap pfm = new PortableFloatMap(context.getContentResolver().openInputStream(uriPFM));
125
126        final float[] floatPixels = pfm.getPixelArray();
127        int[] intPixels = new int[floatPixels.length];
128        final MinMax minMax = findMinAndMax(floatPixels);
129        final float near = minMax.min;
130        final float far  = minMax.max;
131        DepthTransform transform = new RangeInverseDepthTransform(near, far);
132
133/*
134        android.util.Log.v("DepthImage", "near = " + String.format("%g", near));
135        android.util.Log.v("DepthImage", "far  = " + String.format("%g", far));
136*/
137        int width = pfm.getWidth();
138        int height = pfm.getHeight();
139
140        for (int i = 0; i < intPixels.length; i++) {
141            int value = transform.quantize(floatPixels[i]) & 0xFF;
142            intPixels[i] = value | (value << 8) | (value << 16) | (0xFF << 24);
143/*
144            if (i >= intPixels.length - width) {
145                android.util.Log.v("DepthImage", "float pixel " + i + ":" + String.format("%g", floatPixels[i]));
146                android.util.Log.v("DepthImage", "int pixel   " + i + ":" + String.format("0x%02X", intPixels[i]));
147            }
148*/
149        }
150
151        Bitmap bitmap = Bitmap.createBitmap(intPixels, width, height, Bitmap.Config.ARGB_8888);
152
153        // MediaStoreSaver.savePNG(bitmap, "depthmap", "balls", context);
154
155        return new DepthImage(RangeInverseDepthTransform.FORMAT,
156                              far,
157                              near,
158                              bitmap, // depthmap
159                              400.0,  // blur at ininity
160                              //15.0,  // blur at ininity
161                              19.6,   // focal distance
162                              //near+(far-near)*0.2,   // focal distance
163                              //5.0,    // flocal distance
164                              0.005,    // depth of field
165                              0.4,    // x of focal point
166                              0.4,    // y of focal point
167                              transform);
168    }
169
170    public Bitmap getDepthBitmap() {
171        return mDepthBitmap;
172    }
173
174    public DepthTransform getDepthTransform() { return mDepthTransform; }
175
176    public String getFormat() {
177        return mFormat;
178    }
179
180    public double getFar() {
181        return mFar;
182    }
183
184    public double getNear() {
185        return mNear;
186    }
187
188    public double getBlurAtInfinity() {
189        return mBlurAtInfinity;
190    }
191
192    public double getFocalDistance() {
193        return mFocalDistance;
194    }
195
196    public double getDepthOfField() {return mDepthOfField; }
197
198    public double getFocalPointX() {
199        return mFocalPointX;
200    }
201
202    public double getFocalPointY() {
203        return mFocalPointY;
204    }
205}
206
207