I'm saat ini bekerja pada sebuah sederhana 3D panorama viewer untuk sebuah situs web. Untuk kinerja ponsel alasan saya'm menggunakan three.js
CSS3 renderer. Hal ini memerlukan sebuah kubus peta, dibagi menjadi 6 gambar tunggal.
I'm merekam gambar pada iPhone dengan Google Fotosfer app, atau aplikasi sejenis yang menciptakan 2:1 persegi panjang panorama. Saya kemudian mengubah ukuran dan mengkonversi ke cubemap dengan website ini: http://gonchar.me/panorama/ (Flash)
Sebaiknya, aku'd suka melakukan konversi sendiri, baik pada fly di three.js jika itu's mungkin, atau di Photoshop. Saya menemukan Andrew Hazelden's Photoshop actions, dan sepertinya mereka dekat, tapi tidak ada konversi langsung tersedia. Apakah ada cara matematika untuk mengkonversi, atau semacam script yang melakukan itu? I'd seperti untuk menghindari pergi melalui aplikasi 3D seperti Blender, jika mungkin.
Mungkin ini adalah tembakan panjang, tapi saya pikir saya'd meminta. Aku baik-baik saja dengan javascript, tapi aku'm cukup baru untuk three.js
. I'm juga ragu-ragu untuk mengandalkan WebGL fungsi, karena tampaknya baik lambat atau kereta pada perangkat mobile. Dukungan juga masih jerawatan.
Jika anda ingin melakukan hal ini sisi server ada banyak pilihan. http://www.imagemagick.org/ memiliki banyak command line tools yang bisa mengiris gambar anda menjadi potongan-potongan. Anda bisa menempatkan perintah untuk melakukan hal ini dalam naskah dan hanya menjalankan bahwa setiap kali anda memiliki gambar baru.
Sulit untuk mengatakan apa yang algoritma yang digunakan dalam program. Kita dapat mencoba dan merekayasa cukup apa yang terjadi dengan makan kotak persegi ke dalam program. I've digunakan grid dari wikipedia
Yang memberikan hal Ini memberikan kita petunjuk bagaimana kotak dibangun.
Pencitraan bidang dengan garis-garis lintang dan bujur yang satu ini, dan cube di sekitarnya. Sekarang proyek dari titik pusat bola menghasilkan terdistorsi grid pada kubus.
Secara matematis mengambil koordinat polar r, θ, ø, untuk bidang r=1, 0 < θ < π, π/4 < ø < 7π/4
pusat proyek ini untuk kubus. Pertama kita membagi menjadi empat wilayah dengan lintang -π/4 < ø < π/4, π/4 < ø < 3π/4, 3π/4 < ø < 5π/4, 5π/4 < ø < 7π/4. Ini akan baik proyek untuk salah satu dari empat sisi atas atau bawah.
Asumsikan kita berada dalam sisi pertama -π/4 < ø < π/4. Pusat proyeksi (sin θ cos ø sin θ sin ø cos θ) akan (a sin θ cos ø, a sin θ sin ø, a cos θ) yang adalah x=1 pesawat ketika
jadi
dan proyeksi titik
Jika | cot θ / cos ø | < 1 ini akan berada di depan wajah. Jika tidak, itu akan diproyeksikan di bagian atas atau bawah dan anda akan perlu proyeksi yang berbeda untuk itu. Sebuah tes yang lebih baik untuk bagian atas menggunakan fakta bahwa nilai minimum dari cos ø akan cos π/4 = 1/√2, sehingga proyeksi titik selalu di atas jika cot θ / (1/√2) > 1 atau tan θ < 1/√2. Ini bekerja sebagai θ < 35º atau 0.615 radian.
Menempatkan ini bersama-sama di python
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()
The proyeksi
fungsi mengambil theta
dan phi
nilai-nilai dan kembali koordinat dalam sebuah kubus dari -1 ke 1 di setiap arah. Yang cubeToImg mengambil (x,y,z) coords dan menerjemahkan mereka ke output gambar coords.
Algoritma di atas tampaknya untuk mendapatkan geometri yang tepat menggunakan image dari buckingham palace kita mendapatkan Hal ini tampaknya untuk mendapatkan sebagian besar dari garis-garis di paving kanan.
Kami mendapatkan beberapa gambar artefak. Ini adalah karena tidak memiliki 1 ke 1 peta piksel. Apa yang perlu kita lakukan adalah menggunakan invers transformasi. Daripada loop melalui setiap pixel dalam sumber dan menemukan pixel yang sesuai dengan target kita loop melalui target gambar dan menemukan terdekat sesuai sumber pixel.
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()
Hasil ini
Diberikan setiap jawaban yang diterima, saya ingin menambahkan saya yang sesuai c++ pelaksanaan berdasarkan OpenCV.
Bagi mereka yang tidak akrab dengan OpenCV, berpikir Mat
sebagai gambar. Pertama-tama kita membangun dua peta yang di-remap dari persegi panjang gambar kita sesuai cubemap wajah. Kemudian, kita melakukan angkat berat (yaitu remapping dengan interpolasi) dengan menggunakan OpenCV.
Kode dapat dibuat lebih kompak, jika dibaca tidak menjadi perhatian.
// 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));
}
Diberikan input berikut:
Berikut wajah-wajah yang dihasilkan:
Image courtesy of Optonaut.
Saya menulis naskah untuk memotong dihasilkan cubemap ke file individual (posx.png, negx.png, posy.png, negy.png, posz.png dan negz.png). Hal ini juga akan mengemas 6 file ke .file zip.
Sumber di sini: https://github.com/dankex/compv/blob/master/3d-graphics/skybox/cubemap-cut.py
Anda dapat memodifikasi array untuk mengatur file gambar:
name_map = [ \
["", "", "posy", ""],
["negz", "negx", "posz", "posx"],
["", "", "negy", ""]]
File yang dikonversi adalah:
Berikut ini's (naif) versi modifikasi dari Salix Alba's benar-benar fantastis jawaban yang mengubah satu wajah pada satu waktu, meludah keluar enam gambar yang berbeda dan mempertahankan gambar asli's jenis file.
Selain dari fakta kebanyakan kasus penggunaan mungkin mengharapkan enam gambar yang terpisah, keuntungan utama dari mengubah satu wajah pada satu waktu adalah bahwa hal itu membuat bekerja dengan gambar besar banyak memori kurang intensif.
#!/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])
Menemukan pertanyaan ini, dan meskipun jawaban yang baik, saya pikir masih ada beberapa tanah yang terbongkar, jadi di sini adalah saya dua sen.
Pertama: kecuali anda benar-benar harus mengkonversi gambar anda sendiri (yaitu, karena beberapa spesifik kebutuhan perangkat lunak), don't.
Alasannya adalah bahwa, meskipun ada yang sangat sederhana pemetaan antara persegi panjang proyeksi dan kubik proyeksi, pemetaan antara daerah yang tidak sederhana: ketika anda membangun korespondensi antara titik tertentu dari tujuan anda gambar dan titik di sumber dengan dasar perhitungan, segera setelah anda mengkonversi poin untuk piksel dengan pembulatan anda melakukan sangat baku pendekatan yang doesn't mempertimbangkan ukuran piksel, dan kualitas gambar pasti akan rendah.
Kedua: bahkan jika anda perlu untuk melakukan konversi pada saat runtime, apakah anda yakin bahwa anda perlu untuk melakukan konversi sama sekali? Kecuali ada beberapa yang sangat ketat masalah kinerja, jika anda hanya perlu skybox, membuat yang sangat besar bola, menjahit persegi panjang texure di atasnya, dan anda pergi. Tiga JS menyediakan bola sudah, sejauh yang saya ingat ;-)
Ketiga: NASA menyediakan alat untuk mengkonversi antara semua dibayangkan proyeksi (saya baru tahu, diuji, dan bekerja seperti pesona). Anda dapat menemukannya di sini:
G. Proyektor — Peta Global Proyektor
dan saya menemukan akal untuk berpikir bahwa orang-orang tahu apa yang mereka lakukan ;-)
Semoga ini bisa membantu
UPDATE: ternyata bahwa "orang" tahu apa yang mereka lakukan hingga beberapa titik: dihasilkan cubemap memiliki mengerikan perbatasan yang membuat konversi tidak mudah...
UPDATE 2: ditemukan alat definitif untuk persegi panjang untuk cubemap konversi, dan's disebut erect2cubic
.
It's utilitas kecil yang menghasilkan naskah yang akan diumpankan ke hugin, cara ini:
$ erect2cubic --erect=input.png --ptofile=cube.pto
$ nona -o cube_prefix cube.pto
(informasi tersedot dari Vinay's Hacks halaman)
dan akan menghasilkan semua 6 cubemap wajah. I'm menggunakannya untuk proyek saya dan itu bekerja seperti pesona!
Satu-satunya kelemahan dari pendekatan ini adalah bahwa script erect2cubit
it's tidak ada dalam standar Ubuntu distribusi (yang adalah apa yang saya'm menggunakan) dan aku harus resor untuk petunjuk yang ada di link ini:
Blog yang menjelaskan cara menginstal dan menggunakan erect2cubic
untuk mengetahui bagaimana cara menginstalnya.
Benar-benar layak!
cmft Studio mendukung konversi/penyaringanberbagai
HDR/LDRproyeksi untuk
cubemaps`.
Berikut ini's JavaScript versi Benjamn Dobell's code. The convertFace
harus melewati dua ìmageData
benda-benda dan face ID (0-6).
Kode yang disediakan dapat dengan aman digunakan di web pekerja, karena tidak memiliki dependensi.
// 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) {...}
Yang sangat sederhana C++ aplikasi yang mengubah sebuah persegi panjang panorama untuk kubus peta berdasarkan jawaban oleh Salix Alba => https://github.com/denivip/panorama
Mungkin aku kehilangan sesuatu di sini. Tapi tampaknya bahwa kebanyakan jika tidak semua disajikan transformasi kode mungkin agak salah. Mereka mengambil panorama bola (persegi panjang --- 360 derajat secara horizontal dan 180 derajat secara vertikal) dan tampaknya untuk mengkonversi ke wajah kubus menggunakan kartesius <-> silinder transformasi. Mereka tidak boleh menggunakan kartesius <-> bulat transformasi. Lihat http://mathworld.wolfram.com/SphericalCoordinates.html
Saya kira bahwa selama mereka membalikkan perhitungan untuk pergi dari wajah kubus untuk panorama, maka harus bekerja keluar. Tapi gambar wajah kubus mungkin akan sedikit berbeda ketika menggunakan bola transformasi.
Jika saya mulai dengan persegi panjang (spherical panorama):
Kemudian jika saya menggunakan silinder transformasi (yang saya tidak 100% yakin benar pada saat ini), saya mendapatkan hasil seperti ini:
Tapi jika saya menggunakan bola transformasi, saya mendapatkan hasil seperti ini:
Mereka tidak sama. Tapi saya bulat transformasi hasilnya sesuai dengan hasil Danke Xie, tapi link nya tidak menunjukkan jenis transformasi yang ia gunakan, sebagai terbaik yang saya bisa membacanya.
Jadi aku kesalahpahaman kode yang digunakan oleh banyak kontributor untuk topik ini?
Saya membuat sebuah solusi untuk masalah ini dengan menggunakan OpenGL dan membuat alat baris perintah di sekitar itu. Ini bekerja baik dengan gambar dan video, dan itu adalah alat tercepat yang saya temukan di luar sana.
Convert360 - Project di GitHub.
Shader OpenGL - fragment shader digunakan untuk re-proyeksi.
Penggunaan yang sederhana seperti:
$ pip install convert360
$ convert360 -i ~/Pictures/Barcelona/sagrada-familia.jpg -o example.png -s 300 300
Untuk mendapatkan sesuatu seperti ini:
Ada berbagai representasi peta lingkungan. Berikut ini adalah gambaran yang baik.
Jika anda menggunakan Fotosfer (atau aplikasi panorama untuk hal ini), anda kemungkinan besar sudah memiliki horisontal latitude / longitude representasi. Anda kemudian dapat hanya menggambar bertekstur three.js SphereGeometry. Berikut ini adalah tutorial tentang cara untuk membuat bumi ini.
Tutorial - Cara Membuat Bumi dalam WebGL?
Best of luck :).