| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- import cv2
- from pathlib import Path
- import concurrent.futures
- import logging
- from tqdm import tqdm
- from fry_project_classes.stitch_img_template_match import ImageStitcherTemplateMatch
- from utils.utils import natural_sort_key
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
- def stitch_single_row(row_index, row_image_paths, stitch_params):
- NUM_COLS = len(row_image_paths)
- OUTPUT_DIR = stitch_params['OUTPUT_DIR']
- ESTIMATE_OVERLAP_HORIZONTAL_PIXELS = stitch_params['ESTIMATE_OVERLAP_HORIZONTAL_PIXELS']
- BLEND_TYPE = stitch_params['BLEND_TYPE']
- LIGHT_COMPENSATION = stitch_params['LIGHT_COMPENSATION']
- DEBUG_MODE = stitch_params['DEBUG_MODE']
- current_row_image = cv2.imread(str(row_image_paths[0]))
- if current_row_image is None:
- logging.error(f"错误: 无法读取图片 {row_image_paths[0]}")
- return row_index, None
- for j in range(1, NUM_COLS):
- stitcher_h = ImageStitcherTemplateMatch(
- estimate_overlap_pixels=ESTIMATE_OVERLAP_HORIZONTAL_PIXELS,
- stitch_type="horizontal",
- blend_type=BLEND_TYPE,
- light_uniformity_compensation_enabled=LIGHT_COMPENSATION,
- debug=DEBUG_MODE,
- debug_dir=str(OUTPUT_DIR / f'debug_h_row{row_index + 1}_col{j}vs{j + 1}')
- )
- next_image = cv2.imread(str(row_image_paths[j]))
- if next_image is None:
- logging.error(f"错误: 无法读取图片 {row_image_paths[j]}")
- return row_index, current_row_image
- current_row_image = stitcher_h.stitch_main(current_row_image, next_image)
- return row_index, current_row_image
- def stitch_img(IMAGE_DIR: Path, OUTPUT_DIR: Path, NUM_COLS: int, NUM_ROWS: int,
- ESTIMATE_OVERLAP_HORIZONTAL_PIXELS: int, ESTIMATE_OVERLAP_VERTICAL_PIXELS: int,
- BLEND_TYPE: str, LIGHT_COMPENSATION: bool,
- DEBUG_MODE: bool) -> Path | None:
- """
- 基于模板匹配的图像拼接函数。
- 成功时返回最终图像的路径,失败时返回 None。
- """
- OUTPUT_DIR.mkdir(exist_ok=True, parents=True)
- logging.info("--- [模板匹配] 图像拼接开始 ---")
- logging.info(f"配置: {NUM_ROWS}行 x {NUM_COLS}列, 图片目录: {IMAGE_DIR}")
- image_paths = sorted(list(IMAGE_DIR.glob("*.jpg")), key=natural_sort_key)
- if len(image_paths) != NUM_COLS * NUM_ROWS:
- logging.error(f"错误: 找到 {len(image_paths)} 张图片, 但预期需要 {NUM_COLS * NUM_ROWS} 张。")
- return None
- # --- 阶段一:并行水平拼接每一行 ---
- logging.info("--- 阶段一: 并行水平拼接每一行 ---")
- stitch_params = {
- 'OUTPUT_DIR': OUTPUT_DIR,
- 'ESTIMATE_OVERLAP_HORIZONTAL_PIXELS': ESTIMATE_OVERLAP_HORIZONTAL_PIXELS,
- 'BLEND_TYPE': BLEND_TYPE,
- 'LIGHT_COMPENSATION': LIGHT_COMPENSATION,
- 'DEBUG_MODE': DEBUG_MODE
- }
- stitched_rows = [None] * NUM_ROWS
- with concurrent.futures.ProcessPoolExecutor() as executor:
- futures = [
- executor.submit(stitch_single_row, i, image_paths[i * NUM_COLS: i * NUM_COLS + NUM_COLS], stitch_params) for
- i in range(NUM_ROWS)]
- for future in tqdm(concurrent.futures.as_completed(futures), total=NUM_ROWS, desc="[模板]处理行"):
- try:
- row_index, result_image = future.result()
- if result_image is not None:
- stitched_rows[row_index] = result_image
- else:
- logging.warning(f"第 {row_index + 1} 行拼接失败。")
- except Exception as exc:
- logging.error(f"一个行拼接任务生成了异常: {exc}")
- if any(row is None for row in stitched_rows):
- logging.error("错误: 存在拼接失败的行,无法进行垂直拼接。")
- return None
- # --- 阶段二:垂直拼接所有行 ---
- logging.info("--- 阶段二: 垂直拼接所有行 ---")
- final_image = stitched_rows[0]
- for i in tqdm(range(1, NUM_ROWS), desc="[模板]拼接行"):
- stitcher_v = ImageStitcherTemplateMatch(
- estimate_overlap_pixels=ESTIMATE_OVERLAP_VERTICAL_PIXELS, stitch_type="vertical",
- blend_type=BLEND_TYPE, light_uniformity_compensation_enabled=LIGHT_COMPENSATION,
- debug=DEBUG_MODE, debug_dir=str(OUTPUT_DIR / f'debug_v_row{i}vs{i + 1}')
- )
- next_row_image = stitched_rows[i]
- final_image = stitcher_v.stitch_main(final_image, next_row_image)
- # --- 保存并返回结果 ---
- final_output_path = OUTPUT_DIR / "final_stitched_image.jpg"
- cv2.imwrite(str(final_output_path), final_image)
- logging.info(f"--- [模板匹配] 拼接任务完成!最终图已暂存至: {final_output_path} ---")
- return final_output_path
|