Получить местоположение всего текста, присутствующего в изображении, используя opencv

11

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

введите описание изображения здесь

Как я могу получить кординаты, а также весь текст (цифры и алфавиты) в моем изображении. Например, 10B, 44, 16, 38, 22B и т. Д.

Пулькит Бхатнагар
источник
какая у вас версия tenorflow? Если у вас версия 2.1, попробуйте установить 2.0
gellezzz
1
Бросать щедрость на плохие вопросы - не очень хорошая практика. Вы не показали, как это сделать, и похоже, что вы просто пытаетесь заманить разработчиков к написанию законченного решения в обмен на несколько повторений. Я не ожидаю увидеть идеальные ответы по этой причине, но я верю, что вы можете получить лучшие решения на внештатных веб-сайтах, если будете платить людям за их время.
Карлфиллип
@karlphillip очень жаль, но я новичок, мне нужно что-то начать, верно? Можете ли вы помочь мне с этим
Пулькит Бхатнагар

Ответы:

13

Вот потенциальный подход с использованием морфологических операций для фильтрации нетекстовых контуров. Идея заключается в следующем:

  1. Получить бинарное изображение. Загрузите изображение, оттенки серого, затем порог Оцу

  2. Удалить горизонтальные и вертикальные линии. Создать горизонтальные и вертикальные ядра, используя cv2.getStructuringElementзатем удалить линии сcv2.drawContours

  3. Удалите диагональные линии, круговые объекты и изогнутые контуры. Фильтруйте, используя область cv2.contourArea контура и аппроксимацию контура, cv2.approxPolyDP чтобы изолировать нетекстовые контуры

  4. Извлечение текста ROI и OCR. Найти контуры и фильтр для областей интереса, а затем OCR, используя Pytesseract .


Убраны горизонтальные линии, выделенные зеленым

введите описание изображения здесь

Убраны вертикальные линии

введите описание изображения здесь

Удалены разные нетекстовые контуры (диагональные линии, круглые объекты и кривые)

введите описание изображения здесь

Обнаруженные текстовые области

введите описание изображения здесь

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()
nathancy
источник
Хорошая идея сначала удалить эти строки.
Карлфиллип
плохая идея также удалять части писем ...
Уолтер Тросс
8

Хорошо, вот еще одно возможное решение. Я знаю, что вы работаете с Python - я работаю с C ++. Я дам вам несколько идей и, надеюсь, если вы захотите, вы сможете реализовать этот ответ.

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

Я пытаюсь не использовать предварительную обработку, потому что: 1) фильтры и морфологические стадии могут ухудшить качество сгустков и 2) ваши целевые сгустки обладают некоторыми характеристиками, которые мы могли бы использовать, в основном: соотношение сторон и площадь .

Проверьте это, цифры и буквы кажутся выше, чем шире ... кроме того, они кажутся разными в пределах определенного значения площади. Например, вы хотите отбросить объекты "слишком широкие" или "слишком большие" .

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

Давайте работать над алгоритмом. Начните с чтения изображения и изменения его размера до половины размеров. Ваше изображение слишком велико. Преобразовать в оттенки серого и получить двоичное изображение через отсу, вот в псевдокоде:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

Круто. Мы будем работать с этим изображением. Вам нужно изучить каждый белый шарик и применить «фильтр свойств» . Я использую связанные компоненты со статистикой, чтобы пройти через каждый большой объект и получить его площадь и соотношение сторон, в C ++ это делается следующим образом:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

Теперь мы применим фильтр свойств. Это всего лишь сравнение с заранее рассчитанными порогами. Я использовал следующие значения:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

Внутри вашего forцикла сравните текущие свойства BLOB-объектов с этими значениями. Если тесты положительные, вы «рисуете» черную каплю. Продолжая внутри forцикла:

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

После цикла создайте отфильтрованное изображение:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

И ... вот и все. Вы отфильтровали все элементы, которые не похожи на то, что вы ищете. Запустив алгоритм вы получите такой результат:

введите описание изображения здесь

Я также нашел Bounding Boxs от BLOB-объектов, чтобы лучше визуализировать результаты:

введите описание изображения здесь

Как видите, некоторые элементы не обнаружены. Вы можете уточнить «фильтр свойств», чтобы лучше идентифицировать символы, которые вы ищете. Более глубокое решение, включающее немного машинного обучения, требует построения «идеального вектора признаков», извлечения признаков из BLOB-объектов и сравнения обоих векторов с помощью меры сходства. Вы можете также применить некоторые пост -переработкой улучшить результаты ...

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

eldesgraciado
источник
Любой шанс, что вы можете конвертировать ту же программу в Python
Pulkit Bhatnagar
@PulkitBhatnagar Да, конечно. Просто держись крепче, через несколько минут у меня будет отличный порт.
eldesgraciado
?? ты сделал это, чтобы я мог присудить тебе
награду
О да. Мне очень жаль, сэр, у меня возникли некоторые проблемы, но преобразование идет хорошо. Просто подожди. Спасибо.
eldesgraciado
Никогда не задумывался, что это может быть сарказмом.
Пулькит Бхатнагар
4

Одним из способов является использование раздвижного окна (это дорого).

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

saveearth
источник
Я думаю, что 100bounty за ответ
Химаншу Поддар