Last active
January 11, 2016 14:45
-
-
Save Samsy/8fe8a385eb696f238491 to your computer and use it in GitHub Desktop.
DepthDecoder Streetview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const base64 = require('base-64'); | |
const zpipe = require("zpipe"); | |
const jsonpClient = require('jsonp-client') | |
export default class DepthDecoder { | |
constructor(panoID) { | |
this.getDepth(panoID) | |
} | |
getDepth(panoID) { | |
let url = "http://maps.google.com/cbk?output=json&cb_client=maps_sv&v=4&dm=1&pm=1&ph=1&hl=en&panoid=" + panoID; | |
jsonpClient(this.addCallback(url), (err, data) => { | |
if (err) { | |
console.log(err) | |
return; | |
} else { | |
this.decodeDepth(data) | |
} | |
}); | |
} | |
decodeDepth(data) { | |
let decoded = this.decode(data.model.depth_map); | |
this.depthMap = this.parse(decoded); | |
let canvas = document.createElement("canvas"); | |
let context = canvas.getContext('2d'); | |
let w = this.depthMap.width; | |
let h = this.depthMap.height; | |
canvas.setAttribute('width', w); | |
canvas.setAttribute('height', h); | |
let image = context.getImageData(0, 0, w, h); | |
let y = 0 | |
while (y < h) { | |
let x = 0 | |
while (x < w) { | |
let c = this.depthMap.depthMap[y * w + x] / 50 * 255; | |
image.data[4 * (y * w + x)] = c; | |
image.data[4 * (y * w + x) + 1] = c; | |
image.data[4 * (y * w + x) + 2] = c; | |
image.data[4 * (y * w + x) + 3] = 255; | |
x++ | |
} | |
y++ | |
} | |
context.putImageData(image, 0, 0); | |
// gDepthMap = this.depthMap; | |
canvas.style.top = '0px' | |
canvas.style.position = 'absolute' | |
document.body.appendChild(canvas); | |
} | |
parse(depthmap) { | |
let depthMapData; | |
let header; | |
let data; | |
let depthMap; | |
depthMapData = new DataView(depthmap.buffer); | |
header = this.parseHeader(depthMapData); | |
data = this.parsePlanes(header, depthMapData); | |
depthMap = this.computeDepthMap(header, data.indices, data.planes); | |
console.log(depthMap) | |
return depthMap; | |
} | |
parseHeader(depthMap) { | |
return { | |
headerSize: depthMap.getUint8(0), | |
numberOfPlanes: depthMap.getUint16(1, true), | |
width: depthMap.getUint16(3, true), | |
height: depthMap.getUint16(5, true), | |
offset: depthMap.getUint16(7, true) | |
}; | |
} | |
parsePlanes(header, depthMap) { | |
var planes = [], | |
indices = [], | |
i, | |
n = [0, 0, 0], | |
d, | |
byteOffset; | |
for (i = 0; i < header.width * header.height; ++i) { | |
indices.push(depthMap.getUint8(header.offset + i)); | |
} | |
for (i = 0; i < header.numberOfPlanes; ++i) { | |
byteOffset = header.offset + header.width * header.height + i * 4 * 4; | |
n[0] = depthMap.getFloat32(byteOffset, true); | |
n[1] = depthMap.getFloat32(byteOffset + 4, true); | |
n[2] = depthMap.getFloat32(byteOffset + 8, true); | |
d = depthMap.getFloat32(byteOffset + 12, true); | |
planes.push({ | |
n: n.slice(0), | |
d: d | |
}); | |
} | |
return { | |
planes: planes, | |
indices: indices | |
}; | |
} | |
computeDepthMap(header, indices, planes) { | |
var depthMap = null, | |
x, y, | |
planeIdx, | |
phi, theta, | |
v = [0, 0, 0], | |
w = header.width, | |
h = header.height, | |
plane, t, p; | |
depthMap = new Float32Array(w * h); | |
var sin_theta = new Float32Array(h); | |
var cos_theta = new Float32Array(h); | |
var sin_phi = new Float32Array(w); | |
var cos_phi = new Float32Array(w); | |
for (y = 0; y < h; ++y) { | |
theta = (h - y - 0.5) / h * Math.PI; | |
sin_theta[y] = Math.sin(theta); | |
cos_theta[y] = Math.cos(theta); | |
} | |
for (x = 0; x < w; ++x) { | |
phi = (w - x - 0.5) / w * 2 * Math.PI + Math.PI / 2; | |
sin_phi[x] = Math.sin(phi); | |
cos_phi[x] = Math.cos(phi); | |
} | |
for (y = 0; y < h; ++y) { | |
for (x = 0; x < w; ++x) { | |
planeIdx = indices[y * w + x]; | |
v[0] = sin_theta[y] * cos_phi[x]; | |
v[1] = sin_theta[y] * sin_phi[x]; | |
v[2] = cos_theta[y]; | |
if (planeIdx > 0) { | |
plane = planes[planeIdx]; | |
t = Math.abs(plane.d / (v[0] * plane.n[0] + v[1] * plane.n[1] + v[2] * plane.n[2])); | |
depthMap[y * w + (w - x - 1)] = t; | |
} else { | |
depthMap[y * w + (w - x - 1)] = 9999999999999999999.; | |
} | |
} | |
} | |
return { | |
width: w, | |
height: h, | |
depthMap: depthMap | |
}; | |
} | |
decode(rawDepthMap) { | |
let compressedDepthMapData; | |
let depthMap; | |
let decompressedDepthMap; | |
// Append '=' in order to make the length of the array a multiple of 4 | |
while (rawDepthMap.length % 4 != 0) { | |
rawDepthMap += '='; | |
} | |
// Replace '-' by '+' and '_' by '/' | |
rawDepthMap = rawDepthMap.replace(/-/g, '+'); | |
rawDepthMap = rawDepthMap.replace(/_/g, '/'); | |
// Decode and decompress data | |
compressedDepthMapData = base64.decode(rawDepthMap); | |
decompressedDepthMap = zpipe.inflate(compressedDepthMapData); | |
// Convert output of decompressor to Uint8Array | |
depthMap = new Uint8Array(decompressedDepthMap.length); | |
for (var i = 0; i < decompressedDepthMap.length; ++i) { | |
depthMap[i] = decompressedDepthMap.charCodeAt(i); | |
} | |
return depthMap; | |
} | |
ajaxGet(url) { | |
return new Promise(function(resolve, reject) { | |
let req = new XMLHttpRequest(); | |
req.responseType = 'jsonp'; | |
req.open("GET", url); | |
req.onload = function() { | |
if (req.status === 200) { | |
resolve(req.response); | |
} else { | |
reject(new Error(req.statusText)); | |
} | |
}; | |
req.onerror = function() { | |
reject(new Error("Network error")); | |
}; | |
req.send(); | |
}); | |
} | |
addCallback(url) { | |
// The URL already has a callback | |
if (url.match(/callback=[a-z]/i)) { | |
return url; | |
} | |
return url + ("&callback=cb" + Math.random()).replace('.', ''); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment