1fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni/*
2fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * Copyright (C) 2015 The Android Open Source Project
3fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni *
4fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * Licensed under the Apache License, Version 2.0 (the "License");
5fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * you may not use this file except in compliance with the License.
6fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * You may obtain a copy of the License at
7fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni *
8fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni *      http://www.apache.org/licenses/LICENSE-2.0
9fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni *
10fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * Unless required by applicable law or agreed to in writing, software
11fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * distributed under the License is distributed on an "AS IS" BASIS,
12fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * See the License for the specific language governing permissions and
14fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni * limitations under the License.
15fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni */
16fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
174cead8034aab0e20e402baee87cbe9129db00192Stephen Hinespackage com.android.rs.refocus;
18de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
19de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.content.ContentResolver;
20de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.content.Context;
21de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.graphics.Bitmap;
22de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.graphics.BitmapFactory;
23de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.graphics.Color;
24de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.net.Uri;
25de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport android.util.Log;
26fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Niimport com.android.rs.refocus.image.RangeInverseDepthTransform;
27de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport java.io.FileNotFoundException;
28de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangimport java.io.IOException;
29de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
30de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang/**
31de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * An RGBZ image, where Z stands for depth, i.e. a color+depth image.
32de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * The RGBZ always has a preview image, which represents the latest rendering of the RGBZ.
33de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * The preview is encoded as the normal jpeg content for client compatibility,
34de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * while the color channel and depth channels are encoded as XMP data.
35de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * The class supports lazy initialization where the XMP meta data is loaded only when first
36de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang * accessed.
37de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang */
38de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wangpublic class RGBZ {
39de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public static final String TAG = "RGBZ";
40de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
41de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private Bitmap bitmap;
42de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private Bitmap preview;
43de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private Bitmap depthBitmap;
44de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private DepthTransform depthTransform;
45de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private DepthImage depthImage;
46de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
47de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
48de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * Creates an RGBZ from a content uri.
49de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   *
50de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @param uri The uri name of the RGBZ
51de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @throws FileNotFoundException if the RGBZ could not be read
52de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
53de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public RGBZ(Uri uri, ContentResolver contentResolver, Context context) throws IOException {
54de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    preview = BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
55de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    if (preview == null) {
56de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      throw new FileNotFoundException(uri.toString());
57de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    }
58fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthImage = DepthImage.createFromXMPMetadata(context, uri);
59fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthBitmap = depthImage.getDepthBitmap();
60fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    //MediaStoreSaver.savePNG(depthBitmap, "depthmap", "depthmap", context);
61fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    bitmap = setAlphaChannel(preview, depthBitmap);
62fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthTransform = depthImage.getDepthTransform();
63fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni  }
64fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
65fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni  /**
66fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   * Creates an RGBZ from uris to an image and a depthmap.
67fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   *
68fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   * @param uriImage The uri name of the image
69fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   * @param uriDepthmap The uri name of the depthmap
70fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   * @throws FileNotFoundException if the RGBZ could not be read
71fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni   */
72fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni  public RGBZ(Uri uriImage, Uri uriDepthmap, ContentResolver contentResolver,
73fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni              Context context) throws IOException {
74fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    preview = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
75fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    if (preview == null) {
76fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni      throw new FileNotFoundException(uriImage.toString());
77fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    }
78fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthImage = DepthImage.createFromDepthmap(context, uriDepthmap);
79fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthBitmap = depthImage.getDepthBitmap();
80fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    bitmap = setAlphaChannel(preview, depthBitmap);
81fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    depthTransform = depthImage.getDepthTransform();
82de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
83de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
84fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
85fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    public RGBZ(Bitmap image, DepthImage depthImage) {
86fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        preview = image;
87fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        this.depthImage = depthImage;
88fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        depthBitmap = depthImage.getDepthBitmap();
89fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        bitmap = setAlphaChannel(preview, depthBitmap);
90fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        depthTransform = depthImage.getDepthTransform();
91fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    }
92fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
93fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    public static RGBZ createFromBitmapDepthmap(Uri uriImage, Uri uriDepthmap,
94fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni                                                ContentResolver contentResolver, Context context)
95fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni            throws IOException {
96fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        Bitmap image = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
97fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        if (image == null) {
98fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni            throw new FileNotFoundException(uriImage.toString());
99fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        }
100fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        DepthImage depthImage = DepthImage.createFromDepthmap(context, uriDepthmap);
101fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        return new RGBZ(image, depthImage);
102fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    }
103fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
104fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    public static RGBZ createFromPFMDepthmap(Uri uriImage, Uri uriDepthmap,
105fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni                                             ContentResolver contentResolver, Context context)
106fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni            throws IOException {
107fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        Bitmap image = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
108fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        if (image == null) {
109fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni            throw new FileNotFoundException(uriImage.toString());
110fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        }
111fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        DepthImage depthImage = DepthImage.createFromPFM(context, uriDepthmap);
112fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        MediaStoreSaver.savePNG(depthImage.getDepthBitmap(), "depthmap",
113fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni                                "depthmap", context);
114fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni        return new RGBZ(image, depthImage);
115fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni    }
116fb2af6b101911d7abf65a519704cdaebb0cae6fdYang Ni
117de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
118de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return Whether the RGBZ has a depth channel
119de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
120de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public boolean hasDepthmap() {
121de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return depthTransform != null;
122de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
123de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
124de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
125de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return The color+depth {@code Bitmap}
126de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
127de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public Bitmap getBitmap() {
128de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return bitmap;
129de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
130de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
131de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
132de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return The depthmap component of this RGBZ
133de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
134de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public DepthTransform getDepthTransform() {
135de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return depthTransform;
136de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
137de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
138de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public double getFocusDepth() {
139de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return this.depthImage.getFocalDistance();
140de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
141de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
142de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public double getDepthOfField() {
143de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return this.depthImage.getDepthOfField();
144de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
145de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
146de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public double getBlurInfinity() {
147de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return this.depthImage.getBlurAtInfinity();
148de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
149de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
150de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
151de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return the width of this {@code RGBZ}
152de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
153de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public int getWidth() {
154de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return bitmap.getWidth();
155de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
156de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
157de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
158de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return the height of this {@code RGBZ}
159de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
160de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public int getHeight() {
161de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return bitmap.getHeight();
162de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
163de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
164de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
165de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * @return the depth value of the given pixel
166de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
167de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
168de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public float getDepth(int x, int y) {
169de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    if (!hasDepthmap()) {
170de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      return 0.0f;
171de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    }
172de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    if (x < 0 || x > depthBitmap.getWidth() ||
173de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang            y < 0 || y > depthBitmap.getHeight()) {
174de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      Log.e("RGBZ getDepth", "index out of bound");
175de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      return 0;
176de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    }
177de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return getDepthTransform().reconstruct(Color.blue(depthBitmap.getPixel(x, y)));
178de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
179de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
180de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  /**
181de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   * Sets the depthmap as the alpha channel of the {@code Bitmap}.
182de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang   */
183de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  public Bitmap setAlphaChannel(Bitmap bitmap, Bitmap depthBitmap) {
184de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    if (bitmap == null) {
185de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      return bitmap;
186de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    }
187de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888, true);
188de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    // set the alpha channel of depthBitmap to alpha of bitmap
189de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    result = setAlphaChannelFromBitmap(depthBitmap, bitmap, result);
190de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return result;
191de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
192de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
193de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  private Bitmap setAlphaChannelFromBitmap(Bitmap depth, Bitmap orig, Bitmap dest) {
194de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    int w = orig.getWidth();
195de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    int h = orig.getHeight();
196de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    int[] orig_data = new int[w*h];
197de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    int[] depth_data = new int[w*h];
198de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
199de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    orig.getPixels(orig_data, 0, w, 0, 0, w, h);
200de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    depth.getPixels(depth_data, 0, w, 0, 0, w, h);
201de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    for (int i = 0; i < orig_data.length; i++) {
202de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      int v = orig_data[i] & 0x00FFFFFF;
203de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      int temp = (depth_data[i] & 0x000000FF) << 24;
204de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      v = v | temp;
205de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang      orig_data[i] = v;
206de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    }
207de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    dest.setPixels(orig_data, 0, w, 0, 0, w, h);
208de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang    return dest;
209de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang  }
210de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang}
211de2f182fdb522689e05280e449a39ec2c1b53e1fCindy Wang
212