title: Convert dual fisheye video to equirectangular
body: <p>I used open framework to convert dual fisheye video (received from ricoh theta S camera) to equirectangular format (for an iOS application). </p>
  
  <p>dual fisheye image sample:
  <a href="https://i.stack.imgur.com/agUJf.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/agUJf.jpg" alt="enter image description here"></a></p>
  
  <p>equirectangular image sample:
  <a href="https://i.stack.imgur.com/izy37.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/izy37.jpg" alt="enter image description here"></a></p>
  
  <p>I use below shader: </p>
  
  <p><code>equirectangular.frag</code></p>
  
  <pre><code>  // based on ThetaShaderPack_20150926 (http://stereoarts.jp/) written by Nora.
  #ifdef GL_ES
  // define default precision for float, vec, mat.
  precision highp float;
  #endif
  
  #define PI 3.14159265358979
  #define _THETA_S_Y_SCALE    (640.0 / 720.0)
  
  uniform sampler2D mainTex;
  uniform float radius;
  uniform vec4 uvOffset;
  
  varying vec2 texCoordVarying;
  
  void main (void) {
      vec2 revUV = texCoordVarying.st;
      if (texCoordVarying.x &lt;= 0.5) {
          revUV.x = revUV.x * 2.0;
      } else {
          revUV.x = (revUV.x - 0.5) * 2.0;
      }
  
      revUV *= PI;
  
      vec3 p = vec3(cos(revUV.x), cos(revUV.y), sin(revUV.x));
      p.xz *= sqrt(1.0 - p.y * p.y);
  
      float r = 1.0 - asin(p.z) / (PI / 2.0);
      vec2 st = vec2(p.y, p.x);
  
      st *= r / sqrt(1.0 - p.z * p.z);
      st *= radius;
      st += 0.5;
  
      if (texCoordVarying.x &lt;= 0.5) {
          st.x *= 0.5;
          st.x += 0.5;
          st.y = 1.0 - st.y;
          st.xy += uvOffset.wz;
      } else {
          st.x = 1.0 - st.x;
          st.x *= 0.5;
          st.xy += uvOffset.yx;
      }
  
      st.y = st.y * _THETA_S_Y_SCALE;
  
      gl_FragColor = texture2D(mainTex, st);
  }
  </code></pre>
  
  <p><code>equirectanguler.vert</code></p>
  
  <pre><code>uniform mat4 projectionMatrix;
  uniform mat4 modelViewMatrix;
  uniform mat4 textureMatrix;
  uniform mat4 modelViewProjectionMatrix;
  
  attribute vec4  position;
  attribute vec4  color;
  attribute vec3  normal;
  attribute vec2  texcoord;
  
  varying vec2 texCoordVarying;
  
  void main() {
      texCoordVarying = texcoord;
      gl_Position = modelViewProjectionMatrix * position;
  }
  </code></pre>
  
  <p>with below code:
  <code>main.mm</code></p>
  
  <pre><code>#include "ofApp.h"
  
  int main() {
  
      //  here are the most commonly used iOS window settings.
      //------------------------------------------------------
      ofiOSWindowSettings settings;
      settings.enableRetina = false; // enables retina resolution if the device supports it.
      settings.enableDepth = false; // enables depth buffer for 3d drawing.
      settings.enableAntiAliasing = false; // enables anti-aliasing which smooths out graphics on the screen.
      settings.numOfAntiAliasingSamples = 0; // number of samples used for anti-aliasing.
      settings.enableHardwareOrientation = false; // enables native view orientation.
      settings.enableHardwareOrientationAnimation = false; // enables native orientation changes to be animated.
      settings.glesVersion = OFXIOS_RENDERER_ES2; // type of renderer to use, ES1, ES2, ES3
      settings.windowMode = OF_FULLSCREEN;
      ofCreateWindow(settings);
  
      return ofRunApp(new ofApp);
  }
  
  ```
  
  `offApp.mm`
  
  ```
  #include "ofApp.h"
  
  //--------------------------------------------------------------
  void ofApp::setup(){    
      ofDisableArbTex();
  
      devices = theta.listDevices();
      bool isDeviceConnected = false;
      for(int i = 0; i &lt; devices.size(); i++){
          if(devices[i].deviceName == "RICOH THETA S"){
              theta.setDeviceID(devices[i].id);
              isDeviceConnected = true;
          }
      }
      if(!isDeviceConnected){
          ofLog(OF_LOG_ERROR, "RICOH THETA S is not found.");
      }
      theta.initGrabber(360, 568);
  
      shader.load("shaders/equirectanguler");
  
      fbo.allocate(320, 568);
  
      sphere = ofSpherePrimitive(568, 64).getMesh();
      for(int i=0;i&lt;sphere.getNumTexCoords();i++){
          sphere.setTexCoord(i, ofVec2f(1.0) - sphere.getTexCoord(i));
      }
      for(int i=0;i&lt;sphere.getNumNormals();i++){
          sphere.setNormal(i, sphere.getNormal(i) * ofVec3f(-1));
      }
  
      offset.set("uvOffset", ofVec4f(0,0.0,0,0.0), ofVec4f(-0.1), ofVec4f(0.1));
      radius.set("radius", 0.445, 0.0, 1.0);
      showSphere.set("showSphere", false);
      thetaParams.add(offset);
      thetaParams.add(radius);
      gui.setup(thetaParams);
      gui.add(showSphere);
  
      cam.setAutoDistance(false);
      cam.setDistance(0);
  }
  
  //--------------------------------------------------------------
  void ofApp::update(){
      theta.update();
  }
  
  //--------------------------------------------------------------
  void ofApp::draw(){
      if(theta.isFrameNew()){
  
          fbo.begin();
          ofClear(0);
          shader.begin();
          shader.setUniformTexture("mainTex", theta.getTexture(), 0);
          shader.setUniforms(thetaParams);
          theta.draw(0, 0, 320, 568);
          shader.end();
          fbo.end();
  
      }
  
      if(!showSphere){
  
          fbo.draw(0, 0, 320, 568);
  
      }else{
  
          ofEnableDepthTest();
          cam.begin();
          fbo.getTexture().bind();
          sphere.draw();
          fbo.getTexture().unbind();
          cam.end();
  
      }
  
      ofDisableDepthTest();
      gui.draw();
  }
  
  //--------------------------------------------------------------
  void ofApp::exit(){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::touchDown(ofTouchEventArgs &amp; touch){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::touchMoved(ofTouchEventArgs &amp; touch){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::touchUp(ofTouchEventArgs &amp; touch){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::touchDoubleTap(ofTouchEventArgs &amp; touch){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::touchCancelled(ofTouchEventArgs &amp; touch){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::lostFocus(){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::gotFocus(){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::gotMemoryWarning(){
  
  }
  
  //--------------------------------------------------------------
  void ofApp::deviceOrientationChanged(int newOrientation){
  
  }
  </code></pre>
  
  <p><code>offApp.h</code></p>
  
  <pre><code>#pragma once
  
  #include "ofxiOS.h"
  #include "ofxGui.h"
  
  class ofApp : public ofxiOSApp {
  
      public:
          void setup();
          void update();
          void draw();
          void exit();
  
          void touchDown(ofTouchEventArgs &amp; touch);
          void touchMoved(ofTouchEventArgs &amp; touch);
          void touchUp(ofTouchEventArgs &amp; touch);
          void touchDoubleTap(ofTouchEventArgs &amp; touch);
          void touchCancelled(ofTouchEventArgs &amp; touch);
  
          void lostFocus();
          void gotFocus();
          void gotMemoryWarning();
          void deviceOrientationChanged(int newOrientation);
  
      ofVideoGrabber theta;
      vector&lt;ofVideoDevice&gt; devices;
      ofShader shader;
      ofFbo fbo;
      ofEasyCam cam;
      ofVboMesh sphere;
  
      ofParameter&lt;ofVec4f&gt; offset;
      ofParameter&lt;float&gt; radius;
      ofParameter&lt;bool&gt; showSphere;
      ofParameterGroup thetaParams;
      ofxPanel gui;
  };
  </code></pre>
  
  <p>And here is the result:
  on iphone5s
  <a href="https://i.stack.imgur.com/B2nVZ.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/B2nVZ.jpg" alt="enter image description here"></a></p>
  
  <p>pls point me out what i missed.</p>