Удалить буквенные артефакты из изображения - PullRequest
2 голосов
/ 09 октября 2019

У меня есть подмножества изображений, которые содержат цифры. Каждое подмножество читается Tesseract для OCR. К сожалению, для некоторых изображений обрезка исходного изображения не является оптимальной.

enter image description here

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

enter image description here

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

import cv2
inp = cv2.imread("testing_file.tif")
inp = cv2.cvtColor(inp, cv2.COLOR_BGR2GRAY)
_,inp = cv2.threshold(inp, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

ax = inp.shape[1]
ay = inp.shape[0]

out = inp.copy()
for i in range(ax):
    j = 0
    while j in range(ay):
        if out[j,i] == 255:
            out[j,i] = 0
        else:
            break
        j+=1

out = cv2.bitwise_not(out)    
cv2.imwrite('output.png',out)

Но результат совсем не хорош:

enter image description here

ТогдаЯ наткнулся на функцию flood_fill от scipy ( здесь ), но обнаружил, что это слишком много времени и все еще неэффективно. Аналогичный вопрос был задан на SO здесь , но он не очень помог. Может быть, можно рассмотреть подход к ближайшему соседу? Я также обнаружил, что методы, которые заключаются в объединении соседних пикселей по некоторым критериям, называются растущими методами, среди которых наиболее распространена одиночная связь ( здесь ).

Что бы вы порекомендовали удалить верхний и нижний артефакты?

1 Ответ

2 голосов
/ 09 октября 2019

Вот простой подход:

  • Преобразование изображения в оттенки серого
  • Порог Оцу для получения двоичного изображения
  • Сковать специальное горизонтальное ядро ​​и расширить
  • Обнаружение горизонтальных линий, сортировка по наибольшему контуру и рисование на маске
  • Побитовый и

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

enter image description here

# Read in image, convert to 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]

Далее мы создаем длинное горизонтальное ядро ​​и расширяем его, чтобы соединить числа вместе

enter image description here

# Create special horizontal kernel and dilate 
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (70,1))
dilate = cv2.dilate(thresh, horizontal_kernel, iterations=1)

Отсюда мы обнаруживаем горизонтальные линии и сортируем по наибольшему контуру. Идея состоит в том, что наибольшим контуром будет средняя часть чисел, где все числа «завершены». Любые меньшие контуры будут частичными или обрезанными числами, поэтому мы отфильтруем их здесь. Мы рисуем этот наибольший контур на маске

enter image description here

# Detect horizontal lines, sort for largest contour, and draw on mask
mask = np.zeros(image.shape, dtype=np.uint8)
detected_lines = cv2.morphologyEx(dilate, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    cv2.drawContours(mask, [c], -1, (255,255,255), -1)
    break

Теперь, когда у нас есть контур желаемых чисел, мы просто поразрядно - и с нашимиИсходное изображение и цвет фона белый, чтобы получить наш результат

enter image description here

# Bitwise-and to get result and color background white
mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image,image,mask=mask)
result[mask==0] = (255,255,255)

Полный код для полноты

import cv2
import numpy as np

# Read in image, convert to 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]

# Create special horizontal kernel and dilate 
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (70,1))
dilate = cv2.dilate(thresh, horizontal_kernel, iterations=1)

# Detect horizontal lines, sort for largest contour, and draw on mask
mask = np.zeros(image.shape, dtype=np.uint8)
detected_lines = cv2.morphologyEx(dilate, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    cv2.drawContours(mask, [c], -1, (255,255,255), -1)
    break

# Bitwise-and to get result and color background white
mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image,image,mask=mask)
result[mask==0] = (255,255,255)

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('result', result)
cv2.waitKey()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...