draw_red_panel_mask.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 创建透明PNG图片并绘制红线格子区域
  5. """
  6. import cv2
  7. import json
  8. import sys
  9. from pathlib import Path
  10. import numpy as np
  11. def draw_red_lines_on_transparent_image(origin_image_path, json_path, output_path):
  12. """
  13. 根据原图片尺寸创建透明PNG,并根据JSON文件绘制红线格子区域
  14. """
  15. # 1. 从原图片获取真实尺寸
  16. try:
  17. # 使用cv2读取原图片获取尺寸(支持中文路径)
  18. img_data = np.fromfile(str(origin_image_path), dtype=np.uint8)
  19. origin_img = cv2.imdecode(img_data, cv2.IMREAD_COLOR)
  20. if origin_img is None:
  21. raise ValueError(f"无法读取原图片: {origin_image_path}")
  22. height, width = origin_img.shape[:2]
  23. print(f"[INFO] 从原图片获取尺寸: {width}x{height}")
  24. except Exception as e:
  25. print(f"[ERROR] 无法读取原图片: {e}")
  26. # 如果读取失败,使用默认尺寸
  27. width, height = 1334, 1940
  28. print(f"[INFO] 使用默认图片尺寸: {width}x{height}")
  29. # 2. 读取格子位置JSON
  30. with open(json_path, 'r', encoding='utf-8') as f:
  31. panel_data = json.load(f)
  32. panels = panel_data.get('panels', [])
  33. print(f"[INFO] 图片尺寸: {width}x{height}")
  34. print(f"[INFO] 需要绘制 {len(panels)} 个格子边框")
  35. # 创建透明背景图片 (RGBA格式)
  36. mask = np.zeros((height, width, 4), dtype=np.uint8)
  37. # 绘制每个格子的红色边框
  38. drawn_count = 0
  39. for i, panel in enumerate(panels):
  40. try:
  41. # 兼容两种格式:bbox数组 或 x,y,width,height字段
  42. if 'bbox' in panel:
  43. # 格式1: bbox数组 [x1, y1, x2, y2]
  44. bbox = panel['bbox']
  45. if isinstance(bbox, list) and len(bbox) >= 4:
  46. x1, y1, x2, y2 = int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])
  47. else:
  48. print(f"[WARN] 格子 {i+1} bbox格式无效: {bbox}")
  49. continue
  50. elif 'x' in panel and 'y' in panel and 'width' in panel and 'height' in panel:
  51. # 格式2: x,y,width,height字段
  52. x = int(panel['x'])
  53. y = int(panel['y'])
  54. w = int(panel['width'])
  55. h = int(panel['height'])
  56. x1, y1, x2, y2 = x, y, x + w, y + h
  57. else:
  58. print(f"[WARN] 格子 {i+1} 缺少坐标信息")
  59. continue
  60. # 确保坐标在图片范围内
  61. x1 = max(0, min(x1, width-1))
  62. y1 = max(0, min(y1, height-1))
  63. x2 = max(0, min(x2, width-1))
  64. y2 = max(0, min(y2, height-1))
  65. if x2 > x1 and y2 > y1:
  66. # 绘制红色矩形框,线宽3,颜色为红色(0,0,255,255)
  67. cv2.rectangle(mask, (x1, y1), (x2, y2), (0, 0, 255, 255), 3)
  68. drawn_count += 1
  69. print(f"[INFO] 绘制格子 {drawn_count}: ({x1},{y1}) -> ({x2},{y2})")
  70. else:
  71. print(f"[WARN] 跳过无效格子 {i+1}: ({x1},{y1}) -> ({x2},{y2})")
  72. except Exception as e:
  73. print(f"[ERROR] 绘制格子 {i+1} 失败: {str(e)}")
  74. print(f"[INFO] 成功绘制 {drawn_count} 个格子边框")
  75. # 保存为PNG格式(支持透明度)
  76. try:
  77. # 先尝试简单保存
  78. output_path_str = str(output_path)
  79. print(f"[INFO] 尝试保存到: {output_path_str}")
  80. # 使用cv2.imencode处理中文路径
  81. success, encoded_img = cv2.imencode('.png', mask)
  82. if success:
  83. # 写入文件
  84. output_path.parent.mkdir(parents=True, exist_ok=True) # 确保目录存在
  85. with open(output_path_str, 'wb') as f:
  86. f.write(encoded_img.tobytes())
  87. print(f"[SUCCESS] 已保存红线透明底遮罩图: {output_path}")
  88. else:
  89. raise RuntimeError(f"图片编码失败")
  90. except Exception as e:
  91. print(f"[ERROR] 详细错误信息: {str(e)}")
  92. import traceback
  93. traceback.print_exc()
  94. raise RuntimeError(f"保存图片失败: {output_path}, 错误: {str(e)}")
  95. def main():
  96. if len(sys.argv) != 4:
  97. print("用法: python draw_red_transparent_image.py <原图片路径> <JSON文件路径> <输出图片路径>")
  98. sys.exit(1)
  99. origin_image_path = Path(sys.argv[1])
  100. json_path = Path(sys.argv[2])
  101. output_path = Path(sys.argv[3])
  102. try:
  103. draw_red_lines_on_transparent_image(origin_image_path, json_path, output_path)
  104. except Exception as e:
  105. print(f"[ERROR] 绘制失败: {e}")
  106. sys.exit(1)
  107. if __name__ == "__main__":
  108. main()