draw_panels.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. # -*- coding: utf-8 -*-
  2. """
  3. 在原图上绘制格子区域的绿色线框
  4. 根据panels JSON文件中的bbox信息,在原图上标记格子区域
  5. """
  6. import sys
  7. import json
  8. import cv2
  9. import numpy as np
  10. from pathlib import Path
  11. # Windows编码修复
  12. if sys.platform == 'win32':
  13. import io
  14. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
  15. sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
  16. def draw_panels(image_path, json_path, output_path, line_color=(0, 255, 0), line_thickness=2):
  17. """
  18. 在原图上绘制格子区域的绿色线框
  19. 参数:
  20. image_path: 原图路径
  21. json_path: panels JSON文件路径(包含格子bbox信息)
  22. output_path: 输出图片路径
  23. line_color: 线框颜色(BGR格式,默认绿色 (0, 255, 0))
  24. line_thickness: 线框粗细(默认2像素)
  25. 返回:
  26. 成功返回True,失败返回False
  27. """
  28. try:
  29. # 读取原图(处理中文路径)
  30. img_array = np.fromfile(str(image_path), dtype=np.uint8)
  31. img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
  32. if img is None:
  33. print(f"[ERROR] 无法读取图片: {image_path}")
  34. return False
  35. print(f"[INFO] 读取图片: {Path(image_path).name}")
  36. print(f"[INFO] 图片尺寸: {img.shape[1]}x{img.shape[0]}")
  37. # 读取JSON文件
  38. with open(json_path, 'r', encoding='utf-8') as f:
  39. json_data = json.load(f)
  40. print(f"[INFO] 读取JSON文件: {Path(json_path).name}")
  41. # 提取格子bbox信息
  42. panels = []
  43. # 支持多种JSON格式:
  44. # 1. panels格式:{ "panels": [{"x": int, "y": int, "width": int, "height": int}, ...] }
  45. # 2. 直接数组格式:[{"x": int, "y": int, "width": int, "height": int}, ...]
  46. if 'panels' in json_data:
  47. panels = json_data['panels']
  48. print(f"[INFO] 检测到 {len(panels)} 个格子")
  49. elif isinstance(json_data, list):
  50. panels = json_data
  51. print(f"[INFO] 使用根级别数组,检测到 {len(panels)} 个格子")
  52. else:
  53. print(f"[WARN] JSON文件中未找到 'panels' 字段或数组格式")
  54. print(f"[ERROR] 无法解析JSON文件格式")
  55. return False
  56. # 在原图上绘制绿色矩形框
  57. drawn_count = 0
  58. for i, panel in enumerate(panels):
  59. # 支持多种bbox格式:
  60. # 1. { "x": int, "y": int, "width": int, "height": int }
  61. # 2. { "x1": int, "y1": int, "x2": int, "y2": int }
  62. x1, y1, x2, y2 = None, None, None, None
  63. if isinstance(panel, dict):
  64. if 'x' in panel and 'y' in panel and 'width' in panel and 'height' in panel:
  65. x1 = int(panel['x'])
  66. y1 = int(panel['y'])
  67. x2 = int(panel['x'] + panel['width'])
  68. y2 = int(panel['y'] + panel['height'])
  69. elif 'x1' in panel and 'y1' in panel and 'x2' in panel and 'y2' in panel:
  70. x1, y1, x2, y2 = int(panel['x1']), int(panel['y1']), int(panel['x2']), int(panel['y2'])
  71. if x1 is not None and y1 is not None and x2 is not None and y2 is not None:
  72. # 确保坐标在图片范围内
  73. x1 = max(0, min(x1, img.shape[1] - 1))
  74. y1 = max(0, min(y1, img.shape[0] - 1))
  75. x2 = max(0, min(x2, img.shape[1] - 1))
  76. y2 = max(0, min(y2, img.shape[0] - 1))
  77. # 确保 x1 < x2, y1 < y2
  78. if x1 >= x2 or y1 >= y2:
  79. print(f" [WARN] 格子 {i + 1} 的bbox无效: ({x1}, {y1}) -> ({x2}, {y2}),跳过")
  80. continue
  81. # 绘制绿色矩形框
  82. cv2.rectangle(img, (x1, y1), (x2, y2), line_color, line_thickness)
  83. # 可选:在框上标注序号
  84. label = str(i + 1)
  85. font = cv2.FONT_HERSHEY_SIMPLEX
  86. font_scale = 0.8
  87. font_thickness = 2
  88. (text_width, text_height), baseline = cv2.getTextSize(label, font, font_scale, font_thickness)
  89. # 在左上角绘制文字背景(白色矩形)
  90. cv2.rectangle(img, (x1, y1 - text_height - baseline - 4),
  91. (x1 + text_width + 4, y1), (255, 255, 255), -1)
  92. # 绘制文字(绿色)
  93. cv2.putText(img, label, (x1 + 2, y1 - baseline - 2),
  94. font, font_scale, line_color, font_thickness, cv2.LINE_AA)
  95. drawn_count += 1
  96. else:
  97. print(f" [WARN] 格子 {i + 1} 的bbox格式不支持,跳过")
  98. print(f"[INFO] 成功绘制 {drawn_count} 个格子区域")
  99. # 保存图片(处理中文路径)
  100. success, encoded_img = cv2.imencode('.png', img)
  101. if success:
  102. encoded_img.tofile(str(output_path))
  103. print(f"[OK] 已保存标记后的图片: {output_path}")
  104. return True
  105. else:
  106. print(f"[ERROR] 保存图片失败: {output_path}")
  107. return False
  108. except Exception as e:
  109. print(f"[ERROR] 处理失败: {e}")
  110. import traceback
  111. traceback.print_exc()
  112. return False
  113. if __name__ == '__main__':
  114. if len(sys.argv) < 4:
  115. print("用法: python draw_panels.py <image_path> <json_path> <output_path> [line_thickness]")
  116. print("示例: python draw_panels.py image.png panels.json output.png 2")
  117. sys.exit(1)
  118. image_path = sys.argv[1]
  119. json_path = sys.argv[2]
  120. output_path = sys.argv[3]
  121. line_thickness = int(sys.argv[4]) if len(sys.argv) > 4 else 2
  122. success = draw_panels(image_path, json_path, output_path,
  123. line_color=(0, 255, 0), line_thickness=line_thickness)
  124. sys.exit(0 if success else 1)