在街景中获取像素的标题和音高 [英] Get heading and pitch from pixels on Street View

查看:122
本文介绍了在街景中获取像素的标题和音高的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图获得嵌入式Google街景视图中任何点击标题和标题。

I think this is the best place for this question.

我知道并能得到的唯一信息是:


  • 视野(度)

  • 中心点的标题和音高(以度为单位)以及x和y像素位置
  • 鼠标点击的x和y像素位置

    I am trying to get the heading and pitch of any clicked point on an embedded Google Street View.

    我在这里列出了一个以简化测量为例的屏幕截图:

    The only pieces of information I know and can get are:

    我最初只是想你可以将视野除以像素宽度以获得每像素的度数,但它更复杂,我认为它与投射到球体内部有关,相机位于球体的中心?

    • The field of view (degrees)
    • The center point's heading and pitch (in degrees) and x and y pixel position
    • The x and y pixel position of the mouse click

    说明:
    目标不是将视图移动到点击点,而是提供有关点击点的信息。每像素度数方法不起作用,因为视口不是线性的。

    I've included here a screenshot with simplified measurements as an example:

    这里的数值只是一个例子,但是视野可以更大或更小(从[0.something,180]开始,并且中心不是固定的,它可以是[0,360]和垂直[-90,90]范围内的任何值,点[0,0]仅仅是照片加密时的照片的标题(水平度)和间距(垂直度)

    标题和音高参数 h0 p0 f 来缩放该方向矢量,可以获得三维坐标(u0,v0)的视口中心的$ c>(x0,y0,z0):

    pre $ x0 = f * cos(p0)* sin(h0)
    y0 = f * cos(p0)* cos(h0)
    z0 = f * sin(p0)
    $ b $ p
    $ b

    现在的目标是找到某个给定像素坐标点的三维坐标(u,v) 在图像中。首先,将这些像素坐标从视口中心映射到像素偏移(du,dv)(向右和向上):

    Then a local orthonormal 2D basis of the viewport in 3D has to be found. The unit vector (ux, uy, uz) supports the x-axis (to the right along the direction of increasing headings) and the vector (vx, vy, vz) supports the y-axis (to the top along the direction of increasing pitches) of the image. Once these two vectors are determined, the 3D coordinates of the point on the viewport matching the (du, dv) pixel offset in the viewport are simply:

    x = x0 + du * ux + dv * vx
    y = y0 + du * uy + dv * vy
    z = z0 + du * uz + dv * vz
    

    然后必须找到3D视口的局部正交二维基础。单位向量(ux,uy,uz)支持x轴(沿着增加标题的方向向右)和向量(vx ,vy,vz)支持图像的y轴(沿着增加节距的方向顶部)。一旦确定了这两个向量,视口上匹配(du,dv)像素偏移量的点的3D坐标就是:

    And the heading and pitch parameters h and p for this point are then:

    R = sqrt( x * x + y * y + z * z )
    h = atan2( x, y )
    p = asin( z / R )
    

    标题和音高参数 h p 就是这样:

    Finally to get the two unit vectors (ux, uy, uz) and (vx, vy, vz), compute the derivatives of the spherical coordinates by the heading and pitch parameters at (p0, h0), and one should get:

    vx = -sin( p0 ) * sin ( h0 )
    vy = -sin( p0 ) * cos ( h0 )
    vz =  cos( p0 ) 
    
    ux =  sgn( cos ( p0 ) ) * cos( h0 )
    uy = -sgn( cos ( p0 ) ) * sin( h0 )
    uz = 0
    

    最后得到两个单位向量(ux,uy,uz)(vx,vy,vz),根据(p0,h0)中的航向和俯仰参数计算球面坐标的导数,并且应该得到:

    where sgn( a ) is +1 if a >= 0 else -1.

    Complements:

    其中 sgn(a) +1 if a> = 0 其他 -1

    补充:


    • 焦距是从水平视野中获得的, image:

      f = (w / 2) / Math.tan(fov / 2)

    • The reverse mapping from heading and pitch parameters to pixel coordinates can be done similarly:


      1. 找到与指定航向和俯仰参数相对应的射线方向的3D坐标(x,y,z)
      2. 查找与视口中心对应的光线方向的3D坐标(x0,y0,z0)图像平面位于(x0,y0,z0),其中(x0,y0,z0) normal),

      3. 使用图像平面将指定的标题和音高参数与射线相交,这会给出3D偏移fr
      4. 以本地方式投影此3D偏移量,获得2D偏移量 du dv

      5. 地图 du dv 至绝对像素坐标。

      1. Find the 3D coordinates (x, y, z) of the direction of the ray corresponding to the specified heading and pitch parameters,
      2. Find the 3D coordinates (x0, y0, z0) of the direction of the ray corresponding to the viewport center (an associated image plane is located at (x0, y0, z0) with an (x0, y0, z0) normal),
      3. Intersect the ray for the specified heading and pitch parameters with the image plane, this gives the 3D offset from the viewport center,
      4. Project this 3D offset on the local basis, getting the 2D offsets du and dv
      5. Map du and dv to absolute pixel coordinates.

    • 实际上,这种方法在正方形和矩形视口上看起来效果很好。

    • In practice, this approach seems to work similarly well on both square and rectangular viewports.

      验证概念代码(在函数中调用 onLoad()函数)包含一个带有全景ID的大小的canvas元素的网页)

      Proof of concept code (call the onLoad() function on a web page containing a sized canvas element with a "panorama" id)

      'use strict';
      
      var viewer;
      
      function onClick(e) {
        viewer.click(e);
      }
      
      function onLoad() {
        var element = document.getElementById("panorama");
        viewer = new PanoramaViewer(element);
        viewer.update();
      }
      
      function PanoramaViewer(element) {
        this.element = element;
        this.width = element.width;
        this.height = element.height;
        this.pitch = 0;
        this.heading = 0;
      
        element.addEventListener("click", onClick, false);
      }
      
      PanoramaViewer.FOV = 90;
      
      PanoramaViewer.prototype.makeUrl = function() {
        var fov = PanoramaViewer.FOV;
      
        return "https://maps.googleapis.com/maps/api/streetview?location=40.457375,-80.009353&size=" + this.width + "x" + this.height + "&fov=" + fov + "&heading=" + this.heading + "&pitch=" + this.pitch;
      }
      
      PanoramaViewer.prototype.update = function() {
        var element = this.element;
      
        element.style.backgroundImage = "url(" + this.makeUrl() + ")";
      
        var width = this.width;
        var height = this.height;
      
        var context = element.getContext('2d');
      
        context.strokeStyle = '#FFFF00';
      
        context.beginPath();
        context.moveTo(0, height / 2);
        context.lineTo(width, height / 2);
        context.stroke();
      
        context.beginPath();
        context.moveTo(width / 2, 0);
        context.lineTo(width / 2, height);
        context.stroke();
      }
      
      function sgn(x) {
        return x >= 0 ? 1 : -1;
      }
      
      PanoramaViewer.prototype.unmap = function(heading, pitch) {
        var PI = Math.PI
        var cos = Math.cos;
        var sin = Math.sin;
        var tan = Math.tan;
      
        var fov = PanoramaViewer.FOV * PI / 180.0;
        var width = this.width;
        var height = this.height;
      
        var f = 0.5 * width / tan(0.5 * fov);
      
        var h = heading * PI / 180.0;
        var p = pitch * PI / 180.0;
      
        var x = f * cos(p) * sin(h);
        var y = f * cos(p) * cos(h);
        var z = f * sin(p);
      
        var h0 = this.heading * PI / 180.0;
        var p0 = this.pitch * PI / 180.0;
      
        var x0 = f * cos(p0) * sin(h0);
        var y0 = f * cos(p0) * cos(h0);
        var z0 = f * sin(p0);
      
        //
        // Intersect the ray O, v = (x, y, z)
        // with the plane at M0 of normal n = (x0, y0, z0)
        //
        //   n . (O + t v - M0) = 0
        //   t n . v = n . M0 = f^2
        //
        var t = f * f / (x0 * x + y0 * y + z0 * z);
      
        var ux = sgn(cos(p0)) * cos(h0);
        var uy = -sgn(cos(p0)) * sin(h0);
        var uz = 0;
      
        var vx = -sin(p0) * sin(h0);
        var vy = -sin(p0) * cos(h0);
        var vz = cos(p0);
      
        var x1 = t * x;
        var y1 = t * y;
        var z1 = t * z;
      
        var dx10 = x1 - x0;
        var dy10 = y1 - y0;
        var dz10 = z1 - z0;
      
        // Project on the local basis (u, v) at M0
        var du = ux * dx10 + uy * dy10 + uz * dz10;
        var dv = vx * dx10 + vy * dy10 + vz * dz10;
      
        return {
          u: du + width / 2,
          v: height / 2 - dv,
        };
      }
      
      PanoramaViewer.prototype.map = function(u, v) {
        var PI = Math.PI;
        var cos = Math.cos;
        var sin = Math.sin;
        var tan = Math.tan;
        var sqrt = Math.sqrt;
        var atan2 = Math.atan2;
        var asin = Math.asin;
      
        var fov = PanoramaViewer.FOV * PI / 180.0;
        var width = this.width;
        var height = this.height;
      
        var h0 = this.heading * PI / 180.0;
        var p0 = this.pitch * PI / 180.0;
      
        var f = 0.5 * width / tan(0.5 * fov);
      
        var x0 = f * cos(p0) * sin(h0);
        var y0 = f * cos(p0) * cos(h0);
        var z0 = f * sin(p0);
      
        var du = u - width / 2;
        var dv = height / 2 - v;
      
        var ux = sgn(cos(p0)) * cos(h0);
        var uy = -sgn(cos(p0)) * sin(h0);
        var uz = 0;
      
        var vx = -sin(p0) * sin(h0);
        var vy = -sin(p0) * cos(h0);
        var vz = cos(p0);
      
        var x = x0 + du * ux + dv * vx;
        var y = y0 + du * uy + dv * vy;
        var z = z0 + du * uz + dv * vz;
      
        var R = sqrt(x * x + y * y + z * z);
        var h = atan2(x, y);
        var p = asin(z / R);
      
        return {
          heading: h * 180.0 / PI,
          pitch: p * 180.0 / PI
        };
      }
      
      PanoramaViewer.prototype.click = function(e) {
        var rect = e.target.getBoundingClientRect();
        var u = e.clientX - rect.left;
        var v = e.clientY - rect.top;
      
        var uvCoords = this.unmap(this.heading, this.pitch);
      
        console.log("current viewport center");
        console.log("  heading: " + this.heading);
        console.log("  pitch: " + this.pitch);
        console.log("  u: " + uvCoords.u)
        console.log("  v: " + uvCoords.v);
      
        var hpCoords = this.map(u, v);
        uvCoords = this.unmap(hpCoords.heading, hpCoords.pitch);
      
        console.log("click at (" + u + "," + v + ")");
        console.log("  heading: " + hpCoords.heading);
        console.log("  pitch: " + hpCoords.pitch);
        console.log("  u: " + uvCoords.u);
        console.log("  v: " + uvCoords.v);
      
        this.heading = hpCoords.heading;
        this.pitch = hpCoords.pitch;
        this.update();
      }
      

      这篇关于在街景中获取像素的标题和音高的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆