merge_images.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- coding: utf-8 -*-
  2. """
  3. 合并两张图片(通常是格子遮罩图和文字遮罩图)
  4. """
  5. import sys
  6. import cv2
  7. import numpy as np
  8. from pathlib import Path
  9. # Windows编码修复
  10. if sys.platform == 'win32':
  11. import io
  12. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
  13. sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
  14. def merge_images(background_path, overlay_path, output_path):
  15. """
  16. 将透明PNG图片叠加到背景图片上
  17. 参数:
  18. background_path: 背景图片路径(textGreenPanelImgPath,底层)
  19. overlay_path: 叠加图片路径(panelImgPath,顶层,支持透明通道)
  20. output_path: 输出图片路径
  21. """
  22. background_path = Path(background_path)
  23. overlay_path = Path(overlay_path)
  24. output_path = Path(output_path)
  25. # 确保输出目录存在
  26. output_path.parent.mkdir(parents=True, exist_ok=True)
  27. try:
  28. # 读取背景图片(支持中文路径)
  29. print(f"[INFO] 读取背景图片: {background_path.name}")
  30. bg_data = np.fromfile(str(background_path), dtype=np.uint8)
  31. background = cv2.imdecode(bg_data, cv2.IMREAD_COLOR)
  32. if background is None:
  33. raise ValueError(f"无法读取背景图片: {background_path}")
  34. print(f"[INFO] 背景图片尺寸: {background.shape[1]}x{background.shape[0]}")
  35. # 读取叠加图片(支持透明通道)
  36. print(f"[INFO] 读取叠加图片: {overlay_path.name}")
  37. overlay_data = np.fromfile(str(overlay_path), dtype=np.uint8)
  38. overlay = cv2.imdecode(overlay_data, cv2.IMREAD_UNCHANGED)
  39. if overlay is None:
  40. raise ValueError(f"无法读取叠加图片: {overlay_path}")
  41. print(f"[INFO] 叠加图片尺寸: {overlay.shape[1]}x{overlay.shape[0]}")
  42. print(f"[INFO] 叠加图片通道数: {overlay.shape[2] if len(overlay.shape) > 2 else 1}")
  43. # 确保两张图片尺寸一致
  44. if background.shape[:2] != overlay.shape[:2]:
  45. print(f"[WARN] 图片尺寸不匹配,调整叠加图片尺寸")
  46. overlay = cv2.resize(overlay, (background.shape[1], background.shape[0]))
  47. print(f"[INFO] 调整后叠加图片尺寸: {overlay.shape[1]}x{overlay.shape[0]}")
  48. # 处理叠加图片的透明通道
  49. if len(overlay.shape) == 3 and overlay.shape[2] == 4:
  50. # RGBA格式,有透明通道
  51. print(f"[INFO] 检测到RGBA透明图片,进行Alpha混合")
  52. # 分离RGB和Alpha通道
  53. overlay_rgb = overlay[:, :, :3]
  54. alpha = overlay[:, :, 3] / 255.0 # 归一化到0-1
  55. # 扩展alpha通道到3个维度
  56. alpha_3ch = np.stack([alpha, alpha, alpha], axis=2)
  57. # Alpha混合:result = background * (1 - alpha) + overlay * alpha
  58. # 透明区域(alpha=0)显示背景,不透明区域(alpha=1)显示叠加图片
  59. result = background * (1 - alpha_3ch) + overlay_rgb * alpha_3ch
  60. result = result.astype(np.uint8)
  61. print(f"[INFO] Alpha混合完成,透明区域保留背景")
  62. elif len(overlay.shape) == 3 and overlay.shape[2] == 3:
  63. # RGB格式,没有透明通道,将白色区域视为透明
  64. print(f"[INFO] 检测到RGB图片,将白色区域视为透明")
  65. # 创建mask:白色区域(255,255,255)为透明,其他为不透明
  66. white_threshold = 250 # 接近白色的阈值
  67. white_mask = np.all(overlay >= white_threshold, axis=2)
  68. alpha = (~white_mask).astype(float) # 非白色区域alpha=1,白色区域alpha=0
  69. # 扩展alpha到3个维度
  70. alpha_3ch = np.stack([alpha, alpha, alpha], axis=2)
  71. # Alpha混合
  72. result = background * (1 - alpha_3ch) + overlay * alpha_3ch
  73. result = result.astype(np.uint8)
  74. print(f"[INFO] 白色区域视为透明处理完成")
  75. else:
  76. # 灰度图或其他格式,简单叠加
  77. print(f"[INFO] 其他格式图片,使用简单叠加")
  78. if len(overlay.shape) == 2:
  79. overlay = cv2.cvtColor(overlay, cv2.COLOR_GRAY2BGR)
  80. # 对于灰度图,将接近白色的区域视为透明
  81. if len(overlay.shape) == 3:
  82. gray_overlay = cv2.cvtColor(overlay, cv2.COLOR_BGR2GRAY)
  83. white_mask = gray_overlay >= 250
  84. alpha = (~white_mask).astype(float)
  85. alpha_3ch = np.stack([alpha, alpha, alpha], axis=2)
  86. result = background * (1 - alpha_3ch) + overlay * alpha_3ch
  87. result = result.astype(np.uint8)
  88. else:
  89. result = cv2.addWeighted(background, 0.7, overlay, 0.3, 0)
  90. print(f"[INFO] 合并完成,结果图片尺寸: {result.shape[1]}x{result.shape[0]}")
  91. # 保存结果图片(支持中文路径)
  92. print(f"[INFO] 正在保存合并图片到: {output_path.name}")
  93. success, encoded_img = cv2.imencode('.png', result)
  94. if success:
  95. encoded_img.tofile(str(output_path))
  96. print(f"[SUCCESS] 已保存合并图片: {output_path.name}")
  97. else:
  98. raise RuntimeError(f"图片编码失败: {output_path}")
  99. except Exception as e:
  100. print(f"[ERROR] 详细错误信息: {str(e)}")
  101. import traceback
  102. traceback.print_exc()
  103. raise RuntimeError(f"合并图片失败: {str(e)}")
  104. if __name__ == '__main__':
  105. if len(sys.argv) < 4:
  106. print("用法: python merge_images.py <背景图片路径> <叠加图片路径> <输出图片路径>")
  107. sys.exit(1)
  108. background_path = sys.argv[1]
  109. overlay_path = sys.argv[2]
  110. output_path = sys.argv[3]
  111. try:
  112. merge_images(background_path, overlay_path, output_path)
  113. except Exception as e:
  114. print(f"[ERROR] 合并失败: {e}")
  115. import traceback
  116. traceback.print_exc()
  117. sys.exit(1)