From 9af872ca5da6b94bb7995aba4591cda6137188fd Mon Sep 17 00:00:00 2001 From: kaiza_hikaru Date: Sun, 8 Jun 2025 20:21:47 +0800 Subject: [PATCH] first commit --- stitching-test-yuan.py | 199 +++++++++++++++++++++++++++++++++++++++++ stitching-test.py | 89 ++++++++++++++++++ two-stitching-lr.py | 68 ++++++++++++++ two-stitching-tb.py | 70 +++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 stitching-test-yuan.py create mode 100644 stitching-test.py create mode 100644 two-stitching-lr.py create mode 100644 two-stitching-tb.py diff --git a/stitching-test-yuan.py b/stitching-test-yuan.py new file mode 100644 index 0000000..d73accc --- /dev/null +++ b/stitching-test-yuan.py @@ -0,0 +1,199 @@ +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") diff --git a/stitching-test.py b/stitching-test.py new file mode 100644 index 0000000..e5c9ed0 --- /dev/null +++ b/stitching-test.py @@ -0,0 +1,89 @@ +import cv2 +import numpy as np +from matplotlib import pyplot as plt + +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__": + row, col = 10, 10 + + left_matrix = np.zeros((row - 1, col, 2), dtype=np.int32) + top_matrix = np.zeros((row, col - 1, 2), dtype=np.int32) + + # 计算纵向配准 + for i in range(row): + for j in range(col - 1): + print(f"{i}-{j}.jpg and {i}-{j + 1}.jpg") + dx, dy = tb_match(f"cell/{i}-{j}.jpg", f"cell/{i}-{j + 1}.jpg") + top_matrix[i, j] = dx, dy + + print(top_matrix) + + # 计算横向配准 + for j in range(col): + for i in range(row - 1): + print(f"{i}-{j}.jpg and {i + 1}-{j}.jpg") + dx, dy = lr_match(f"cell/{i}-{j}.jpg", f"cell/{i + 1}-{j}.jpg") + left_matrix[i, j] = dx, dy + + print(left_matrix) diff --git a/two-stitching-lr.py b/two-stitching-lr.py new file mode 100644 index 0000000..b759278 --- /dev/null +++ b/two-stitching-lr.py @@ -0,0 +1,68 @@ +import cv2 +import numpy as np +from matplotlib import pyplot as plt + +if __name__ == "__main__": + left_img = cv2.imread("left.jpg") + right_img = cv2.imread("right.jpg") + + # 配准 + 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}") + + # 拼接 + new_h, new_w = h + abs(dy), w + dx + canvas = np.zeros((new_h, new_w, 3), dtype=np.uint8) + left_mask = np.zeros((new_h, new_w), dtype=np.uint8) + right_mask = np.zeros((new_h, new_w), dtype=np.uint8) + iou = np.zeros((new_h, new_w), dtype=np.uint8) + + if dy >= 0: + canvas[0:h, 0:w] = left_img + canvas[dy:new_h, dx:new_w] = right_img + + left_mask[0:h, 0:w] = 255 + right_mask[dy:new_h, dx:new_w] = 255 + iou = cv2.bitwise_and(left_mask, right_mask) + else: + dy = -dy + canvas[dy:new_h, 0:w] = left_img + canvas[0:h, dx:new_w] = right_img + + left_mask[dy:new_h, 0:w] = 255 + right_mask[0:h, dx:new_w] = 255 + iou = cv2.bitwise_and(left_mask, right_mask) + + plt.subplot(221) + plt.imshow(cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)) + + plt.subplot(222) + plt.imshow(iou, cmap='hot') + + plt.subplot(223) + plt.imshow(left_mask, cmap='hot') + + plt.subplot(224) + plt.imshow(right_mask, cmap='hot') + + plt.show() diff --git a/two-stitching-tb.py b/two-stitching-tb.py new file mode 100644 index 0000000..94ddebb --- /dev/null +++ b/two-stitching-tb.py @@ -0,0 +1,70 @@ +import cv2 +import numpy as np +from matplotlib import pyplot as plt + +if __name__ == "__main__": + top_img = cv2.imread("top.jpg") + bottom_img = cv2.imread("bottom.jpg") + + # 配准 + 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}") + + # 拼接 + new_h, new_w = h + dy, w + abs(dx) + canvas = np.zeros((new_h, new_w, 3), dtype=np.uint8) + top_mask = np.zeros((new_h, new_w), dtype=np.uint8) + bottom_mask = np.zeros((new_h, new_w), dtype=np.uint8) + iou = np.zeros((new_h, new_w), dtype=np.uint8) + + if dx >= 0: + canvas[0:h, 0:w] = top_img + canvas[dy:new_h, dx:new_w] = bottom_img + + top_mask[0:h, 0:w] = 255 + bottom_mask[dy:new_h, dx:new_w] = 255 + iou = cv2.bitwise_and(top_mask, bottom_mask) + else: + dx = -dx + canvas[0:h, dx:new_w] = top_img + canvas[dy:new_h, 0:w] = bottom_img + + top_mask[0:h, dx:new_w] = 255 + bottom_mask[dy:new_h, 0:w] = 255 + iou = cv2.bitwise_and(top_mask, bottom_mask) + + plt.subplot(221) + plt.imshow(cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)) + + plt.subplot(222) + plt.imshow(iou, cmap='hot') + + plt.subplot(223) + plt.imshow(top_mask, cmap='hot') + + plt.subplot(224) + plt.imshow(bottom_mask, cmap='hot') + + plt.show() + + cv2.imwrite("stitching-tb.jpg", canvas)