【Android】お手軽VR画像撮影・体験 のバックアップソース(No.2)

#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