python - 对齐和裁剪相同的场景图像

标签 python linux opencv imagemagick

您好,我使用包围曝光拍摄了不同的图像(相同的场景不同的曝光),我需要对齐图像并裁剪每一张,以便它们完全匹配。 (因为拍摄这些图像时有相机抖动)
我不想合并它们,我只想剪切、旋转或缩放..等每一个,以便它们完全对齐,然后保存它们。如果我知道如何做到这一点,我会添加一个代码示例。但我不知道。我是opencv的新手。
这是一个例子:
enter image description here
这是一个示例的真实示例:(此示例有很大的错位,大多数示例都需要进行小幅调整,因为与此不同的是晃动)
enter image description here
我需要的是裁剪每张图像以使它们相同(仅保留共享区域)
谢谢 !

最佳答案

在这个答案中,我描述了一种实现 的方法图像对齐 其中包括使用欧几里得模型根据图像2(中心图像)对图像1(左侧)和图像3(右侧)进行变换。
enter image description here
enter image description here
但是,我想快速指出,共享的图像非常具有挑战性:不仅对比度差异很大,而且它们在 上也有显着差异。翻译 , 规模 , 旋转 , 甚至可能是 的一点点剪切 .它们以非常低的分辨率发布的事实也无济于事。
无论如何,我指出了他们在 方面的差异。 2D 变换 因为在选择合适的模型来执行图像对齐时,您始终需要牢记这一点。 This nice picture is from a tutorial that describes the models in greater detail :


该方法包括以下步骤:

  • 使用增强相关系数 (ECC) 算法在图像 2 和 1 之间使用欧几里德模型进行图像对齐;
  • 然后使用返回的变换矩阵在 cv2.warpAffine() 的帮助下对图像 1 进行变换。并计算图像1中变换的近似矩形区域;
  • 重复相同的步骤对图像 3 进行变换:使用增强相关系数 (ECC) 算法在图像 2 和 3 之间使用欧几里德模型进行图像对齐;
  • 然后使用返回的变换矩阵在 cv2.warpAffine() 的帮助下对图像 3 进行变换。并计算变换的近似矩形面积。
  • 这些操作的结果是对齐的图像,可以在下图中看到。绿色矩形表示变换区域:

  • enter image description here
  • 中心图像中的红色矩形,用于创建转换模型的引用图像,是图像 1 和 3 上区域之间的交集,可以看作是所有 3 个图像之间的公共(public)区域。
  • 然后可以使用红色矩形裁剪图像 1、2 和 3,并提供图像对齐的漂亮外观。请注意所有这些图像上的地形和天空看起来是如何完美对齐的:

  • enter image description here
    注意到这种方法的成本是很有趣的:因为图像 1 没有捕捉到在图像 2 和 3 上很容易看到的所有地形特征,最终结果是图像 2 和 3 最终失去了那部分地形。因此,所有图像都显示照片的完全相同的区域。
    Python源代码 :
    ###
    # reference:
    #   https://www.learnopencv.com/image-alignment-ecc-in-opencv-c-python/
    ###
    import numpy as np
    import cv2
    
    # internalRect: returns the intersection between two rectangles
    #
    #  p1 ---------------- p2
    #   |                  |
    #   |                  |
    #   |                  |
    #  p4 ---------------- p3
    def internalRect(r1, r2):
        x = 0
        y = 1
        w = 2
        h = 3
    
        rect1_pt1 = [       r1[x], r1[y]       ]
        rect1_pt2 = [ r1[x]+r1[w], r1[y]       ]
        rect1_pt3 = [ r1[x]+r1[w], r1[y]+r1[h] ]
        rect1_pt4 = [       r1[x], r1[y]+r1[h] ]
    
        rect2_pt1 = [       r2[x], r2[y]       ]
        rect2_pt2 = [ r2[x]+r2[w], r2[y]       ]
        rect2_pt3 = [ r2[x]+r2[w], r2[y]+r2[h] ]
        rect2_pt4 = [       r2[x], r2[y]+r2[h] ]
    
        int_pt1   = [ max(rect1_pt1[x], rect2_pt1[x]), max(rect1_pt1[y], rect2_pt1[y]) ]
        int_pt2   = [ min(rect1_pt2[x], rect2_pt2[x]), max(rect1_pt2[y], rect2_pt2[y]) ]
        int_pt3   = [ min(rect1_pt3[x], rect2_pt3[x]), min(rect1_pt3[y], rect2_pt3[y]) ]
        int_pt4   = [ max(rect1_pt4[x], rect2_pt4[x]), min(rect1_pt4[y], rect2_pt4[y]) ]
    
        rect =  [ int_pt1[x], int_pt1[y], int_pt2[x]-int_pt1[x], int_pt4[y]-int_pt1[y] ]
        return rect
    
    
    # align_image: use src1 as the reference image to transform src2
    def align_image(src1, src2, warp_mode=cv2.MOTION_TRANSLATION):
        # convert images to grayscale
        img1_gray = cv2.cvtColor(src1, cv2.COLOR_BGR2GRAY)
        img2_gray = cv2.cvtColor(src2, cv2.COLOR_BGR2GRAY)
    
        # define 2x3 or 3x3 matrices and initialize it to a identity matrix
        if warp_mode == cv2.MOTION_HOMOGRAPHY:
            warp_matrix = np.eye(3, 3, dtype=np.float32)
        else:
            warp_matrix = np.eye(2, 3, dtype=np.float32)
    
        # number of iterations:
        num_iters = 1000
    
        # specify the threshold of the increment in the correlation coefficient between two iterations
        termination_eps = 1e-8    
    
        # Define termination criteria
        criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, num_iters,  termination_eps)
    
        print('findTransformECC() may take a while...')
    
        # perform ECC: use the selected model to calculate the transformation required to align src2 with src1. The resulting transformation matrix is stored in warp_matrix:
        (cc, warp_matrix) = cv2.findTransformECC(img1_gray, img2_gray, warp_matrix, warp_mode, criteria, inputMask=None, gaussFiltSize=1)
    
        if (warp_mode == cv2.MOTION_HOMOGRAPHY):
            img2_aligned = cv2.warpPerspective(src2, warp_matrix, (src1.shape[1], src1.shape[0]), flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)
        else :
            # use warpAffine() for: translation, euclidean and affine models
            img2_aligned = cv2.warpAffine(src2, warp_matrix, (src1.shape[1], src1.shape[0]), flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_CONSTANT, borderValue=0)
    
        #print('warp_matrix shape', warp_matrix.shape, 'data=\n', warp_matrix)
        #print(warp_matrix, warp_matrix)
    
        # compute the cropping area to remove the black bars from the transformed image
        x = 0
        y = 0
        w = src1.shape[1]
        h = src1.shape[0]
    
        if (warp_matrix[0][2] < 0):
            x = warp_matrix[0][2] * -1
            w -= x
    
        if (warp_matrix[1][2] < 0):
            y = warp_matrix[1][2] * -1
            h -= y
    
        if (warp_matrix[1][2] > 0):
            h -= warp_matrix[1][2]
        
        matchArea = [ int(x), int(y), int(w), int(h) ]
      
        #print('src1 w=', src1.shape[1], 'h=', src1.shape[0])
        #print('matchedRect=', matchArea[0], ',', matchArea[1], '@', matchArea[2], 'x', matchArea[3], '\n')
        return img2_aligned, matchArea
    
    
    ##########################################################################################
    
    
    img1 = cv2.imread("img1.png")
    img2 = cv2.imread("img2.png")
    img3 = cv2.imread("img3.png")
    
    # TODO: adjust contrast on all input images
    
    ###
    # resize images to be the same size as the smallest image for debug purposes
    ###
    max_h = img1.shape[0]
    max_h = max(max_h, img2.shape[0])
    max_h = max(max_h, img3.shape[0])
    max_w = img1.shape[1]
    max_w = max(max_w, img2.shape[1])
    max_w = max(max_w, img3.shape[1])
    img1_padded = cv2.resize(img1, (max_w, max_h), interpolation=cv2.INTER_AREA)
    img2_padded = cv2.resize(img2, (max_w, max_h), interpolation=cv2.INTER_AREA)
    img3_padded = cv2.resize(img3, (max_w, max_h), interpolation=cv2.INTER_AREA)
    
    # stack them horizontally for display
    hStack = np.hstack((img1_padded, img2_padded))  # stack images side-by-side
    input_stacked = np.hstack((hStack, img3_padded))      # stack images side-by-side
    cv2.imwrite("input_stacked.jpg", input_stacked)
    cv2.imshow("input_stacked", input_stacked)
    cv2.waitKey(0)
    
    ###
    # perform image alignment
    ###
    
    # specify the motion model
    warp_mode = cv2.MOTION_EUCLIDEAN   # cv2.MOTION_TRANSLATION, cv2.MOTION_EUCLIDEAN, cv2.MOTION_AFFINE, cv2.MOTION_HOMOGRAPHY
    
    # for testing purposes: img2 will be the reference image
    img1_aligned, matchArea1 = align_image(img2, img1, warp_mode)
    img1_aligned_cpy = img1_aligned.copy()
    cv2.rectangle(img1_aligned_cpy, (matchArea1[0], matchArea1[1]),  (matchArea1[0]+matchArea1[2], matchArea1[1]+matchArea1[3]), (0, 255, 0), 2)
    cv2.imwrite("img1_aligned.jpg", img1_aligned_cpy)
    
    print('\n###############################################\n')
    
    # for testing purposes: img2 will be the reference image again
    img3_aligned, matchArea3 = align_image(img2, img3, warp_mode)
    img3_aligned_cpy = img3_aligned.copy()
    cv2.rectangle(img3_aligned_cpy, (matchArea3[0], matchArea3[1]),  (matchArea3[0]+matchArea3[2], matchArea3[1]+matchArea3[3]), (0, 255, 0), 2)
    cv2.imwrite("img3_aligned.jpg", img3_aligned_cpy)
    
    # compute the crop area in the reference image and draw a red rectangle
    cropRect = internalRect(matchArea1, matchArea3)
    print('cropRect=', cropRect[0], ',', cropRect[1], '@', cropRect[2], 'x', cropRect[3], '\n')
    
    img2_eq_cpy = img2.copy()
    cv2.rectangle(img2_eq_cpy, (cropRect[0], cropRect[1]),  (cropRect[0]+cropRect[2], cropRect[1]+cropRect[3]), (0, 0, 255), 2)
    cv2.imwrite("img2_eq.jpg", img2_eq_cpy)
    
    # stack results horizontally for display
    res_hStack = np.hstack((img1_aligned_cpy, img2_eq_cpy))                 # stack images side-by-side
    aligned_stacked = np.hstack((res_hStack, img3_aligned_cpy))             # stack images side-by-side
    cv2.imwrite("aligned_stacked.jpg", aligned_stacked)
    cv2.imshow("aligned_stacked", aligned_stacked)
    cv2.waitKey(0)
    
    print('\n###############################################\n')
    
    # crop images to the smallest internal area between them
    img1_aligned_cropped = img1_aligned[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]
    img3_aligned_cropped = img3_aligned[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]
    img2_eq_cropped      =         img2[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]
    
    cropped_hStack = np.hstack((img1_aligned_cropped, img2_eq_cropped))     # stack images side-by-side
    cropped_stacked = np.hstack((cropped_hStack, img3_aligned_cropped))     # stack images side-by-side
    cv2.imwrite("cropped_stacked.jpg", cropped_stacked)
    cv2.imshow("cropped_stacked", cropped_stacked)
    cv2.waitKey(0)
    

    关于python - 对齐和裁剪相同的场景图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62495112/

    相关文章:

    java - Android 设备中的 OpenCV Java API 或 OpenCV C++ API?

    python - 在图像中仅显示 45 度线

    python - 根据第 n 列中的值拆分无序 csv 文件/

    python - 可以使用 cURL 访问 JIRA API,但不能使用 Requests

    python - 在python中分配一个列表

    regex - 将一个字符串中的单词与另一个字符串中的另一个单词进行比较

    linux - screen 远程分离时网络连接丢失

    xml - 使用 Linux Bash 脚本从 XML 文件中获取数据

    java - XBee 系列 2 尝试接收数据包时挂起并超时

    opencv - 如何处理对象检测的遮挡、扭曲或透视变形?