Отримайте розташування всього тексту, присутнього на зображенні, за допомогою opencv


11

У мене є це зображення, яке містить в ньому текст (цифри та алфавіти). Я хочу отримати розташування всього тексту та цифр, присутніх на цьому зображенні. Також я хочу витягнути весь текст.

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

Як мені отримати кординати, а також весь текст (цифри та алфавіти) на моєму зображенні. Наприклад, 10B, 44, 16, 38, 22B тощо


яка ваша версія тензорфлоу? Якщо ваша версія 2.1, спробуйте встановити 2.0
gellezzz

1
Кидати банатів на погані запитання, це не є хорошою практикою. Ви не показали знань, як це зробити, щоб виглядати, що ви просто намагаєтесь заманювати розробників у кодування повного рішення в обмін на кілька пунктів повторення. З цієї причини я не сподіваюсь побачити ідеальні відповіді, але я вважаю, що ви можете отримати кращі рішення на веб-сайтах-фрілансерах, якщо заплатите людям за їх час.
karlphillip

@karlphillip так шкода, але я початківець, мені потрібно щось почати, правда? Чи можете ви мені допомогти з цим
Пулкіт Бхатнагар,

Відповіді:


13

Ось потенційний підхід із використанням морфологічних операцій для фільтрації нетекстових контурів. Ідея така:

  1. Отримайте двійкове зображення. Завантажте зображення, відтінки сірого, потім поріг Оцу

  2. Видаліть горизонтальні та вертикальні лінії. Створіть горизонтальні та вертикальні ядра за допомогою cv2.getStructuringElementвидалення рядків за допомогоюcv2.drawContours

  3. Видаліть діагональні лінії, об’єкти кола та вигнуті контури. Фільтруйте за допомогою контурної площі cv2.contourArea та наближення контуру, cv2.approxPolyDP щоб виділити нетекстові контури

  4. Витяг текстових рентабелів інвестицій та OCR. Знайдіть контури та фільтруйте ROI, а потім 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()

Гарна ідея спочатку видалити ці рядки.
karlphillip

погана ідея також видаляти частини букв ...
Уолтер Трос

8

Добре, ось ще одне можливе рішення. Я знаю, що ви працюєте з Python - я працюю з C ++. Я дам вам кілька ідей і сподіваюся, якщо ви цього захочете, ви зможете реалізувати цю відповідь.

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

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

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

Ідея полягає в тому, що я фільтрую все, що не належить до попередньо обчислених значень. Я вивчив символи (цифри та літери) і прийшов із мінімальними, максимальними значеннями площі та мінімальним співвідношенням сторін (тут співвідношення між висотою та шириною).

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

//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циклу порівняйте поточні властивості блобу з цими значеннями. Якщо тести позитивні, ви "фарбуєте" крапку в чорний колір. Продовжуючи всередині 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];
    }
}

І ... це майже все. Ви відфільтрували всі елементи, не схожі на те, що шукаєте. Запустивши алгоритм, ви отримаєте такий результат:

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

Крім того, я знайшов обмежувальні коробки крапель, щоб краще візуалізувати результати:

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

Як бачите, деякі елементи виявляються пропущеними. Ви можете вдосконалити "фільтр властивостей", щоб краще визначити шуканих символів. Більш глибоке рішення, що передбачає трохи машинного навчання, вимагає побудови "ідеального вектора ознак", вилучення функцій з крапель і порівняння обох векторів за допомогою міри подібності. Ви можете також застосувати деякі пост -перероблення поліпшити результати ...

Що б не було, чоловіче, твоя проблема не є дрібницею і не легко масштабується, і я просто даю тобі ідеї. Сподіваємось, ви зможете реалізувати своє рішення.


Будь-який шанс ви можете перетворити ту саму програму на python
Pulkit Bhatnagar

@PulkitBhatnagar Так, звичайно. Ви просто тримаєтесь міцно, у мене буде готовий ідеальний порт за кілька хвилин.
eldesgraciado

?? ти це зробив, щоб я міг нагородити тебе
нагородою

Ага так. Мені страшенно шкода, мій пане, я зіткнувся з деякими клопотами, але конверсія йде добре. Тільки ти чекаєш. Дякую.
eldesgraciado

ніколи не замислювався, що це може бути сарказм.
Pulkit Bhatnagar

4

Одним із методів є використання розсувного вікна (Це дорого).

Визначте розмір символів на зображенні (усі символи мають однаковий розмір, як видно на зображенні) та встановіть розмір вікна. Спробуйте tesseract для виявлення (вхідне зображення вимагає попередньої обробки). Якщо вікно виявляє символи послідовно, то зберігайте координати вікна. Об'єднайте координати і отримайте область на символах.


Я думаю, що 100bounty - це відповідь
Himanshu Poddar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.