Circular Fisheye Image dewarp to flat image

こ雲淡風輕ζ 提交于 2020-03-27 06:36:29

问题


UPDATE as on 12 Nov 2015

I used PanoTools plugin with Photoshop and Hugin and played with all those parameters. End up i found the parameters for projection, HFOV and image output size that fulfill my lowest requirement.

Parameteres:

Processed Output:

My question is then how can i convert all these parameters and values into C# algorithm coding so that when I provide the original image, i will get the corrected output image?

Thanks a lot.


I have a square image captured from a circular fisheye camera. The size is 2650 * 2650 pixels.

Now, i will need to programmatically dewarp the image to a flat panorama image using C# language. I had look around from internet with different algorithm example from Link for code below , Link1 and Link2 but just can't make it success. My maths sincerely sucks and can't help me with that. Hopefully someone able to guide me through this. Thanks a lot.

Example of image output from the camera:

--Image grabbed from Wikipedia Fisheye Lens & size modified to fit my sample pixel.

The code i tried to dewarp it but no luck:

        Bitmap sourceImage = (Bitmap)Bitmap.FromFile("circularfisheye.jpg");
        double factor = 0.5;
        Boolean autoCrop = false;
        Color backgroundColor = Color.White;

        Bitmap StartImage = null;
        BitmapData srcBitmapData = null;
        Byte[] srcPixels = null;
        Byte[] dstPixels = null;
        Bitmap NewImage = null;
        BitmapData dstBitmapData = null;

        try
        {

            // Checks whether bpp ​​( Bits Per Pixel ) is 8 , 24, or 32
            int Depth = System.Drawing.Bitmap.GetPixelFormatSize(sourceImage.PixelFormat);
            if (Depth != 8 && Depth != 24 && Depth != 32)
            {
                throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
            }

            // Retrieves the count of the color components
            int cCount = Depth / 8;

            Size baseSize = new Size(sourceImage.Width, sourceImage.Height);

            // check if a low image resize and need to improve the quality
            // and not generate image aliasing
            Int32 maxSize = Math.Max(sourceImage.Width, sourceImage.Height);
            if (maxSize < 3000)
            {
                float percent = 3000F / (float)maxSize;
                baseSize = new Size((Int32)((float)sourceImage.Width * percent), (Int32)((float)sourceImage.Height * percent));
            }

            StartImage = new Bitmap(baseSize.Width, baseSize.Height, sourceImage.PixelFormat);
            StartImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);

            // Create the drawing object and white background
            Graphics g = Graphics.FromImage(StartImage);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            g.DrawImage(sourceImage, new Rectangle(-1, -1, baseSize.Width + 1, baseSize.Height + 1), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel);
            g.Dispose();
            // Locks the source image and copies it to the byte array and releases the source image
            srcBitmapData = StartImage.LockBits(new Rectangle(0, 0, StartImage.Width, StartImage.Height), ImageLockMode.ReadOnly, StartImage.PixelFormat);
            srcPixels = new byte[StartImage.Width * StartImage.Height * (Depth / 8)];
            Marshal.Copy(srcBitmapData.Scan0, srcPixels, 0, srcPixels.Length);
            StartImage.UnlockBits(srcBitmapData);
            srcBitmapData = null;

            // Create the target image byte array
            dstPixels = new Byte[srcPixels.Length];

            // Fill the entire frame with the selected background color
            Int32 index = ((1 * StartImage.Width) + 1) * cCount; //index = ((Y * Width) + X) * cCount
            do
            {
                if (Depth == 32) //For 32 bpp defines Red , Green, Blue and Alpha
                {
                    dstPixels[index++] = backgroundColor.B;
                    dstPixels[index++] = backgroundColor.G;
                    dstPixels[index++] = backgroundColor.R;
                    dstPixels[index++] = backgroundColor.A; // a
                }
                if (Depth == 24) //For 24 bpp defines Red , Green and Blue
                {
                    dstPixels[index++] = backgroundColor.B;
                    dstPixels[index++] = backgroundColor.G;
                    dstPixels[index++] = backgroundColor.R;
                }
                if (Depth == 8)
                // For 8 bpp defines the value of color ( Red , Green and Blue to be the same thing)
                {
                    dstPixels[index++] = backgroundColor.B;
                }

            } while (index < srcPixels.Length);
            // Calculate the maximum possible extent for the image and multiply by the desired factor
            double amp = 0;
            double ang = Math.PI * 0.5;
            for (Int32 a = 0; a < StartImage.Height; a++)
            {
                int y = (int)((StartImage.Height / 2) - amp * Math.Sin(ang));
                if ((y < 0) || (y > StartImage.Height))
                    break;
                amp = a;
            }
            amp = (amp - 2) * (factor < -1 ? -1 : (factor > 1 ? 1 : factor));
            // Define variables that calculates the cutoff points (if any)
            Int32 x1, y1, x2, y2;
            x1 = StartImage.Width;
            y1 = StartImage.Height;
            x2 = 0;
            y2 = 0;


            // Copy pixel by pixel for the new positions
            index = ((1 * StartImage.Width) + 1) * cCount;
            do
            {

                Int32 y = (Int32)((index / cCount) / StartImage.Width);
                Int32 x = (index / cCount) - (y * StartImage.Width);

                Point pt = NewPoint(new Point(x, y), StartImage.Width, StartImage.Height, amp, factor < 0);

                //Values ​​for crop
                if (factor >= 0)
                {
                    if (x == StartImage.Width / 2)
                    {
                        if (pt.Y < y1)
                            y1 = pt.Y;

                        if (pt.Y > y2)
                            y2 = pt.Y;
                    }

                    if (y == StartImage.Height / 2)
                    {
                        if (pt.X < x1)
                            x1 = pt.X;

                        if (pt.X > x2)
                            x2 = pt.X;
                    }
                }
                else
                {
                    if ((x == 1) && (y == 1))
                    {
                        y1 = pt.Y;
                        x1 = pt.X;
                    }

                    if ((x == StartImage.Width - 1) && (y == StartImage.Height - 1))
                    {
                        y2 = pt.Y;
                        x2 = pt.X;
                    }
                }

                //Bytes Index which will apply the pixel
                Int32 dstIndex = ((pt.Y * StartImage.Width) + pt.X) * cCount;

                if (Depth == 32)
                {
                    dstPixels[dstIndex] = srcPixels[index++];
                    dstPixels[dstIndex + 1] = srcPixels[index++];
                    dstPixels[dstIndex + 2] = srcPixels[index++];
                    dstPixels[dstIndex + 3] = srcPixels[index++]; // a
                }
                if (Depth == 24)
                {
                    dstPixels[dstIndex] = srcPixels[index++];
                    dstPixels[dstIndex + 1] = srcPixels[index++];
                    dstPixels[dstIndex + 2] = srcPixels[index++];
                }
                if (Depth == 8)
                {
                    dstPixels[dstIndex] = srcPixels[index++];
                }

            } while (index < srcPixels.Length);

            //Creates a new image based on the byte array previously created
            NewImage = new Bitmap(StartImage.Width, StartImage.Height, StartImage.PixelFormat);
            NewImage.SetResolution(StartImage.HorizontalResolution, StartImage.VerticalResolution);
            dstBitmapData = NewImage.LockBits(new Rectangle(0, 0, StartImage.Width, StartImage.Height), ImageLockMode.WriteOnly, StartImage.PixelFormat);
            Marshal.Copy(dstPixels, 0, dstBitmapData.Scan0, dstPixels.Length);
            NewImage.UnlockBits(dstBitmapData);


            //Generates the final image to crop or resize the real coo
            Bitmap FinalImage = new Bitmap(sourceImage.Width + 1, sourceImage.Height, StartImage.PixelFormat);
            NewImage.SetResolution(StartImage.HorizontalResolution, StartImage.VerticalResolution);

            Graphics g1 = Graphics.FromImage(FinalImage);
            g1.SmoothingMode = SmoothingMode.AntiAlias;
            g1.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g1.PixelOffsetMode = PixelOffsetMode.HighQuality;

            //Performs the cut if enabled automatic cutting and there is need to cut
            if ((autoCrop) && ((x1 > 0) || (y1 > 0) || (x2 < NewImage.Height) || (y2 < NewImage.Height)))
            {
                Rectangle cropRect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
                g1.DrawImage(NewImage, new Rectangle(-1, -1, FinalImage.Width + 1, FinalImage.Height + 1), cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height, GraphicsUnit.Pixel);
            }
            else
            {
                g1.DrawImage(NewImage, new Rectangle(-1, -1, FinalImage.Width + 1, FinalImage.Height + 1), 0, 0, NewImage.Width, NewImage.Height, GraphicsUnit.Pixel);
            }

            g1.Dispose();
            g1 = null;

            NewImage = null;
            FinalImage.Save("output.jpg");
            FinalImage.Dispose();
        }
        finally
        {
            srcBitmapData = null;
            srcPixels = null;
            dstPixels = null;
            dstBitmapData = null;
        }

回答1:


Such a distortion as a symmetry of revolution.

In polar coordinates, with the pole at the center of the image, it is expressed as

r' = f(r)
Θ' = Θ

where the quote indicates the distorted coordinates. The function f is unknown and should be measured empirically, by calibration (looking at a regular target).

To correct the image, you need to invert the function f and apply the reverse transform to the image. In fact, it is easier to measure g directly by calibration. As a starting approximation, a simple model like

r = r' + a.r'³ 

can do.

Most probably you don't have a picture of a grid taken with the same lens. Your last resort is to implement the undistortion function with adjustable parameters, and optimize these by trial and error.

It should also be possible to derive the calibration curve by looking at the deformation of straight lines, but this is more "technical".


In Cartesian coordinates, you can express the correction transform as

x = g(r').x'/r'
y = g(r').y'/r'

where r' = √x'²+y'².




回答2:


Use the algorithm from here:

http://www.helviojunior.com.br/fotografia/barrel-and-pincushion-distortion/

It worked for me



来源:https://stackoverflow.com/questions/33560580/circular-fisheye-image-dewarp-to-flat-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!