2015-12-13 13 views
11

Tôi muốn chuyển đổi từ bản đồ khối lập phương [figure1] thành một ảnh toàn cảnh equirectangular [figure2].Chuyển đổi một Sơ đồ trang web thành Ảnh toàn cảnh trực tiếp

Figure1 Figure1

Figure2 enter image description here

Có thể đi từ cầu để Cubic (bằng cách làm theo: Convert 2:1 equirectangular panorama to cube map), nhưng bị mất trên làm thế nào để đảo ngược nó.

Hình 2 sẽ được hiển thị thành hình cầu sử dụng Unity.

Trả lời

8

Giả sử các hình ảnh đầu vào có định dạng cubemap sau:

Cubemap image

Mục đích là để tạo ra hình ảnh sang định dạng equirectangular như vậy:

Equirectangular image

Thuật toán chuyển đổi khá đơn giản. Để tính toán tốt nhất ước tính màu tại mỗi điểm ảnh trong hình ảnh equirectangular cho một cubemap với 6 gương mặt:

  • Thứ nhất, tính toán tọa độ cực tương ứng với mỗi điểm ảnh trong hình ảnh cầu.
  • Thứ hai, sử dụng các tọa độ cực tạo thành một vec-tơ và xác định trên mặt nào của hình lập phương và điểm ảnh nào đối diện với vectơ nằm; giống như một raycast từ trung tâm của một khối lập phương sẽ đánh một trong số các mặt của nó và một điểm cụ thể ở phía đó.

Hãy nhớ rằng có nhiều phương pháp để ước tính màu của điểm ảnh trong hình ảnh equirectangular cho một tọa độ chuẩn hóa (u, v) trên một khuôn mặt cụ thể của một hình khối. Phương pháp cơ bản nhất, là một xấp xỉ rất thô và sẽ được sử dụng trong câu trả lời này vì mục đích đơn giản, là làm tròn các tọa độ thành một pixel cụ thể và sử dụng pixel đó. Các phương pháp nâng cao khác có thể tính trung bình một vài pixel lân cận.

Việc triển khai thuật toán sẽ thay đổi tùy theo ngữ cảnh. Tôi đã thực hiện một cách nhanh chóng trong Unity3D C# cho thấy làm thế nào để thực hiện các thuật toán trong một kịch bản thế giới thực. Nó chạy trên CPU, có rất nhiều chỗ để cải thiện nhưng nó rất dễ hiểu.

using UnityEngine; 

public static class CubemapConverter 
{ 
    public static byte[] ConvertToEquirectangular(Texture2D sourceTexture, int outputWidth, int outputHeight) 
    { 
     Texture2D equiTexture = new Texture2D(outputWidth, outputHeight, TextureFormat.ARGB32, false); 
     float u, v; //Normalised texture coordinates, from 0 to 1, starting at lower left corner 
     float phi, theta; //Polar coordinates 
     int cubeFaceWidth, cubeFaceHeight; 

     cubeFaceWidth = sourceTexture.width/4; //4 horizontal faces 
     cubeFaceHeight = sourceTexture.height/3; //3 vertical faces 


     for (int j = 0; j < equiTexture.height; j++) 
     { 
      //Rows start from the bottom 
      v = 1 - ((float)j/equiTexture.height); 
      theta = v * Mathf.PI; 

      for (int i = 0; i < equiTexture.width; i++) 
      { 
       //Columns start from the left 
       u = ((float)i/equiTexture.width); 
       phi = u * 2 * Mathf.PI; 

       float x, y, z; //Unit vector 
       x = Mathf.Sin(phi) * Mathf.Sin(theta) * -1; 
       y = Mathf.Cos(theta); 
       z = Mathf.Cos(phi) * Mathf.Sin(theta) * -1; 

       float xa, ya, za; 
       float a; 

       a = Mathf.Max(new float[3] { Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z) }); 

       //Vector Parallel to the unit vector that lies on one of the cube faces 
       xa = x/a; 
       ya = y/a; 
       za = z/a; 

       Color color; 
       int xPixel, yPixel; 
       int xOffset, yOffset; 

       if (xa == 1) 
       { 
        //Right 
        xPixel = (int)((((za + 1f)/2f) - 1f) * cubeFaceWidth); 
        xOffset = 2 * cubeFaceWidth; //Offset 
        yPixel = (int)((((ya + 1f)/2f)) * cubeFaceHeight); 
        yOffset = cubeFaceHeight; //Offset 
       } 
       else if (xa == -1) 
       { 
        //Left 
        xPixel = (int)((((za + 1f)/2f)) * cubeFaceWidth); 
        xOffset = 0; 
        yPixel = (int)((((ya + 1f)/2f)) * cubeFaceHeight); 
        yOffset = cubeFaceHeight; 
       } 
       else if (ya == 1) 
       { 
        //Up 
        xPixel = (int)((((xa + 1f)/2f)) * cubeFaceWidth); 
        xOffset = cubeFaceWidth; 
        yPixel = (int)((((za + 1f)/2f) - 1f) * cubeFaceHeight); 
        yOffset = 2 * cubeFaceHeight; 
       } 
       else if (ya == -1) 
       { 
        //Down 
        xPixel = (int)((((xa + 1f)/2f)) * cubeFaceWidth); 
        xOffset = cubeFaceWidth; 
        yPixel = (int)((((za + 1f)/2f)) * cubeFaceHeight); 
        yOffset = 0; 
       } 
       else if (za == 1) 
       { 
        //Front 
        xPixel = (int)((((xa + 1f)/2f)) * cubeFaceWidth); 
        xOffset = cubeFaceWidth; 
        yPixel = (int)((((ya + 1f)/2f)) * cubeFaceHeight); 
        yOffset = cubeFaceHeight; 
       } 
       else if (za == -1) 
       { 
        //Back 
        xPixel = (int)((((xa + 1f)/2f) - 1f) * cubeFaceWidth); 
        xOffset = 3 * cubeFaceWidth; 
        yPixel = (int)((((ya + 1f)/2f)) * cubeFaceHeight); 
        yOffset = cubeFaceHeight; 
       } 
       else 
       { 
        Debug.LogWarning("Unknown face, something went wrong"); 
        xPixel = 0; 
        yPixel = 0; 
        xOffset = 0; 
        yOffset = 0; 
       } 

       xPixel = Mathf.Abs(xPixel); 
       yPixel = Mathf.Abs(yPixel); 

       xPixel += xOffset; 
       yPixel += yOffset; 

       color = sourceTexture.GetPixel(xPixel, yPixel); 
       equiTexture.SetPixel(i, j, color); 
      } 
     } 

     equiTexture.Apply(); 
     var bytes = equiTexture.EncodeToPNG(); 
     Object.DestroyImmediate(equiTexture); 

     return bytes; 
    } 
} 

Để sử dụng GPU, tôi đã tạo bóng đổ thực hiện cùng một chuyển đổi. Nó nhanh hơn nhiều so với việc chạy pixel chuyển đổi theo pixel trên CPU nhưng tiếc là Unity áp đặt các giới hạn độ phân giải trên các hình khối, vì vậy tính hữu ích của nó bị hạn chế trong các trường hợp khi sử dụng hình ảnh đầu vào có độ phân giải cao.

Shader "Conversion/CubemapToEquirectangular" { 
    Properties { 
     _MainTex ("Cubemap (RGB)", CUBE) = "" {} 
    } 

    Subshader { 
     Pass { 
      ZTest Always Cull Off ZWrite Off 
      Fog { Mode off }  

      CGPROGRAM 
       #pragma vertex vert 
       #pragma fragment frag 
       #pragma fragmentoption ARB_precision_hint_fastest 
       //#pragma fragmentoption ARB_precision_hint_nicest 
       #include "UnityCG.cginc" 

       #define PI 3.141592653589793 
       #define TWOPI 6.283185307179587 

       struct v2f { 
        float4 pos : POSITION; 
        float2 uv : TEXCOORD0; 
       }; 

       samplerCUBE _MainTex; 

       v2f vert(appdata_img v) 
       { 
        v2f o; 
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 
        o.uv = v.texcoord.xy * float2(TWOPI, PI); 
        return o; 
       } 

       fixed4 frag(v2f i) : COLOR 
       { 
        float theta = i.uv.y; 
        float phi = i.uv.x; 
        float3 unit = float3(0,0,0); 

        unit.x = sin(phi) * sin(theta) * -1; 
        unit.y = cos(theta) * -1; 
        unit.z = cos(phi) * sin(theta) * -1; 

        return texCUBE(_MainTex, unit); 
       } 
      ENDCG 
     } 
    } 
    Fallback Off 
} 

Chất lượng của hình ảnh kết quả có thể được cải thiện đáng kể bằng cách sử dụng phương pháp phức tạp hơn để ước tính màu của pixel trong quá trình chuyển đổi hoặc xử lý hậu quả ảnh (hoặc cả hai). Ví dụ một hình ảnh có kích thước lớn hơn có thể được tạo ra để áp dụng một bộ lọc mờ và sau đó downsample nó đến kích thước mong muốn.

Tôi đã tạo một dự án Unity đơn giản với hai trình hướng dẫn trình chỉnh sửa cho biết cách sử dụng đúng mã C# hoặc trình đổ bóng được hiển thị ở trên. Nhận nó ở đây: https://github.com/Mapiarz/CubemapToEquirectangular

Ghi để đặt thiết lập nhập khẩu thích hợp trong Unity cho hình ảnh đầu vào của bạn:

  • Point lọc
  • định dạng TrueColor
  • Disable mipmaps
  • Non Sức mạnh của 2: Không (chỉ dành cho 2DTextures)
  • Bật đọc/ghi (chỉ dành cho 2DTextures)
+0

Nếu tôi muốn xoay cubemap trước khi ánh xạ lên hình cầu (sau đó được ánh xạ trên mặt 2D), tôi sẽ làm như thế nào? – CrushedPixel

+0

@Bartosz - Tôi đã chuyển mã này thành C++ openCV, tôi đang gặp vấn đề ở dòng này 'a = Mathf.Max (new float [3] {Mathf.Abs (x), Mathf.Abs (y), Mathf .Abs (z)}); '- bởi vì a được trả về bằng 0 khi x, y, z giá trị dưới 1, làm cho xa, xy và xz là không xác định, do chia cho số 0, tôi thiếu cái gì? dòng C++ của tôi trông giống như sau: 'a = tối đa (abs (x), abs (y), abs (z));' (Tôi đã thêm hàm tối đa của riêng mình). –

+0

hi! Tôi đã phân tích bạn đang viết mã trong một thời gian. Bạn có bất kỳ ideea của một giải pháp cho hơn 6 khuôn mặt? Trong trường hợp của bạn, bạn có 6 khuôn mặt, mỗi khuôn mặt cho 1 trục (x, y, z, -x, -y, -z) Trong trường hợp nhiều khuôn mặt/hình ảnh, mỗi hình ảnh sẽ có khuôn mặt riêng ", cũng là" trục "của riêng nó. Đây là ideea của tôi, bởi tôi không thể tiếp tục từ đây. Bạn có thể giúp tôi không? – user297876

1

cube2sphere tự động hóa toàn bộ quá trình. Ví dụ:

$ cube2sphere front.jpg back.jpg right.jpg left.jpg top.jpg bottom.jpg -r 2048 1024 -fTGA -ostitched 
Các vấn đề liên quan