Introducción al procesamiento de imágenes en Python con OpenCV

    Introducción

    En este tutorial, aprenderemos cómo podemos procesar imágenes usando el lenguaje Python. No nos vamos a limitar a una biblioteca o marco; sin embargo, hay uno que usaremos con más frecuencia, el CV de la biblioteca abierta . Comencemos hablando un poco sobre el procesamiento de imágenes y luego avancemos para ver diferentes aplicaciones / escenarios en los que el procesamiento de imágenes puede ser útil. ¡Entonces empecemos!

    ¿Qué es el procesamiento de imágenes?

    Es importante saber qué es exactamente el procesamiento de imágenes y qué papel juega en el panorama general antes de sumergirse en él. El procesamiento de imágenes se conoce comúnmente como «procesamiento de imágenes digitales» y el dominio más utilizado es «visión por computadora». No se confunda, hablaremos sobre estos dos términos y cómo se conectan. Los algoritmos de procesamiento de imágenes y los algoritmos de visión por computadora (CV) aceptan imágenes como entrada; sin embargo, en el procesamiento de imágenes, la salida también es una imagen, pero en la visión por computadora, la salida puede contener algunas características / información sobre la imagen.

    ¿Por qué lo necesitamos?

    Los datos que recopilamos o generamos son en gran parte datos sin procesar, es decir, no son adecuados para su uso en aplicaciones directamente por varias razones posibles. Por lo tanto, primero debemos analizarlo, realizar el preprocesamiento necesario y luego utilizarlo.

    Por ejemplo, supongamos que queríamos construir un clasificador de gatos. Nuestro programa tomaría una imagen como entrada y luego nos diría si hay un gato en la imagen o no. El primer paso para construir este clasificador fue recolectar cientos de imágenes de gatos. Un problema común es que no todas las imágenes que escribimos serán del mismo tamaño / dimensiones, por lo que antes de enviarlas al modelo para el entrenamiento, tendríamos que redimensionarlas / preprocesarlas a un tamaño estándar.

    Esta es solo una de las muchas razones por las que el procesamiento de imágenes es esencial para cualquier aplicación de visión por computadora.

    Prerrequisitos

    Antes de continuar, analicemos lo que necesita para seguir este tutorial con facilidad. Primero, debe tener algunos conocimientos básicos de programación en cualquier idioma. En segundo lugar, debe saber qué es el Machine Learning y los conceptos básicos de cómo funciona, ya que utilizaremos algunos algoritmos de Machine Learning para el procesamiento de imágenes en este artículo. Como beneficio adicional, sería útil si tuviera alguna divulgación básica o conocimiento de Open CV antes de continuar con este tutorial. Pero esto no es obligatorio.

    Una cosa que definitivamente debe saber para seguir este tutorial es cómo se muestra exactamente una imagen en la memoria. Cada imagen está representada por un conjunto de píxeles, es decir, una matriz de valores de píxeles. Para una imagen en escala de grises, los valores de píxeles están entre 0 y 255 e indican la intensidad de esos píxeles. Por ejemplo, si tiene una imagen de 20 x 20 dimensiones, estaría representada por una matriz de 20×20 (valores de 400 píxeles en total).

    Si se trata de una imagen en color, debe saber que hay tres formas: rojo, verde y azul (RGB). Por tanto, habría tres matrices de este tipo para una sola imagen.

    Te puede interesar:Corutinas en Python

    Instalación

    Nota: Dado que vamos a utilizar OpenCV a través de Python, es un requisito implícito que ya tenga Python (versión 3) instalado en su estación de trabajo.

    Windows

    $ pip install opencv-python
    

    Mac OS

    $ brew install opencv3 --with-contrib --with-python3
    

    Linux

    $ sudo apt-get install libopencv-dev python-opencv
    

    Para verificar si su instalación fue exitosa o no, ejecute el siguiente comando en un shell de Python o en el símbolo del sistema:

    import cv2
    

    Algunos conceptos básicos que debe saber

    Antes de pasar a utilizar el procesamiento de imágenes en una aplicación, es importante tener una idea de los tipos de operaciones asociadas con esta categoría y cómo realizar esas operaciones. Estas operaciones, junto con otras operaciones, se utilizarían más adelante en nuestras aplicaciones. Así que hagámoslo.

    Usaremos la siguiente imagen para este artículo:

    Nota : La imagen se ha escalado para mostrarla en este artículo, pero el tamaño original que estamos usando es de aproximadamente 1180×786.

    Probablemente haya notado que la imagen está coloreada actualmente, lo que significa que hay tres formas de color, es decir, rojo, verde y azul. Cambiaremos el tamaño de la imagen a escala de grises, además de dividir la imagen en canales individuales usando el código a continuación.

    Encontrar datos de imagen

    Después de cargar la imagen con la imread()función, podemos recuperar algunas propiedades simples sobre ella, como el número de píxeles y las dimensiones:

    import cv2
    
    img = cv2.imread('rose.jpg')
    
    print("Image Properties")
    print("- Number of Pixels: " + str(img.size))
    print("- Shape/Dimensions: " + str(img.shape))
    

    Salida:

    Te puede interesar:OpenGL avanzado en Python con PyGame y PyOpenGL
    Image Properties
    - Number of Pixels: 2782440
    - Shape/Dimensions: (1180, 786, 3)
    

    División de imágenes en canales individuales

    Ahora dividiremos la imagen en componentes rojo, verde y azul usando OpenCV y los mostraremos:

    from google.colab.patches import cv2_imshow
    
    blue, green, red = cv2.split(img) # Split the image into its channels
    img_gs = cv2.imread('rose.jpg', cv2.IMREAD_GRAYSCALE) # Convert image to grayscale
    
    cv2_imshow(red) # Display the red channel in the image
    cv2_imshow(blue) # Display the red channel in the image
    cv2_imshow(green) # Display the red channel in the image
    cv2_imshow(img_gs) # Display the grayscale version of image
    

    Para mayor nitidez, solo mostraremos la imagen en escala de grises.

    Imagen en escala de grises:

    Umbral de imagen

    El concepto del portal es bastante sencillo. Como se discutió anteriormente en la representación de la imagen, un valor de píxel puede ser cualquier valor entre 0 y 255. Digamos que queremos convertir una imagen en una imagen binaria, es decir, asignando un píxel a un valor de 0 o 1. Para hacer esto, podemos operar un umbral. Por ejemplo, si el valor de umbral (T) es 125, se asignaría un valor de 1 a cada píxel con valores superiores a 125, y se asignaría un valor de 0 a cada píxel con valores menores que o igual a. eso es a través del código para comprender mejor.

    Imagen utilizada para el portal:

    import cv2
    
    # Read image
    img = cv2.imread('image.png', 0)
    
    # Perform binary thresholding on the image with T = 125
    r, threshold = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY)
    cv2_imshow(threshold)
    

    Salida:

    Como puede ver, en la imagen resultante, se establecieron dos regiones, es decir, la región negra (valor de píxel 0) y la región blanca (valor de píxel 1). Resultó que el umbral que establecimos estaba en el centro de la imagen, por lo que los valores de blanco y negro se dividen.

    Solicitud

    # 1: Eliminación de ruido de la imagen

    Ahora que tiene una idea básica de qué es el procesamiento de imágenes y cómo se usa, sigamos adelante y aprendamos sobre algunas de sus funciones específicas.

    Te puede interesar:Implementación de aplicaciones Django en Heroku desde GitHub

    En la mayoría de los casos, los datos sin procesar que recopilamos contienen ruido, es decir, características no deseadas que dificultan la detección de la imagen. Si bien estas imágenes se pueden usar directamente para la extracción de características, la precisión del algoritmo sufriría mucho. Esta es la razón por la que el procesamiento de imágenes se aplica a la imagen antes de enviarla al algoritmo para una mayor precisión.

    Hay muchos tipos diferentes de ruido, como el ruido gaussiano, el ruido de sal y pimienta, etc. Podemos eliminar ese ruido de una imagen aplicando un filtro que elimine ese ruido, o al menos minimice su efecto. También hay muchas opciones de filtros, cada una de las cuales tiene diferentes méritos, por lo que es la mejor para un tipo de ruido en particular.

    Para comprender esto correctamente, vamos a agregar ruido de «sal y pimienta» a la versión en escala de grises de la imagen de la rosa que consideramos anteriormente, y luego intentaremos eliminar ese ruido de nuestra imagen de ruido usando varios filtros y ver cuál es el más adecuado para ese tipo.

    import numpy as np
    
    # Adding salt & pepper noise to an image
    def salt_pepper(prob):
          # Extract image dimensions
          row, col = img_gs.shape
    
          # Declare salt & pepper noise ratio
          s_vs_p = 0.5
          output = np.copy(img_gs)
    
          # Apply salt noise on each pixel individually
          num_salt = np.ceil(prob * img_gs.size * s_vs_p)
          coords = [np.random.randint(0, i - 1, int(num_salt))
                for i in img_gs.shape]
          output[coords] = 1
    
          # Apply pepper noise on each pixel individually
          num_pepper = np.ceil(prob * img_gs.size * (1. - s_vs_p))
          coords = [np.random.randint(0, i - 1, int(num_pepper))
                for i in img_gs.shape]
          output[coords] = 0
          cv2_imshow(output)
    
          return output
    
    # Call salt & pepper function with probability = 0.5
    # on the grayscale image of rose
    sp_05 = salt_pepper(0.5)
    
    # Store the resultant image as 'sp_05.jpg'
    cv2.imwrite('sp_05.jpg', sp_05)
    

    Bien, hemos agregado ruido a nuestra imagen de rosa, y ahora se ve así:

    Imagen ruidosa:

    Ahora aplique diferentes filtros y observe nuestras observaciones, es decir, qué tan bien cada filtro reduce el ruido.

    Filtro aritmético con núcleo afilado
    # Create our sharpening kernel, the sum of all values must equal to one for uniformity
    kernel_sharpening = np.array([[-1,-1,-1],
                                  [-1, 9,-1],
                                  [-1,-1,-1]])
    
    # Applying the sharpening kernel to the grayscale image & displaying it.
    print("nn--- Effects on S&P Noise Image with Probability 0.5 ---nn")
    
    # Applying filter on image with salt & pepper noise
    sharpened_img = cv2.filter2D(sp_05, -1, kernel_sharpening)
    cv2_imshow(sharpened_img)
    

    La imagen resultante se muestra a continuación, de aplicar un filtro aritmético a la imagen con el ruido de sal y pimienta. En comparación con la imagen en escala de grises original, podemos ver que ilumina demasiado la imagen y tampoco puede resaltar los puntos brillantes de la rosa. Por lo tanto, se puede concluir que un filtro aritmético no elimina el ruido de sal y pimienta.

    Salida de filtro aritmético:

    Te puede interesar:Tareas asincrónicas con Flask, Redis y Apio
    Filtro de punto medio
    from scipy.ndimage import maximum_filter, minimum_filter
    
    def midpoint(img):
        maxf = maximum_filter(img, (3, 3))
        minf = minimum_filter(img, (3, 3))
        midpoint = (maxf + minf) / 2
        cv2_imshow(midpoint)
    
    print("nn---Effects on S&P Noise Image with Probability 0.5---nn")
    midpoint(sp_05)
    

    A continuación se muestra la imagen resultante, de aplicar el filtro de punto medio a la imagen con el ruido de sal y pimienta. Cuando se compara con la imagen en escala de grises original, podemos ver que, al igual que el método del núcleo anterior, aclara demasiado la imagen; sin embargo, es capaz de resaltar los puntos brillantes de la rosa. Por tanto, podemos decir que es una mejor opción que el filtro aritmético, pero aún así no recupera por completo la imagen original.

    Salida de filtro de punto medio:

    Medios de filtro contraarmónicos

    Nota : La aplicación de estos filtros se puede encontrar fácilmente en línea y cómo funcionan exactamente está fuera del alcance de este tutorial. Examinaremos las aplicaciones desde un nivel abstracto / superior.

    def contraharmonic_mean(img, size, Q):
        num = np.power(img, Q + 1)
        denom = np.power(img, Q)
        kernel = np.full(size, 1.0)
        result = cv2.filter2D(num, -1, kernel) / cv2.filter2D(denom, -1, kernel)
        return result
    
    print("nn--- Effects on S&P Noise Image with Probability 0.5 ---nn")
    cv2_imshow(contraharmonic_mean(sp_05, (3,3), 0.5))
    

    La imagen resultante, de la aplicación de Contraharmonic Media, se muestra debajo de la imagen con el ruido de sal y pimienta. Cuando se compara con la imagen en escala de grises original, podemos ver que reproducía prácticamente la misma imagen que el original. Su nivel de intensidad / brillo es el mismo y también resalta los puntos brillantes de la rosa. Por lo tanto, podemos concluir que el medio de filtro contraarmónico es muy eficaz para tratar el ruido de sal y pimienta.

    Filtro contraarmónico de salida promedio:

    Ahora que hemos encontrado el mejor filtro para recuperar la imagen original de una imagen de ruido, podemos pasar a la siguiente aplicación.

    # 2: Detección de bordes usando Canny Edge Detector

    La imagen de la rosa que estamos usando hasta ahora tiene un fondo estable, es decir, negro, por lo que usaremos una imagen diferente para esta aplicación para mostrar mejor las capacidades del algoritmo. La razón es que, si el fondo es estable, hace que la tarea de detección de bordes sea relativamente simple, y no queremos eso.

    Hablamos sobre un clasificador de gatos anteriormente en este tutorial, avancemos ese ejemplo y veamos cómo el procesamiento de imágenes juega un papel clave en eso.

    Te puede interesar:Autoencoders para la reconstrucción de imágenes en Python y Keras

    En un algoritmo de clasificación, primero se escanea la imagen en busca de ‘objetos’, es decir, cuando ingresa una imagen, el algoritmo encuentra todos los objetos en esa imagen y luego los compara con las características del objeto que está tratando de ver. Para un clasificador de gatos, compararía todos los objetos que se encuentran en una imagen y las características de una imagen de gato, y si se encuentran sirvientas, nos dice que la imagen de entrada contiene un gato.

    Dado que estamos usando el clasificador de gatos como ejemplo, es justo usar una imagen de gato a medida que avanzamos. A continuación se muestra la imagen que usaremos:

    Imagen utilizada para la detección de bordes:

    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    # Declaring the output graph's size
    plt.figure(figsize=(16, 16))
    
    # Convert image to grayscale
    img_gs = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
    cv2.imwrite('gs.jpg', img_gs)
    
    # Apply canny edge detector algorithm on the image to find edges
    edges = cv2.Canny(img_gs, 100,200)
    
    # Plot the original image against the edges
    plt.subplot(121), plt.imshow(img_gs)
    plt.title('Original Gray Scale Image')
    plt.subplot(122), plt.imshow(edges)
    plt.title('Edge Image')
    
    # Display the two images
    plt.show()
    

    Salida de detección de margen:

    Como puede ver, la parte de la imagen que contiene un objeto, en este caso un gato, está punteada / separada por detección de bordes. Ahora tienes que pensar, qué es el Canny Edge Detector y cómo hizo que esto sucediera; así que hablemos de eso ahora.

    Para comprender lo anterior, hay tres pasos principales que deben discutirse. Primero, reduce el ruido en la imagen de la misma manera que discutimos antes. En segundo lugar, utiliza la primera derivada de cada píxel para obtener bordes. La lógica detrás de esto es que hay un cambio repentino de intensidad, el punto donde hay un borde, que hace un pico en el valor de la primera derivada, y también lo hace ese píxel como un «píxel de borde».

    Al final, alcanza la histéresis; dijimos anteriormente que el valor de la primera derivada es un pico en un borde, pero no mencionamos ‘qué tan alto’ debe ser el pico para ser clasificado como un borde – ¡esto se llama umbral! Anteriormente en este tutorial discutimos qué es un umbral simple. El umbral de histéresis es una mejora, utiliza dos valores de umbral en lugar de uno. La razón de esto es que si el valor de umbral es demasiado alto, podemos perder algunos bordes negativos reales (verdaderos) y si el valor es demasiado bajo, obtendríamos muchos puntos clasificados como bordes no reales (). Cosas falsas positivas) . Un valor de umbral establecido es alto y el otro es bajo. Todos los puntos por encima del ‘valor de umbral alto’ se identifican como márgenes, entonces se consideran todos los puntos por encima del valor de umbral bajo pero por debajo del valor de umbral alto; los puntos que son adyacentes a puntos que se conocen como bordes o que son vecinos se identifican como bordes y el resto se destruye.

    Estos son los conceptos / métodos básicos utilizados por el algoritmo Canny Edge Detector para identificar los bordes en una imagen.

    Te puede interesar:Clasificación de burbujas en Python

    Conclusión

    En este artículo, aprendimos cómo instalar OpenCV, la biblioteca de procesamiento de imágenes más popular en Python, en varias plataformas como Windows, MacOS y Linux, y también cómo verificar que la instalación fue exitosa.

    Continuamos discutiendo qué es el procesamiento de imágenes y sus usos en el campo de la visión por computadora del Machine Learning. Hablamos sobre algunos tipos comunes de ruido y cómo podemos eliminarlo de nuestras imágenes usando varios filtros, antes de usar las imágenes en nuestras aplicaciones.

    Además, aprendimos cómo el procesamiento de imágenes juega un papel clave en aplicaciones de alta gama, como la detección o clasificación de objetos. Tenga en cuenta que este artículo fue solo la punta del iceberg, y hay mucho más en el procesamiento de imágenes digitales en la tienda que posiblemente no se puedan cubrir en un tutorial. Esto debería permitirle profundizar y aprender sobre otros conceptos avanzados relacionados con el procesamiento de imágenes. ¡Buena suerte!

     

    5/5 - (2 votos)

    Etiquetas: