【Android】お手軽VR画像撮影・体験
Last-modified: 2020-05-14 (木) 18:33:37 (1404d)
100均とスマホでVR画像撮影 †
今ではある程度当たり前になってきたVR技術ですが、それでも自分でVR画像を撮影したりするのはあまり一般的ではありません。
鑑賞するだけなら100均の簡易なゴーグルでもいいのですが、自分で撮影するとなるとRICOH THETAのような専用デバイスが必要になってきます。
……本当にそうでしょうか?
考えてみればRICOH THETAは広角の魚眼レンズを前後に張り付けただけのものです。幸い魚眼レンズなら100均で手に入ります。スマホで魚眼画像をVR表示するソフトもVR Media Playerなどがあります。
というわけで試してみました。
VR Media Playerで写真を選択して、キャン★ドゥの魚眼レンズの場合LensはFisheye3、3DはNon-3DにしてVR表示をすると、若干反応が遅いもののそれなりにVRな体験ができました。
魚眼画像の正距円筒画像変換 †
魚眼のままVR表示できるのは楽でいいのですが、他のソフトでの表示なども考えると、正距円筒画像に変換しておきたくなります。
ひとまずstackoverflowで見つけたコード。正方形でないとうまく動かないようです。
いずれきちんとしたものを作りましょう。
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; } } }
アップロード †
変換した画像はハコスコストアやfacebookなどのパノラマ表示可能なサイトに投稿することもできます。