Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast algorithm for image distortion

I am working on a tool which distorts images, the purpose of the distortion is to project images to a sphere screen. The desired output is as the following image.

The code I use is as follow - for every Point(x, y) in the destination area, I calculate the corresponding pixel (sourceX, sourceY) in the original image to retrieve from.

But this approach is awkwardly slow, in my test, processing the sunset.jpg (800*600) requires more than 1500ms, if I remove the Mathematical/Trigonometrical calculations, calling cvGet2D and cvSet2D alone require more than 1200ms.

Is there a better way to do this? I am using Emgu CV (a .NET wrapper library for OpenCV) but examples in other language is also OK.

private static void DistortSingleImage()
{
    System.Diagnostics.Stopwatch stopWatch = System.Diagnostics.Stopwatch.StartNew();
    using (Image<Bgr, Byte> origImage = new Image<Bgr, Byte>("sunset.jpg"))
    {
        int frameH = origImage.Height;
        using (Image<Bgr, Byte> distortImage = new Image<Bgr, Byte>(2 * frameH, 2 * frameH))
        {
            MCvScalar pixel;
            for (int x = 0; x < 2 * frameH; x++)
            {
                for (int y = 0; y < 2 * frameH; y++)
                {
                    if (x == frameH && y == frameH) continue; 
                    int x1 = x - frameH;
                    int y1 = y - frameH;
                    if (x1 * x1 + y1 * y1 < frameH * frameH) 
                    {
                        double radius = Math.Sqrt(x1 * x1 + y1 * y1);
                        double theta = Math.Acos(x1 / radius);
                        int sourceX = (int)(theta * (origImage.Width - 1) / Math.PI);
                        int sourceY = (int)radius;
                        pixel = CvInvoke.cvGet2D(origImage.Ptr, sourceY, sourceX);
                        CvInvoke.cvSet2D(distortImage, y, x, pixel);
                    }
                }
            }
            distortImage.Save("Distort.jpg");
        }
        Console.WriteLine(stopWatch.ElapsedMilliseconds);
    }
}

The desired distortion

like image 706
kennyzx Avatar asked May 10 '26 01:05

kennyzx


1 Answers

From my personal experience, I was doing some stereoscopic vision stuff, the best way to talk to openCV is through own wrapper, you could put your method in c++ and call it from c#, that would give you 1 call to native, faster code, and because under the hood Emgu's keeping OpenCV data, it's also possible to create an image with emgu, process it natively and enjoy processed image in c# again.

The get/set methods looks like Gdi's GetPixel / SetPixel ones, and, according to documentation they are "slow but safe way".

For staying with Emgu only, documentation tells that if you want to iterate over pixels, you should access .Data property:

The safe (slow) way

Suppose you are working on an Image. You can obtain the pixel on the y-th row and x-th column by calling Bgr color = img[y, x]; Setting the pixel on the y-th row and x-th column is also simple img[y,x] = color;

The fast way

The Image pixels values are stored in the Data property, a 3D array. Use this property if you need to iterate through the pixel values of the image.

like image 122
Marcin Deptuła Avatar answered May 12 '26 17:05

Marcin Deptuła