Android Q - Get depth map from portrait mode photos

708 views
Skip to first unread message

Bourgaf

unread,
Nov 22, 2019, 1:34:29 PM11/22/19
to android-platform
Hello everyone !

I'm trying to build a sample Android App to extract the depth map of portrait mode photos taken with the google camera app. I know that it's saved along the blurred photos.

I read the Dynamic Depth Format documentation coming from Google : https://developer.android.com/training/camera2/Dynamic-depth-v1.0.pdf
It's pretty new and I don't find any resource related to this subject or how to manage the extraction of an android portrait's depth map.

I used metadata-extractor java library to read file's metadata programmatically, particularly the XMP Part as it's where informations are described for depth. I wrote a sample code in Kotlin to try to extract the depth map:


val inputStream = contentResolver.openInputStream(imageUri)


inputStream
?.let { stream ->
    val metadata
= JpegMetadataReader.readMetadata(stream)
    val directories
= metadata.directories
    val xmpDirectories
= metadata.getDirectoriesOfType(XmpDirectory::class.java)


   
for (xmpDirectory in xmpDirectories) {
        val xmpMeta
= xmpDirectory.xmpMeta
        val itr
= xmpMeta.iterator()


       
while (itr.hasNext()) {
            val propertyInfo
= itr.next() as XMPPropertyInfo
           
            println
(propertyInfo.path + " :: " + propertyInfo.value)
       
}
   
}


    stream
.close()
}


the resulting output :



    xmpNote
:HasExtendedXMP :: 5c970bbab778024b23c5a8269325455c
   
null :: null
   
GCreations:CameraBurstID :: 9e286063-a919-4a74-96ee-d7e02d2a17d2
   
null :: null
   
GCamera:BurstID :: 9e286063-a919-4a74-96ee-d7e02d2a17d2
   
GCamera:BurstPrimary :: 1
   
GCamera:SpecialTypeID ::
   
GCamera:SpecialTypeID[1] :: com.google.android.apps.camera.gallery.specialtype.SpecialType-PORTRAIT
   
null :: null
   
Device:Container ::
   
Device:Container/Container:Directory ::
   
Device:Container/Container:Directory[1] ::
   
Device:Container/Container:Directory[1]/Item:Mime :: image/jpeg
   
Device:Container/Container:Directory[1]/Item:Length :: 0
   
Device:Container/Container:Directory[1]/Item:DataURI :: primary_image
   
Device:Container/Container:Directory[1]/rdf:type :: http://ns.google.com/photos/dd/1.0/container/:Item
   
Device:Container/Container:Directory[2] ::
   
Device:Container/Container:Directory[2]/Item:Mime :: image/jpeg
   
Device:Container/Container:Directory[2]/Item:Length :: 1499039
   
Device:Container/Container:Directory[2]/Item:DataURI :: android/original_image
   
Device:Container/Container:Directory[2]/rdf:type :: http://ns.google.com/photos/dd/1.0/container/:Item
   
Device:Container/Container:Directory[3] ::
   
Device:Container/Container:Directory[3]/Item:Mime :: image/jpeg
   
Device:Container/Container:Directory[3]/Item:Length :: 316885
   
Device:Container/Container:Directory[3]/Item:DataURI :: android/depthmap
   
Device:Container/Container:Directory[3]/rdf:type :: http://ns.google.com/photos/dd/1.0/container/:Item
   
Device:Container/Container:Directory[4] ::
   
Device:Container/Container:Directory[4]/Item:Mime :: image/jpeg
   
Device:Container/Container:Directory[4]/Item:Length :: 65189
   
Device:Container/Container:Directory[4]/Item:DataURI :: android/confidencemap
   
Device:Container/Container:Directory[4]/rdf:type :: http://ns.google.com/photos/dd/1.0/container/:Item
   
Device:Profiles ::
   
Device:Profiles[1] ::
   
Device:Profiles[1]/Profile:Type :: DepthPhoto
   
Device:Profiles[1]/Profile:CameraIndices ::
   
Device:Profiles[1]/Profile:CameraIndices[1] :: 0
   
Device:Profiles[1]/rdf:type :: http://ns.google.com/photos/dd/1.0/device/:Profile
   
Device:Cameras ::
   
Device:Cameras[1] ::
   
Device:Cameras[1]/Camera:Trait :: Physical
   
Device:Cameras[1]/Camera:Image ::
   
Device:Cameras[1]/Camera:Image/Image:ItemSemantic :: Original
   
Device:Cameras[1]/Camera:Image/Image:ItemURI :: android/original_image
   
Device:Cameras[1]/Camera:DepthMap ::
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:ItemSemantic :: Depth
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:Format :: RangeInverse
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:Units :: Diopters
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:Near :: 0.302570
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:Far :: 1.754560
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:DepthURI :: android/depthmap
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:MeasureType :: OpticalAxis
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:ConfidenceURI :: android/confidencemap
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:FocalTableEntryCount :: 256
   
Device:Cameras[1]/Camera:DepthMap/DepthMap:FocalTable :: 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
   
Device:Cameras[1]/Camera:ImagingModel ::
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:FocalLengthX :: 3187.589355
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:FocalLengthY :: 3187.589355
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:ImageWidth :: 4032
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:ImageHeight :: 3024
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:PrincipalPointX :: 2000.483154
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:PrincipalPointY :: 1541.417236
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:Skew :: 0.000000
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:PixelAspectRatio :: 1.000000
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:DistortionCount :: 4
   
Device:Cameras[1]/Camera:ImagingModel/ImagingModel:Distortion :: AACAPwAAAABBQ7w9AAAAAEgGf74AAAAA5SpFPgAAAAA
   
Device:Cameras[1]/rdf:type :: http://ns.google.com/photos/dd/1.0/device/:Camera


according to the Google's documentation, the depth map image is serialize as a base64 string XMP property. but i don't know how to extract it to generate a new image based this depth data. I think that I almost solved my issue but I miss some acknowledgment about Adobe XMP standard. 

I found something called "sidecar Xmp files" and maybe the depth map that I'm trying to find is in it. 

I managed to see that the depth map is embedded in the photo by uploading it on https://www.photopea.com i didn't find any other software or web-app able to do this.

i'm stuck at this step, i don't know how to go further, please help !

gjs

unread,
Nov 24, 2019, 9:12:11 PM11/24/19
to android-platform
Hi,

From that documentation - " P38... Depth maps are images of integer or real values" & from your output -

    Device:Container/Container:Directory[3]/Item:Mime :: image/jpeg
    
Device:Container/Container:Directory[3]/Item:Length :: 316885
    
Device:Container/Container:Directory[3]/Item:DataURI :: android/depthmap

So the depth map is more likely to be an embedded (16bit?) monochrome jpeg image and the Focal table is a base 64 encoded string.

This library & example should help you with extracting the depth map -


see in the example -

fin = new FileInputStream("images/table.jpg");
        JPEGMeta.extractDepthMap(fin, "table");

        fin.close();
...
cheers

Nick ZHOU

unread,
Mar 3, 2020, 10:53:01 PM3/3/20
to android-platform
Hi, any progress on this? I have the same issue here, the doc is not that clear to explain how to transform back from the focal table back to a depth map image
Reply all
Reply to author
Forward
0 new messages