c++ एक क्यूबमप को समानांतर पैनोरमा में परिवर्तित करना




opencv image-processing (2)

इनपुट छवि मानते हुए निम्नलिखित cubemap प्रारूप में है:

इसका उद्देश्य समानता के समान छवि को चित्रित करना है:

रूपांतरण एल्गोरिदम बल्कि सीधा है। 6 चेहरों के साथ एक cubemap दी समानतागत छवि में प्रत्येक पिक्सेल पर रंग का सबसे अच्छा अनुमान लगाने के लिए:

  • सबसे पहले, ध्रुवीय निर्देशांक की गणना करें जो गोलाकार छवि में प्रत्येक पिक्सेल के अनुरूप हैं।
  • दूसरे, ध्रुवीय निर्देशांक का प्रयोग करके वेक्टर बनाते हैं और यह निर्धारित करते हैं कि किस क्यूमेप का चेहरे और वेक्टर के उस पिक्सेल के चेहरे पर निहित है; जैसे कि घन के केंद्र से एक रेयानका, उसके पक्षों में से एक और उस तरफ एक विशिष्ट बिंदु को मारा जाएगा।

ध्यान रखें कि क्यूमेप के विशिष्ट चेहरे पर एक सामान्यीकृत निर्देशांक (यू, वी) को दिए जाने वाली समानता वाली छवि में एक पिक्सेल के रंग का अनुमान लगाने के कई तरीके हैं। सबसे बुनियादी विधि, जो एक बहुत ही कच्चे सन्निकटन है और सादगी के लिए इस उत्तर में उपयोग की जाएगी, एक विशिष्ट पिक्सेल के लिए निर्देशांक गोल करना और उस पिक्सेल का उपयोग करना है अन्य और उन्नत तरीके कुछ पड़ोसी पिक्सल के औसत की गणना कर सकते हैं।

एल्गोरिथ्म का कार्यान्वयन संदर्भ के आधार पर भिन्न होगा। मैंने यूनिटी 3 डी सी # में एक त्वरित कार्यान्वयन किया जो कि एक वास्तविक दुनिया परिदृश्य में एल्गोरिथ्म को कैसे कार्यान्वित करें। यह सीपीयू पर चलता है, सुधार के लिए एक बहुत कमरा है लेकिन यह समझना आसान है।

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;
    }
}

जीपीयू का उपयोग करने के लिए मैंने एक शेडर बनाया जो एक ही रूपांतरण करता है। यह रूपांतरण पिक्सेल को सीपीयू पर पिक्सेल द्वारा चलाने के मुकाबले बहुत तेज है लेकिन दुर्भाग्य से क्यूबामैप्स पर यूनिटी ने रिज़ॉल्यूशन सीमाएं लगाई हैं इसलिए इसकी उपयोगिता उच्च परिदृश्य इनपुट छवि का उपयोग करने के लिए परिदृश्यों में सीमित है।

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
}

परिणामस्वरूप छवियों की गुणवत्ता में या तो रूपांतरण के दौरान पिक्सेल के रंग का अनुमान लगाने या परिणामी छवि (या दोनों, वास्तव में) के बाद प्रसंस्करण के माध्यम से एक और अधिक परिष्कृत विधि को नियोजित करके बहुत सुधार किया जा सकता है। उदाहरण के लिए बड़े आकार की एक छवि को एक धुंधला फिल्टर लागू करने के लिए तैयार किया जा सकता है और फिर इसे इच्छित आकार में डाउनसैम्प कर सकता है।

मैंने दो संपादक विज़ार्ड के साथ एक सरल एकता प्रोजेक्ट बनाया है जो दिखाते हैं कि कैसे ठीक से सी # कोड या ऊपर दिखाए गए शाडर का उपयोग किया जाए इसे यहां प्राप्त करें: https://github.com/Mapiarz/CubemapToEquirectangular

अपनी इनपुट छवियों के लिए यूनिटी में उचित आयात सेटिंग सेट करना याद रखें:

  • प्वाइंट फ़िल्टरिंग
  • Truecolor प्रारूप
  • Mipmaps अक्षम करें
  • 2 की गैर शक्ति: कोई नहीं (केवल 2 डीटेक्चरर्स के लिए)
  • पढ़ें / लिखना सक्षम करें (केवल 2DTextures के लिए)

मैं घन मैप [आकृति 1] से एक समांतर चौराहे में परिवर्तित करना चाहता हूं [आकृति 2]।

आकृति 1

चित्र 2

गोलाकार से क्यूबिक (संभवतः कन्वर्ट 2: 1 घन मैप में कनवर्ट 2: 1 परिक्रमात्मक पैनोरामा ) से जाना संभव है, लेकिन इसे कैसे उलट करना है पर खोना संभव है।

चित्रा 2 को एकता में एकता के रूप में प्रस्तुत करना है।


cube2sphere पूरी प्रक्रिया को स्वचालित करता है उदाहरण:

$ cube2sphere front.jpg back.jpg right.jpg left.jpg top.jpg bottom.jpg -r 2048 1024 -fTGA -ostitched




unity3d