【问题标题】:Panorama stitching for text文本的全景拼接
【发布时间】:2017-08-10 11:49:12
【问题描述】:

我正在寻找一个很好的文本全景拼接库。我试过OpenCVOpenPano。它们都适用于普通照片,但在文字上却失败了。例如我需要拼接以下3张图片:

图像之间有大约 45% 的重叠。

如果可以选择让上述库之一在文本图像上正常工作,而不是寻找另一个库,那就太好了。

  • 我需要该库才能在 linux arm 上工作。

【问题讨论】:

    标签: opencv panoramas image-stitching


    【解决方案1】:

    OpenCV 3 有一个 Stitcher 类,可以对文本和照片进行拼接。

    
    import cv2
    imageFiles = [YOUR IMAGE FILE NAMES]
    images = []
    for filename in imagefiles:
        img = cv2.imread(filename)
        images.append(img)
    
    

    stitcher = cv2.createStitcher()

    status, result = stitcher.stitch(images)

    我使用您的图像得到了这个结果。

    【讨论】:

      【解决方案2】:

      OpenPano 无法拼接文本,因为它无法检索足够的特征点(或关键点)来执行拼接过程。

      文本拼接不需要对旋转稳健但仅对翻译具有稳健性的匹配方法。 OpenCV 方便地提供了这样的功能。它被称为:Template Matching

      我将开发的解决方案是基于这个 OpenCV 的特性。


      管道

      我现在将解释我的解决方案的主要步骤(更多详细信息,请查看下面提供的代码)。

      匹配过程

      为了匹配两个连续的图像(在matchImages函数中完成,见下面的代码):

      1. 我们通过获取第一张图像的 45% (H_templ_ratio) 创建一个 模板 图像,如下所示:

      这一步在我的代码中由函数genTemplate 完成。

      1. 我们将黑色边距添加到第二张图像(我们要在其中找到 模板)。如果输入图像中的文本未对齐(尽管这些示例图像就是这种情况),则此步骤是必要的。这是边缘处理后图像的样子。如您所见,仅在图像下方和上方需要边距:

      模板 图像理论上可以在此边缘图像中的任何位置找到。这个过程在addBlackMargins函数中完成。

      1. 我们将canny filter 应用于模板 图像和我们想要找到它的图像(在Mat2Edges 函数内完成)。这将为匹配过程增加稳健性。这是一个例子:

      1. 我们使用matchTemplate模板 与图像匹配,并使用minMaxLoc 函数检索最佳匹配位置。

      计算最终图像大小

      这一步包括计算最终矩阵大小,我们会将所有图像拼接在一起。如果所有输入图像的高度不同,则尤其需要这样做。

      这一步在calcFinalImgSize 函数中完成。我不会在这里详细介绍,因为即使它看起来有点复杂(至少对我而言),这只是简单的数学运算(加法、减法、乘法)。如果您想了解公式,请带上笔和纸。

      拼接过程

      一旦我们有了每个输入图像的匹配位置,我们只需做简单的数学运算即可复制右侧的输入图像 最终图像的位置。同样,我建议您检查代码以了解实现细节(请参阅stitchImages 函数)。


      结果

      这是您输入图像的结果:

      如您所见,结果不是“像素完美”,但对于OCR 来说应该已经足够了。

      这是另一个不同高度的输入图像的结果:


      代码(Python)

      我的程序是用 Python 编写的,使用 cv2 (OpenCV) 和 numpy 模块。然而,它可以(轻松地)移植到其他语言中,例如 C++JavaC#

      import numpy as np
      import cv2
      
      def genTemplate(img): 
          global H_templ_ratio
          # we get the image's width and height
          h, w = img.shape[:2]
          # we compute the template's bounds
          x1 = int(float(w)*(1-H_templ_ratio))
          y1 = 0
          x2 = w
          y2 = h
          return(img[y1:y2,x1:x2]) # and crop the input image
      
      def mat2Edges(img): # applies a Canny filter to get the edges
          edged = cv2.Canny(img, 100, 200)
          return(edged)
      
      def addBlackMargins(img, top, bottom, left, right): # top, bottom, left, right: margins width in pixels
          h, w = img.shape[:2]
          result = np.zeros((h+top+bottom, w+left+right, 3), np.uint8)
          result[top:top+h,left:left+w] = img
          return(result)
      
      # return the y_offset of the first image to stitch and the final image size needed
      def calcFinalImgSize(imgs, loc):
          global V_templ_ratio, H_templ_ratio
          y_offset = 0
          max_margin_top = 0; max_margin_bottom = 0 # maximum margins that will be needed above and bellow the first image in order to stitch all the images into one mat
          current_margin_top = 0; current_margin_bottom = 0
      
          h_init, w_init = imgs[0].shape[:2]
          w_final = w_init
          
          for i in range(0,len(loc)):
              h, w = imgs[i].shape[:2]
              h2, w2 = imgs[i+1].shape[:2]
              # we compute the max top/bottom margins that will be needed (relatively to the first input image) in order to stitch all the images
              current_margin_top += loc[i][1] # here, we assume that the template top-left corner Y-coordinate is 0 (relatively to its original image)
              current_margin_bottom += (h2 - loc[i][1]) - h
              if(current_margin_top > max_margin_top): max_margin_top = current_margin_top
              if(current_margin_bottom > max_margin_bottom): max_margin_bottom = current_margin_bottom
              # we compute the width needed for the final result
              x_templ = int(float(w)*H_templ_ratio) # x-coordinate of the template relatively to its original image
              w_final += (w2 - x_templ - loc[i][0]) # width needed to stitch all the images into one mat
      
          h_final = h_init + max_margin_top + max_margin_bottom
          return (max_margin_top, h_final, w_final)
      
      # match each input image with its following image (1->2, 2->3) 
      def matchImages(imgs, templates_loc):
          for i in range(0,len(imgs)-1):
              template = genTemplate(imgs[i])
              template = mat2Edges(template)
              h_templ, w_templ = template.shape[:2]
              # Apply template Matching
              margin_top = margin_bottom = h_templ; margin_left = margin_right = 0
              img = addBlackMargins(imgs[i+1],margin_top, margin_bottom, margin_left, margin_right) # we need to enlarge the input image prior to call matchTemplate (template needs to be strictly smaller than the input image)
              img = mat2Edges(img)
              res = cv2.matchTemplate(img,template,cv2.TM_CCOEFF) # matching function
              _, _, _, templ_pos = cv2.minMaxLoc(res) # minMaxLoc gets the best match position
              # as we added margins to the input image we need to subtract the margins width to get the template position relatively to the initial input image (without the black margins)
              rectified_templ_pos = (templ_pos[0]-margin_left, templ_pos[1]-margin_top) 
              templates_loc.append(rectified_templ_pos)
              print("max_loc", rectified_templ_pos)
      
      def stitchImages(imgs, templates_loc):
          y_offset, h_final, w_final = calcFinalImgSize(imgs, templates_loc) # we calculate the "surface" needed to stitch all the images into one mat (and y_offset, the Y offset of the first image to be stitched) 
          result = np.zeros((h_final, w_final, 3), np.uint8)
      
          #initial stitch
          h_init, w_init = imgs[0].shape[:2]
          result[y_offset:y_offset+h_init, 0:w_init] = imgs[0]
          origin = (y_offset, 0) # top-left corner of the last stitched image (y,x)
          # stitching loop
          for j in range(0,len(templates_loc)):
              h, w = imgs[j].shape[:2]
              h2, w2 = imgs[j+1].shape[:2]
              # we compute the coordinates where to stitch imgs[j+1]
              y1 = origin[0] - templates_loc[j][1]
              y2 = origin[0] - templates_loc[j][1] + h2
              x_templ = int(float(w)*(1-H_templ_ratio)) # x-coordinate of the template relatively to its original image's right side
              x1 = origin[1] + x_templ - templates_loc[j][0]
              x2 = origin[1] + x_templ - templates_loc[j][0] + w2
              result[y1:y2, x1:x2] = imgs[j+1] # we copy the input image into the result mat
              origin = (y1,x1) # we update the origin point with the last stitched image
      
          return(result)
      
      if __name__ == '__main__':
      
          # input images
          part1 = cv2.imread('part1.jpg')
          part2 = cv2.imread('part2.jpg')
          part3 = cv2.imread('part3.jpg')
          imgs = [part1, part2, part3]
          
          H_templ_ratio = 0.45 # H_templ_ratio: horizontal ratio of the input that we will keep to create a template
          templates_loc = [] # templates location
      
          matchImages(imgs, templates_loc)
          
          result = stitchImages(imgs, templates_loc)
      
          cv2.imshow("result", result)
      

      【讨论】:

      • 非常感谢!它看起来非常专业。我过几天试试
      • 谢谢,您的代码对我有用。如果重叠百分比发生变化会发生什么?我是否必须相应地更改H_templ_ratio,还是有更好的方法?
      • @AlaaM 是的,这确实是这个变量的目的。如果答案对您有所帮助,请考虑接受。
      猜你喜欢
      • 2011-12-12
      • 2018-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-09
      相关资源
      最近更新 更多