process_pano.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import cv2
  2. import numpy as np
  3. import py360convert
  4. import requests
  5. import json
  6. import os
  7. import argparse
  8. from pathlib import Path
  9. def imread_url(url):
  10. """从 URL 读取图片并转换为 OpenCV 格式"""
  11. try:
  12. response = requests.get(url, timeout=10)
  13. response.raise_for_status()
  14. image_array = np.asarray(bytearray(response.content), dtype="uint8")
  15. img = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
  16. return img
  17. except Exception as e:
  18. print(f"❌ 无法从 URL 下载图片: {url}, 错误: {e}")
  19. return None
  20. def generate_pinhole_from_array(img, points, output_size=(600, 800)):
  21. """处理图像数组,根据给定的四个点生成 Pinhole 图像"""
  22. h, w = img.shape[:2]
  23. pts = np.array(points, dtype=np.float32)
  24. # 处理全景图跨界逻辑
  25. if pts[1][0] < pts[0][0]:
  26. pts[1][0] += w
  27. if pts[2][0] < pts[3][0]:
  28. pts[2][0] += w
  29. center_x = np.mean(pts[:, 0]) % w
  30. center_y = np.mean(pts[:, 1])
  31. u_deg = (center_x / w) * 360 - 180
  32. v_deg = 90 - (center_y / h) * 180
  33. span_x = np.max(pts[:, 0]) - np.min(pts[:, 0])
  34. span_y = np.max(pts[:, 1]) - np.min(pts[:, 1])
  35. fov_h = (span_x / w) * 360 * 1.2
  36. fov_v = (span_y / h) * 180 * 1.2
  37. fov_h = min(fov_h, 110)
  38. fov_v = min(fov_v, 110)
  39. pinhole_img = py360convert.e2p(
  40. img,
  41. fov_deg=(fov_h, fov_v),
  42. u_deg=u_deg,
  43. v_deg=v_deg,
  44. out_hw=output_size,
  45. )
  46. return pinhole_img
  47. def main():
  48. # 1. 定义参数解析器
  49. parser = argparse.ArgumentParser(description="全景图区域批量切图工具")
  50. parser.add_argument("--json", type=str, required=True, help="输入的 JSON 文件路径")
  51. parser.add_argument("--output", type=str, default="cropped_results", help="输出根目录 (默认: cropped_results)")
  52. parser.add_argument("--width", type=int, default=5671, help="输出图片的宽度 (默认: 800)")
  53. parser.add_argument("--height", type=int, default=3186, help="输出图片的高度 (默认: 600)")
  54. args = parser.parse_args()
  55. # 2. 读取 JSON
  56. if not os.path.exists(args.json):
  57. print(f"❌ 找不到文件: {args.json}")
  58. return
  59. with open(args.json, 'r', encoding='utf-8') as f:
  60. data = json.load(f)
  61. scene_code = data.get("sceneCode", "unknown_scene")
  62. image_cache = {}
  63. output_size = (args.height, args.width) # 注意 opencv 是 (h, w)
  64. # 3. 循环处理
  65. for item in data.get("list", []):
  66. sid = item.get("tag", {}).get("sid", "unknown_sid")
  67. snaps = item.get("snap", [])
  68. for idx, snap in enumerate(snaps):
  69. img_url = snap.get("imageUrl")
  70. pano_id = snap.get("panoId", "0")
  71. corners = snap.get("corners", [])
  72. if not img_url or len(corners) < 4:
  73. continue
  74. if img_url not in image_cache:
  75. print(f"🌐 正在下载全景图: {img_url}")
  76. image_cache[img_url] = imread_url(img_url)
  77. origin_img = image_cache[img_url]
  78. if origin_img is None: continue
  79. pts_list = [(c['x'], c['y']) for c in corners]
  80. try:
  81. print(f"🎨 处理 [SID: {sid}] [Pano: {pano_id}] - 区域 {idx}")
  82. result_img = generate_pinhole_from_array(origin_img, pts_list, output_size=output_size)
  83. # 创建保存目录
  84. save_dir = Path(args.output) / scene_code / sid
  85. save_dir.mkdir(parents=True, exist_ok=True)
  86. save_path = save_dir / f"pano_{pano_id}_snap_{idx}.jpg"
  87. cv2.imwrite(str(save_path), result_img)
  88. except Exception as e:
  89. print(f"⚠️ 处理失败: {e}")
  90. print("\n✨ 处理完成!")
  91. if __name__ == "__main__":
  92. main()