import cv2 import numpy as np from matplotlib import pyplot as plt from tqdm import tqdm import time def lr_match(left_path, right_path): left_img = cv2.imread(left_path) right_img = cv2.imread(right_path) # 配准 left_gray = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY) right_gray = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY) h, w = left_gray.shape print(f"图像尺寸: 宽度={w}像素, 高度={h}像素") template = left_gray[100:h - 100, w - 100:w] print(f"模板区域: 上边界=100, 下边界={h - 100}, 左边界={w - 100}, 右边界={w}") search_area = right_gray[:, :200] print(f"搜索区域: 宽度={200}, 高度={h}") res = cv2.matchTemplate(search_area, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) print(f"匹配质量: {max_val:.4f} (1.0=完美匹配)") match_x, match_y = max_loc print(f"匹配位置: x={match_x}, y={match_y} (在搜索区域内)") dx = w - 100 - match_x dy = 100 - match_y print(f"计算平移量: dx={dx}, dy={dy}") return dx, dy def tb_match(top_path, bottom_path): top_img = cv2.imread(top_path) bottom_img = cv2.imread(bottom_path) # 配准 top_gray = cv2.cvtColor(top_img, cv2.COLOR_BGR2GRAY) bottom_gray = cv2.cvtColor(bottom_img, cv2.COLOR_BGR2GRAY) h, w = top_gray.shape print(f"图像尺寸: 宽度={w}像素, 高度={h}像素") template = top_gray[h - 100:h, 100:w - 100] print(f"模板区域: 上边界={h - 100}, 下边界={h}, 左边界={100}, 右边界={w - 100}") search_area = bottom_gray[:200, :] print(f"搜索区域: 宽度={w}, 高度={200}") res = cv2.matchTemplate(search_area, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) print(f"匹配质量: {max_val:.4f} (1.0=完美匹配)") match_x, match_y = max_loc print(f"匹配位置: x={match_x}, y={match_y} (在搜索区域内)") dx = 100 - match_x dy = h - 100 - match_y print(f"计算平移量: dx={dx}, dy={dy}") return dx, dy if __name__ == "__main__": start_time = time.time() # ====== 1. 计算相对配准关系 ====== max_x, max_y = 9, 9 # 假设图像范围:x 从 0 到 max_x, y 从 0 到 max_y right_disp = [[None] * (max_y+1) for _ in range(max_x)] # 大小为 [max_x][max_y+1] down_disp = [[None] * max_y for _ in range(max_x+1)] # 大小为 [max_x+1][max_y] # 计算左右相邻位移(横向关系) for x in range(max_x): # x 从 0 到 max_x-1 for y in range(max_y + 1): # y 从 0 到 max_y img_left = f"cell/{x}-{y}.jpg" img_right = f"cell/{x+1}-{y}.jpg" print(f"Matching: {img_left} and {img_right}") dx, dy = lr_match(img_left, img_right) # 右图相对于左图的位移 right_disp[x][y] = (dx, dy) # 存储结果 # 计算上下相邻位移(纵向关系) for x in range(max_x + 1): # x 从 0 到 max_x for y in range(max_y): # y 从 0 到 max_y-1 img_top = f"cell/{x}-{y}.jpg" img_bottom = f"cell/{x}-{y+1}.jpg" dx, dy = tb_match(img_top, img_bottom) # 下图相对于上图的位移 down_disp[x][y] = (dx, dy) # 存储结果 # ====== 2. 计算绝对配准关系,即相对于 0-0 的配准关系 ====== global_disp = [[(0, 0)] * (max_y + 1) for _ in range(max_x + 1)] # 初始化 # 初始化第一行 (y=0) for x in range(1, max_x + 1): dx_prev, dy_prev = global_disp[x-1][0] # 左邻居 (x-1,0) 的位移 dx_right, dy_right = right_disp[x-1][0] # (x-1,0) 到 (x,0) 的位移 global_disp[x][0] = (dx_prev + dx_right, dy_prev + dy_right) # 初始化第一列 (x=0) for y in range(1, max_y + 1): dx_prev, dy_prev = global_disp[0][y-1] # 上邻居 (0,y-1) 的位移 dx_down, dy_down = down_disp[0][y-1] # (0,y-1) 到 (0,y) 的位移 global_disp[0][y] = (dx_prev + dx_down, dy_prev + dy_down) # 计算内部点 (x≥1, y≥1) for x in range(1, max_x + 1): for y in range(1, max_y + 1): # 通过左侧邻居 (x-1,y) 计算 dx_left, dy_left = global_disp[x-1][y] dx_r, dy_r = right_disp[x-1][y] # (x-1,y) 到 (x,y) 的位移 from_left = (dx_left + dx_r, dy_left + dy_r) # 通过上方邻居 (x,y-1) 计算 dx_top, dy_top = global_disp[x][y-1] dx_d, dy_d = down_disp[x][y-1] # (x,y-1) 到 (x,y) 的位移 from_top = (dx_top + dx_d, dy_top + dy_d) # 取平均值(整数四舍五入) global_disp[x][y] = ( round((from_left[0] + from_top[0]) / 2), round((from_left[1] + from_top[1]) / 2) ) print(global_disp) # ====== 3. 正数化全局坐标 ====== min_dx = 0 min_dy = 0 # 遍历所有位移值,找到最小偏移量 for x in range(len(global_disp)): for y in range(len(global_disp[0])): dx, dy = global_disp[x][y] min_dx = min(min_dx, dx) min_dy = min(min_dy, dy) # 计算偏移量(将所有位移变为非负) offset_x = -min_dx # 当 min_dx 为负时,offset_x 为正 offset_y = -min_dy # 当 min_dy 为负时,offset_y 为正 # 创建新矩阵存储非负位移 global_disp_abs = [] for x in range(len(global_disp)): row = [] for y in range(len(global_disp[0])): dx, dy = global_disp[x][y] # 应用偏移使所有值非负 abs_dx = dx + offset_x abs_dy = dy + offset_y row.append((abs_dx, abs_dy)) global_disp_abs.append(row) print(global_disp_abs) # ====== 4. 根据全局坐标进行拼接 ====== # 计算画布尺寸,创建空白画布 sample_img = cv2.imread("cell/0-0.jpg") h, w = sample_img.shape[:2] max_dx = max(dx for row in global_disp_abs for dx, dy in row) max_dy = max(dy for row in global_disp_abs for dx, dy in row) canvas_width = max_dx + w canvas_height = max_dy + h canvas = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8) total_images = len(global_disp_abs) * len(global_disp_abs[0]) with tqdm(total=total_images, desc="拼接图像") as pbar: for x in range(len(global_disp_abs)): for y in range(len(global_disp_abs[0])): img = cv2.imread(f"cell/{x}-{y}.jpg") # 获取该图像的位移 dx, dy = global_disp_abs[x][y] # 将图像放置到画布上 try: # 将图像复制到画布对应位置 canvas[dy:dy+h, dx:dx+w] = img except ValueError: # 处理可能的尺寸不匹配问题 print(f"错误: 无法放置图像 cell/{x}-{y}.jpg 在位置({dx},{dy})") continue pbar.update(1) # 保存拼接结果 cv2.imwrite("result.jpg", canvas) print(f"拼接完成!结果保存至: result.jpg") end_time = time.time() print(f"总耗时:{end_time - start_time}s")