#author("2020-04-29T11:23:19+00:00","default:fuzuki","fuzuki")
* 100均とスマホでVR画像撮影 [#zad998cc]
今ではある程度当たり前になってきたVR技術ですが、それでも自分でVR画像を撮影したりするのはあまり一般的ではありません。~
鑑賞するだけなら100均の簡易なゴーグルでもいいのですが、自分で撮影するとなると[https://theta360.com/ja/ RICOH THETA]のような専用デバイスが必要になってきます。~
……本当にそうでしょうか?~
考えてみればRICOH THETAは広角の魚眼レンズを前後に張り付けただけのものです。幸い魚眼レンズなら100均で手に入ります。スマホで魚眼画像をVR表示するソフトも[https://play.google.com/store/apps/details?id=com.xojot.vrplayer&hl=ja VR Media Player]などがあります。~
というわけで試してみました。~
#ref(./IMG_20200428_194020.jpg,50%);
#ref(./IMG_20200429_095347.jpg,50%);
VR Media Playerで写真を選択して、キャン★ドゥの魚眼レンズの場合LensはFisheye3、3DはNon-3DにしてVR表示をすると、若干反応が遅いもののそれなりにVRな体験ができました。~
* 魚眼画像の正距円筒画像変換 [#y02b2ff4]
魚眼のままVR表示できるのは楽でいいのですが、他のソフトでの表示なども考えると、正距円筒画像に変換しておきたくなります。~
ひとまず[https://stackoverflow.com/questions/56986420/convert-a-fisheye-image-to-an-equirectangular-image-with-opencv4 stackoverflow]で見つけたコード。正方形でないとうまく動かないようです。~
いずれきちんとしたものを作りましょう。~
#ref(./im2.jpg,30%);
using OpenCvSharp;
using System;
namespace FisheyeTest
{
class Program
{
const string PATH_IMAGE = "fisheyetest.jpg";
const int ESC = 27;
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Mat fisheyeImage, equirectangularImage;
int Wf, Hf;
float FOV;
int We, He;
fisheyeImage = Cv2.ImRead(PATH_IMAGE);
Cv2.NamedWindow("Fisheye Image");
Cv2.ImShow("fisheye Image", fisheyeImage);
Wf = fisheyeImage.Size().Width;
Hf = fisheyeImage.Size().Height;
FOV = (180 * (float)Math.PI) / 180;
We = Wf;
He = Hf;
while (Cv2.WaitKey(0) != ESC)
{
}
equirectangularImage = new Mat();
equirectangularImage.Create(He, We, MatType.CV_8UC3);
for (int Xe = 0; Xe < We; Xe++)
{
for (int Ye = 0; Ye < He; Ye++)
{
Point2f fisheyePoint = findCorrespondingFisheyePoint(Xe, Ye, We, He, FOV);
if (fisheyePoint.X >= We || fisheyePoint.Y >= He)
continue;
if (fisheyePoint.X < 0 || fisheyePoint.Y < 0)
continue;
//equirectangularImage.At<Vec3b>(Xe, Ye) = fisheyeImage.At<Vec3b>((int)fisheyePoint.X, (int)fisheyePoint.Y);
equirectangularImage.Set<Vec3b>(Xe,Ye, fisheyeImage.At<Vec3b>((int)fisheyePoint.X, (int)fisheyePoint.Y));
}
}
Cv2.NamedWindow("Equirectangular Image");
Cv2.ImShow("Equirectangular Image", equirectangularImage);
while (Cv2.WaitKey(0) != ESC)
{
}
Cv2.ImWrite("im2.jpg", equirectangularImage);
}
static Point2f findCorrespondingFisheyePoint(int Xe, int Ye, int We, int He, float FOV)
{
Point2f fisheyePoint = new Point2f();
float theta, phi, r;
Point3f sphericalPoint = new Point3f();
theta = (float)(Math.PI * (Xe / ((float)We) - 0.5));
phi = (float)(Math.PI * (Ye / ((float)He) - 0.5));
sphericalPoint.X = (float)(Math.Cos(phi) * Math.Sin(theta));
sphericalPoint.Y = (float)(Math.Cos(phi) * Math.Cos(theta));
sphericalPoint.Z = (float)Math.Sin(phi);
theta = (float)Math.Atan2(sphericalPoint.Z, sphericalPoint.X);
phi = (float)Math.Atan2(Math.Sqrt(Math.Pow(sphericalPoint.X, 2) + Math.Pow(sphericalPoint.Z, 2)), sphericalPoint.Y);
r = ((float)We) * phi / FOV;
fisheyePoint.X = (int)(0.5 * ((float)We) + r * Math.Cos(theta));
fisheyePoint.Y = (int)(0.5 * ((float)He) + r * Math.Sin(theta));
return fisheyePoint;
}
}
}
*** 参考リンク [#mde80d98]
- http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20160629
- http://panoramania.co.jp/diary/archives/2013/01/how-to-defish.html
- https://boofcv.org/index.php?title=Example_Fisheye_To_Equirectangular