c++ - OpenCV-Eliminación de ruido en imagen.
image-processing noise-reduction (9)
Como sé, el filtro mediano es la mejor solución para reducir el ruido. Yo recomendaría usar filtro de mediana con ventana de 3x3. Ver función cv::medianBlur() .
Pero tenga cuidado cuando utilice cualquier filtración de ruido simultáneamente con OCR. Su puede conducir a la disminución de la precisión de reconocimiento.
También recomendaría probar el par de funciones (cv :: erode () y cv :: dilate ()). Pero no estoy seguro de que sea la mejor solución que cv :: medianBlur () con la ventana 3x3.
Tengo una imagen aquí con una tabla. En la columna de la derecha, el fondo está lleno de ruido.
¿Cómo detectar las zonas con ruido? Solo quiero aplicar algún tipo de filtro en las partes con ruido porque necesito hacer OCR en él y cualquier tipo de filtro reducirá el reconocimiento general
¿Y qué tipo de filtro es el mejor para eliminar el ruido de fondo en la imagen?
Como he dicho, necesito hacer OCR en la imagen.
Intenta poner un umbral a la imagen como esta. Asegúrate de que tu src
esté en escala de grises. Este método solo retendrá los píxeles que están entre 150 y 255 de intensidad.
threshold(src, output, 150, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Es posible que desee invertir la imagen mientras intenta negar los píxeles grises. Después de la operación, inviértalo nuevamente para obtener el resultado deseado.
Iría con desenfoque medio (probablemente 5 * 5 kernel).
Si está planeando aplicar OCR a la imagen. Te aconsejaría lo siguiente:
- Filtra la imagen usando el filtro mediano.
- Encuentre contornos en la imagen filtrada, solo obtendrá contornos de texto (Llámelos F ).
- Encuentra contornos en la imagen original (llámalos O ).
- aísle todos los contornos en O que tengan intersección con cualquier contorno en F.
Solución más rápida:
- Encuentra contornos en la imagen original.
- Filtrarlos en función del tamaño.
Mi solución se basa en los umbrales para obtener la imagen resultante en 4 pasos.
- Leer imagen por
OpenCV 3.2.0
. - Aplique
GaussianBlur()
para suavizar la imagen, especialmente la región en color gris. - Enmascara la imagen para cambiar el texto a blanco y el resto a negro.
- Invertir la imagen enmascarada en texto negro en blanco.
El código está en Python 2.7
. Se puede cambiar a C++
fácilmente.
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# read Danish doc image
img = cv2.imread(''./images/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()
Lo siguiente son las imágenes trazadas por el código de referencia.
Aquí está la imagen del resultado en 2197 x 3218 píxeles.
Probé algunos filtros / operaciones en OpenCV y parece funcionar bastante bien.
Paso 1: Dilatar la imagen -
kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)
Como se ve, el ruido se ha ido pero los caracteres son muy ligeros, así que erosioné la imagen.
Paso 2: erosionar la imagen -
kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)
Como puede ver, el ruido desaparece, sin embargo, algunos caracteres de las otras columnas están rotos. Recomendaría ejecutar estas operaciones solo en la columna ruidosa. Es posible que desee utilizar HoughLines para encontrar la última columna. Luego puede extraer esa columna solamente, ejecutar dilatación + erosión y reemplazarla con la columna correspondiente en la imagen original. Además, la dilatación + erosión es en realidad una operación llamada cierre . Esto se puede llamar directamente usando -
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
Como sugirió @Ermlg, medianBlur con un núcleo de 3 también funciona maravillosamente.
cv2.medianBlur(img, 3)
Paso alternativo
Como puede ver, todos estos filtros funcionan, pero es mejor si implementa estos filtros solo en la parte donde está el ruido. Para hacer eso, usa lo siguiente:
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)**
Entonces, puedes extraer esta parte solo como:
extract = img[y1:h, x1:w] // w, h are width and height of the image
Luego, implementa el filtro (mediana o cierre) en esta imagen. Después de eliminar el ruido, debe colocar esta imagen filtrada en lugar de la parte borrosa en la imagen original. imagen [y1: h, x1: w] = mediana
Esto es sencillo en C ++:
extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))
Resultado final con método alternativo
Si el tiempo de procesamiento no es un problema, un método muy efectivo en este caso sería calcular todos los componentes conectados en negro y eliminar aquellos más pequeños que unos pocos píxeles. Eliminaría todos los puntos ruidosos (aparte de los que tocan un componente válido), pero conservaría todos los caracteres y la estructura del documento (líneas, etc.).
La función a utilizar estaría connectedComponentWithStats con THRESH_BINARY_INV
(antes de que probablemente necesite producir la imagen negativa, la función de threshold con THRESH_BINARY_INV
funcionaría en este caso), dibujando rectángulos blancos donde se encontraran pequeños componentes conectados.
De hecho, este método podría usarse para encontrar caracteres, definidos como componentes conectados de un tamaño mínimo y máximo dado, y con una relación de aspecto en un rango determinado.
Si está muy preocupado de eliminar píxeles que podrían dañar su detección de OCR. Sin agregar artefactos, sé tan puro como el original. Entonces deberías crear un filtro de burbujas. Y elimine cualquier blobs que sea más pequeño que n píxeles aproximadamente.
No voy a escribir código, pero sé que esto funciona muy bien ya que lo uso yo mismo, aunque no uso openCV (escribí mi propio blobfilter multihilo por razones de velocidad). Y lo siento pero no puedo compartir mi código aquí. Sólo describiendo cómo hacerlo.
Ya había enfrentado el mismo problema y obtuve la mejor solución. Convierta la imagen de origen a una imagen en escala de grises y aplique la función fastNlMeanDenoising y luego aplique el umbral .
Así: fastNlMeansDenoising (grey, dst, 3.0,21,7); umbral (dst, finaldst, 150,255, THRESH_BINARY);
El uso TAMBIÉN puede ajustar el umbral según la imagen de ruido de fondo por ejemplo, umbral (dst, finaldst, 200,255, THRESH_BINARY);
NOTA: si se eliminaron las líneas de columna ... Puede tomar una máscara de líneas de columna de la imagen de origen y puede aplicarlas a la imagen resultante sin ruido utilizando las operaciones BITWISE como AND, OR, XOR.