import cv2
import numpy as np

img = cv2.imread("/data/passport/模板/m03.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
W, H = img.shape[1], img.shape[0]
print(f"Image: {W}x{H}")

# PASSPORT region
pass_region = gray[1000:1100, 250:600]
_, binary = cv2.threshold(pass_region, 100, 255, cv2.THRESH_BINARY_INV)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(largest)
cx_pass = x + 250 + w // 2
cy_pass = y + 1000 + h // 2
print(f"PASSPORT: cx={cx_pass}, cy={cy_pass}, area={cv2.contourArea(largest):.0f}")

# MOHA region
moha_region = gray[1380:1450, 900:1300]
_, binary_m = cv2.threshold(moha_region, 100, 255, cv2.THRESH_BINARY_INV)
contours_m, _ = cv2.findContours(binary_m, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest_m = max(contours_m, key=cv2.contourArea)
xm, ym, wm, hm = cv2.boundingRect(largest_m)
cx_moha = xm + 900 + wm // 2
cy_moha = ym + 1380 + hm // 2
print(f"MOHA: cx={cx_moha}, cy={cy_moha}, area={cv2.contourArea(largest_m):.0f}")

# REPUBLIC region
rep_region = gray[880:970, 500:1200]
_, binary_r = cv2.threshold(rep_region, 100, 255, cv2.THRESH_BINARY_INV)
contours_r, _ = cv2.findContours(binary_r, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest_r = max(contours_r, key=cv2.contourArea)
xr, yr, wr, hr = cv2.boundingRect(largest_r)
cx_rep = xr + 500 + wr // 2
cy_rep = yr + 880 + hr // 2
print(f"REPUBLIC: cx={cx_rep}, cy={cy_rep}, area={cv2.contourArea(largest_r):.0f}")

# Portrait right edge
portrait = gray[118:318, 150:520]
_, portrait_bin = cv2.threshold(portrait, 220, 255, cv2.THRESH_BINARY_INV)
portrait_contours, _ = cv2.findContours(portrait_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
max_right = 0
for c in portrait_contours:
    xr_p, yr_p, wr_p, hr_p = cv2.boundingRect(c)
    max_right = max(max_right, 150 + xr_p + wr_p)
print(f"Portrait right edge: x={max_right}")

# Reference points from m02 (baseline)
ref_pass = np.array([432.0, 1063.0])
ref_moha = np.array([1090.0, 1397.0])
ref_rep = np.array([845.0, 932.0])

# Dual-point affine calibration
pts_ref = np.array([ref_pass, ref_moha], dtype=np.float32)
pts_curr = np.array([(cx_pass, cy_pass), (cx_moha, cy_moha)], dtype=np.float32)

# Compute scale
dist_ref = np.linalg.norm(pts_ref[0] - pts_ref[1])
dist_curr = np.linalg.norm(pts_curr[0] - pts_curr[1])
scale = dist_curr / dist_ref
print(f"\nScale: {scale:.4f} (ref_dist={dist_ref:.1f}, curr_dist={dist_curr:.1f})")

# Compute rotation angle
angle_ref = np.arctan2(pts_ref[1][1] - pts_ref[0][1], pts_ref[1][0] - pts_ref[0][0])
angle_curr = np.arctan2(pts_curr[1][1] - pts_curr[0][1], pts_curr[1][0] - pts_curr[0][0])
angle_diff = np.degrees(angle_curr - angle_ref)
print(f"Angle diff: {angle_diff:.2f} degrees")

# Affine transform from ref to curr using 2 points (similarity transform)
# We need 3 points for full affine, but 2 points gives us similarity (scale + rotation + translation)
# Use cv2.estimateAffinePartial2D
M, inliers = cv2.estimateAffinePartial2D(pts_ref.reshape(1, 2, 2), pts_curr.reshape(1, 2, 2))
print(f"\nAffine matrix M:\n{M}")

# Transform ref_rep to check
ref_rep_h = np.append(ref_rep, 1)
transformed = M @ ref_rep_h
print(f"REPUBLIC expected: ({transformed[0]:.1f}, {transformed[1]:.1f}) vs actual: ({cx_rep}, {cy_rep})")

# Transform all reference field positions
M02_FIELDS = {
    "surname": (600+140, 1020),
    "given_name": (906+110, 1020),
    "sex": (600+140, 1092),
    "dob": (600+140, 1160),
    "birth_place": (600+140, 1226),
    "issue_date": (600+140, 1300),
    "expiry_date": (600+140, 1361),
    "authority": (908+20, 1380),
}

print("\nTransformed field positions:")
for name, (lx, ly) in M02_FIELDS.items():
    pt = np.array([lx, ly, 1.0], dtype=np.float32)
    out = M @ pt
    print(f"  {name}: ({out[0]:.1f}, {out[1]:.1f})")

