diff --git a/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0017.JPG b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0017.JPG new file mode 100755 index 0000000..b926649 Binary files /dev/null and b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0017.JPG differ diff --git a/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0018.JPG b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0018.JPG new file mode 100755 index 0000000..055c529 Binary files /dev/null and b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0018.JPG differ diff --git a/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0019.JPG b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0019.JPG new file mode 100755 index 0000000..667a81b Binary files /dev/null and b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0019.JPG differ diff --git a/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0020.JPG b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0020.JPG new file mode 100755 index 0000000..116db6e Binary files /dev/null and b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0020.JPG differ diff --git a/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0021.JPG b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0021.JPG new file mode 100755 index 0000000..12f0bb9 Binary files /dev/null and b/data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0021.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6004.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6004.JPG new file mode 100755 index 0000000..e2c2c5a Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6004.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6006.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6006.JPG new file mode 100755 index 0000000..50e3b7b Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6006.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6007.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6007.JPG new file mode 100755 index 0000000..35be00c Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6007.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6008.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6008.JPG new file mode 100755 index 0000000..e68ae96 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6008.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6010.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6010.JPG new file mode 100755 index 0000000..149c403 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6010.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6011.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6011.JPG new file mode 100755 index 0000000..5b724a5 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6011.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6013.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6013.JPG new file mode 100755 index 0000000..5114020 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6013.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6015.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6015.JPG new file mode 100755 index 0000000..3b9910b Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6015.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6016.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6016.JPG new file mode 100755 index 0000000..018b249 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6016.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6019.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6019.JPG new file mode 100755 index 0000000..2c53e71 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6019.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6031.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6031.JPG new file mode 100755 index 0000000..353deb0 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6031.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6034.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6034.JPG new file mode 100755 index 0000000..83d0b53 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6034.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7006.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7006.JPG new file mode 100755 index 0000000..74887f6 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7006.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7010.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7010.JPG new file mode 100755 index 0000000..a17ff02 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7010.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7012.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7012.JPG new file mode 100755 index 0000000..41a4e8d Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7012.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7013.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7013.JPG new file mode 100755 index 0000000..c27ace4 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7013.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7019.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7019.JPG new file mode 100755 index 0000000..758ea4f Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7019.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8001.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8001.JPG new file mode 100755 index 0000000..3ea9bca Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8001.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8002.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8002.JPG new file mode 100755 index 0000000..ff98eb2 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8002.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8005.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8005.JPG new file mode 100755 index 0000000..6cdf5ae Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8005.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8007.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8007.JPG new file mode 100755 index 0000000..104cdf4 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8007.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8010.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8010.JPG new file mode 100755 index 0000000..177401c Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8010.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8015.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8015.JPG new file mode 100755 index 0000000..fd8cf81 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8015.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8017.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8017.JPG new file mode 100755 index 0000000..a118ca2 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8017.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8019.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8019.JPG new file mode 100755 index 0000000..3ef0d5e Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8019.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8020.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8020.JPG new file mode 100755 index 0000000..a34bcbc Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8020.JPG differ diff --git a/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8022.JPG b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8022.JPG new file mode 100755 index 0000000..9c5a326 Binary files /dev/null and b/data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8022.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1002.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1002.JPG new file mode 100755 index 0000000..f06a1a0 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1002.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1017.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1017.JPG new file mode 100755 index 0000000..9d7244d Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1017.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1020.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1020.JPG new file mode 100755 index 0000000..36bf6c1 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1020.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1025.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1025.JPG new file mode 100755 index 0000000..1de7d4f Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1025.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1029.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1029.JPG new file mode 100755 index 0000000..e4732e3 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1029.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1031.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1031.JPG new file mode 100755 index 0000000..a123c30 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1031.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1034.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1034.JPG new file mode 100755 index 0000000..a1c2fdc Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1034.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1035.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1035.JPG new file mode 100755 index 0000000..6566654 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1035.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1037.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1037.JPG new file mode 100755 index 0000000..9a8140d Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1037.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1038.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1038.JPG new file mode 100755 index 0000000..3f4979d Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1038.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1039.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1039.JPG new file mode 100755 index 0000000..a8b348e Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1039.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1041.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1041.JPG new file mode 100755 index 0000000..2495256 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1041.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1044.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1044.JPG new file mode 100755 index 0000000..1c8b637 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1044.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1046.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1046.JPG new file mode 100755 index 0000000..1665ae3 Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1046.JPG differ diff --git a/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1047.JPG b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1047.JPG new file mode 100755 index 0000000..5618c6c Binary files /dev/null and b/data/callibration/XT1-16mm28-1mFokus_f8/DSCF1047.JPG differ diff --git a/data/callibration/calibration_XPro2_16mm_1m_f8.npz b/data/callibration/calibration_XPro2_16mm_1m_f8.npz new file mode 100644 index 0000000..4ddc42d Binary files /dev/null and b/data/callibration/calibration_XPro2_16mm_1m_f8.npz differ diff --git a/data/callibration/calibration_XT1-16mm28-1mFokus_f8.npz b/data/callibration/calibration_XT1-16mm28-1mFokus_f8.npz new file mode 100644 index 0000000..9a544d9 Binary files /dev/null and b/data/callibration/calibration_XT1-16mm28-1mFokus_f8.npz differ diff --git a/data/callibration/callibrate_12mm.py b/data/callibration/callibrate_12mm.py new file mode 100755 index 0000000..e923817 --- /dev/null +++ b/data/callibration/callibrate_12mm.py @@ -0,0 +1,280 @@ +import cv2 +import numpy as np +import glob +import os +import rawpy +from concurrent.futures import ThreadPoolExecutor, as_completed + +# ============================================================ +# CONFIG +# ============================================================ + +CHECKERBOARD = (10, 7) # inner corners +SQUARE_SIZE = 25.0 / 1000.0 # 25 mm -> meters + +IMAGE_PATTERNS = [ + "XPro2-12mm56-1mFokus_f8/*.JPG", + "XPro2-12mm56-1mFokus_f8/*.RAF" +] + + +# Reject blurry images +MIN_SHARPNESS = 380.0 + +# Downscale for faster detection +MAX_WIDTH = 2000 + +# ============================================================ +# PREPARE OBJECT POINTS +# ============================================================ + +objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) +objp[:, :2] = np.mgrid[ + 0:CHECKERBOARD[0], + 0:CHECKERBOARD[1] +].T.reshape(-1, 2) + +objp *= SQUARE_SIZE + + +# ============================================================ +# IMAGE PROCESSING +# ============================================================ + +blurry_images = [] +failed_images = [] + +def process_image(fname): + + try: + print(f"\nProcessing: {fname}") + + ext = os.path.splitext(fname)[1].lower() + + if ext == ".raf" or ext == ".RAF": + + with rawpy.imread(fname) as raw: + + rgb = raw.postprocess( + use_camera_wb=True, + no_auto_bright=True, + output_bps=8 + ) + + img = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) + + else: + img = cv2.imread(fname) + + if img is None: + print(" -> Could not read image") + return None + + #gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = img[:, :, 2] + + original_size = gray.shape[::-1] + + # ---------------------------------------------------- + # DOWNSCALE LARGE IMAGES + # ---------------------------------------------------- + + scale = 1.0 + + if gray.shape[1] > MAX_WIDTH: + scale = MAX_WIDTH / gray.shape[1] + + gray_small = cv2.resize( + gray, + None, + fx=scale, + fy=scale, + interpolation=cv2.INTER_AREA + ) + else: + gray_small = gray + + # ---------------------------------------------------- + # SHARPNESS CHECK + # ---------------------------------------------------- + + sharpness = cv2.Laplacian(gray_small, cv2.CV_64F).var() + + print(f" Sharpness: {sharpness:.1f}") + + if sharpness < MIN_SHARPNESS: + print(" -> Rejected (too blurry)") + failed_images.append(fname) + return None + + # ---------------------------------------------------- + # FIND CHESSBOARD + # ---------------------------------------------------- + + flags = ( + cv2.CALIB_CB_NORMALIZE_IMAGE | + cv2.CALIB_CB_EXHAUSTIVE + ) + + # Newer and more robust detector + ret, corners = cv2.findChessboardCornersSB( + gray_small, + CHECKERBOARD, + flags + ) + + if not ret: + print(" -> No corners found") + return None + + # ---------------------------------------------------- + # SCALE CORNERS BACK + # ---------------------------------------------------- + + if scale != 1.0: + corners /= scale + + # ---------------------------------------------------- + # SUBPIX REFINEMENT + # ---------------------------------------------------- + + corners2 = cv2.cornerSubPix( + gray, + corners.astype(np.float32), + (11, 11), + (-1, -1), + ( + cv2.TERM_CRITERIA_EPS + + cv2.TERM_CRITERIA_MAX_ITER, + 30, + 1e-6 + ) + ) + + print(" -> Success") + + return { + "objpoints": objp, + "imgpoints": corners2, + "img_size": original_size + } + + except Exception as e: + print(f" -> ERROR: {e}") + return None + + +# ============================================================ +# LOAD IMAGES +# ============================================================ + +images = [] + +for pattern in IMAGE_PATTERNS: + images.extend(glob.glob(pattern)) + +print(f"Found images: {len(images)}") + +if len(images) == 0: + raise RuntimeError("No images found.") + +# ============================================================ +# PROCESS IMAGES IN PARALLEL +# ============================================================ + +results = [] + +with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + + futures = [ + executor.submit(process_image, fname) + for fname in images + ] + + for future in as_completed(futures): + + result = future.result() + + if result is not None: + results.append(result) + +# ============================================================ +# COLLECT VALID RESULTS +# ============================================================ + +if len(results) == 0: + raise RuntimeError("No valid checkerboards detected.") + +objpoints = [r["objpoints"] for r in results] +imgpoints = [r["imgpoints"] for r in results] +img_size = results[0]["img_size"] + +print(f"\nValid images: {len(results)} / {len(images)}") + +# ============================================================ +# CALIBRATION +# ============================================================ + +print("\nRunning calibration...") + +ret, K, D, rvecs, tvecs = cv2.calibrateCamera( + objpoints, + imgpoints, + img_size, + None, + None +) + +# ============================================================ +# RESULTS +# ============================================================ + +print("\n=== Calibration Results ===") + +print(f"RMS reprojection error: {ret:.4f}") + +print("\nCamera Matrix:") +print(K) + +print("\nDistortion Coefficients:") +print(D) + +# ============================================================ +# SAVE +# ============================================================ + +output_file = "calibration_XPro2-12mm56-1mFokus_f8.npz" + +np.savez( + output_file, + camera_matrix=K, + dist_coeffs=D +) + +print(f"\nSaved calibration to: {output_file}") + + +# ------------------------------------------------------------ +# DELETE COMMANDS +# ------------------------------------------------------------ + + +if blurry_images: + + print("\nDelete blurry images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in blurry_images + ) + + print(delete_cmd) + +if failed_images: + + print("\nDelete failed corner images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in failed_images + ) + + print(delete_cmd) \ No newline at end of file diff --git a/data/callibration/callibrate_XT1_16mm.py b/data/callibration/callibrate_XT1_16mm.py new file mode 100755 index 0000000..c101755 --- /dev/null +++ b/data/callibration/callibrate_XT1_16mm.py @@ -0,0 +1,278 @@ +import cv2 +import numpy as np +import glob +import os +from concurrent.futures import ThreadPoolExecutor, as_completed + +# ============================================================ +# CONFIG +# ============================================================ + +CHECKERBOARD = (10, 7) # inner corners +SQUARE_SIZE = 25.0 / 1000.0 # 25 mm -> meters + +IMAGE_PATTERNS = [ + "XT1-16mm28-1mFokus_f8/*.JPG" +] + + +# Reject blurry images +MIN_SHARPNESS = 80.0 + +# Downscale for faster detection +MAX_WIDTH = 2000 + +# ============================================================ +# PREPARE OBJECT POINTS +# ============================================================ + +objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) +objp[:, :2] = np.mgrid[ + 0:CHECKERBOARD[0], + 0:CHECKERBOARD[1] +].T.reshape(-1, 2) + +objp *= SQUARE_SIZE + + +# ============================================================ +# IMAGE PROCESSING +# ============================================================ + +blurry_images = [] +failed_images = [] + +def process_image(fname): + + try: + print(f"\nProcessing: {fname}") + + ext = os.path.splitext(fname)[1].lower() + + if ext == ".raf" or ext == ".RAF": + + with rawpy.imread(fname) as raw: + + rgb = raw.postprocess( + use_camera_wb=True, + no_auto_bright=True, + output_bps=8 + ) + + img = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) + + else: + img = cv2.imread(fname) + + if img is None: + print(" -> Could not read image") + return None + + #gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = img[:, :, 2] + + original_size = gray.shape[::-1] + + # ---------------------------------------------------- + # DOWNSCALE LARGE IMAGES + # ---------------------------------------------------- + + scale = 1.0 + + if gray.shape[1] > MAX_WIDTH: + scale = MAX_WIDTH / gray.shape[1] + + gray_small = cv2.resize( + gray, + None, + fx=scale, + fy=scale, + interpolation=cv2.INTER_AREA + ) + else: + gray_small = gray + + # ---------------------------------------------------- + # SHARPNESS CHECK + # ---------------------------------------------------- + + sharpness = cv2.Laplacian(gray_small, cv2.CV_64F).var() + + print(f" Sharpness: {sharpness:.1f}") + + if sharpness < MIN_SHARPNESS: + print(" -> Rejected (too blurry)") + failed_images.append(fname) + return None + + # ---------------------------------------------------- + # FIND CHESSBOARD + # ---------------------------------------------------- + + flags = ( + cv2.CALIB_CB_NORMALIZE_IMAGE | + cv2.CALIB_CB_EXHAUSTIVE + ) + + # Newer and more robust detector + ret, corners = cv2.findChessboardCornersSB( + gray_small, + CHECKERBOARD, + flags + ) + + if not ret: + print(" -> No corners found") + return None + + # ---------------------------------------------------- + # SCALE CORNERS BACK + # ---------------------------------------------------- + + if scale != 1.0: + corners /= scale + + # ---------------------------------------------------- + # SUBPIX REFINEMENT + # ---------------------------------------------------- + + corners2 = cv2.cornerSubPix( + gray, + corners.astype(np.float32), + (11, 11), + (-1, -1), + ( + cv2.TERM_CRITERIA_EPS + + cv2.TERM_CRITERIA_MAX_ITER, + 30, + 1e-6 + ) + ) + + print(" -> Success") + + return { + "objpoints": objp, + "imgpoints": corners2, + "img_size": original_size + } + + except Exception as e: + print(f" -> ERROR: {e}") + return None + + +# ============================================================ +# LOAD IMAGES +# ============================================================ + +images = [] + +for pattern in IMAGE_PATTERNS: + images.extend(glob.glob(pattern)) + +print(f"Found images: {len(images)}") + +if len(images) == 0: + raise RuntimeError("No images found.") + +# ============================================================ +# PROCESS IMAGES IN PARALLEL +# ============================================================ + +results = [] + +with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + + futures = [ + executor.submit(process_image, fname) + for fname in images + ] + + for future in as_completed(futures): + + result = future.result() + + if result is not None: + results.append(result) + +# ============================================================ +# COLLECT VALID RESULTS +# ============================================================ + +if len(results) == 0: + raise RuntimeError("No valid checkerboards detected.") + +objpoints = [r["objpoints"] for r in results] +imgpoints = [r["imgpoints"] for r in results] +img_size = results[0]["img_size"] + +print(f"\nValid images: {len(results)} / {len(images)}") + +# ============================================================ +# CALIBRATION +# ============================================================ + +print("\nRunning calibration...") + +ret, K, D, rvecs, tvecs = cv2.calibrateCamera( + objpoints, + imgpoints, + img_size, + None, + None +) + +# ============================================================ +# RESULTS +# ============================================================ + +print("\n=== Calibration Results ===") + +print(f"RMS reprojection error: {ret:.4f}") + +print("\nCamera Matrix:") +print(K) + +print("\nDistortion Coefficients:") +print(D) + +# ============================================================ +# SAVE +# ============================================================ + +output_file = "calibration_XT1-16mm28-1mFokus_f8.npz" + +np.savez( + output_file, + camera_matrix=K, + dist_coeffs=D +) + +print(f"\nSaved calibration to: {output_file}") + + +# ------------------------------------------------------------ +# DELETE COMMANDS +# ------------------------------------------------------------ + + +if blurry_images: + + print("\nDelete blurry images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in blurry_images + ) + + print(delete_cmd) + +if failed_images: + + print("\nDelete failed corner images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in failed_images + ) + + print(delete_cmd) \ No newline at end of file diff --git a/data/callibration/callibrate_v2.py b/data/callibration/callibrate_v2.py new file mode 100755 index 0000000..7ac8d00 --- /dev/null +++ b/data/callibration/callibrate_v2.py @@ -0,0 +1,280 @@ +import cv2 +import numpy as np +import glob +import os +import rawpy +from concurrent.futures import ThreadPoolExecutor, as_completed + +# ============================================================ +# CONFIG +# ============================================================ + +CHECKERBOARD = (10, 7) # inner corners +SQUARE_SIZE = 25.0 / 1000.0 # 25 mm -> meters + +IMAGE_PATTERNS = [ + "XPro2-16mm28-1mFokus_f8/*.JPG", + "XPro2-16mm28-1mFokus_f8/*.RAF" +] + + +# Reject blurry images +MIN_SHARPNESS = 80.0 + +# Downscale for faster detection +MAX_WIDTH = 2000 + +# ============================================================ +# PREPARE OBJECT POINTS +# ============================================================ + +objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) +objp[:, :2] = np.mgrid[ + 0:CHECKERBOARD[0], + 0:CHECKERBOARD[1] +].T.reshape(-1, 2) + +objp *= SQUARE_SIZE + + +# ============================================================ +# IMAGE PROCESSING +# ============================================================ + +blurry_images = [] +failed_images = [] + +def process_image(fname): + + try: + print(f"\nProcessing: {fname}") + + ext = os.path.splitext(fname)[1].lower() + + if ext == ".raf" or ext == ".RAF": + + with rawpy.imread(fname) as raw: + + rgb = raw.postprocess( + use_camera_wb=True, + no_auto_bright=True, + output_bps=8 + ) + + img = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) + + else: + img = cv2.imread(fname) + + if img is None: + print(" -> Could not read image") + return None + + #gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = img[:, :, 2] + + original_size = gray.shape[::-1] + + # ---------------------------------------------------- + # DOWNSCALE LARGE IMAGES + # ---------------------------------------------------- + + scale = 1.0 + + if gray.shape[1] > MAX_WIDTH: + scale = MAX_WIDTH / gray.shape[1] + + gray_small = cv2.resize( + gray, + None, + fx=scale, + fy=scale, + interpolation=cv2.INTER_AREA + ) + else: + gray_small = gray + + # ---------------------------------------------------- + # SHARPNESS CHECK + # ---------------------------------------------------- + + sharpness = cv2.Laplacian(gray_small, cv2.CV_64F).var() + + print(f" Sharpness: {sharpness:.1f}") + + if sharpness < MIN_SHARPNESS: + print(" -> Rejected (too blurry)") + failed_images.append(fname) + return None + + # ---------------------------------------------------- + # FIND CHESSBOARD + # ---------------------------------------------------- + + flags = ( + cv2.CALIB_CB_NORMALIZE_IMAGE | + cv2.CALIB_CB_EXHAUSTIVE + ) + + # Newer and more robust detector + ret, corners = cv2.findChessboardCornersSB( + gray_small, + CHECKERBOARD, + flags + ) + + if not ret: + print(" -> No corners found") + return None + + # ---------------------------------------------------- + # SCALE CORNERS BACK + # ---------------------------------------------------- + + if scale != 1.0: + corners /= scale + + # ---------------------------------------------------- + # SUBPIX REFINEMENT + # ---------------------------------------------------- + + corners2 = cv2.cornerSubPix( + gray, + corners.astype(np.float32), + (11, 11), + (-1, -1), + ( + cv2.TERM_CRITERIA_EPS + + cv2.TERM_CRITERIA_MAX_ITER, + 30, + 1e-6 + ) + ) + + print(" -> Success") + + return { + "objpoints": objp, + "imgpoints": corners2, + "img_size": original_size + } + + except Exception as e: + print(f" -> ERROR: {e}") + return None + + +# ============================================================ +# LOAD IMAGES +# ============================================================ + +images = [] + +for pattern in IMAGE_PATTERNS: + images.extend(glob.glob(pattern)) + +print(f"Found images: {len(images)}") + +if len(images) == 0: + raise RuntimeError("No images found.") + +# ============================================================ +# PROCESS IMAGES IN PARALLEL +# ============================================================ + +results = [] + +with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + + futures = [ + executor.submit(process_image, fname) + for fname in images + ] + + for future in as_completed(futures): + + result = future.result() + + if result is not None: + results.append(result) + +# ============================================================ +# COLLECT VALID RESULTS +# ============================================================ + +if len(results) == 0: + raise RuntimeError("No valid checkerboards detected.") + +objpoints = [r["objpoints"] for r in results] +imgpoints = [r["imgpoints"] for r in results] +img_size = results[0]["img_size"] + +print(f"\nValid images: {len(results)} / {len(images)}") + +# ============================================================ +# CALIBRATION +# ============================================================ + +print("\nRunning calibration...") + +ret, K, D, rvecs, tvecs = cv2.calibrateCamera( + objpoints, + imgpoints, + img_size, + None, + None +) + +# ============================================================ +# RESULTS +# ============================================================ + +print("\n=== Calibration Results ===") + +print(f"RMS reprojection error: {ret:.4f}") + +print("\nCamera Matrix:") +print(K) + +print("\nDistortion Coefficients:") +print(D) + +# ============================================================ +# SAVE +# ============================================================ + +output_file = "calibration_XPro2_16mm_1m_f8.npz" + +np.savez( + output_file, + camera_matrix=K, + dist_coeffs=D +) + +print(f"\nSaved calibration to: {output_file}") + + +# ------------------------------------------------------------ +# DELETE COMMANDS +# ------------------------------------------------------------ + + +if blurry_images: + + print("\nDelete blurry images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in blurry_images + ) + + print(delete_cmd) + +if failed_images: + + print("\nDelete failed corner images:") + + delete_cmd = "rm " + " ".join( + f'"{f}"' for f in failed_images + ) + + print(delete_cmd) \ No newline at end of file diff --git a/data/callibration/callibriate.py b/data/callibration/callibriate.py index 50a77e3..6e069ad 100755 --- a/data/callibration/callibriate.py +++ b/data/callibration/callibriate.py @@ -1,54 +1,99 @@ + import cv2 import numpy as np import glob # -# Create callibration .npz from checkerboard images. -# +# Create calibration .npz from checkerboard images # +# Parameters +CHECKERBOARD = (10, 7) # inner corners +square_size = 25.0 / 1000.0 # 25 mm -> meters -# Parameter -CHECKERBOARD = (9, 6) # innere Ecken -square_size = 25.0 / 1000.0 # 25 mm → Meter - -# Objektpunkte vorbereiten -objp = np.zeros((CHECKERBOARD[0]*CHECKERBOARD[1], 3), np.float32) +# Prepare object points +objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) objp *= square_size -objpoints = [] -imgpoints = [] +objpoints = [] # 3D points +imgpoints = [] # 2D points -images = glob.glob("calib_images/*.jpg") +# Load images +images = glob.glob("XPro2-16mm28-1mFokus/*.JPG") +print("Found images:", len(images)) + +img_size = None for fname in images: img = cv2.imread(fname) + + print(f"Processing {fname}...") + if img is None: + print(f"Warning: could not read {fname}") + continue + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # Save image size once + if img_size is None: + img_size = gray.shape[::-1] + + print(f" Gray") ret, corners = cv2.findChessboardCorners( - gray, CHECKERBOARD, + gray, + CHECKERBOARD, flags=cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE + cv2.CALIB_CB_FAST_CHECK ) + print(" Corners found") if ret: corners2 = cv2.cornerSubPix( - gray, corners, (11,11), (-1,-1), + gray, + corners, + (11, 11), + (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6) ) + objpoints.append(objp) imgpoints.append(corners2) -# Kalibrieren + print(f"āœ… Corners found in {fname}") + else: + print(f"āŒ No corners found in {fname}") + +print(f"\nTotal valid images: {len(objpoints)} / {len(images)}" ) +# Sanity checks +if img_size is None: + raise RuntimeError("No images were successfully loaded.") + +if len(objpoints) == 0: + raise RuntimeError("No chessboard corners detected in any image. Calibration failed.") + +print("\n=== Sanity Checks Passed ===") +# Calibration ret, K, D, rvecs, tvecs = cv2.calibrateCamera( - objpoints, imgpoints, gray.shape[::-1], None, None + objpoints, + imgpoints, + img_size, + None, + None ) -print("RMS reprojection error:", ret) -# Speichern -np.savez("calibration_cam0.npz", - camera_matrix=K, - dist_coeffs=D) \ No newline at end of file +print("\n=== Calibration Results ===") +print("RMS reprojection error:", ret) +print("Camera matrix:\n", K) +print("Distortion coefficients:\n", D) + +# Save calibration +np.savez( + "calibration_XPro2_16mm.npz", + camera_matrix=K, + dist_coeffs=D +) + +print("\nāœ… Calibration saved to calibration_XPro2_16mm.npz")