Callibration Fuji 16mm
BIN
data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0017.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0018.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0019.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0020.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-12mm56-1mFokus_f8/DSCF0021.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6004.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6006.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6007.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6008.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6010.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6011.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6013.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6015.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6016.JPG
Executable file
|
After Width: | Height: | Size: 214 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6019.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6031.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF6034.JPG
Executable file
|
After Width: | Height: | Size: 378 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7006.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7010.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7012.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7013.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF7019.JPG
Executable file
|
After Width: | Height: | Size: 11 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8001.JPG
Executable file
|
After Width: | Height: | Size: 263 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8002.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8005.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8007.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8010.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8015.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8017.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8019.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8020.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XPro2-16mm28-1mFokus_f8/DSCF8022.JPG
Executable file
|
After Width: | Height: | Size: 12 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1002.JPG
Executable file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1017.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1020.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1025.JPG
Executable file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1029.JPG
Executable file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1031.JPG
Executable file
|
After Width: | Height: | Size: 4.8 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1034.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1035.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1037.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1038.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1039.JPG
Executable file
|
After Width: | Height: | Size: 4.8 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1041.JPG
Executable file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1044.JPG
Executable file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1046.JPG
Executable file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
data/callibration/XT1-16mm28-1mFokus_f8/DSCF1047.JPG
Executable file
|
After Width: | Height: | Size: 4.8 MiB |
BIN
data/callibration/calibration_XPro2_16mm_1m_f8.npz
Normal file
BIN
data/callibration/calibration_XT1-16mm28-1mFokus_f8.npz
Normal file
280
data/callibration/callibrate_12mm.py
Executable file
@@ -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)
|
||||
278
data/callibration/callibrate_XT1_16mm.py
Executable file
@@ -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)
|
||||
280
data/callibration/callibrate_v2.py
Executable file
@@ -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)
|
||||
@@ -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
|
||||
# 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",
|
||||
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)
|
||||
dist_coeffs=D
|
||||
)
|
||||
|
||||
print("\n✅ Calibration saved to calibration_XPro2_16mm.npz")
|
||||
|
||||