OpenCV - удаление шума в изображении
У меня есть изображение здесь со столом. В столбце справа фон заполнен шумом
Как определить области с шумом? Я только хочу применить какой-то фильтр на деталях с шумом, потому что мне нужно сделать OCR на нем, и любой фильтр уменьшит общее распознавание.
И какой фильтр лучше всего удалять фоновый шум на изображении?
Как сказано, мне нужно сделать OCR на изображении
![введите описание изображения здесь]()
Ответы
Ответ 1
Я пробовал некоторые фильтры/операции в OpenCV, и, похоже, он работает очень хорошо.
Шаг 1: Развернуть изображение -
kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)
![Dilated Image]()
Как вы видите, шум ушел, но символы очень легкие, поэтому я подорвал изображение.
Шаг 2: Erode изображение -
kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)
![Разъеденное расширенное изображение]()
Как вы можете видеть, шум ушел, однако некоторые символы на других столбцах сломаны. Я бы рекомендовал использовать эти операции только для столбца с помехами. Вы можете использовать HoughLines, чтобы найти последний столбец. Затем вы можете извлечь только этот столбец, запустить dilation + erosion и заменить его соответствующим столбцом исходного изображения.
Кроме того, dilation + erosion - это операция, называемая закрытие. Это можно было бы вызвать напрямую -
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
Как предположил @Ermlg, медианный Блур с ядром 3 также прекрасно работает.
cv2.medianBlur(img, 3)
![Среднее размытие]()
Альтернативный шаг
Как вы можете видеть, все эти фильтры работают, но лучше использовать эти фильтры только в той части, где есть шум. Для этого используйте следующее:
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
![Извлеченное изображение]()
Затем реализуем фильтр (медиана или закрытие) на этом изображении. После удаления шума вам нужно поместить это отфильтрованное изображение вместо размытой части исходного изображения. изображение [y1: h, x1: w] = медиана
Это просто в С++:
extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))
Конечный результат с альтернативным методом
Надеюсь, это поможет!
Ответ 2
Мое решение основано на пороговом значении для получения полученного изображения за 4 шага.
- Считать изображение
OpenCV 3.2.0
.
- Примените
GaussianBlur()
, чтобы сгладить изображение, особенно в сером цвете.
- Маска изображения для изменения текста на белый, а остальное - на черный.
- Инвертировать замаскированное изображение в черный текст в белый цвет.
Код находится в 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()
Ниже приведены построенные изображения с помощью кода для справки.
![введите описание изображения здесь]()
Вот изображение результата с разрешением 2197 x 3218 пикселей.
![введите описание изображения здесь]()
Ответ 3
Как я знаю, срединный фильтр - лучшее решение для снижения шума. Я бы рекомендовал использовать медианный фильтр с окном 3х3. См. Функцию cv:: medianBlur().
Но будьте осторожны при использовании любой фильтрации шума одновременно с OCR. Это может привести к снижению точности распознавания.
Также я бы рекомендовал попробовать использовать пару функций (cv:: erode() и cv:: dilate()). Но я не уверен, что это лучшее решение, а cv:: medianBlur() с окном 3x3.
Ответ 4
Я бы пошел со средним размытием (возможно, 5 * 5 ядром).
если вы планируете применять OCR изображение. Я бы посоветовал вам следующее:
- Отфильтруйте изображение с помощью медианного фильтра.
- Найдите контуры в отфильтрованном изображении, вы получите только текстовые контуры (назовите их F).
- Найти контуры в исходном изображении (назовите их O).
- изолировать все контуры в O, которые имеют пересечение с любым контуром в F.
Быстрое решение:
- Найти контуры в исходном изображении.
- Отфильтруйте их по размеру.
Ответ 5
Результат:
![введите описание изображения здесь]()
Ответ 6
Если вы очень обеспокоены удалением пикселей, которые могут повредить обнаружение OCR. Без добавления артефактов ea быть как можно более чистым к оригиналу. Затем вы должны создать фильтр blob. И удалите любые капли, которые меньше, чем n пикселей или около того.
Не собираюсь писать код, но я знаю, что это отлично работает, поскольку я использую это сам, хотя я не использую openCV (я написал свой собственный многопоточный blobfilter из соображений скорости). И извините, но я не могу поделиться своим кодом здесь. Просто описывая, как это сделать.
Ответ 7
Если время обработки не является проблемой, очень эффективным методом в этом случае было бы вычисление всех черных подключенных компонентов и удаление меньших, чем несколько пикселей. Он удалит все шумные точки (кроме тех, которые касаются действительного компонента), но сохранит все символы и структуру документа (строки и т.д.).
Функция для использования будет connectedComponentWithStats (прежде чем вам, вероятно, понадобится создать отрицательное изображение, threshold с THRESH_BINARY_INV
будет работать в этом случае), рисуя белые прямоугольники, где обнаруживаются небольшие подключенные компоненты.
Фактически, этот метод можно было бы использовать для поиска символов, определяемых как подключенные компоненты заданного минимального и максимального размера, и с отношением сторон в заданном диапазоне.
Ответ 8
Попробуйте установить изображение таким образом. Убедитесь, что ваш src
находится в оттенках серого. Этот метод будет сохранять только пиксели, которые составляют от 150 до 255.
threshold(src, output, 150, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Возможно, вы захотите инвертировать изображение, поскольку вы пытаетесь свести на нет серые пиксели. После операции инвертируйте ее снова, чтобы получить желаемый результат.