Очищення зображення для OCR


9

Я намагався очистити зображення для OCR: (рядки)

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

Мені потрібно видалити ці рядки, щоб іноді ще більше обробити зображення, і я дуже близький, але багато часу поріг забирає занадто багато тексту:

    copy = img.copy()
    blur = cv2.GaussianBlur(copy, (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=2)

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

    for c in cnts:
        area = cv2.contourArea(c)
        if area > 300:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(copy, (x, y), (x + w, y + h), (36,255,12), 3)

Редагувати: Крім того, використання постійних чисел не буде працювати в разі зміни шрифту. Чи є загальний спосіб зробити це?


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

@YvesDaoust Як можна було б знайти близькість персонажів? (оскільки фільтрація лише за розміром багато разів змішується з символами)
K41F4r

1
Ви могли знайти для кожної краплі відстань до свого найближчого сусіда. Тоді за допомогою гістограмного аналізу відстаней ви знайдете поріг між «близьким» та «окремим» (щось на зразок режиму розподілу) або між «оточеним» та «ізольованим».
Ів Дауст

У випадку кількох малих ліній поруч один з одним, чи не буде їх найближчим сусідом інша мала лінія? Чи буде обчислення середньої відстані до всіх інших крапок занадто дорогим?
K41F4r

"Чи не буде їх найближчим сусідом інший невеликий рядок?": хороший заперечення, ваша честь. Насправді купа близьких коротких сегментів не відрізняється від законного тексту, хоч і зовсім неправдоподібним розташуванням. Можливо, вам доведеться перегрупувати фрагменти ламаних ліній. Я не впевнений, що врятує вас середня відстань до всіх.
Ів Дауст

Відповіді:


14

Ось ідея. Ми розбиваємо цю проблему на кілька кроків:

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

  2. Видаліть великі сторонні контури. Знову повторюємо контури і видаляємо великі контури, якщо вони 5xперевищують середню площу контуру, заповнюючи контур. Замість використання фіксованої порогової області ми використовуємо цей динамічний поріг для більшої надійності.

  3. Діляйте вертикальне ядро ​​для з'єднання символів . Ідея - скористатися спостереженням, що символи вирівнюються у стовпцях. Розширюючи вертикальне ядро, ми з'єднуємо текст разом, щоб шум не включався до цього комбінованого контуру.

  4. Видаліть невеликий шум . Тепер, коли текст, який потрібно зберегти, пов'язаний, ми знаходимо контури та видаляємо контури, менші за 4xсередню площу контуру.

  5. Побітові - і для реконструкції зображення . Оскільки у нас є лише бажані контури, щоб зберегти нашу маску, ми побіжно - і зберегли текст і отримаємо результат.


Ось візуалізація процесу:

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

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

Далі ми будуємо вертикальне ядро і розширюємо для з'єднання символів. Цей крок з'єднує весь потрібний текст, щоб утримати та ізолювати шум в окремих крапках.

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

Тепер ми знаходимо контури і фільтруємо за допомогою контурної області для видалення невеликого шуму

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

Тут усі вилучені шумові частинки, виділені зеленим кольором

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

Результат

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

Код

import cv2

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

# Determine average contour area
average_area = [] 
cnts = cv2.findContours(thresh, 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 = w * h
    average_area.append(area)

average = sum(average_area) / len(average_area)

# Remove large lines if contour area is 5x bigger then average contour area
cnts = cv2.findContours(thresh, 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 = w * h
    if area > average * 5:  
        cv2.drawContours(thresh, [c], -1, (0,0,0), -1)

# Dilate with vertical kernel to connect characters
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

# Remove small noise if contour area is smaller than 4x average
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < average * 4:
        cv2.drawContours(dilate, [c], -1, (0,0,0), -1)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=dilate)
result[dilate==0] = (255,255,255)

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

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


1
Якщо більший шрифт не видалить і текст?
K41F4r

Так, можливо, тому вам доведеться коригувати значення порогової площі. Для більш динамічного підходу ідея полягає у визначенні середньої площі символів, а потім використовувати це як поріг
Натансі

Здається, занадто специфічним для прикладу, використання середньої площі все ще видалить текст багато часу, що погіршить результат для OCR
K41F4r

Чи є у вас інший приклад вхідного зображення, яке ви можете додати до публікації?
Натансі

1
Знайти рішення, яке працює у будь-яких ситуаціях, використовуючи традиційні методи обробки зображень, досить складно. Ви можете вивчити підготовку власного класифікатора за допомогою глибокого навчання. Удачі!
Натансі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.