Обнаружение текстовой области в изображении с использованием python и opencv

Я хочу определить текстовую область изображений с помощью python 2.7 и opencv 2.4.9 и нарисуйте вокруг него прямоугольную область. Как показано на рисунке ниже.

Я новичок в обработке изображений, поэтому любая идея, как это сделать, будет оценена.

проект здания с помеченными комнатами

Ответы

Ответ 1

Существует несколько способов поиска текста в изображении.

Я рекомендую посмотреть этот вопрос здесь, так как он также может ответить на ваш вопрос. Хотя это не в python, код можно легко перевести с С++ на python (просто посмотрите на API и конвертируйте методы из С++ в python, а не из-за этого. Я сделал это сам, когда я попробовал свой код для своей отдельной проблемы), Решения здесь могут не работать для вашего случая, но я рекомендую попробовать их.

Если бы я это сделал, я бы сделал следующий процесс:

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

# must import if working with opencv in python
import numpy as np
import cv2

# removes pixels in image that are between the range of
# [lower_val,upper_val]
def remove_gray(img,lower_val,upper_val):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower_bound = np.array([0,0,lower_val])
    upper_bound = np.array([255,255,upper_val])
    mask = cv2.inRange(gray, lower_bound, upper_bound)
    return cv2.bitwise_and(gray, gray, mask = mask)

Теперь, когда у вас есть черный текст, цель состоит в том, чтобы получить эти поля. Как было сказано ранее, есть разные способы обойти это.

Трансформация ширины обводки (SWT)

Типичный способ поиска текстовых областей: вы можете найти текстовые области, используя преобразование ширины штриха, как показано в "Обнаружение текста в естественных сценах с преобразованием ширины штриха" Бориса Эпштейна, Эяля Офэка и Йонатана Векслера. Если честно, если это так же быстро и надежно, как я считаю, то этот метод является более эффективным методом, чем мой код ниже. Вы все равно можете использовать приведенный выше код, чтобы удалить проект чертежа, и это может помочь общей производительности алгоритма swt.

Вот c-библиотека, которая реализует их алгоритм, но, как утверждается, она очень сырая, и документация заявлена ​​как неполная. Очевидно, для использования этой библиотеки с python потребуется оболочка, и на данный момент я не вижу официального предложения.

Связанная мной библиотека CCV. Это библиотека, предназначенная для использования в ваших приложениях, а не для воссоздания алгоритмов. Таким образом, это инструмент, который нужно использовать, что противоречит стремлению OP сделать его из "первых принципов", как указано в комментариях. Тем не менее, полезно знать, что он существует, если вы не хотите самостоятельно кодировать алгоритм.


Домашний сваренный метод без SWT

Если у вас есть метаданные для каждого изображения, скажем, в xml файле, в котором указано, сколько комнат помечено на каждом изображении, вы можете получить доступ к этому XML файлу, получить данные о том, сколько ярлыков находятся на изображении, и затем сохраните это число в некоторой переменной, скажем, num_of_labels. Теперь возьмите свое изображение и поместите его через цикл while, который размывается с заданной скоростью, которую вы укажете, находя внешние контуры в изображении в каждом цикле и останавливая цикл, если у вас есть такое же количество внешних контуров, что и ваш num_of_labels. Затем просто найдите ограничивающий прямоугольник каждого контура и вы закончите.

# erodes image based on given kernel size (erosion = expands black areas)
def erode( img, kern_size = 3 ):
    retval, img = cv2.threshold(img, 254.0, 255.0, cv2.THRESH_BINARY) # threshold to deal with only black and white.
    kern = np.ones((kern_size,kern_size),np.uint8) # make a kernel for erosion based on given kernel size.
    eroded = cv2.erode(img, kern, 1) # erode your image to blobbify black areas
    y,x = eroded.shape # get shape of image to make a white boarder around image of 1px, to avoid problems with find contours.
    return cv2.rectangle(eroded, (0,0), (x,y), (255,255,255), 1)

# finds contours of eroded image
def prep( img, kern_size = 3 ):    
    img = erode( img, kern_size )
    retval, img = cv2.threshold(img, 200.0, 255.0, cv2.THRESH_BINARY_INV) #   invert colors for findContours
    return cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # Find Contours of Image

# given img & number of desired blobs, returns contours of blobs.
def blobbify(img, num_of_labels, kern_size = 3, dilation_rate = 10):
    prep_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count.
    while len(contours) > num_of_labels:
        kern_size += dilation_rate # add dilation_rate to kern_size to increase the blob. Remember kern_size must always be odd.
        previous = (prep_img, contours, hierarchy)
        processed_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count, again.
    if len(contours) < num_of_labels:
        return (processed_img, contours, hierarchy)
    else:
        return previous

# finds bounding boxes of all contours
def bounding_box(contours):
    bBox = []
    for curve in contours:
        box = cv2.boundingRect(curve)
    bBox.append(box)
    return bBox

Результирующие поля из вышеописанного метода будут иметь место вокруг меток, и это может включать часть исходного дизайна, если ящики применяются к исходному изображению. Чтобы избежать этого, создайте интересующие вас области с помощью новых найденных ящиков и обрезайте пробел. Затем сохраните эту roi-форму в качестве нового поля.

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

Или вы можете попробовать найти контуры оставшегося содержимого после удаления дизайна и объединить ограничивающие прямоугольники в один прямоугольник на основе их расстояния друг от друга.

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


Модуль обнаружения текста сцены в OpenCV 3

Как уже упоминалось в комментариях к вашему вопросу, в opencv уже существует средство обнаружения текста сцены (не распознавание текста документа). Я понимаю, что у вас нет возможности переключать версии, но для тех, у кого есть тот же вопрос и не ограничиваясь более старой версией, я решил включить это в конце. Документацию для обнаружения текста сцены можно найти с помощью простого поиска в Google.

Модуль opencv для обнаружения текста также поставляется с распознаванием текста, который реализует tessaract, который является бесплатным модулем распознавания текста с открытым исходным кодом. Падение tessaract и, следовательно, модуль распознавания текста сцены opencv заключается в том, что он не так утончен, как коммерческие приложения и требует много времени. Таким образом, снижение его производительности, но его бесплатное использование, поэтому его лучшее, что мы получили, не заплатив деньги, если вы хотите также распознавать текст.

Ссылки:

Честно говоря, мне не хватает опыта и опыта как в opencv, так и в обработке изображений, чтобы предоставить подробный способ реализации их модуля обнаружения текста. То же самое с алгоритмом SWT. В последние несколько месяцев я попал в этот материал, но, узнав больше, я отредактирую этот ответ.

Ответ 2

enter image description here

Вот традиционный подход к обработке изображений, использующий только пороговую настройку и контурную фильтрацию:

  • Преобразование изображения в оттенки серого и размытие по Гауссу
  • Адаптивный порог
  • Дилат
  • Поиск контуров и фильтрация с использованием области контуров
  • Выделите ROI и/или извлеките ROI

Использование этого исходного входного изображения (удалены красные линии)

enter image description here

После преобразования изображения в градации серого и размытие по Гауссу мы адаптируем порог для получения двоичного изображения

Затем мы расширяем, чтобы объединить текст в один контур

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

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

enter image description here

Код

import cv2

image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
dilate = cv2.dilate(thresh, kernel, iterations=4)

cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

ROI_number = 0
for c in cnts:
    area = cv2.contourArea(c)
    if area > 10000:
        x,y,w,h = cv2.boundingRect(c)
        cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
        # ROI = image[y:y+h, x:x+w]
        # cv2.imwrite('ROI_{}.png'.format(ROI_number), ROI)
        # ROI_number += 1

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()