200 lines
7.1 KiB
Python
200 lines
7.1 KiB
Python
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")
|