3

A lot of questions about removing radial (or barrel) distortion, but how would I add it?

Visually, I want to take my input, which is presumed to be image (a), and distort it, to be like image (b):

enter image description here

And ideally I'd like a tunable parameter of some "radius" to control "how much distortion" I get. Based on what I want to do, it looks like I'd just need one parameter to control the 'radius of distortion' or whatever it would be called (correct me if I'm wrong).

How can I achieve this with OpenCV? I figure it must be possible because a lot of people try going the other way for things like this. I'm just not as familiar with the proper math operations and library calls to do it.

Any help much appreciated, cheers.

JDS
  • 16,388
  • 47
  • 161
  • 224
  • I suggest you call ImageMagick -distort barrel from OpenCV subprocess call. See https://imagemagick.org/Usage/distorts/#barrel – fmw42 Jan 16 '20 at 22:37
  • You can distort by using the undistort with different parameters, follow along with this: https://stackoverflow.com/questions/26602981/correct-barrel-distortion-in-opencv-manually-without-chessboard-image and tinker with the parameters. – shortcipher3 Jan 17 '20 at 00:37
  • do you need this for artistic reasons or for engineering (camera calibration)? – Christoph Rackwitz Sep 08 '22 at 18:05

2 Answers2

2

The program below creates barrel distortion

from wand.image import Image
import numpy as np
import cv2

with Image(filename='Path/to/Img/') as img:
    print(img.size)
    img.virtual_pixel = 'transparent'
    img.distort('barrel', (0.1, 0.0, 0.0, 1.0)) # play around these values to create distortion
    img.save(filename='filname.png')
    # convert to opencv/numpy array format
    img_opencv = np.array(img)

# display result with opencv
cv2.imshow("BARREL", img_opencv)
cv2.waitKey(0)
cv2.destroyAllWindows()
Teddy 1993
  • 43
  • 7
1

Here is one way to produce barrel or pincushion distortion in Python/OpenCV by creating the X and Y distortion maps and then using cv.remap() to do the warping.

Input:

enter image description here

import numpy as np
import cv2 as cv
import math

img = cv.imread('lena.jpg')

# grab the dimensions of the image
(h, w, _) = img.shape

# set up the x and y maps as float32
map_x = np.zeros((h, w), np.float32)
map_y = np.zeros((h, w), np.float32)

scale_x = 1
scale_y = 1
center_x = w/2
center_y = h/2
radius = w/2
#amount = -0.75   # negative values produce pincushion
amount = 0.75   # positive values produce barrel

# create map with the barrel pincushion distortion formula
for y in range(h):
    delta_y = scale_y * (y - center_y)
    for x in range(w):
        # determine if pixel is within an ellipse
        delta_x = scale_x * (x - center_x)
        distance = delta_x * delta_x + delta_y * delta_y
        if distance >= (radius * radius):
            map_x[y, x] = x
            map_y[y, x] = y
        else:
            factor = 1.0
            if distance > 0.0:
                factor = math.pow(math.sin(math.pi * math.sqrt(distance) / radius / 2), amount)
            map_x[y, x] = factor * delta_x / scale_x + center_x
            map_y[y, x] = factor * delta_y / scale_y + center_y
            

# do the remap
dst = cv.remap(img, map_x, map_y, cv.INTER_LINEAR)

# save the result
#cv.imwrite('lena_pincushion.jpg',dst)
cv.imwrite('lena_barrel.jpg',dst)

# show the result
cv.imshow('src', img)
cv.imshow('dst', dst)

cv.waitKey(0)
cv.destroyAllWindows()

Barrel (positive amount):

enter image description here

Pincushion (negative amount):

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • This method works, but it drops my camera FPS to 0.5fps – Ezequiel Adrian Jan 14 '23 at 23:14
  • Thanks @fmw42, could you please briefly the method? I'm particularly confused about code coming within the else statement block. – San Askaruly Feb 08 '23 at 09:39
  • The above code is really for "explode/implode" not barrel/pincusion. Outside the ellipse, the distortion is zero. That is the map_x=x and map_y=y. Inside the ellipse, the distortion needs to be magnified. That is the difference from the center is scale by the pow function which is sin(90deg * ratio of sqrt(distance/radius)), when ratio is 1, sin(90)=1 and when ratio is 0 sin(0)=0. So we are doing a pow of a fraction in the range 0 to 1 raised to the amount. – fmw42 Feb 09 '23 at 01:28