OpenCV:使用findHomography()和warpPerspective将较大的图像对齐到较小的图像

时间:2016-12-07 09:40:10

标签: python c++ opencv image-processing computer-vision

我的目标是

  1. 对扫描图像进行校正,使其文本完美地放置在原始图像的文本顶部。 (减去图像会删除文本)
  2. 防止在偏斜图像上丢失任何信息
  3. 我使用SURF功能来提供findHomography功能。然后我使用warpPerspective函数来转换扫描图像。生成的图像几乎完全适合原始图像。

    然而,扫描图像的角落内容在转换后会丢失,因为扫描图像中的文本较小且必须按比例放大。

    Original and scanned image 纠正文字略小的图像

    Transformed image lost content

    裁剪图像边界处的信息

    为了避免任何信息丢失,我将图像转换为RGBA并在warpPerspective中设置borderValue参数,以便任何添加的背景都具有透明色。我再次转换后删除透明像素。这个程序有效,但效率很低。

    1. 问题:我正在寻找一个工作代码示例(C ++或Python),它展示了如何更有效地执行此操作。
    2. Images aren't aligned because transformed image has different dimensions 图像已被歪斜,内容得以保留。但是,这两张照片的文字不再是彼此之上

      Text isn't aligned anymore

      文本位置关闭,因为扭曲图像的大小与warpPerspective预期的大小不同

      转换图像后,问题是两幅图像不再对齐,因为转换后图像的尺寸与warpPerspective方法预期的尺寸不同。

      1. 问题:如何重新对齐这两张图片?如果有办法将此结合到上一步中,那将是很好的。再一次,一个有效的代码示例将非常有用。
      2. 这是我到目前为止的代码。它在保留图像内容时对图像进行了校正,但是,文本不再位于原始文本之上。

        import math
        import cv2
        import numpy as np
        
        
        class Deskewer:
            def __init__(self, hessianTreshold = 5000):
                self.__hessianThresh = hessianTreshold
                self.imgOrigGray, self.imgSkewed, self.imgSkewedGray = None, None, None
        
            def start(self, imgOrig, imgSkewed):
                self.imgOrigGray = cv2.cvtColor(imgOrig, cv2.COLOR_BGR2GRAY)
                self.imgSkewed = imgSkewed  # final transformation will be performed on color image
                self.imgSkewedGray = cv2.cvtColor(imgSkewed, cv2.COLOR_BGR2GRAY)  # prior calculation is faster on gray
        
                kp1, des1, kp2, des2 = self.__detectFeatures()
                goodMatches = self.__flannMatch(des1, des2)
        
                MIN_MATCH_COUNT = 10
                M = None
                if len(goodMatches) > MIN_MATCH_COUNT:
                    M, _ = self.__findHomography(kp1, kp2, goodMatches)
                else:
                    print("Not  enough  matches are found   -   %d/%d" % (len(goodMatches), MIN_MATCH_COUNT))
                    return
        
                return self.__deskew(M)
        
        
            def __detectFeatures(self):
                surf = cv2.xfeatures2d.SURF_create(self.__hessianThresh)
                kp1, des1 = surf.detectAndCompute(self.imgOrigGray, None)
                kp2, des2 = surf.detectAndCompute(self.imgSkewedGray, None)
        
                return kp1, des1, kp2, des2
        
            def __flannMatch(self, des1, des2):
                global matches
                FLANN_INDEX_KDTREE = 0
                index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
                search_params = dict(checks=50)
                flann = cv2.FlannBasedMatcher(index_params, search_params)
                matches = flann.knnMatch(des1, des2, k=2)
        
                # store all the good matches as per Lowe's ratio test.
                good = []
                for m, n in matches:
                    if m.distance < 0.7 * n.distance:
                        good.append(m)
        
                return good
        
            def __findHomography(self, kp1, kp2, goodMatches):
                src_pts = np.float32([kp1[m.queryIdx].pt for m in goodMatches
                                      ]).reshape(-1, 1, 2)
                dst_pts = np.float32([kp2[m.trainIdx].pt for m in goodMatches
                                      ]).reshape(-1, 1, 2)
        
                M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
                matchesMask = mask.ravel().tolist()
                i = matchesMask.index(1)
        
                # TODO: This is a matching point before the warpPerspective call. How can I calculate this point AFTER the call?
                print("POINTS: object(", src_pts[i][0][1], ",", src_pts[i][0][0], ") - scene(", dst_pts[i][0][1], ",", dst_pts[i][0][0], ")")
        
                return M, mask
        
            def getComponents(self, M):
                # ((translationx, translationy), rotation, (scalex, scaley), shear)
                a = M[0, 0]
                b = M[0, 1]
                c = M[0, 2]
                d = M[1, 0]
                e = M[1, 1]
                f = M[1, 2]
        
                p = math.sqrt(a * a + b * b)
                r = (a * e - b * d) / (p)
                q = (a * d + b * e) / (a * e - b * d)
        
                translation = (c, f)
                scale = (p, r)  # p = x-Axis, r = y-Axis
                shear = q
                theta = math.atan2(b, a)
                degrees = math.atan2(b, a) * 180 / math.pi
        
                return (translation, theta, degrees, scale, shear)
        
            def __deskew(self, M):
                # this info might come in handy here for calculating the dsize of warpPerspective?
                translation, theta, degrees, scale, shear = self.getComponents(M)
        
                # Alpha channel allows me to set unique feature to pixels that are created during warpPerspective
                imSkewedAlpha = cv2.cvtColor(self.imgSkewed, cv2.COLOR_BGR2BGRA)
        
                # These sizes have been randomly choosen to make sure that all the contents fit in the new canvas
                height = 5000
                width = 5000
                shift = -500
        
                M2 = np.array([[1, 0, shift],
                              [0, 1, shift],
                              [0, 0, 1]])
                M3 = np.dot(M, M2)
        
                # TODO: How can I calculate the dsize argument?
                # Newly created pixels are set to transparent
                im_out = cv2.warpPerspective(imSkewedAlpha, M3,
                                             (height, width), flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_CONSTANT, borderValue=(255, 0, 0, 0))
        
                # http://codereview.stackexchange.com/a/132933
                # Mask of non-black pixels (assuming image has a single channel).
                mask = im_out[:, :, 3] == 255
        
                # Coordinates of non-black pixels.
                coords = np.argwhere(mask)
        
                # Bounding box of non-black pixels.
                x0, y0 = coords.min(axis=0)
                x1, y1 = coords.max(axis=0) + 1  # slices are exclusive at the top
        
                # Get the contents of the bounding box.
                cropped = im_out[x0:x1, y0:y1]
        
                # TODO: The warped image needs to align nicely on the original image
                return cropped
        
        origImg = cv2.imread("Letter.png")
        skewedImg = cv2.imread("A4.png")
        deskewed = Deskewer().start(origImg, skewedImg)
        cv2.imshow("Original", origImg)
        cv2.imshow("Deskewed", deskewed)
        cv2.waitKey(0)
        

        Original image for testing Skewed image for testing 用于测试的原始和倾斜图像(附加内容)

0 个答案:

没有答案