Saltar al contenido

Segmentación de caracteres de matrículas

Ya no busques más por todo internet porque llegaste al espacio perfecto, contamos con la respuesta que necesitas hallar y sin problemas.

Solución:

Antes de comenzar, sé que está buscando una implementación de este algoritmo en OpenCV C ++, pero mi algoritmo requiere la FFT y la numpy / scipy los paquetes son increíbles para eso. Como tal, te daré una implementación del algoritmo en OpenCV. usando Python en lugar de. En realidad, el código es bastante similar a la API de C ++, por lo que puede transcribirlo fácilmente en su lugar. De esa manera, minimiza la cantidad de tiempo que me llevará aprender (o más bien volver a aprender …) la API y prefiero darte el algoritmo y los pasos que hice para realizar esta tarea para no perder tiempo en absoluto. .

Como tal, les daré una descripción general de lo que haría. Luego te mostraré el código Python que usa numpy, scipy y los paquetes OpenCV. Como beneficio adicional para aquellos que usan MATLAB, les mostraré el equivalente de MATLAB, ¡con código MATLAB para arrancar!


Lo que puede hacer es intentar utilizar un filtrado homomórfico. En términos básicos, podemos representar una imagen en términos de un producto de iluminación y reflectancia. Se supone que la iluminación varía lentamente y es el principal contribuyente del rango dinámico. Este es esencialmente contenido de baja frecuencia. La reflectancia representa los detalles de los objetos y se supone que varía rápidamente. Este es también el principal contribuyente al contraste local y es esencialmente contenido de alta frecuencia.

La imagen se puede representar como un producto de estos dos. El filtrado homomórfico intenta y divide estos componentes y los filtramos individualmente. Luego combinamos los resultados juntos cuando hayamos terminado. Como se trata de un modelo multiplicativo, se acostumbra utilizar un Iniciar sesión operación para que podamos expresar el producto como una suma de dos términos. Estos dos términos se filtran individualmente, se escalan para enfatizar o restar énfasis a sus contribuciones a la imagen, se suman y luego se toma el anti-log.

El sombreado se debe a la iluminación, por lo que lo que podemos hacer es disminuir la contribución que este sombreado hace sobre la imagen. También podemos aumentar la reflectancia para que podamos obtener mejores bordes, ya que los bordes están asociados con información de alta frecuencia.

Por lo general, filtramos la iluminación con un filtro de paso bajo, mientras que la reflectancia con un filtro de paso alto. En este caso, elegiré un kernel gaussiano con un sigma de 10 como filtro de paso bajo. Se puede obtener un filtro de paso alto tomando 1 y restar con el filtro de paso bajo. Transformo la imagen en el dominio de registro, luego filtro la imagen en el dominio de frecuencia utilizando los filtros de paso bajo y paso alto. Luego escalo los resultados de paso bajo y paso alto, agrego estos componentes nuevamente y luego tomo el anti-registro. Esta imagen ahora es más adecuada para el umbral, ya que la imagen tiene poca variación.

Lo que hago como posprocesamiento adicional es umbralizar la imagen. Las letras son más oscuras que el fondo general, por lo que cualquier píxel que sea inferior a un cierto umbral se clasificará como texto. Elegí que el umbral fuera la intensidad 65. Después de esto, también borro los píxeles que están tocando el borde, luego elimino cualquier área de la imagen que tenga menos de 160 (MATLAB) o 120 (Python) píxeles de área total. También recorto algunas de las columnas de la imagen, ya que no son necesarias para nuestro análisis.


Aquí hay un par de advertencias para ti:

Advertencia n. ° 1: eliminación de bordes

Eliminar cualquier píxel que toque el borde es no integrado en OpenCV. Sin embargo, MATLAB tiene un equivalente llamado imclearborder. Usaré esto en mi código MATLAB, pero para OpenCV, este fue el siguiente algoritmo:

  • Encuentra todos los contornos en la imagen
  • Para cada contorno que está en la imagen, verifique si alguno de los píxeles del contorno está dentro del borde de la imagen.
  • Si hay alguno, marque este contorno para eliminarlo.
  • Para cada contorno que queramos eliminar, simplemente dibuje todo este contorno en negro.

Creé un método llamado imclearborder(imgBW, radius) en mi código, donde radius es la cantidad de píxeles dentro del borde que desea borrar.

Advertencia n. ° 2: eliminar áreas de píxeles debajo de un área determinada

Eliminar cualquier área en la que sea inferior a una cierta cantidad también es no implementado en OpenCV. En MATLAB, esto se da convenientemente usando bwareaopen. El algoritmo básico para esto es:

  • Encuentra todos los contornos en la imagen
  • Analice cuánto se llena el área de cada contorno si tuviera que llenar el interior
  • Cualquier área que sea menor a una cierta cantidad, borre este contorno llenando el interior con negro

Creé un método llamado bwareaopen(imgBW) que hace esto por nosotros.

Advertencia n. ° 3: parámetro de área para eliminar áreas de píxeles

Para el código Python, tuve que jugar con este parámetro y me conformé con 120. 160 se usó para MATLAB. Para Python, 120 se deshizo de algunos de los caracteres, lo que no es deseado. Supongo que mi implementación de bwareaopen en comparación con MATLAB es diferente, por lo que probablemente obtengo resultados diferentes.


Sin más preámbulos, aquí está el código. Toma nota que no usé filtrado espacial. Podrías usar filter2D en OpenCV y convolucionar esta imagen con el kernel gaussiano, pero no lo hice porque el filtrado homomórfico cuando se utilizan filtros de paso bajo y paso alto se realiza tradicionalmente en el dominio de la frecuencia. Podrías explorar esto usando filtrado espacial, pero también tendrías que conocer el Talla de sus granos de antemano. Con el filtrado de dominio de frecuencia, solo necesita conocer la desviación estándar del filtro, y ese es solo un parámetro en comparación con dos.

Además, para el código Python, descargué su imagen en mi computadora y ejecuté el script. Para MATLAB, puede hacer referencia directamente al hipervínculo a la imagen cuando la lee con la caja de herramientas de procesamiento de imágenes.


Código Python

import cv2 # For OpenCV modules (For Image I/O and Contour Finding)
import numpy as np # For general purpose array manipulation
import scipy.fftpack # For FFT2 

#### imclearborder definition

def imclearborder(imgBW, radius):

    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # Get dimensions of image
    imgRows = imgBW.shape[0]
    imgCols = imgBW.shape[1]    

    contourList = [] # ID list of contours that touch the border

    # For each contour...
    for idx in np.arange(len(contours)):
        # Get the i'th contour
        cnt = contours[idx]

        # Look at each point in the contour
        for pt in cnt:
            rowCnt = pt[0][1]
            colCnt = pt[0][0]

            # If this is within the radius of the border
            # this contour goes bye bye!
            check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows-1-radius and rowCnt < imgRows)
            check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols-1-radius and colCnt < imgCols)

            if check1 or check2:
                contourList.append(idx)
                break

    for idx in contourList:
        cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### bwareaopen definition
def bwareaopen(imgBW, areaPixels):
    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # For each contour, determine its total occupying area
    for idx in np.arange(len(contours)):
        area = cv2.contourArea(contours[idx])
        if (area >= 0 and area <= areaPixels):
            cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### Main program

# Read in image
img = cv2.imread('5DnwY.jpg', 0)

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Remove some columns from the beginning and end
img = img[:, 59:cols-20]

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Convert image to 0 to 1, then do log(1 + I)
imgLog = np.log1p(np.array(img, dtype="float") / 255)

# Create Gaussian mask of sigma = 10
M = 2*rows + 1
N = 2*cols + 1
sigma = 10
(X,Y) = np.meshgrid(np.linspace(0,N-1,N), np.linspace(0,M-1,M))
centerX = np.ceil(N/2)
centerY = np.ceil(M/2)
gaussianNumerator = (X - centerX)**2 + (Y - centerY)**2

# Low pass and high pass filters
Hlow = np.exp(-gaussianNumerator / (2*sigma*sigma))
Hhigh = 1 - Hlow

# Move origin of filters so that it's at the top left corner to
# match with the input image
HlowShift = scipy.fftpack.ifftshift(Hlow.copy())
HhighShift = scipy.fftpack.ifftshift(Hhigh.copy())

# Filter the image and crop
If = scipy.fftpack.fft2(imgLog.copy(), (M,N))
Ioutlow = scipy.real(scipy.fftpack.ifft2(If.copy() * HlowShift, (M,N)))
Iouthigh = scipy.real(scipy.fftpack.ifft2(If.copy() * HhighShift, (M,N)))

# Set scaling factors and add
gamma1 = 0.3
gamma2 = 1.5
Iout = gamma1*Ioutlow[0:rows,0:cols] + gamma2*Iouthigh[0:rows,0:cols]

# Anti-log then rescale to [0,1]
Ihmf = np.expm1(Iout)
Ihmf = (Ihmf - np.min(Ihmf)) / (np.max(Ihmf) - np.min(Ihmf))
Ihmf2 = np.array(255*Ihmf, dtype="uint8")

# Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf2 < 65
Ithresh = 255*Ithresh.astype("uint8")

# Clear off the border.  Choose a border radius of 5 pixels
Iclear = imclearborder(Ithresh, 5)

# Eliminate regions that have areas below 120 pixels
Iopen = bwareaopen(Iclear, 120)

# Show all images
cv2.imshow('Original Image', img)
cv2.imshow('Homomorphic Filtered Result', Ihmf2)
cv2.imshow('Thresholded Result', Ithresh)
cv2.imshow('Opened Result', Iopen)
cv2.waitKey(0)
cv2.destroyAllWindows()

Código MATLAB

clear all;
close all;

% Read in image
I = imread('http://i.stack.imgur.com/5DnwY.jpg');

% Remove some columns from the beginning and end
I = I(:,60:end-20);

% Cast to double and do log.  We add with 1 to avoid log(0) error.
I = im2double(I);
I = log(1 + I);

% Create Gaussian mask in frequency domain
% We must specify our mask to be twice the size of the image to avoid
% aliasing.
M = 2*size(I,1) + 1;
N = 2*size(I,2) + 1;
sigma = 10;
[X, Y] = meshgrid(1:N,1:M);
centerX = ceil(N/2);
centerY = ceil(M/2);
gaussianNumerator = (X - centerX).^2 + (Y - centerY).^2;

% Low pass and high pass filters
Hlow = exp(-gaussianNumerator./(2*sigma.^2));
Hhigh = 1 - Hlow;

% Move origin of filters so that it's at the top left corner to match with
% input image
Hlow = ifftshift(Hlow);
Hhigh = ifftshift(Hhigh);

% Filter the image, and crop
If = fft2(I, M, N);
Ioutlow = real(ifft2(Hlow .* If));
Iouthigh = real(ifft2(Hhigh .* If));

% Set scaling factors then add
gamma1 = 0.3;
gamma2 = 1.5;
Iout = gamma1*Ioutlow(1:size(I,1),1:size(I,2)) + ...
       gamma2*Iouthigh(1:size(I,1),1:size(I,2));

% Anti-log then rescale to [0,1]
Ihmf = exp(Iout) - 1;
Ihmf = (Ihmf - min(Ihmf(:))) / (max(Ihmf(:)) - min(Ihmf(:)));

% Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf < 65/255;

% Remove border pixels
Iclear = imclearborder(Ithresh, 8);

% Eliminate regions that have areas below 160 pixels
Iopen = bwareaopen(Iclear, 160);

% Show all of the results
figure;
subplot(4,1,1);
imshow(I);
title('Original Image');
subplot(4,1,2);
imshow(Ihmf);
title('Homomorphic Filtered Result');
subplot(4,1,3);
imshow(Ithresh);
title('Thresholded Result');
subplot(4,1,4);
imshow(Iopen);
title('Opened Result');

Este es el resultado que obtengo:

Pitón

Tenga en cuenta que reorganicé las ventanas para que estén alineadas en una sola columna.

ingrese la descripción de la imagen aquí

MATLAB

ingrese la descripción de la imagen aquí

Sección de Reseñas y Valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *