it-roy-ru.com

OpenCV - удаление шума на изображении

У меня есть изображение здесь с таблицей .. В столбце справа фон заполнен шумом

Как обнаружить участки с шумом? Я только хочу применить какой-то фильтр к частям с шумом, потому что мне нужно сделать OCR для него, и любой вид фильтра уменьшит общее распознавание

А какой фильтр лучше всего удаляет фоновые шумы на изображении?

Как сказал мне нужно сделать OCR на изображении

 enter image description here

18
clarkk

Я пробовал некоторые фильтры/операции в OpenCV, и это, кажется, работает довольно хорошо.

Шаг 1: Расширить изображение - 

kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)

 Dilated Image

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

Шаг 2: Разрушить изображение - 

kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)

 Eroded dilated image

Как видите, шум исчез, однако некоторые символы в других столбцах не работают. Я бы порекомендовал выполнять эти операции только на шумном столбце. Вы можете использовать HoughLines , чтобы найти последний столбец. Затем вы можете извлечь только этот столбец, выполнить расширение + эрозию и заменить его соответствующим столбцом в исходном изображении. Кроме того, расширение + эрозия на самом деле является операцией, называемой close. Это вы можете позвонить напрямую, используя - 

cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

Как предположил @Ermlg, medianBlur с ядром 3 также прекрасно работает.

cv2.medianBlur(img, 3)

 Median Blur

Альтернативный шаг

Как видите, все эти фильтры работают, но лучше применять эти фильтры только в той части, где присутствует шум. Для этого используйте следующее:

edges = cv2.Canny(img, 50, 150, apertureSize = 3) // img is gray here
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, 1000, 50) // last two arguments are minimum line length and max gap between two lines respectively.
for line in lines: 
    for x1, y1, x2, y2 in line: 
        print x1, y1
// This gives the start coordinates for all the lines. You should take the x value which is between (0.75 * w, w) where w is the width of the entire image. This will give you essentially **(x1, y1) = (1896, 766)**

Затем вы можете извлечь эту часть только как:

extract = img[y1:h, x1:w] // w, h are width and height of the image

 Extracted image

Затем примените фильтр (медиана или закрытие) на этом изображении. После устранения шума вам нужно поместить это отфильтрованное изображение вместо размытой части исходного изображения. изображение [y1: h, x1: w] = медиана

Это просто в C++: 

extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))

Окончательный результат с альтернативным методом

 Final Result Надеюсь, поможет!

9
Rick M.

Мое решение основано на пороговой настройке, чтобы получить изображение в 4 шага.

  1. Прочитать изображение по OpenCV 3.2.0
  2. Примените GaussianBlur(), чтобы сгладить изображение, особенно область серым цветом. 
  3. Маска изображения, чтобы изменить текст на белый, а остальные на черный. 
  4. Инвертировать замаскированное изображение в черный текст белым.

Код находится в Python 2.7. Его можно легко изменить на C++.

import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline 

# read Danish doc image 
img = cv2.imread('./imagesStackoverflow/danish_invoice.png')

# apply GaussianBlur to smooth image
blur = cv2.GaussianBlur(img,(5,3), 1) 

# threshhold gray region to white (255,255, 255) and sets the rest to black(0,0,0)
mask=cv2.inRange(blur,(0,0,0),(150,150,150))

# invert the image to have text black-in-white
res = 255 - mask

plt.figure(1)
plt.subplot(121), plt.imshow(img[:,:,::-1]), plt.title('original') 
plt.subplot(122), plt.imshow(blur, cmap='gray'), plt.title('blurred')
plt.figure(2)
plt.subplot(121), plt.imshow(mask, cmap='gray'), plt.title('masked')
plt.subplot(122), plt.imshow(res, cmap='gray'), plt.title('result')
plt.show()

Ниже приведены нанесенные изображения по коду для справки.

 enter image description here

Вот изображение result с разрешением 2197 x 3218 пикселей.

 enter image description here

7
thewaywewere

Как я знаю, медианный фильтр - лучшее решение для снижения шума. Я бы рекомендовал использовать медианный фильтр с окном 3х3. Смотрите функцию cv :: medianBlur ()

Но будьте осторожны при использовании любой фильтрации шума одновременно с OCR. Это может привести к снижению точности распознавания.

Также я бы порекомендовал попробовать использовать пару функций (cv :: erode () и cv :: dlate ()). Но я не уверен, что это будет лучшим решением, чем cv :: medianBlur () с окном 3x3.

3
ErmIg

Я бы пошел с медианным размытием (вероятно, 5 * 5 ядро). 

если вы планируете применить OCR изображение. Я бы посоветовал вам следующее:

  1. Фильтруйте изображение с помощью Median Filter.
  2. Найдя контуры на отфильтрованном изображении, вы получите только текстовые контуры (Назовите ихF).
  3. Найти контуры в исходном изображении (назовите ихO).
  4. изолировать все контуры вO, которые пересекаются с любым контуром вF.

Более быстрое решение:

  1. Найти контуры в исходном изображении.
  2. Фильтруйте их по размеру.
2
Humam Helfawi

Результат:

 enter image description here

2
Antonio

Если время обработки не является проблемой, очень эффективным методом в этом случае будет вычисление всех компонентов, соединенных черным, и удаление тех, которые меньше нескольких пикселей. Это удалит все зашумленные точки (кроме тех, которые касаются допустимого компонента), но сохранит все символы и структуру документа (строки и т.д.).

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

Фактически, этот метод может использоваться для поиска символов, определенных как связанные компоненты заданного минимального и максимального размера и с соотношением сторон в заданном диапазоне.

1
Antonio

Я уже сталкивался с той же проблемой и получил лучшее решение . Преобразовать исходное изображение в изображение в градациях серого и применить fastNlMeanDenoising function, а затем применить threshold .

Вот так - fastNlMeansDenoising (серый, dst, 3.0,21,7); Порог (ДСТ, finaldst, 150255, THRESH_BINARY);

ТАКЖЕ использование может регулировать порог в соответствии с вашим изображением фонового шума. eg- порог (dst, finaldst, 200,255, THRESH_BINARY);

ПРИМЕЧАНИЕ. - Если строки столбцов были удалены ... Вы можете взять маску строк столбцов из исходного изображения и применить к обесцвеченному полученному изображению, используя такие операции BITWISE, как AND, OR, XOR.

1
Amul Mittal

Если вы очень беспокоитесь об удалении пикселей, которые могут повредить обнаружение распознавания текста. Не добавляя артефакты, вы можете быть настолько чистыми, насколько это возможно. Затем вы должны создать BLOB-фильтр. И удалите любые капли, которые меньше n пикселей или около того. 

Не собираюсь писать код, но я знаю, что это прекрасно работает, так как я использую это сам, хотя я не использую openCV (я написал свой собственный многопоточный blobfilter из соображений скорости). И извините, но я не могу поделиться своим кодом здесь. Просто описываю, как это сделать.

1
user3800527