You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}")

关键修复点说明

  1. 轮廓检测替代全局极值:通过cv2.findContours找到有效区域的轮廓,再用cv2.minAreaRect获取精准的倾斜矩形顶点,这才是你需要的四个角落。
  2. 边界计算逻辑匹配需求:对四个顶点的x/y值排序后取中间两个值,刚好对应你描述的“第二小y作为顶部,第二大y作为底部”的规则,确保得到的是有效区域内的最大直立矩形。
  3. 增加有效性检查:在重叠区域计算时加入边界有效性判断,避免出现无效的裁剪区域导致程序崩溃。

这样调整后,应该就能正确裁剪出两张图像的重叠区域,生成符合预期的立体图了。

备注:内容来源于stack exchange,提问作者ChrisOfBristol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:34:51