it-swarm.com.de

Konvertieren Sie ein gleichseitiges 2: 1-Panorama in eine Würfelkarte

Ich arbeite gerade an einem einfachen 3D-Panorama-Viewer für eine Website. Aus Gründen der mobilen Leistung verwende ich den three.jsCSS3-Renderer . Dies erfordert eine Würfelkarte, die in 6 Einzelbilder aufgeteilt ist.

Ich nehme die Bilder auf dem iPhone mit der Google Photosphere-App oder ähnlichen Apps auf, die gleichseitige 2: 1-Panoramen erstellen. Diese Größe skaliere ich dann und konvertiere sie in eine Cubemap mit dieser Website: http://gonchar.me/panorama/ (Flash)

Vorzugsweise möchte ich die Konvertierung selbst durchführen, entweder on-the-fly in three.js (falls möglich) oder in Photoshop. Ich fand die Photoshop-Aktionen von Andrew Hazelden, und sie scheinen ziemlich nahe zu sein, aber es gibt keine direkte Konvertierung. Gibt es einen mathematischen Weg, diese zu konvertieren, oder eine Art Skript, das es tut? Ich möchte möglichst vermeiden, eine 3D-App wie Blender durchzugehen.

Vielleicht ist das ein Weitschuss, aber ich dachte, ich würde fragen. Ich habe okay Erfahrung mit Javascript, aber ich bin zu three.js ziemlich neu. Ich zögere auch, mich auf die WebGL-Funktionalität zu verlassen, da sie auf mobilen Geräten entweder langsam oder fehlerhaft erscheint. Der Support ist auch immer noch sporadisch.

39
oelna

Wenn Sie es auf der Serverseite tun wollen, gibt es viele Möglichkeiten. http://www.imagemagick.org/ verfügt über eine Reihe von Befehlszeilen-Tools, die Ihr Bild in Stücke schneiden könnten. Sie können den Befehl dazu in ein Skript schreiben und dieses jedes Mal ausführen, wenn Sie ein neues Image haben.

Es ist schwer zu sagen, welcher Algorithmus im Programm verwendet wird. Wir können versuchen, das, was passiert, rückzuentwickeln, indem wir ein quadratisches Gitter in das Programm einfügen. Ich habe ein Gitter von Wikipedia verwendet

64 by 64 grid

Was gibt projected gridDies gibt uns einen Hinweis darauf, wie die Box aufgebaut ist. 

Bildgebungsbereich mit Linien von Längen- und Breitengraden und einem umgebenden Würfel. Projizieren Sie nun vom Mittelpunkt in der Kugelmitte aus ein verzerrtes Gitter auf dem Würfel.

Man nehme mathematisch Polarkoordinaten r, θ, ø, für die Kugel r = 1, 0 <θ <π, -π/4 <ø <7π/4

  • x = r sin θ cos ø 
  • y = r sin θ sin ø 
  • z = r cosθ

projizieren Sie diese zentral auf den Würfel. Zuerst teilen wir uns in vier Bereiche durch die Breite -π/4 <ø <π/4, π/4 <ø <3π/4, 3π/4 <ø <5π/4, 5π/4 <ø <7π/4. Diese ragen entweder auf eine der vier Seiten nach oben oder nach unten. 

Angenommen, wir befinden uns auf der ersten Seite -π/4 <ø <π/4. Die zentrale Projektion von __ (sin θ cos ø, sin θ sin ø, cos θ) ist (ein sin θ cos ø, ein sin θ sin ø, ein cos θ), die auf die Ebene x = 1 trifft

  • a sin θ cos ø = 1

so

  • a = 1/(sin θ cos ø)

und der projizierte Punkt ist

  • (1, tan ø, Kinderbett θ/cos ø)

Wenn | Kinderbett θ/cos ø | <1 wird auf der Vorderseite sein. Andernfalls wird sie oben oder unten projiziert, und Sie benötigen dafür eine andere Projektion. Ein besserer Test für die Spitze nutzt die Tatsache, dass der Mindestwert von cos ø cos π/4 = 1/√2 ist. Wenn Cot θ/(1/√2)> 1 oder ist, befindet sich der projizierte Punkt immer oben tan θ <1/√2. Dies ergibt sich als θ <35º oder 0,615 Bogenmaß. 

Setze dies in Python zusammen

import sys
from PIL import Image
from math import pi,sin,cos,tan

def cot(angle):
    return 1/tan(angle)

# Project polar coordinates onto a surrounding cube
# assume ranges theta is [0,pi] with 0 the north poll, pi south poll
# phi is in range [0,2pi] 
def projection(theta,phi): 
        if theta<0.615:
            return projectTop(theta,phi)
        Elif theta>2.527:
            return projectBottom(theta,phi)
        Elif phi <= pi/4 or phi > 7*pi/4:
            return projectLeft(theta,phi)
        Elif phi > pi/4 and phi <= 3*pi/4:
            return projectFront(theta,phi)
        Elif phi > 3*pi/4 and phi <= 5*pi/4:
            return projectRight(theta,phi)
        Elif phi > 5*pi/4 and phi <= 7*pi/4:
            return projectBack(theta,phi)

def projectLeft(theta,phi):
        x = 1
        y = tan(phi)
        z = cot(theta) / cos(phi)
        if z < -1:
            return projectBottom(theta,phi)
        if z > 1:
            return projectTop(theta,phi)
        return ("Left",x,y,z)

def projectFront(theta,phi):
        x = tan(phi-pi/2)
        y = 1
        z = cot(theta) / cos(phi-pi/2)
        if z < -1:
            return projectBottom(theta,phi)
        if z > 1:
            return projectTop(theta,phi)
        return ("Front",x,y,z)

def projectRight(theta,phi):
        x = -1
        y = tan(phi)
        z = -cot(theta) / cos(phi)
        if z < -1:
            return projectBottom(theta,phi)
        if z > 1:
            return projectTop(theta,phi)
        return ("Right",x,-y,z)

def projectBack(theta,phi):
        x = tan(phi-3*pi/2)
        y = -1
        z = cot(theta) / cos(phi-3*pi/2)
        if z < -1:
            return projectBottom(theta,phi)
        if z > 1:
            return projectTop(theta,phi)
        return ("Back",-x,y,z)

def projectTop(theta,phi):
        # (a sin θ cos ø, a sin θ sin ø, a cos θ) = (x,y,1)
        a = 1 / cos(theta)
        x = tan(theta) * cos(phi)
        y = tan(theta) * sin(phi)
        z = 1
        return ("Top",x,y,z)

def projectBottom(theta,phi):
        # (a sin θ cos ø, a sin θ sin ø, a cos θ) = (x,y,-1)
        a = -1 / cos(theta)
        x = -tan(theta) * cos(phi)
        y = -tan(theta) * sin(phi)
        z = -1
        return ("Bottom",x,y,z)

# Convert coords in cube to image coords 
# coords is a Tuple with the side and x,y,z coords
# Edge is the length of an Edge of the cube in pixels
def cubeToImg(coords,Edge):
    if coords[0]=="Left":
        (x,y) = (int(Edge*(coords[2]+1)/2), int(Edge*(3-coords[3])/2) )
    Elif coords[0]=="Front":
        (x,y) = (int(Edge*(coords[1]+3)/2), int(Edge*(3-coords[3])/2) )
    Elif coords[0]=="Right":
        (x,y) = (int(Edge*(5-coords[2])/2), int(Edge*(3-coords[3])/2) )
    Elif coords[0]=="Back":
        (x,y) = (int(Edge*(7-coords[1])/2), int(Edge*(3-coords[3])/2) )
    Elif coords[0]=="Top":
        (x,y) = (int(Edge*(3-coords[1])/2), int(Edge*(1+coords[2])/2) )
    Elif coords[0]=="Bottom":
        (x,y) = (int(Edge*(3-coords[1])/2), int(Edge*(5-coords[2])/2) )
    return (x,y)

# convert the in image to out image
def convert(imgIn,imgOut):
    inSize = imgIn.size
    outSize = imgOut.size
    inPix = imgIn.load()
    outPix = imgOut.load()
    Edge = inSize[0]/4   # the length of each Edge in pixels
    for i in xrange(inSize[0]):
        for j in xrange(inSize[1]):
            pixel = inPix[i,j]
            phi = i * 2 * pi / inSize[0]
            theta = j * pi / inSize[1]
            res = projection(theta,phi)
            (x,y) = cubeToImg(res,Edge)
            #if i % 100 == 0 and j % 100 == 0:
            #   print i,j,phi,theta,res,x,y
            if x >= outSize[0]:
                #print "x out of range ",x,res
                x=outSize[0]-1
            if y >= outSize[1]:
                #print "y out of range ",y,res
                y=outSize[1]-1
            outPix[x,y] = pixel

imgIn = Image.open(sys.argv[1])
inSize = imgIn.size
imgOut = Image.new("RGB",(inSize[0],inSize[0]*3/4),"black")
convert(imgIn,imgOut)
imgOut.show()

Die Funktion projection nimmt die Werte theta und phi und gibt die Koordinaten in einem Würfel von -1 bis 1 in jede Richtung zurück. Das cubeToImg nimmt die (x, y, z) -Koordinaten und übersetzt sie in die Ausgabebildkoordinaten.

Der obige Algorithmus scheint die Geometrie unter Verwendung eines Images von Buckingham Palace zu bekommen, das wir bekommen cube map of buckingham palace Dies scheint die meisten Linien in der Pflasterung richtig zu machen. 

Wir bekommen einige Bildartefakte. Dies liegt daran, dass keine 1: 1-Karte von Pixeln vorhanden ist. Was wir tun müssen, ist eine inverse Transformation. Anstatt durch jedes Pixel in der Quelle zu schleifen und das entsprechende Pixel im Ziel zu finden, durchlaufen wir die Zielbilder und suchen das nächstgelegene entsprechende Quellpixel. 

import sys
from PIL import Image
from math import pi,sin,cos,tan,atan2,hypot,floor
from numpy import clip

# get x,y,z coords from out image pixels coords
# i,j are pixel coords
# face is face number
# Edge is Edge length
def outImgToXYZ(i,j,face,Edge):
    a = 2.0*float(i)/Edge
    b = 2.0*float(j)/Edge
    if face==0: # back
        (x,y,z) = (-1.0, 1.0-a, 3.0 - b)
    Elif face==1: # left
        (x,y,z) = (a-3.0, -1.0, 3.0 - b)
    Elif face==2: # front
        (x,y,z) = (1.0, a - 5.0, 3.0 - b)
    Elif face==3: # right
        (x,y,z) = (7.0-a, 1.0, 3.0 - b)
    Elif face==4: # top
        (x,y,z) = (b-1.0, a -5.0, 1.0)
    Elif face==5: # bottom
        (x,y,z) = (5.0-b, a-5.0, -1.0)
    return (x,y,z)

# convert using an inverse transformation
def convertBack(imgIn,imgOut):
    inSize = imgIn.size
    outSize = imgOut.size
    inPix = imgIn.load()
    outPix = imgOut.load()
    Edge = inSize[0]/4   # the length of each Edge in pixels
    for i in xrange(outSize[0]):
        face = int(i/Edge) # 0 - back, 1 - left 2 - front, 3 - right
        if face==2:
            rng = xrange(0,Edge*3)
        else:
            rng = xrange(Edge,edge*2)

        for j in rng:
            if j<Edge:
                face2 = 4 # top
            Elif j>=2*Edge:
                face2 = 5 # bottom
            else:
                face2 = face

            (x,y,z) = outImgToXYZ(i,j,face2,Edge)
            theta = atan2(y,x) # range -pi to pi
            r = hypot(x,y)
            phi = atan2(z,r) # range -pi/2 to pi/2
            # source img coords
            uf = ( 2.0*Edge*(theta + pi)/pi )
            vf = ( 2.0*Edge * (pi/2 - phi)/pi)
            # Use bilinear interpolation between the four surrounding pixels
            ui = floor(uf)  # coord of pixel to bottom left
            vi = floor(vf)
            u2 = ui+1       # coords of pixel to top right
            v2 = vi+1
            mu = uf-ui      # fraction of way across pixel
            nu = vf-vi
            # Pixel values of four corners
            A = inPix[ui % inSize[0],clip(vi,0,inSize[1]-1)]
            B = inPix[u2 % inSize[0],clip(vi,0,inSize[1]-1)]
            C = inPix[ui % inSize[0],clip(v2,0,inSize[1]-1)]
            D = inPix[u2 % inSize[0],clip(v2,0,inSize[1]-1)]
            # interpolate
            (r,g,b) = (
              A[0]*(1-mu)*(1-nu) + B[0]*(mu)*(1-nu) + C[0]*(1-mu)*nu+D[0]*mu*nu,
              A[1]*(1-mu)*(1-nu) + B[1]*(mu)*(1-nu) + C[1]*(1-mu)*nu+D[1]*mu*nu,
              A[2]*(1-mu)*(1-nu) + B[2]*(mu)*(1-nu) + C[2]*(1-mu)*nu+D[2]*mu*nu )

            outPix[i,j] = (int(round(r)),int(round(g)),int(round(b)))

imgIn = Image.open(sys.argv[1])
inSize = imgIn.size
imgOut = Image.new("RGB",(inSize[0],inSize[0]*3/4),"black")
convertBack(imgIn,imgOut)
imgOut.save(sys.argv[1].split('.')[0]+"Out2.png")
imgOut.show()

Die Ergebnisse davon sind Using the inverse transformation

70
Salix alba

Angesichts der ausgezeichneten akzeptierten Antwort wollte ich meine entsprechende c ++ - Implementierung hinzufügen, die auf OpenCV basiert. 

Für diejenigen, die nicht mit OpenCV vertraut sind, können Sie sich Mat als Image vorstellen. Zuerst konstruieren wir zwei Karten, die vom gleichrechteckigen Bild auf unsere entsprechende Cubemap-Fläche übertragen werden. Dann führen wir das schwere Anheben (d. H. Remapping mit Interpolation) mit OpenCV durch. 

Der Code kann kompakter gestaltet werden, wenn die Lesbarkeit nicht von Belang ist. 

// Define our six cube faces. 
// 0 - 3 are side faces, clockwise order
// 4 and 5 are top and bottom, respectively
float faceTransform[6][2] = 
{ 
    {0, 0},
    {M_PI / 2, 0},
    {M_PI, 0},
    {-M_PI / 2, 0},
    {0, -M_PI / 2},
    {0, M_PI / 2}
};

// Map a part of the equirectangular panorama (in) to a cube face
// (face). The ID of the face is given by faceId. The desired
// width and height are given by width and height. 
inline void createCubeMapFace(const Mat &in, Mat &face, 
        int faceId = 0, const int width = -1, 
        const int height = -1) {

    float inWidth = in.cols;
    float inHeight = in.rows;

    // Allocate map
    Mat mapx(height, width, CV_32F);
    Mat mapy(height, width, CV_32F);

    // Calculate adjacent (ak) and opposite (an) of the
    // triangle that is spanned from the sphere center 
    //to our cube face.
    const float an = sin(M_PI / 4);
    const float ak = cos(M_PI / 4);

    const float ftu = faceTransform[faceId][0];
    const float ftv = faceTransform[faceId][1];

    // For each point in the target image, 
    // calculate the corresponding source coordinates. 
    for(int y = 0; y < height; y++) {
        for(int x = 0; x < width; x++) {

            // Map face pixel coordinates to [-1, 1] on plane
            float nx = (float)y / (float)height - 0.5f;
            float ny = (float)x / (float)width - 0.5f;

            nx *= 2;
            ny *= 2;

            // Map [-1, 1] plane coords to [-an, an]
            // thats the coordinates in respect to a unit sphere 
            // that contains our box. 
            nx *= an; 
            ny *= an; 

            float u, v;

            // Project from plane to sphere surface.
            if(ftv == 0) {
                // Center faces
                u = atan2(nx, ak);
                v = atan2(ny * cos(u), ak);
                u += ftu; 
            } else if(ftv > 0) { 
                // Bottom face 
                float d = sqrt(nx * nx + ny * ny);
                v = M_PI / 2 - atan2(d, ak);
                u = atan2(ny, nx);
            } else {
                // Top face
                float d = sqrt(nx * nx + ny * ny);
                v = -M_PI / 2 + atan2(d, ak);
                u = atan2(-ny, nx);
            }

            // Map from angular coordinates to [-1, 1], respectively.
            u = u / (M_PI); 
            v = v / (M_PI / 2);

            // Warp around, if our coordinates are out of bounds. 
            while (v < -1) {
                v += 2;
                u += 1;
            } 
            while (v > 1) {
                v -= 2;
                u += 1;
            } 

            while(u < -1) {
                u += 2;
            }
            while(u > 1) {
                u -= 2;
            }

            // Map from [-1, 1] to in texture space
            u = u / 2.0f + 0.5f;
            v = v / 2.0f + 0.5f;

            u = u * (inWidth - 1);
            v = v * (inHeight - 1);

            // Save the result for this pixel in map
            mapx.at<float>(x, y) = u;
            mapy.at<float>(x, y) = v; 
        }
    }

    // Recreate output image if it has wrong size or type. 
    if(face.cols != width || face.rows != height || 
        face.type() != in.type()) {
        face = Mat(width, height, in.type());
    }

    // Do actual resampling using OpenCV's remap
    remap(in, face, mapx, mapy, 
         CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
}

Gegeben die folgende Eingabe:

 enter image description here

Die folgenden Gesichter werden generiert: 

 enter image description here

Bild mit freundlicher Genehmigung von Optonaut .

11
Emiswelt

Ich habe ein Skript geschrieben, um die generierte Cubemap in einzelne Dateien zu schneiden (posx.png, negx.png, posy.png, negy.png, posz.png und negz.png). Es werden auch die 6 Dateien in eine ZIP-Datei gepackt.

Die Quelle ist hier: https://github.com/dankex/compv/blob/master/3d-graphics/skybox/cubemap-cut.py

Sie können das Array ändern, um die Bilddateien festzulegen:

name_map = [ \
 ["", "", "posy", ""],
 ["negz", "negx", "posz", "posx"],
 ["", "", "negy", ""]]

Die konvertierten Dateien sind:

 enter image description here  enter image description here  enter image description here  enter image description here  enter image description here  enter image description here

9
Danke Xie

Hier ist eine (naiv) modifizierte Version von Salix Albas absolut fantastische Antwort die jeweils ein Gesicht konvertiert, sechs verschiedene Bilder ausspuckt und den Dateityp des Originalbildes beibehält.

Abgesehen von der Tatsache, dass die meisten Anwendungsfälle wahrscheinlich sechs separate Bilder erwarten, besteht der Hauptvorteil der Umwandlung von Gesichtern auf einmal darin, dass die Arbeit mit großen Bildern viel weniger Speicher erfordert. 

#!/usr/bin/env python
import sys
from PIL import Image
from math import pi, sin, cos, tan, atan2, hypot, floor
from numpy import clip

# get x,y,z coords from out image pixels coords
# i,j are pixel coords
# faceIdx is face number
# faceSize is Edge length
def outImgToXYZ(i, j, faceIdx, faceSize):
    a = 2.0 * float(i) / faceSize
    b = 2.0 * float(j) / faceSize

    if faceIdx == 0: # back
        (x,y,z) = (-1.0, 1.0 - a, 1.0 - b)
    Elif faceIdx == 1: # left
        (x,y,z) = (a - 1.0, -1.0, 1.0 - b)
    Elif faceIdx == 2: # front
        (x,y,z) = (1.0, a - 1.0, 1.0 - b)
    Elif faceIdx == 3: # right
        (x,y,z) = (1.0 - a, 1.0, 1.0 - b)
    Elif faceIdx == 4: # top
        (x,y,z) = (b - 1.0, a - 1.0, 1.0)
    Elif faceIdx == 5: # bottom
        (x,y,z) = (1.0 - b, a - 1.0, -1.0)

    return (x, y, z)

# convert using an inverse transformation
def convertFace(imgIn, imgOut, faceIdx):
    inSize = imgIn.size
    outSize = imgOut.size
    inPix = imgIn.load()
    outPix = imgOut.load()
    faceSize = outSize[0]

    for xOut in xrange(faceSize):
        for yOut in xrange(faceSize):
            (x,y,z) = outImgToXYZ(xOut, yOut, faceIdx, faceSize)
            theta = atan2(y,x) # range -pi to pi
            r = hypot(x,y)
            phi = atan2(z,r) # range -pi/2 to pi/2

            # source img coords
            uf = 0.5 * inSize[0] * (theta + pi) / pi
            vf = 0.5 * inSize[0] * (pi/2 - phi) / pi

            # Use bilinear interpolation between the four surrounding pixels
            ui = floor(uf)  # coord of pixel to bottom left
            vi = floor(vf)
            u2 = ui+1       # coords of pixel to top right
            v2 = vi+1
            mu = uf-ui      # fraction of way across pixel
            nu = vf-vi

            # Pixel values of four corners
            A = inPix[ui % inSize[0], clip(vi, 0, inSize[1]-1)]
            B = inPix[u2 % inSize[0], clip(vi, 0, inSize[1]-1)]
            C = inPix[ui % inSize[0], clip(v2, 0, inSize[1]-1)]
            D = inPix[u2 % inSize[0], clip(v2, 0, inSize[1]-1)]

            # interpolate
            (r,g,b) = (
              A[0]*(1-mu)*(1-nu) + B[0]*(mu)*(1-nu) + C[0]*(1-mu)*nu+D[0]*mu*nu,
              A[1]*(1-mu)*(1-nu) + B[1]*(mu)*(1-nu) + C[1]*(1-mu)*nu+D[1]*mu*nu,
              A[2]*(1-mu)*(1-nu) + B[2]*(mu)*(1-nu) + C[2]*(1-mu)*nu+D[2]*mu*nu )

            outPix[xOut, yOut] = (int(round(r)), int(round(g)), int(round(b)))

imgIn = Image.open(sys.argv[1])
inSize = imgIn.size
faceSize = inSize[0] / 4
components = sys.argv[1].rsplit('.', 2)

FACE_NAMES = {
  0: 'back',
  1: 'left',
  2: 'front',
  3: 'right',
  4: 'top',
  5: 'bottom'
}

for face in xrange(6):
  imgOut = Image.new("RGB", (faceSize, faceSize), "black")
  convertFace(imgIn, imgOut, face)
  imgOut.save(components[0] + "_" + FACE_NAMES[face] + "." + components[1])
7
Benjamin Dobell

Ich habe diese Frage gefunden, und auch wenn die Antworten gut sind, denke ich, dass noch einiges aufgedeckt ist, also hier meine zwei Cents.

Erstens: Sofern Sie die Bilder nicht wirklich selbst konvertieren müssen (d. H. Aufgrund einer bestimmten Softwareanforderung), ist nicht.

Der Grund ist, dass, obwohl es eine sehr einfache Zuordnung zwischen der gleichwinkligen Projektion und der kubischen Projektion gibt, die Zuordnung zwischen den Bereichen nicht einfach ist: wenn Sie eine Entsprechung zwischen einem bestimmten Punkt Ihres Zielbilds und einem Punkt festlegen die Quelle mit einer elementaren Berechnung, sobald Sie beide Punkte durch Runden von in Pixel konvertieren, führen Sie eine sehr rohe Näherung durch, bei der die Größe der Pixel und die Qualität nicht berücksichtigt werden Das Bild ist zwangsläufig niedrig.

Zweitens: Selbst wenn Sie die Konvertierung zur Laufzeit durchführen müssen, sind Sie sicher, dass Sie die Konvertierung überhaupt durchführen müssen? Wenn es nicht ein sehr strenges Leistungsproblem gibt, wenn Sie nur eine Skybox benötigen, erstellen Sie eine sehr große Kugel, nähen Sie die gleichwinklige Textur darauf und los geht's. Drei JS liefert die Kugel schon, soweit ich mich erinnere ;-)

Drittens: Die NASA bietet ein Werkzeug zum Konvertieren zwischen allen möglichen Projektionen (ich habe es gerade herausgefunden, getestet und funktioniert wie ein Zauber). Sie finden es hier:

G.Projector - Global Map Projektor

und ich finde vernünftig zu denken, dass die Jungs wissen, was sie tun ;-)

Hoffe das hilft

UPDATE: es stellt sich heraus, dass die "guys" wissen, was sie bis zu einem gewissen Punkt tun: Die generierte Cubemap hat einen abscheulichen Rand, der die Konvertierung nicht so einfach macht ...

UPDATE 2: hat das endgültige Werkzeug für die Konvertierung von Äquirektangular in Cubemap gefunden. Es heißt erect2cubic.

Es ist ein kleines Hilfsprogramm, das auf folgende Weise ein Skript generiert, das an hugin übergeben wird:

$ erect2cubic --erect=input.png --ptofile=cube.pto
$ nona -o cube_prefix cube.pto 

(Informationen aus Vinay's Hacks Seite)

und erzeugt alle 6 Cubemap-Flächen. Ich verwende es für mein Projekt und es funktioniert wie ein Zauber!

Der einzige Nachteil dieses Ansatzes ist, dass das Skript erect2cubit nicht in der Standard-Ubuntu-Distribution enthalten ist (was ich verwende) und ich musste auf die Anweisungen unter diesem Link zurückgreifen:

Blog, das beschreibt, wie man erect2cubic installiert und verwendet

um herauszufinden, wie man es installiert.

Total wert! 

6
Rick77

cmftStudio unterstützt conversion/filtering verschiedener HDR/LDR-Projektionen für cubemaps.

https://github.com/dariomanesku/cmftStudio

1
planetboy

Vielleicht fehlt mir hier etwas. Es scheint jedoch, dass die meisten, wenn nicht der gesamte vorgelegte Transformationscode etwas falsch sein kann. Sie nehmen ein sphärisches Panorama (gleichwinklig - 360 Grad horizontal und 180 Grad vertikal) an und scheinen sich mit einer kartesischen <-> zylindrischen Transformation in die Würfelflächen zu verwandeln. Sollten sie keine kartesische <-> sphärische Transformation verwenden. Siehe http://mathworld.wolfram.com/SphericalCoordinates.html

Ich nehme an, dass, solange sie die Berechnung umkehren, um von den Würfelgesichtern zum Panorama zu gelangen, es klappen sollte. Die Bilder der Würfelflächen können jedoch bei Verwendung der sphärischen Transformation leicht abweichen.

Wenn ich mit diesem gleichwinkligen (sphärischen Panorama) beginne:

 enter image description here

Wenn ich dann eine zylindrische Transformation verwende (was zu diesem Zeitpunkt nicht 100% sicher ist), erhalte ich dieses Ergebnis:

 enter image description here

Wenn ich eine sphärische Transformation verwende, erhalte ich folgendes Ergebnis:

 enter image description here

Sie sind nicht gleich. Mein sphärisches Transformationsergebnis scheint jedoch mit dem Ergebnis von Danke Xie übereinzustimmen, aber sein Link zeigt nicht die Art der Transformation, die er verwendet, so gut ich es lesen kann.

Verstehe ich den Code, der von vielen Mitwirkenden zu diesem Thema verwendet wird, falsch?

0
fmw42

Ich habe mit OpenGL eine Lösung für dieses Problem erstellt und ein Befehlszeilentool erstellt. Es funktioniert sowohl mit Bildern als auch Videos und es ist das schnellste Werkzeug, das ich dort gefunden habe.

Convert360 - Projekt auf GitHub.

OpenGL Shader - Der für die erneute Projektion verwendete Fragment-Shader.

Die Verwendung ist so einfach wie:

$ pip install convert360
$ convert360 -i ~/Pictures/Barcelona/sagrada-familia.jpg -o example.png -s 300 300

Um so etwas zu bekommen:

 enter image description here

0
Mateus Zitelli

Eine sehr einfache C++ - App, die basierend auf der Antwort von Salix Alba => https://github.com/denivip/panorama eine gleichrechteckige Panorama-Ansicht in eine Cube-Map umwandelt

0

Hier ist eine JavaScript-Version von Benjamn Dobells Code. Der convertFace müssen zwei ìmageData-Objekte und eine Gesichts-ID (0-6) übergeben werden.

Der bereitgestellte Code kann sicher in einem Web-Worker verwendet werden, da er keine Abhängigkeiten hat.

        // convert using an inverse transformation
        function convertFace(imgIn, imgOut, faceIdx) {
            var inPix = shimImgData(imgIn),
                        outPix = shimImgData(imgOut),
                        faceSize = imgOut.width,
                        pi = Math.PI,
                        pi_2 = pi/2;

            for(var xOut=0;xOut<faceSize;xOut++) {
                    for(var yOut=0;yOut<faceSize;yOut++) {

                    var xyz = outImgToXYZ(xOut, yOut, faceIdx, faceSize);
                    var theta = Math.atan2(xyz.y, xyz.x); // range -pi to pi
                    var r = Math.hypot(xyz.x,xyz.y);
                    var phi = Math.atan2(xyz.z,r); // range -pi/2 to pi/2

                    // source img coords
                    var uf = 0.5 * imgIn.width * (theta + pi) / pi;
                    var vf = 0.5 * imgIn.width * (pi_2 - phi) / pi;

                    // Use bilinear interpolation between the four surrounding pixels
                    var ui = Math.floor(uf);  // coord of pixel to bottom left
                    var vi = Math.floor(vf);
                    var u2 = ui+1;       // coords of pixel to top right
                    var v2 = vi+1;
                    var mu = uf-ui;      // fraction of way across pixel
                    var nu = vf-vi;

                    // Pixel values of four corners
                    var A = inPix.getPx(ui % imgIn.width, clip(vi, 0, imgIn.height-1));
                    var B = inPix.getPx(u2 % imgIn.width, clip(vi, 0, imgIn.height-1));
                    var C = inPix.getPx(ui % imgIn.width, clip(v2, 0, imgIn.height-1));
                    var D = inPix.getPx(u2 % imgIn.width, clip(v2, 0, imgIn.height-1));

                    // interpolate
                    var rgb = {
                      r:A[0]*(1-mu)*(1-nu) + B[0]*(mu)*(1-nu) + C[0]*(1-mu)*nu+D[0]*mu*nu,
                      g:A[1]*(1-mu)*(1-nu) + B[1]*(mu)*(1-nu) + C[1]*(1-mu)*nu+D[1]*mu*nu,
                      b:A[2]*(1-mu)*(1-nu) + B[2]*(mu)*(1-nu) + C[2]*(1-mu)*nu+D[2]*mu*nu
                    };

                    rgb.r=Math.round(rgb.r);
                    rgb.g=Math.round(rgb.g);
                    rgb.b=Math.round(rgb.b);

                    outPix.setPx(xOut, yOut, rgb);

                } // for(var yOut=0;yOut<faceSize;yOut++) {...}
             } // for(var xOut=0;xOut<faceSize;xOut++) {...}
        } // function convertFace(imgIn, imgOut, faceIdx) {...}

        // get x,y,z coords from out image pixels coords
        // i,j are pixel coords
        // faceIdx is face number
        // faceSize is Edge length
        function outImgToXYZ(i, j, faceIdx, faceSize) {
            var a = 2 * i / faceSize,
                    b = 2 * j / faceSize;

            switch(faceIdx) {
                case 0: // back
                return({x:-1, y:1-a, z:1-b});
            case 1: // left
                return({x:a-1, y:-1, z:1-b});
            case 2: // front
                return({x: 1, y:a-1, z:1-b});
            case 3: // right
                return({x:1-a, y:1, z:1-b});
            case 4: // top
                return({x:b-1, y:a-1, z:1});
            case 5: // bottom
                return({x:1-b, y:a-1, z:-1});

            }
        } // function outImgToXYZ(i, j, faceIdx, faceSize) {...}

        function clip(val, min, max) {
            return(val<min?min:(val>max?max:val));
        }

        function shimImgData(imgData) {
            var w=imgData.width*4,
                    d=imgData.data;

            return({
                getPx:function(x,y) {
                    x=x*4+y*w;
                    return([ d[x], d[x+1], d[x+2] ]);
                },
                setPx:function(x,y,rgb) {
                    x=x*4+y*w;
                    d[x]=rgb.r;
                    d[x+1]=rgb.g;
                    d[x+2]=rgb.b;
                    d[x+3]=255; // alpha
                }
            });
        } // function shimImgData(imgData) {...}
0
knee-cola

Es gibt verschiedene Darstellungen von Umgebungskarten. Hier ist eine schöne Übersicht.

Übersicht - Panoramabilder

Wenn Sie Photosphere (oder eine andere Panorama-App für diese Angelegenheit) verwenden, haben Sie höchstwahrscheinlich bereits die horizontale Breiten Längengrad-/Darstellung. Sie können dann einfach eine texturierte Three.js SphereGeometry zeichnen. Hier ist ein Tutorial, wie man Erde rendert.

Tutorial - Wie mache ich die Erde in WebGL?

Viel Glück :).

0
Tobias Gurdan