Unity3D shader to distort a texture with OpenCV distortion model

OpenCV is truly awesome, I hope someday I will be able to contribute to the project.

However, some operations are really costly on CPU, for example the remap() function.

You have an image or video that is distorted because of the lens of the camera, but because you calibrated this camera, you can undistort it with the parameters OpenCV computed. You can find the explanation and the formulas of the distortion model on this page of the documentation.

I managed to use a fragment shader with Unity3D to do this operation in real-time. You simply need the distortion parameters (polynomials factors k1, k2, p1, p2 and k3 are managed).

Once applied on a material, the shader will apply the distortion (or undistortion, depending on your point of view of course) on the texture. So it works well with the WebcamTexture object provided by Unity for example.

Here is the precious code:

Shader "Unlit/ImageDistortion" {
    Properties {
        _MainTex ("_MainTex", 2D) = "green" {}
        Invert("Invert",Int) = 0
        LensOffsetX("LensOffsetX", Range(-1,1)) = 0
        LensOffsetY("LensOffsetY", Range(-1,1)) = 0
        RadialDistortionX("RadialDistortionX", Range(-1.57,1.57)) = 0
        RadialDistortionY("RadialDistortionY", Range(-1.57,1.57)) = 0
        K5Distortion("K5Distortion", Range(-1.57,1.57)) = 0
    }
SubShader 
    {
    Pass
    {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
    #include "UnityCG.cginc"
            sampler2D _MainTex;
            float RadialDistortionX;
            float RadialDistortionY;
            float K5Distortion;
            float LensOffsetX;
            float LensOffsetY;
            int Invert;

            struct VertexInput {
                float4 Position : POSITION;
                float2 uv_MainTex : TEXCOORD0;
            };

            struct FragInput {
                float4 Position : SV_POSITION;
                float2  uv_MainTex : TEXCOORD0;
            };

            FragInput vert(VertexInput In) {
                FragInput Out;

                Out.Position = UnityObjectToClipPos (In.Position );
                Out.uv_MainTex = In.uv_MainTex;

                return Out;
            }

            float2 DistortPixel(float2 Point)
            {
                float Inverse = Invert?-1:1;
                float cx = LensOffsetX;
                float cy = LensOffsetY;
                float k1 = RadialDistortionX * Inverse;
                float k2 = RadialDistortionY * Inverse;
                float k3 = K5Distortion * Inverse;

                float x = Point.x - cx;
                    float y = Point.y - cy;
                float r2 = x*x + y*y;

                float xDistort = x * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2);
                float yDistort = y * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2);

                xDistort = xDistort + cx;
                yDistort = yDistort + cy;

                return float2( xDistort, yDistort);
            }

            float2 CenterUv(float2 uv)
            {
                uv = uv*float2(2,2) - float2(1,1);
                return uv;
            }

            float2 UncenterUv(float2 uv)
            {
                uv = (uv+float2(1,1)) / float2(2,2);
                return uv;
            }

            fixed4 frag(FragInput In) : SV_Target {

                float2 uv = In.uv_MainTex;
                uv = CenterUv(uv);
                uv = DistortPixel( uv );
                uv = UncenterUv(uv);

                if ( uv.x > 1 )
                    return float4(1,0,0,1);
                if ( uv.y > 1 )
                    return float4(0,1,0,1);
                if ( uv.x < 0 )
                    return float4(0,0,1,1);
                if ( uv.y < 0 )
                    return float4(1,1,0,1);

                uv = CenterUv(uv);
                uv = UncenterUv(uv);

                float4 Sample = tex2D( _MainTex, uv );

                return Sample;

            }

        ENDCG
    }   
    } 
}

This article is my 15th oldest. It is 468 words long

© 2018 Stan Larroque. All rights reserved.