analyze_calibration_z1.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #!/usr/bin/env python3
  2. """
  3. 本地分析 zoom=1 校准扫描结果,用多尺度模板匹配 + 中心 ROI 重新定位。
  4. """
  5. import json
  6. from pathlib import Path
  7. import cv2
  8. import numpy as np
  9. def match_center_roi(ptz_img: np.ndarray, panorama_img: np.ndarray,
  10. roi_ratio=0.5, scales=(0.10, 0.12, 0.15, 0.18, 0.20, 0.25)):
  11. """用 PTZ 中心 ROI 在全景图中做模板匹配"""
  12. ph, pw = ptz_img.shape[:2]
  13. # 提取中心 ROI
  14. rw, rh = int(pw * roi_ratio), int(ph * roi_ratio)
  15. x0, y0 = (pw - rw) // 2, (ph - rh) // 2
  16. roi = ptz_img[y0:y0+rh, x0:x0+rw]
  17. roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
  18. pano_gray = cv2.cvtColor(panorama_img, cv2.COLOR_BGR2GRAY)
  19. pano_h, pano_w = pano_gray.shape
  20. best = None
  21. best_score = -1
  22. for scale in scales:
  23. sw, sh = int(rw * scale), int(rh * scale)
  24. if sw > pano_w or sh > pano_h:
  25. continue
  26. resized = cv2.resize(roi_gray, (sw, sh), interpolation=cv2.INTER_AREA)
  27. result = cv2.matchTemplate(pano_gray, resized, cv2.TM_CCOEFF_NORMED)
  28. _, max_val, _, max_loc = cv2.minMaxLoc(result)
  29. if max_val > best_score:
  30. best_score = max_val
  31. center_x = max_loc[0] + sw / 2
  32. center_y = max_loc[1] + sh / 2
  33. best = (center_x / pano_w, center_y / pano_h, scale, float(max_val))
  34. return best
  35. def main():
  36. base = Path('/Users/wenhongquan/Desktop/阿里云同步/项目/dnn/德胜河 AI/dsh/calibration_scan_180_360_z1')
  37. ptz_dir = base / 'ptz_images'
  38. pano_path = base / 'panorama.jpg'
  39. raw_path = base / 'mapping_raw.json'
  40. panorama = cv2.imread(str(pano_path))
  41. pano_h, pano_w = panorama.shape[:2]
  42. print(f'Panorama: {pano_w}x{pano_h}')
  43. with open(raw_path, 'r', encoding='utf-8') as f:
  44. raw_data = json.load(f)
  45. results = []
  46. for r in raw_data['records']:
  47. filename = r['filename']
  48. ptz = cv2.imread(str(ptz_dir / filename))
  49. if ptz is None:
  50. continue
  51. res = match_center_roi(ptz, panorama)
  52. record = dict(r)
  53. if res:
  54. x_ratio, y_ratio, scale, score = res
  55. record['tm_x_ratio'] = round(x_ratio, 4)
  56. record['tm_y_ratio'] = round(y_ratio, 4)
  57. record['tm_panorama_x'] = int(x_ratio * pano_w)
  58. record['tm_panorama_y'] = int(y_ratio * pano_h)
  59. record['tm_scale'] = round(scale, 3)
  60. record['tm_score'] = round(score, 3)
  61. print(f"{filename}: pan={r['pan']:3d} tilt={r['tilt']:+3d} -> x={x_ratio:.3f} y={y_ratio:.3f} scale={scale:.2f} score={score:.3f}")
  62. results.append(record)
  63. # 保存结果
  64. out_path = base / 'mapping_tm_center.json'
  65. with open(out_path, 'w', encoding='utf-8') as f:
  66. json.dump({
  67. 'records': results,
  68. 'panorama_size': {'width': pano_w, 'height': pano_h},
  69. }, f, indent=2, ensure_ascii=False)
  70. print(f'\nSaved: {out_path}')
  71. # 生成 lookup table
  72. valid = [r for r in results if 'tm_x_ratio' in r and r['tm_score'] > 0.3]
  73. pan_lookup = sorted([[r['tm_x_ratio'], float(r['pan'])] for r in valid], key=lambda x: x[0])
  74. tilt_lookup = sorted([[r['tm_y_ratio'], float(r['tilt'])] for r in valid], key=lambda x: x[0])
  75. lookup = {
  76. 'pan_lookup': pan_lookup,
  77. 'tilt_lookup': tilt_lookup,
  78. 'valid_count': len(valid),
  79. }
  80. with open(base / 'lookup_table_tm_center.json', 'w', encoding='utf-8') as f:
  81. json.dump(lookup, f, indent=2, ensure_ascii=False)
  82. print(f'Lookup table: {len(valid)} valid records')
  83. if __name__ == '__main__':
  84. main()