Python+OpenCV图像对齐合并后重叠区域裁剪问题:修复find_corners函数
Python+OpenCV图像对齐合并后重叠区域裁剪问题:修复find_corners函数
看起来你在图像对齐后的重叠区域精准裁剪上卡壳了,核心问题确实出在find_corners函数上——原代码找的是所有非黑像素的全局极值点,而不是你需要的倾斜有效区域的四个顶点,这就导致后续计算的裁剪边界完全不对。咱们一步步来修复它:
问题分析
原find_corners函数通过np.where找所有非黑像素的坐标,然后取x/y的最大最小值,这只能得到整个非黑区域的“外包框”的极值点,而不是你要的倾斜矩形的四个角落顶点。你需要的是先定位到有效区域的轮廓,再提取它的四个顶点,这样才能正确计算出那个“最大直立可用矩形”。
修复后的find_corners函数
我们改用轮廓检测来精准获取倾斜矩形的四个顶点:
def find_corners(image): """ 找到有效图像区域(非黑像素)的四个顶点坐标 返回格式:[(x1,y1), (x2,y2), (x3,y3), (x4,y4)] """ # 转灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 二值化:非黑像素设为255,黑像素设为0 _, binary = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY) # 寻找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return None # 没有有效像素 # 取最大的轮廓(有效区域应该是最大的那个) largest_contour = max(contours, key=cv2.contourArea) # 计算最小外接矩形 rect = cv2.minAreaRect(largest_contour) # 获取矩形的四个顶点坐标,转为整数 corners = cv2.boxPoints(rect).astype(int) # 打印顶点坐标方便调试 print(f"四个顶点坐标: {corners}") return corners
调整get_overlap_region函数
因为修复后的find_corners返回的是四个顶点的列表,我们需要调整后续的边界计算逻辑,来匹配你想要的“取第二小/大值”的规则:
def get_overlap_region(imageL, imageR): # 计算对齐后的最大重叠区域 left_corners = find_corners(imageL) right_corners = find_corners(imageR) if left_corners is None or right_corners is None: print("Error: 无法找到有效图像区域的顶点") return None, None # 处理左图的顶点坐标:提取所有x和y值 left_ys = [corner[1] for corner in left_corners] left_xs = [corner[0] for corner in left_corners] # 排序后取第二小y(顶部边界)和第二大y(底部边界) left_ys.sort() left_top = left_ys[1] left_bottom = left_ys[2] # 排序后取第二小x(左边界)和第二大x(右边界) left_xs.sort() left_left = left_xs[1] left_right = left_xs[2] # 处理右图的顶点坐标 right_ys = [corner[1] for corner in right_corners] right_xs = [corner[0] for corner in right_corners] right_ys.sort() right_top = right_ys[1] right_bottom = right_ys[2] right_xs.sort() right_left = right_xs[1] right_right = right_xs[2] # 计算两个图像的重叠区域:取两个边界的交集 top_limit = max(left_top, right_top) bottom_limit = min(left_bottom, right_bottom) left_limit = max(left_left, right_left) right_limit = min(left_right, right_right) # 检查边界是否有效(top < bottom,left < right) if top_limit >= bottom_limit or left_limit >= right_limit: print("Error: 两张图像没有重叠区域") return None, None # 裁剪重叠区域 return (imageL[top_limit:bottom_limit, left_limit:right_limit], imageR[top_limit:bottom_limit, left_limit:right_limit])
完整修复后的代码
把修复后的函数替换到原代码中,整体代码如下:
#! /usr/bin/python3 import cv2 import numpy as np def find_corners(image): """ 找到有效图像区域(非黑像素)的四个顶点坐标 返回格式:[(x1,y1), (x2,y2), (x3,y3), (x4,y4)] """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return None largest_contour = max(contours, key=cv2.contourArea) rect = cv2.minAreaRect(largest_contour) corners = cv2.boxPoints(rect).astype(int) print(f"四个顶点坐标: {corners}") return corners def find_alignment(imageL, imageR): """ 使用ECC算法对齐imageR到imageL,仅允许旋转和垂直平移 """ grayL = cv2.cvtColor(imageL, cv2.COLOR_BGR2GRAY) grayR = cv2.cvtColor(imageR, cv2.COLOR_BGR2GRAY) warp_matrix = np.eye(2, 3, dtype=np.float32) criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 50, 1e-6) cc, warp_matrix = cv2.findTransformECC(grayL, grayR, warp_matrix, cv2.MOTION_AFFINE, criteria) angle = np.arctan2(warp_matrix[1, 0], warp_matrix[0, 0]) * (180.0 / np.pi) vertical_shift = int(warp_matrix[1, 2]) warp_matrix[0, 2] = 0 # 禁用水平平移 alignedR = cv2.warpAffine(imageR, warp_matrix, (imageR.shape[1], imageR.shape[0])) print(f"ECC对齐结果 → 旋转角度: {angle:.2f}° | 垂直平移: {vertical_shift} 像素") return imageL, alignedR def get_overlap_region(imageL, imageR): # 计算对齐后的最大重叠区域 left_corners = find_corners(imageL) right_corners = find_corners(imageR) if left_corners is None or right_corners is None: print("Error: 无法找到有效图像区域的顶点") return None, None # 处理左图边界 left_ys = [corner[1] for corner in left_corners] left_xs = [corner[0] for corner in left_corners] left_ys.sort() left_top = left_ys[1] left_bottom = left_ys[2] left_xs.sort() left_left = left_xs[1] left_right = left_xs[2] # 处理右图边界 right_ys = [corner[1] for corner in right_corners] right_xs = [corner[0] for corner in right_corners] right_ys.sort() right_top = right_ys[1] right_bottom = right_ys[2] right_xs.sort() right_left = right_xs[1] right_right = right_xs[2] # 计算重叠区域边界 top_limit = max(left_top, right_top) bottom_limit = min(left_bottom, right_bottom) left_limit = max(left_left, right_left) right_limit = min(left_right, right_right) if top_limit >= bottom_limit or left_limit >= right_limit: print("Error: 两张图像没有重叠区域") return None, None return (imageL[top_limit:bottom_limit, left_limit:right_limit], imageR[top_limit:bottom_limit, left_limit:right_limit]) def create_anaglyph(imageL, imageR): if imageL is None or imageR is None: print("Error: 裁剪后的图像无效") return None # 生成红青立体图:左图取红通道,右图取绿蓝通道 red_channel = imageL[:, :, 2] green_channel = imageR[:, :, 1] blue_channel = imageR[:, :, 0] return cv2.merge((blue_channel, green_channel, red_channel)) if __name__ == "__main__": file_prefix = input("输入图像前缀(不含L/R和后缀): ") fileL = f"{file_prefix}L.jpg" fileR = f"{file_prefix}R.jpg" imageL = cv2.imread(fileL, cv2.IMREAD_COLOR) imageR = cv2.imread(fileR, cv2.IMREAD_COLOR) if imageL is None or imageR is None: print("Error: 无法加载一张或两张图像") exit(1) imageL_aligned, imageR_aligned = find_alignment(imageL, imageR) imageL_cropped, imageR_cropped = get_overlap_region(imageL_aligned, imageR_aligned) if imageL_cropped is None or imageR_cropped is None: print("Error: 无法生成立体图,裁剪区域无效") exit(1) # 确保两张裁剪后的图像尺寸一致(防止边界计算微小误差) final_height = min(imageL_cropped.shape[0], imageR_cropped.shape[0]) final_width = min(imageL_cropped.shape[1], imageR_cropped.shape[1]) imageL_cropped = imageL_cropped[:final_height, :final_width] imageR_cropped = imageR_cropped[:final_height, :final_width] anaglyph_image = create_anaglyph(imageL_cropped, imageR_cropped) if anaglyph_image is not None: output_path = f"{file_prefix}-anaglyph.jpg" cv2.imwrite(output_path, anaglyph_image) print(f"立体图已保存到: {output_path}")
关键修复点说明
- 轮廓检测替代全局极值:通过
cv2.findContours找到有效区域的轮廓,再用cv2.minAreaRect获取精准的倾斜矩形顶点,这才是你需要的四个角落。 - 边界计算逻辑匹配需求:对四个顶点的x/y值排序后取中间两个值,刚好对应你描述的“第二小y作为顶部,第二大y作为底部”的规则,确保得到的是有效区域内的最大直立矩形。
- 增加有效性检查:在重叠区域计算时加入边界有效性判断,避免出现无效的裁剪区域导致程序崩溃。
这样调整后,应该就能正确裁剪出两张图像的重叠区域,生成符合预期的立体图了。
备注:内容来源于stack exchange,提问作者ChrisOfBristol
相关产品推荐
相关产品推荐

