How do I perform an image warp or transformation from one coordinate system to another for a barrel distorted image?


I have used this question to help me come up with an undistorted coordinated system of an image. Now, I'm not sure how to implement the new coordinate system into the image so I can be able to produce an undistorted image.

I'm having trouble finding answers that don't involve Matlab, OpenCV, or C++ as I am using R.

The answer I'm using from the cited question has given me the following transformed xy coordinate :

1 -19.50255239, -19.50255239
2 -18.26735544, -18.26735544
3 -17.03391152, -17.03391152
4 -15.80221494, -15.80221494
5 -14.57225998, -14.57225998
6 -13.34404095, -13.34404095
...

and so on for 512 pixels in a 512 x 512 image.

How to apply this back to the original 512 x 512 image is what I'm struggling with. I've seen some mention on pages like the Open CV page here and specific pre-defined shifts, or latitudinal/longitudinal shifts, use SpatialObjectsDataFrames, but not from one user-defined list of xy coordinates to another.

-An example of getting the source image coordinates:

im_coords <- RSAGA::grid.to.xyz(as.matrix(as.raster(im)))

(note, I don't actually want to raster the images, its just what I found at the time)

-Code I'm using to get the transformed coordinates:

undistort <- function(X, Y, a, b, c, d = 1, imWidth = 512, imHeight = 512) {

    #radial barrel distortion
    normX <- X - (imWidth / 2)
    normY <- Y - (imHeight / 2)

    #rmax
    radius_u <- sqrt(imWidth^2 + imHeight^2)

    #normalize r so that its between 0 and 1
    r <- sqrt(normX^2 + normY^2) /  radius_u

    #Barrel distorition equation: where "r" is the destination radius and "Rsrc" is the source pixel to get the pixel color from
    Rsrc <- r * (a*r^3 + b*r^2 + c*r + d)

    theta <- ifelse(Rsrc == 0, 1, 1 / atan(Rsrc) * Rsrc)

    newX <- (imWidth / 2) + theta * normX
    newY <- (imHeight / 2) + theta * normY

    return(data.frame(X = newX, Y = newY))
}

Here is a sample sample 512x512 .png barrel distorted image: https://imgur.com/a/W9Qz70W

enter image description here

I'm wondering if kriging could be useful? Or gdalwarp or proj4string? Not sure how to implement these.

UPDATE: Using Rohit's suggestions I was able to distort a rainbow grid from:

enter image description here

to this:

enter image description here

When I try it with the barrel image I get this weird superimposed image:

enter image description here

Ok, I think it is down to what coefficients you use as seen here:

enter image description here


Answers:


You don't actually need to calculate the transformed xy coordinates. You just need the function that takes the x and y coordinates and returns the undistorted ones. Given your undistort function, write a wrapper around it that uses only x and y as inputs:

im2 <- imwarp(im1, function(x,y){ 
  undistort(x,y,a=1,b=2,c=4) # Give apropriate values for arguements, I'm not an expert.
})

If you want to map specifically from one list to another, then you can do that as well:

df <- expand.grid(x=1:512,y=1:512) # Original grid coordinates
df1 <- undistort(X=df$x,Y=df$y) # Undistorted grid coordinates
im2 <- imwarp(im1, function(x,y){
  df1[df$x==x & df$y==y,] # Map appropriately. Should still work.
})

Try out different options for interpolation to see what works best.