stitch_img_template_match.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. import math
  2. from pathlib import Path
  3. import cv2
  4. import numpy as np
  5. import os
  6. import time
  7. def fry_cv2_imread(filename, flags=cv2.IMREAD_COLOR):
  8. try:
  9. with open(filename, 'rb') as f:
  10. chunk = f.read()
  11. chunk_arr = np.frombuffer(chunk, dtype=np.uint8)
  12. img = cv2.imdecode(chunk_arr, flags)
  13. if img is None:
  14. error_info = f"Warning: Unable to decode image: {filename}"
  15. print("警告", error_info)
  16. return img
  17. except IOError as e:
  18. error_info = f"IOError: Unable to read file: {filename}"
  19. print("错误", error_info)
  20. print("错误", f"Error details: {str(e)}")
  21. return None
  22. def fry_cv2_imwrite(filename, img, params=None):
  23. try:
  24. ext = os.path.splitext(filename)[1].lower()
  25. result, encoded_img = cv2.imencode(ext, img, params)
  26. if result:
  27. with open(filename, 'wb') as f:
  28. encoded_img.tofile(f)
  29. return True
  30. else:
  31. print("警告", f"Warning: Unable to encode image: {filename}")
  32. return False
  33. except Exception as e:
  34. print("错误", f"Error: Unable to write file: {filename}")
  35. print("错误", f"Error details: {str(e)}")
  36. return False
  37. # 覆盖 OpenCV 的原始函数
  38. cv2.imread = fry_cv2_imread
  39. cv2.imwrite = fry_cv2_imwrite
  40. from fry_project_classes.blend_type_mixin import BlendTypeMixin
  41. class ImageStitcherTemplateMatch(BlendTypeMixin):
  42. def __init__(self, estimate_overlap_pixels=800, center_ratio=0.8,
  43. stitch_type="vertical",
  44. blend_type='half_importance',
  45. blend_ratio: float = 0.3,
  46. debug=False, debug_dir='debug_output',
  47. light_uniformity_compensation_enabled=False,
  48. light_uniformity_compensation_width=15,
  49. debug_draw_line_enabled=False
  50. ):
  51. """
  52. 初始化拼图器
  53. 参数:
  54. overlap_pixels: 重叠区域像素数,默认800像素(预估值)
  55. center_ratio: 中心区域比例,默认0.8
  56. debug: 是否开启调试模式,默认False
  57. debug_dir: 调试图片保存目录,默认'debug_output'
  58. use_weight_blend: 是否使用加权融合,默认True
  59. """
  60. print("警告", f"拼图方法:区域")
  61. self.estimate_overlap_pixels = estimate_overlap_pixels
  62. self.estimate_non_overlap_pixels = None
  63. self.center_ratio = center_ratio
  64. self.blend_ratio = blend_ratio
  65. self.blend_type = blend_type
  66. self.stitch_type = stitch_type
  67. self.debug_draw_line_enabled = debug_draw_line_enabled
  68. self.debug = debug
  69. self.init_debug = debug_dir
  70. self.light_uniformity_compensation_enabled = light_uniformity_compensation_enabled
  71. self.light_uniformity_compensation_width = light_uniformity_compensation_width
  72. if self.debug:
  73. self.debug_dir = f"{self.init_debug}_{self.stitch_type}_{self.blend_type}"
  74. os.makedirs(self.debug_dir, exist_ok=True)
  75. os.makedirs(self.debug_dir, exist_ok=True)
  76. # 模板匹配参数
  77. self.best_y = -1
  78. self.best_x = -1
  79. self.best_score = -1
  80. self.template_score = -1
  81. def save_debug_image(self, img, name, normalize=False):
  82. """
  83. 保存调试图片
  84. 参数:
  85. img: 要保存的图片
  86. name: 图片名称
  87. normalize: 是否需要归一化处理(对于模板匹配结果图等)
  88. """
  89. try:
  90. if self.debug:
  91. save_path = os.path.join(self.debug_dir, f"{name}.jpg")
  92. if normalize:
  93. # 归一化到0-255范围
  94. img_normalized = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)
  95. cv2.imwrite(save_path, img_normalized)
  96. else:
  97. cv2.imwrite(save_path, img)
  98. now_info = f"Debug: Saved {save_path}"
  99. # print("信息", now_info)
  100. return True, f"save_debug_image 成功: {save_path}"
  101. else:
  102. return False, "debug mode is not enabled"
  103. except Exception as e:
  104. error_info = f"save_debug_image出现bug: {str(e)}"
  105. print("警告", error_info)
  106. return False, error_info
  107. def visualize_template_match(self, template, search_region, best_y, best_x):
  108. """
  109. 可视化模板匹配结果
  110. 参数:
  111. template: 模板图片
  112. search_region: 搜索区域
  113. best_y, best_x: 最佳匹配位置
  114. """
  115. try:
  116. if self.debug:
  117. vis_img = search_region.copy()
  118. h, w = template.shape[:2]
  119. cv2.rectangle(vis_img, (best_x, best_y),
  120. (best_x + w, best_y + h), (0, 255, 0), 2)
  121. is_ok, msg = self.save_debug_image(vis_img, 'tp_140_template_match_visualization')
  122. return is_ok, msg
  123. else:
  124. return False, "debug mode is not enabled"
  125. except Exception as e:
  126. error_info = f"visualize_template_match 出现bug: {str(e)}"
  127. print("警告", error_info)
  128. return False, msg
  129. def split_image(self, img, is_left_top=True):
  130. """
  131. 分割图片为重叠区域和非重叠区域
  132. """
  133. height, width = img.shape[:2]
  134. overlap_width = min(self.estimate_overlap_pixels, width // 2)
  135. non_overlap_width = width - overlap_width
  136. if is_left_top:
  137. non_overlap_region = img[:, :non_overlap_width]
  138. overlap_region = img[:, non_overlap_width:]
  139. if self.debug:
  140. self.save_debug_image(img, 'split_220_left_top_original')
  141. self.save_debug_image(non_overlap_region, 'split_240_left_top_non_overlap')
  142. self.save_debug_image(overlap_region, 'split_260_left_top_overlap')
  143. else:
  144. overlap_region = img[:, :overlap_width]
  145. non_overlap_region = img[:, overlap_width:]
  146. if self.debug:
  147. self.save_debug_image(img, 'split_320_right_bottom_original')
  148. self.save_debug_image(overlap_region, 'split_340_right_bottom_overlap')
  149. self.save_debug_image(non_overlap_region, 'split_360_right_bottom_non_overlap')
  150. return overlap_region, non_overlap_region
  151. def get_center_region(self, img):
  152. """
  153. 获取图片的中心区域
  154. """
  155. height, width = img.shape[:2]
  156. margin_y = int(height * (1 - self.center_ratio) / 2)
  157. margin_x = int(width * (1 - self.center_ratio) / 2)
  158. center_region = img[margin_y:height - margin_y, margin_x:width - margin_x]
  159. if self.debug:
  160. self.save_debug_image(center_region, 'center_120_template_center_region')
  161. return center_region, margin_x, margin_y
  162. def template_matching(self, template, search_region):
  163. """
  164. 模板匹配
  165. """
  166. result = cv2.matchTemplate(search_region, template, cv2.TM_CCOEFF_NORMED)
  167. min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
  168. best_y, best_x = max_loc[1], max_loc[0]
  169. now_info = f"匹配结果: 最大值{max_val}, 位置{best_x, best_y}"
  170. print("警告", now_info)
  171. self.best_y = best_y
  172. self.best_x = best_x
  173. self.best_score = max_val
  174. self.template_score = max_val
  175. # 如果存在父对象且父对象有 update_match_score 方法,发送匹配分数
  176. # if hasattr(self, 'parent') and hasattr(self.parent, 'update_match_score'):
  177. # self.parent.update_match_score(max_val)
  178. if self.debug:
  179. is_ok1, opt_msg1 = self.save_debug_image(result, 'tp_120_template_matching_result', normalize=True)
  180. is_ok2, opt_msg2 = self.visualize_template_match(template, search_region, best_y, best_x)
  181. is_ok3, opt_msg3 = self.save_debug_image(template, 'tp_220_template')
  182. is_ok4, opt_msg4 = self.save_debug_image(search_region, 'tp_240_search_region')
  183. with open(os.path.join(self.debug_dir, 'tp_320_matching_info.txt'), 'w') as f:
  184. f.write(f"Best match position: ({best_x}, {best_y})\n")
  185. f.write(f"Match score: {max_val}\n")
  186. return best_x, best_y, max_val
  187. def pad_image(self, img: np.ndarray, target_width: int = None, target_height: int = None) -> np.ndarray:
  188. """
  189. 将图片填充到目标尺寸
  190. 参数:
  191. img: 输入图片
  192. target_width: 目标宽度,如果为None则保持原宽度
  193. target_height: 目标高度,如果为None则保持原高度
  194. 返回:
  195. 填充后的图片
  196. """
  197. if self.debug:
  198. self.save_debug_image(img, 'pad_120_before_padding')
  199. current_height, current_width = img.shape[:2]
  200. target_width = target_width if target_width is not None else current_width
  201. target_height = target_height if target_height is not None else current_height
  202. if current_width == target_width and current_height == target_height:
  203. return img
  204. # 创建黑色背景
  205. padded_img = np.zeros((target_height, target_width, 3), dtype=np.uint8)
  206. # 将原图放在中心位置
  207. y_offset = (target_height - current_height) // 2
  208. x_offset = (target_width - current_width) // 2
  209. padded_img[y_offset:y_offset + current_height,
  210. x_offset:x_offset + current_width] = img
  211. if self.debug:
  212. self.save_debug_image(padded_img, 'pad_220_after_padding')
  213. with open(os.path.join(self.debug_dir, 'pad_320_padding_info.txt'), 'w') as f:
  214. f.write(f"Original size: {current_width}x{current_height}\n")
  215. f.write(f"Target size: {target_width}x{target_height}\n")
  216. f.write(f"Padding offsets: x={x_offset}, y={y_offset}\n")
  217. return padded_img
  218. def stitch_horizontal(self, left_img, right_img):
  219. """
  220. 水平拼接两张图片
  221. 水平拼接的图片的高必须一样
  222. """
  223. # self.debug_dir = f"{self.init_debug}_horizontal_{self.blend_type}"
  224. # os.makedirs(self.debug_dir, exist_ok=True)
  225. # 确保两张图片高度相同
  226. max_height = max(left_img.shape[0], right_img.shape[0])
  227. left_img = self.pad_image(left_img, target_height=max_height)
  228. right_img = self.pad_image(right_img, target_height=max_height)
  229. # 1. 分割图片,使用预估的重叠像素
  230. left_overlap, left_non_overlap = self.split_image(left_img, is_left_top=True)
  231. right_overlap, right_non_overlap = self.split_image(right_img, is_left_top=False)
  232. # 2. 获取左图重叠区域的中心部分作为模板
  233. template, template_offset_x, template_offset_y = self.get_center_region(left_overlap)
  234. # 计算模板在左图中的位置
  235. self.estimate_non_overlap_pixels = left_img.shape[1] - self.estimate_overlap_pixels
  236. template_in_left_x = self.estimate_non_overlap_pixels + template_offset_x
  237. template_in_left_y = template_offset_y
  238. # 3. 在右图重叠区域中进行模板匹配
  239. best_x, best_y, max_val = self.template_matching(template, right_overlap)
  240. template_score = max_val
  241. # 计算模板在右图中的位置
  242. template_in_right_x = best_x
  243. template_in_right_y = best_y
  244. # 真实的重叠区域的x和y 这个还不好算
  245. # 04、计算最后拼接的图片的尺寸
  246. left_width_contribution = template_in_left_x # 左图取到模板的左上角,不包含模板
  247. right_width_contribution = right_img.shape[1] - template_in_right_x # 右图取到模板的左上角,包含模板
  248. stitch_img_width = left_width_contribution + right_width_contribution
  249. stitch_img_height = max(left_img.shape[0], right_img.shape[0])
  250. # 计算右图相对于左图的y方向的偏移量
  251. y_offset_right2left = template_in_left_y - template_in_right_y
  252. # 计算真正的重叠区域:右图的左边+模板宽度+左图的右边
  253. real_overlap_width = template_in_right_x + template.shape[1] + (
  254. left_img.shape[1] - template_in_left_x - template.shape[1])
  255. if self.debug:
  256. with open(os.path.join(self.debug_dir, 'h_320_alignment_info.txt'), 'w') as f:
  257. f.write(f"template_in_left_x: {template_in_left_x}\n")
  258. f.write(f"template_in_left_y: {template_in_left_y}\n")
  259. f.write(f"template_in_right_x: {template_in_right_x}\n")
  260. f.write(f"template_in_right_y: {template_in_right_y}\n")
  261. f.write(f"left_width_contribution: {left_width_contribution}\n")
  262. f.write(f"right_width_contribution: {right_width_contribution}\n")
  263. f.write(f"stitch_img_width: {stitch_img_width}\n")
  264. f.write(f"stitch_img_height: {stitch_img_height}\n")
  265. f.write(f"real_overlap_width: {real_overlap_width}\n")
  266. if self.blend_type == 'half_importance':
  267. blend_stitch_img = self.blend_half_importance(left_img, right_img, stitch_img_width, stitch_img_height,
  268. y_offset_right2left, real_overlap_width,
  269. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  270. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  271. if self.debug_draw_line_enabled:
  272. blend_stitch_img_visualize = self.blend_half_importance(left_img, right_img, stitch_img_width,
  273. stitch_img_height, y_offset_right2left,
  274. real_overlap_width, visualize=True)
  275. if self.debug:
  276. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  277. elif self.blend_type == 'right_first':
  278. # 右边优先的拼接方式
  279. blend_stitch_img = self.blend_right_first(left_img, right_img, stitch_img_width, stitch_img_height,
  280. y_offset_right2left)
  281. elif self.blend_type == 'left_first':
  282. blend_stitch_img = self.blend_left_first(left_img, right_img,
  283. stitch_img_width,
  284. stitch_img_height,
  285. y_offset_right2left,
  286. real_overlap_width)
  287. elif self.blend_type == 'half_importance_add_weight':
  288. blend_stitch_img = self.blend_half_importance_add_weight(left_img, right_img, stitch_img_width,
  289. stitch_img_height, y_offset_right2left,
  290. real_overlap_width,
  291. blend_ratio=self.blend_ratio)
  292. elif self.blend_type == 'half_importance_global_brightness':
  293. blend_stitch_img = self.blend_half_importance_global_brightness(left_img, right_img, stitch_img_width,
  294. stitch_img_height, y_offset_right2left,
  295. real_overlap_width,
  296. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  297. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  298. if self.debug_draw_line_enabled:
  299. blend_stitch_img_visualize = self.blend_half_importance_global_brightness(left_img, right_img,
  300. stitch_img_width,
  301. stitch_img_height,
  302. y_offset_right2left,
  303. real_overlap_width,
  304. visualize=True)
  305. if self.debug:
  306. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  307. elif self.blend_type == 'half_importance_partial_brightness':
  308. blend_stitch_img = self.blend_half_importance_partial_brightness(left_img, right_img, stitch_img_width,
  309. stitch_img_height, y_offset_right2left,
  310. real_overlap_width,
  311. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  312. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  313. if self.debug_draw_line_enabled:
  314. blend_stitch_img_visualize = self.blend_half_importance_partial_brightness(left_img, right_img,
  315. stitch_img_width,
  316. stitch_img_height,
  317. y_offset_right2left,
  318. real_overlap_width,
  319. visualize=True)
  320. if self.debug:
  321. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  322. elif self.blend_type == 'blend_half_importance_partial_HV':
  323. blend_stitch_img = self.blend_half_importance_partial_HV(left_img, right_img, stitch_img_width,
  324. stitch_img_height, y_offset_right2left,
  325. real_overlap_width,
  326. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  327. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  328. if self.debug_draw_line_enabled:
  329. blend_stitch_img_visualize = self.blend_half_importance_partial_HV(left_img, right_img,
  330. stitch_img_width, stitch_img_height,
  331. y_offset_right2left,
  332. real_overlap_width, visualize=True)
  333. if self.debug:
  334. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  335. elif self.blend_type == 'blend_half_importance_partial_SV':
  336. blend_stitch_img = self.blend_half_importance_partial_SV(left_img, right_img, stitch_img_width,
  337. stitch_img_height, y_offset_right2left,
  338. real_overlap_width,
  339. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  340. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  341. if self.debug_draw_line_enabled:
  342. blend_stitch_img_visualize = self.blend_half_importance_partial_SV(left_img, right_img,
  343. stitch_img_width, stitch_img_height,
  344. y_offset_right2left,
  345. real_overlap_width, visualize=True)
  346. if self.debug:
  347. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  348. elif self.blend_type == 'blend_half_importance_partial_HSV':
  349. blend_stitch_img = self.blend_half_importance_partial_HSV(left_img, right_img, stitch_img_width,
  350. stitch_img_height, y_offset_right2left,
  351. real_overlap_width,
  352. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  353. light_uniformity_compensation_width=self.light_uniformity_compensation_width)
  354. if self.debug_draw_line_enabled:
  355. blend_stitch_img_visualize = self.blend_half_importance_partial_HSV(left_img, right_img,
  356. stitch_img_width, stitch_img_height,
  357. y_offset_right2left,
  358. real_overlap_width, visualize=True)
  359. if self.debug:
  360. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  361. elif self.blend_type == 'blend_half_importance_partial_brightness_add_weight':
  362. blend_stitch_img = self.blend_half_importance_partial_brightness_add_weight(
  363. left_img=left_img,
  364. right_img=right_img,
  365. stitch_img_width=stitch_img_width,
  366. stitch_img_height=stitch_img_height,
  367. y_offset_right2left=y_offset_right2left,
  368. real_overlap_width=real_overlap_width,
  369. light_uniformity_compensation=self.light_uniformity_compensation_enabled,
  370. light_uniformity_compensation_width=self.light_uniformity_compensation_width,
  371. add_weight_rate=self.blend_ratio
  372. )
  373. if self.debug_draw_line_enabled:
  374. blend_stitch_img_visualize = self.blend_half_importance_partial_brightness_add_weight(left_img=left_img,
  375. right_img=right_img,
  376. stitch_img_width=stitch_img_width,
  377. stitch_img_height=stitch_img_height,
  378. y_offset_right2left=y_offset_right2left,
  379. real_overlap_width=real_overlap_width,
  380. add_weight_rate=self.blend_ratio,
  381. visualize=True)
  382. if self.debug:
  383. self.save_debug_image(blend_stitch_img_visualize, 'h_500_final_result_horizontal_visualize')
  384. else:
  385. # 左边优先的拼接方式
  386. blend_stitch_img = None
  387. if self.debug:
  388. self.save_debug_image(blend_stitch_img, 'h_500_final_result_horizontal')
  389. # return result
  390. return blend_stitch_img
  391. def stitch_vertical(self, top_img, bottom_img):
  392. """
  393. 垂直拼接两张图片
  394. 垂直拼接的图片的宽必须一样
  395. """
  396. # self.debug_dir = f"{self.init_debug}_vertical_{self.blend_type}"
  397. # os.makedirs(self.debug_dir, exist_ok=True)
  398. # 确保两张图片宽度相同
  399. max_width = max(top_img.shape[1], bottom_img.shape[1])
  400. top_img = self.pad_image(top_img, target_width=max_width)
  401. bottom_img = self.pad_image(bottom_img, target_width=max_width)
  402. if self.debug:
  403. self.save_debug_image(top_img, 'v_120_top_original')
  404. self.save_debug_image(bottom_img, 'v_140_bottom_original')
  405. # 将图片逆时针旋转90度
  406. top_rotated = cv2.rotate(top_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
  407. bottom_rotated = cv2.rotate(bottom_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
  408. if self.debug:
  409. self.save_debug_image(top_rotated, 'v_220_top_rotated')
  410. self.save_debug_image(bottom_rotated, 'v_240_bottom_rotated')
  411. # 进行水平拼接
  412. result_rotated = self.stitch_horizontal(top_rotated, bottom_rotated)
  413. # 将结果顺时针旋转90度
  414. result = cv2.rotate(result_rotated, cv2.ROTATE_90_CLOCKWISE)
  415. if self.debug:
  416. self.save_debug_image(result, 'v_500_final_result_vertical')
  417. return result
  418. def stitch_main(self, left_img, right_img):
  419. if self.stitch_type == 'horizontal':
  420. # 确保两张图片高度相同
  421. max_height = max(left_img.shape[0], right_img.shape[0])
  422. left_img = self.pad_image(left_img, target_height=max_height)
  423. right_img = self.pad_image(right_img, target_height=max_height)
  424. return self.stitch_horizontal(left_img, right_img)
  425. elif self.stitch_type == 'vertical':
  426. # 确保两张图片宽度相同
  427. max_width = max(left_img.shape[1], right_img.shape[1])
  428. left_img = self.pad_image(left_img, target_width=max_width)
  429. right_img = self.pad_image(right_img, target_width=max_width)
  430. return self.stitch_vertical(left_img, right_img)
  431. else:
  432. raise ValueError('Invalid stitch type, must be one of horizontal or vertical')
  433. def main():
  434. timestamp = time.strftime("%Y%m%d_%H%M%S")
  435. root_path = r"D:\_241231_fry_gitlab\LA_ai_main_CV_OpenCV\740_project\_250115_Stitch_Image_TemplateMatch\test_images"
  436. root_path_obj = Path(root_path).absolute()
  437. stitch_type = "vertical"
  438. debug_dir_str = str(root_path_obj / f'debug_{timestamp}_{stitch_type}')
  439. debug_dir_obj = Path(debug_dir_str).absolute()
  440. estimate_overlap_ratio = 0.45
  441. estimate_overlap_pixels = int(round(1024 * estimate_overlap_ratio))
  442. # 使用加权融合
  443. # stitcher_weight = ImageStitcher(
  444. # estimate_overlap_pixels=estimate_overlap_pixels,
  445. # center_ratio=0.8,
  446. # debug=True,
  447. # debug_dir=debug_dir+"_weight",
  448. # use_weight_blend=True
  449. # )
  450. # 读取图片
  451. left_img_name = r"20250123_162407_0001.jpg"
  452. right_img_name = r"20250123_162409_0002.jpg"
  453. bottom_img_name = r"20250123_162422_0007.jpg"
  454. left_img_path = str(root_path_obj / left_img_name)
  455. right_img_path = str(root_path_obj / right_img_name)
  456. bottom_img_path = str(root_path_obj / bottom_img_name)
  457. left_img = cv2.imread(left_img_path)
  458. right_img = cv2.imread(right_img_path)
  459. top_img = left_img
  460. bottom_img = cv2.imread(bottom_img_path)
  461. start_time = time.time()
  462. # 使用简单拼接
  463. stitcher_simple = ImageStitcherTemplateMatch(
  464. estimate_overlap_pixels=estimate_overlap_pixels,
  465. center_ratio=0.8,
  466. blend_type='half_importance_add_weight',
  467. # blend_type='left_first',
  468. stitch_type=stitch_type,
  469. blend_ratio=0.5,
  470. debug=True,
  471. debug_dir=debug_dir_str,
  472. )
  473. # 竖直拼接 - 简单拼接
  474. result_img = stitcher_simple.stitch_main(left_img, bottom_img)
  475. save_final_image_path = str(debug_dir_obj / 'result_img.jpg')
  476. cv2.imwrite(save_final_image_path, result_img)
  477. end_time = time.time()
  478. print(f"拼接图片耗时:{end_time - start_time:.2f}秒")
  479. if __name__ == '__main__':
  480. main()