360视频:分段球面投影SSP

分段球面投影(Segmented sphere projection,SSP)将球面分为3个部分:北极、南极、赤道。图1是SSP投影示意图,球面上三个部分的分界线是北纬45度和南纬45度。北极和南极投影后的面是圆,索引分别为0和1,赤道投影方式和ERP相同,赤道投影后被划分为4个相同的正方形面,索引为2到5。投影后面的两极圆的直径和赤道正方形边长相等,因为它们纬度都跨了90度。

图1 SSP投影

Frame Packing

SSP有两者frame packing方式,一种如图1所示,映射后按垂直方向展开。图2是真实图像垂直展开的示例。

图2 SSP垂直展开

SSP还可以按照水平方向展开,如图3所示。

图3 SSP水平展开

展开后各面的定义如表1。

表1 SSP各面的定义

投影变换

2D-3D变换,假设面的直径是A,则2D平面上的点(m,n)按下面方式转换为球面上的\begin{equation*} ( \phi ,\theta ) \end{equation*}

当f=0时,

3D-2D变换,球面上的\begin{equation*} ( \phi ,\theta ) \end{equation*}按下面方式转换为2D平面上的点(m,n),

当f=0时,

 

 当f=1时,

 

Void TSegmentedSphere::map2DTo3D(SPos& IPosIn, SPos *pSPosOut)
{
    POSType u, v,
        square = m_sVideoInfo.iFaceHeight,
        width = m_sVideoInfo.iFaceWidth,
        pitch, yaw;
    //u = IPosIn.x;
    u = IPosIn.x + (POSType)(0.5);
    v = IPosIn.y + (POSType)(0.5);
​
#if SVIDEO_SSP_PADDING_FIX
#else
    if ((u < 0 || u >= m_sVideoInfo.iFaceWidth) && (v >= 0 && v < m_sVideoInfo.iFaceHeight * 2 / 3))
    {
        u = u < 0 ? m_sVideoInfo.iFaceWidth + u : (u - m_sVideoInfo.iFaceWidth);
    }
    else if (v < 0)
    {
        v = -v;
        u = u + (m_sVideoInfo.iFaceWidth >> 1);
        u = u >= m_sVideoInfo.iFaceWidth ? u - m_sVideoInfo.iFaceWidth : u;
    }
    else if (v >= m_sVideoInfo.iFaceHeight)
    {
        v = (m_sVideoInfo.iFaceHeight * 2 / 3 << 1) - v;
        u = u + (m_sVideoInfo.iFaceWidth >> 1);
        u = u >= m_sVideoInfo.iFaceWidth ? u - m_sVideoInfo.iFaceWidth : u;
    }
#endif
    POSType pole_x, pole_y, pole_d;
    if (IPosIn.faceIdx == 0)
    {
        pole_x = u - width / 2;
        pole_y = v - square / 2;
        pole_d = ssqrt(pole_x*pole_x + pole_y*pole_y);
        yaw = (pole_d > 0) ? acos(pole_y / pole_d) : 0;
        yaw = (pole_x < 0) ? S_PI*2 - yaw : yaw;
        pitch = S_PI_2 - pole_d * S_PI_2 / square;
    }
    else if (IPosIn.faceIdx == 1)
    {
        pole_x = u - width / 2;
        pole_y = v - square / 2;
        pole_d = ssqrt(pole_x*pole_x + pole_y*pole_y);
        yaw = (pole_d > 0) ? satan2(pole_y, pole_x) + S_PI_2 : 0;
        pitch = pole_d * S_PI_2 / square - S_PI_2;
    }
    else if (IPosIn.faceIdx == 2)
    {
        yaw = (POSType)(u*S_PI_2 / m_sVideoInfo.iFaceWidth - S_PI);
        pitch = (POSType)(S_PI_2 - (v + square / 2)*S_PI / square / 2);
    }
    else if (IPosIn.faceIdx == 3)
    {
        yaw = (POSType)((u + square)*S_PI_2 / m_sVideoInfo.iFaceWidth - S_PI);
        pitch = (POSType)(S_PI_2 - (v + square / 2)*S_PI / square / 2);
    }
    else if (IPosIn.faceIdx == 4)
    {
        yaw = (POSType)((u + 2*square)*S_PI_2 / m_sVideoInfo.iFaceWidth - S_PI);
        pitch = (POSType)(S_PI_2 - (v + square / 2)*S_PI / square / 2);
    }
    else if (IPosIn.faceIdx == 5)
    {
        yaw = (POSType)((u + 3*square)*S_PI_2 / m_sVideoInfo.iFaceWidth - S_PI);
        pitch = (POSType)(S_PI_2 - (v + square / 2)*S_PI / square / 2);
    }
    else
    {
        assert(!"Face index Error!\n");
    }
​
    pSPosOut->faceIdx = IPosIn.faceIdx;
    pSPosOut->x = (POSType)(scos(pitch)*scos(yaw));
    pSPosOut->y = (POSType)(ssin(pitch));
    pSPosOut->z = -(POSType)(scos(pitch)*ssin(yaw));
}
​
Void TSegmentedSphere::map3DTo2D(SPos *pSPosIn, SPos *pSPosOut)
{
    POSType x = pSPosIn->x;
    POSType y = pSPosIn->y;
    POSType z = pSPosIn->z;
​
    POSType len = ssqrt(x*x + y*y + z*z);
    POSType square = m_sVideoInfo.iFaceHeight;
    POSType yaw = (POSType)(satan2(-z, x)),
        pitch = (POSType)(sasin(y / len));
​
    pSPosOut->z = 0;
    if (y > len*ssqrt(2) / 2)
    {
        pSPosOut->faceIdx = 0;
        pSPosOut->x = square * ssin(yaw) * (S_PI_2 - pitch) / S_PI_2 +
            m_sVideoInfo.iFaceWidth / 2 - 0.5;
        pSPosOut->y = square / 2 * (1 + scos(yaw) * 2 * (S_PI_2 - pitch) / S_PI_2) - 0.5;
    }
    else if (y < -len*ssqrt(2) / 2)
    {
        pSPosOut->faceIdx = 1;
        pSPosOut->x = square * ssin(yaw) * (S_PI_2 + pitch) / S_PI_2 +
            m_sVideoInfo.iFaceWidth / 2 - 0.5;
        pSPosOut->y = square / 2 * (1 - scos(yaw) * 2 * (S_PI_2 + pitch) / S_PI_2) - 0.5;
    }
    else if (z >= 0 && x < 0)
    {
        pSPosOut->faceIdx = 2;
        pSPosOut->x = (S_PI + yaw)*m_sVideoInfo.iFaceWidth / S_PI_2 - 0.5;
        pSPosOut->y = (S_PI_2 - pitch) * square / S_PI_2 - square / 2 - 0.5;
    }
    else if (z > 0 && x >= 0)
    {
        pSPosOut->faceIdx = 3;
        pSPosOut->x = (S_PI + yaw)*m_sVideoInfo.iFaceWidth / S_PI_2 - m_sVideoInfo.iFaceWidth - 0.5;
        pSPosOut->y = (S_PI_2 - pitch) * square / S_PI_2 - square / 2 - 0.5;
    }
    else if (z <= 0 && x > 0)
    {
        pSPosOut->faceIdx = 4;
        pSPosOut->x = (S_PI + yaw)*m_sVideoInfo.iFaceWidth / S_PI_2 - 2 * m_sVideoInfo.iFaceWidth - 0.5;
        pSPosOut->y = (S_PI_2 - pitch) * square / S_PI_2 - square / 2 - 0.5;
    }
    else if (z < 0 && x <= 0)
    {
        pSPosOut->faceIdx = 5;
        pSPosOut->x = (S_PI + yaw)*m_sVideoInfo.iFaceWidth / S_PI_2 - 3 * m_sVideoInfo.iFaceWidth - 0.5;
        pSPosOut->y = (S_PI_2 - pitch) * square / S_PI_2 - square / 2 - 0.5;
    }
}

 

感兴趣的请关注微信公众号Video Coding

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值