pythoncomputer-visioncamera-calibrationopencv

How to apply distortion on an image using OpenCV or any other library?


I am performing calibration as showed here, but with cv.CALIB_RATIONAL_MODEL.

I am using around 40 images and my average re-projection error (calculated by cv2.calibrateCamera) is always less than 0.5.

Now, I want to be able to take any other image and apply to it, the distortion that I got from the calibration.

I know I can calculate and re-distort calibration images by using corners I saved during calibration, but I want to apply distortion on a completely different image using only intrinsic and/or extrinsic parameters that I can save and use.

Is it possible?

I tried passing np.negative(distortion) to cv.undistort() hoping it would undistort with negative values so it will actually distort, but it is not working and giving me totally messed up results.

I tried translating answers on other (one, two, three) threads from c++ to Python, and also creating my own function by studying the theory behind distortion, but it is not working.

Are there any other libraries or theories I can leverage to solve this issue?


Solution

  • OpenCV doesn't provide distort function for image, but you can implement one by yourself. All you need are:

    The rectification transform matrix R can be computed by stereoRectify(), but in mono-camera system, it's normally identity (you can just use empty Mat).


    Recall the procedure when you undistort an image, it takes two steps:

    1. Compute the rectification maps: initUndistortRectifyMap(cam_mtx, dis_cef, R, cam_mtx_ud, image_size_ud, CV_32FC1, map_x, map_y)
    2. Remap the image to undistort: remap(image, image_ud, map_x, map_y, INTER_LINEAR);

    The rectification maps (map_x and map_y) describe pixel-coordinate-map from the undistorted image to the distorted image. With them, every pixel on the undistorted image can find it's location on the distorted image, and then retrieve the pixel value with interpolation. That's how remap works.

    So, to produce the undistort rectification maps, you need to loop on the pixels of destination (undistorted) image and distort the points.


    Now, applying distortion on an image is just similar to undistorting.

    You need to produce the "distort maps" by looping on the pixels of destination (distorted) image and undistort the points. Then, use remap to apply the distortion.

    Here is the code:

    Mat map_x = Mat(image_size, CV_32FC1);
    Mat map_y = Mat(image_size, CV_32FC1);
    vector<Point2f> pts_ud, pts_distort;
    for (int y = 0; y < image_size.height; ++y)
        for (int x = 0; x < image_size.width; ++x)
            pts_distort.emplace_back(x, y);
    undistortPoints(pts_distort, pts_ud, cam_mtx, dis_cef, R, cam_mtx_ud);
    for (int y = 0; y < image_size.height; ++y) {
        float* ptr1 = map_x.ptr<float>(y);
        float* ptr2 = map_y.ptr<float>(y);
        for (int x = 0; x < image_size.width; ++x) {
            const auto& pt = pts_ud[y * image_size.width + x];
            ptr1[x] = pt.x;
            ptr2[x] = pt.y;
        }
    }
    Mat image_distort;
    remap(image_ud, image_distort, map_x, map_y, INTER_LINEAR);
    

    I'm not good with python,so I wrote the code in C++. Sorry about that, but I think the code is not hard to read.