Сшивка панорамы для текста

Я ищу хорошую библиотеку сшивания панорам для текста. Я попробовал OpenCV и OpenPano. Оба они хорошо работают с обычными фотографиями, но не работают с текстом. Например, мне нужно сшить следующие 3 изображения:

1st s2nd 3rd

Изображения имеют примерно 45% перекрытия друг с другом.

Если есть возможность заставить одну из упомянутых библиотек хорошо работать с текстовыми изображениями вместо поиска другой библиотеки, это было бы здорово.

  • Мне нужна библиотека для работы на linux arm.

person Alaa M.    schedule 10.08.2017    source источник


Ответы (2)


OpenPano не сшивает текст, так как не может получить достаточное количество характерных точек (или ключевых точек ) для выполнения процесса сшивания.

Для сшивания текста не требуется метод сопоставления, устойчивый к поворотам, а только к переводам. OpenCV удобно предлагает такую ​​функцию. Он называется: Сопоставление с шаблоном.

Решение, которое я разработаю, основано на этой функции OpenCV.


Трубопровод

Теперь я объясню основные шаги моего решения (для получения дополнительной информации, пожалуйста, взгляните на приведенный ниже код).

Процесс сопоставления

Чтобы сопоставить два последовательных изображения (выполнено в функции matchImages, см. код ниже):

  1. Мы создаем изображение template, взяв 45 % (H_templ_ratio) от первого изображения, как показано ниже:

Шаблон, 45% исходного изображения

Этот шаг выполняется в моем коде функцией genTemplate.

  1. Мы добавляем черные поля ко второму изображению (там, где мы хотим найти шаблон). Этот шаг необходим, если текст не выровнен на входных изображениях (хотя это относится и к этим образцам изображений). Вот как выглядит изображение после обработки полей. Как видите, поля нужны только ниже и выше изображения:

поля нужны только сверху и снизу

Изображение template теоретически можно найти в любом месте этого изображения с полями. Этот процесс выполняется в функции addBlackMargins.

  1. Мы применяем хитрый фильтр к обоим template изображение и изображение, где мы хотим его найти (выполняется внутри функции Mat2Edges). Это повысит надежность процесса сопоставления. Вот пример:

Пример хитрого фильтра

  1. Мы сопоставляем шаблон с изображением, используя matchTemplate и мы получаем местоположение наилучшего соответствия с помощью minMaxLoc функция.

Вычисление окончательного размера изображения

Этот шаг состоит в вычислении размера окончательной матрицы, в которой мы будем соединять все изображения вместе. Это особенно необходимо, если все входные изображения не имеют одинаковой высоты.

Этот шаг выполняется внутри функции calcFinalImgSize. Я не буду вдаваться в подробности здесь, потому что, хотя это выглядит немного сложно (по крайней мере, для меня), это всего лишь простая математика (сложение, вычитание, умножение). Возьмите ручку и бумагу, если хотите понять формулы.

Процесс сшивания

Как только у нас есть соответствующие местоположения для каждого входного изображения, нам нужно только выполнить простые математические действия, чтобы скопировать исходные изображения в правильном месте финальное изображение. Опять же, я рекомендую вам проверить код для деталей реализации (см. функцию stitchImages).


Результаты

Вот результат с вашими входными изображениями:

окончательный результат с предоставленным образцом

Как видите, результат не идеальный по пикселям, но он должен быть достаточно хорошим для распознавание.

А вот еще один результат с входными изображениями разной высоты:

результат с изображениями разной высоты


Код (Питон)

Моя программа написана на Python и использует модули cv2 (OpenCV) и numpy. Однако его можно (легко) портировать на другие языки, такие как C++, Java и C#.

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)
person Elouarn Laine    schedule 11.08.2017
comment
Большое спасибо! Это выглядит очень профессионально. попробую через пару дней - person Alaa M.; 11.08.2017
comment
Спасибо, ваш код сработал для меня. Что произойдет, если процент перекрытия изменится? Должен ли я изменить H_templ_ratio соответственно или есть лучший способ? - person Alaa M.; 13.08.2017
comment
@AlaaM Да, это действительно цель этой переменной. Если ответ помог вам, рассмотрите возможность его принятия. - person Elouarn Laine; 14.08.2017

В 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)

Я получил этот результат, используя ваши изображения.

person Vikas Gupta    schedule 05.06.2018