Удаление зашумленных линий с изображения

У меня есть изображения, которые зашумлены некоторыми случайными линиями, например, следующим:
введите описание изображения здесь
Я хочу применить к ним некоторую предварительную обработку, чтобы удалить нежелательный шум (линии, искажающие текст), чтобы я мог использовать их с OCR (Tesseract).
Мне пришла в голову идея использовать расширение чтобы удалить шум, затем используйте эрозию, чтобы исправить недостающие части надписи на втором этапе.
Для этого я использовал этот код:

import cv2
import numpy as np

img = cv2.imread('linee.png', cv2.IMREAD_GRAYSCALE)
kernel = np.ones((5, 5), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)
cv2.imwrite('delatedtest.png', img)

К сожалению, расширение не сработало, линии шума все еще существуют.

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

превращение всех черных пикселей с двумя или менее соседними черными пикселями в белые.

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


person test    schedule 03.01.2019    source источник
comment
erode удаляет сначала самые тонкие части ... вы можете увидеть это, если внимательно присмотритесь. Строки примерно такой же толщины, как и ваш текст - если вы размываете / расширяете их, ваш текст исчезнет. обычно вы сначала разрушаете, чтобы избавиться от крошечных вещей, затем снова расширяете, чтобы выжившие снова стали толще ... вы используете их наоборот. Зачем?   -  person Patrick Artner    schedule 03.01.2019
comment
Несмотря на то, что изображение было искажено, пробовали ли вы запустить его через OCR, чтобы проверить результаты?   -  person Wayne Phipps    schedule 03.01.2019
comment
@PatrickArtner Я пробовал сначала использовать дилатацию, а затем эрозию, а также сначала пытался использовать эрозию, а затем дилатацию, но тоже не сработало.   -  person test    schedule 03.01.2019
comment
@WaynePhipps да, я пробовал, но ничего не дало, вывод был пуст   -  person test    schedule 03.01.2019


Ответы (2)


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

Вот небольшая демонстрация того, как вы можете его использовать:

import diplib as dip
import matplotlib.pyplot as pp

img = 1 - pp.imread('/home/cris/tmp/DWRTF.png')
lines = dip.PathOpening(img, length=300, mode={'constrained'})

Здесь мы сначала перевернули изображение, потому что в дальнейшем это упрощает другие вещи. Если не инвертировать, используйте вместо этого закрытие пути. Изображение lines:

lines

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

text = img - lines
text = dip.AreaOpening(text, filterSize=5)

текст

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

lines = lines > 0.5
text = text > 0.5
lines -= dip.BinaryPropagation(text, lines, connectivity=-1, iterations=3)
img[lines] = 0

окончательный результат

person Cris Luengo    schedule 03.01.2019
comment
Извините, у меня вопрос: PyDIP работает отлично, но иногда выдает такую ​​ошибку: Traceback (most recent call last): File "pydoc.py", line 5, in <module> lines = dip.PathOpening(img, length=300, mode={'constrained'}) RuntimeError: Image is not scalar in function: void dip::PathOpening(const dip::Image&, const dip::Image&, dip::Image&, dip::uint, const String&, const StringSet&) (/home/hani/cert/pydip/diplib/src/morphology/pathopening.cpp at line number 396) Вы знаете причину этой ошибки? Может дело в разрешении картинки? - person test; 04.01.2019
comment
@test: «Изображение не скалярное» означает, что изображение имеет более одного канала, но на данный момент в морфологических функциях разрешены только скалярные (одноканальные) изображения. Полагаю, у вас есть изображение в формате RGB. Вы должны преобразовать его в оттенки серого, например, с помощью dip.ColorSpaceManager.Convert(img, 'gray'). - person Cris Luengo; 04.01.2019
comment
Извините за беспокойство, но я получил эту ошибку: RuntimeError: Image's number of tensor elements and color space are inconsistent in function: void dip::ColorSpaceManager::Convert(const dip::Image&, dip::Image&, const String&) const (/home/hani/cert/pydip/diplib/src/color/color.cpp at line number 234). - person test; 04.01.2019
comment
@test: Сделайте img = dip.Image(img). Что теперь возвращается img.TensorElements()? А что возвращает img.ColorSpace()? Может быть, у вас есть 3 тензорных элемента (== каналов), но цветовое пространство - это пустая строка? Если да, сделайте img.SetColorSpace('RGB'), тогда вы сможете преобразовать в серый цвет. Другой вариант - сделать img=img.TensorElement(0), чтобы просто извлечь первый канал. - Очевидно, что эта область еще не очень тщательно тестировалась пользователями. :) Я постараюсь улучшить удобство использования. Спасибо за указание на это! - person Cris Luengo; 04.01.2019
comment
Вы правы, img.TensorElements() вернул 4, а img.ColorSpace() вернул пустую строку. Я сделал, как вы объяснили, и все сработало правильно. Я также преобразовал изображение в оттенки серого с помощью opencv img = cv2.imread('img.png', 0), затем сохранил его, прежде чем использовать PyDIP, и это тоже хорошо сработало. Большое спасибо за вашу помощь и объяснения. - person test; 04.01.2019
comment
@test: Замечательно! Вы также должны иметь возможность напрямую использовать изображение, прочитанное OpenCV в PyDIP, нет необходимости сначала сохранять его. Просто используйте OpenCV imread вместо pyplot imread. - person Cris Luengo; 04.01.2019
comment
Спасибо за то, что сделали эту библиотеку с открытым исходным кодом, и еще раз спасибо за объяснение, как ее использовать! - person test; 04.01.2019

Вы можете сделать это, используя createLineSegmentDetector(), функцию из opencv

import cv2

#Read gray image
img = cv2.imread("lines.png",0)

#Create default parametrization LSD
lsd = cv2.createLineSegmentDetector(0)

#Detect lines in the image
lines = lsd.detect(img)[0] #Position 0 of the returned tuple are the detected lines

#Draw the detected lines
drawn_img = lsd.drawSegments(img,lines)

#Save the image with the detected lines
cv2.imwrite('lsdsaved.png', drawn_img)

введите здесь описание изображения
Следующая часть кода удалит только строки, длина которых превышает 50 пикселей:

for element in lines:

  #If the length of the line is more than 50, then draw a white line on it
  if (abs(int(element[0][0]) - int(element[0][2])) > 50 or abs(int(element[0][1]) - int(element[0][3])) > 50): 

    #Draw the white line
    cv2.line(img, (int(element[0][0]), int(element[0][1])), (int(element[0][2]), int(element[0][3])), (255, 255, 255), 12)

#Save the final image
cv2.imwrite('removedzz.png', img)

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

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

person singrium    schedule 06.01.2019