Distortion Maps Undistortion (Python)
#!/usr/bin/env python3
import argparse
import os
import sys
import cv2
import numpy as np
def readCalibDistortionMap(filePathBase):
matrix_path = filePathBase + "_K.json"
if not os.path.isfile(matrix_path):
raise RuntimeError(f"Missing camera matrix file: {matrix_path}")
try:
fs = cv2.FileStorage(matrix_path, cv2.FILE_STORAGE_READ)
try:
if not fs.isOpened():
raise RuntimeError(f"Cannot open camera matrix: {matrix_path}")
K = fs.getNode("K").mat()
finally:
fs.release()
except (cv2.error, SystemError) as error:
raise RuntimeError(f"Invalid OpenCV matrix file {matrix_path}: {error}") from error
if (K is None or K.shape != (3, 3) or K.dtype != np.float64
or not np.isfinite(K).all()):
raise RuntimeError(f"{matrix_path}: expected a finite 3x3 double OpenCV matrix named K")
def read_map(path):
if not os.path.isfile(path):
raise RuntimeError(f"Missing distortion map: {path}")
try:
image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
except cv2.error as error:
raise RuntimeError(f"Cannot decode {path}: {error}") from error
if image is None:
raise RuntimeError(f"Cannot decode {path}; check the file and OpenCV TIFF/Zstandard support")
if image.ndim != 2 or image.dtype != np.float32:
raise RuntimeError(f"{path}: expected a single-channel float32 TIFF")
return image
mapU = read_map(filePathBase + "_mapU.tiff")
mapV = read_map(filePathBase + "_mapV.tiff")
if mapU.shape != mapV.shape:
raise RuntimeError("Distortion U/V map dimensions differ")
# Keep NaNs: they mark invalid lookup coordinates.
return K, mapU, mapV
def main():
parser = argparse.ArgumentParser(description="Undistort an image from libCalibGeom distortion-map exports.")
parser.add_argument("distortion_path_base", help="Path prefix for the exported distortion maps.")
parser.add_argument("image_path", help="Path to the distorted image.")
args = parser.parse_args()
try:
K, mapU, mapV = readCalibDistortionMap(args.distortion_path_base)
image = cv2.imread(args.image_path)
if image is None:
raise RuntimeError(f"Cannot read image {args.image_path}")
if image.shape[:2] != mapU.shape:
raise RuntimeError("Input image dimensions must match the calibrated map dimensions")
# A black border at invalid/out-of-image lookups is not measured scene data.
image_undistorted = cv2.remap(
image, mapU, mapV, cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT, borderValue=0)
filename, file_extension = os.path.splitext(args.image_path)
imageOutPath = filename + "_undistorted" + file_extension
if not cv2.imwrite(imageOutPath, image_undistorted):
raise RuntimeError(f"Cannot write image {imageOutPath}")
except (RuntimeError, cv2.error) as error:
print(f"Distortion map error: {error}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
Classes / Functions