Панорама сшивание для текста

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

первый s2nd третий

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

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

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

2 ответа

Решение

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

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

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


Трубопровод

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

Процесс соответствия

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

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

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

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

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

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

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

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

Пример фильтра canny

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

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

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

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

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

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


Результаты

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

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

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

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

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


Код (Python)

Моя программа написана на 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)

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)

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

Другие вопросы по тегам