Introduccion
Nuestro proyecto implementa un sistema de detección de bordes en tiempo real basado en la captura de cuadros de imagen de una cámara OV7670 y su transmisión a un monitor VGA después de aplicar un filtro de escala de grises y un operador Sobel. Nuestro diseño se basa en una placa FPGA Cyclone IV que nos permite optimizar el rendimiento utilizando las potentes funciones del hardware de bajo nivel y los cálculos paralelos, lo cual es importante para cumplir con los requisitos del sistema en tiempo real.
Utilizamos la placa de desarrollo ZEOWAA FPGA que se basa en Cyclone IV (EP4CE6E22C8N). Además, utilizamos Quartus Prime Lite Edition como entorno de desarrollo y Verilog HDL como lenguaje de programación. Además, utilizamos la interfaz VGA incorporada para controlar el monitor VGA y GPIO (pines generales de entrada y salida) para conectar el hardware externo con nuestra placa.

Arquitectura
Nuestro diseño se divide en 3 partes principales:
- Lectura de los píxeles de datos de la cámara.
- Implementando nuestro algoritmo de detección de bordes (convertidor de escala de grises y operador de Sobel).
- Visualización de la imagen final mediante la interfaz con un monitor VGA.
Además, hay un almacenamiento de memoria intermedio entre leer / escribir los datos y operar con estos datos. Para este propósito, implementamos dos buffers que funcionan como espacio temporal para píxeles antes de que se usen.

Tenga en cuenta que después de tomar el píxel de la cámara, no lo almacenamos directamente en el búfer de memoria intermedia. En cambio, lo convertimos a la escala de grises y luego lo almacenamos en el búfer. Esto se debe a que el almacenamiento de píxeles en escala de grises de 8 bits requiere menos memoria que el almacenamiento de los píxeles coloreados que son de 16 bits. Además, tenemos otro búfer que almacena los datos después de aplicar el operador Sobel para que estén listos para mostrarse en el monitor.
Aquí están los detalles sobre la implementación de nuestra arquitectura:
Cámara
Utilizamos la cámara OV7670, que es uno de los módulos de cámara más baratos que encontramos. Además, esta cámara puede funcionar con 3.3V y no necesita protocolos de comunicación difíciles como I2c o SPI para extraer los datos de la imagen. Solo requiere una interfaz SCCB que sea similar a la interfaz I2c para establecer la configuración de la cámara en términos de formato de color (RGB565, RGB555, YUV, YCbCr 4: 2: 2), resolución (VGA, QVGA, QQVGA, CIF, QCIF) y muchos otros ajustes.

El video consta de cuadros que se están cambiando a una velocidad específica. Un cuadro es una imagen que consta de filas y columnas de píxeles donde cada píxel está representado por valores de color. En este proyecto, utilizamos la configuración predeterminada de la cámara donde el tamaño del marco es la resolución VGA 640 x 480 (0.3 Megapixels), y el formato de color del píxel es RGB565 (5 bits para Rojo, 6 bits para Azul, 5 bits para Verde ) y la tasa de cambio de fotogramas es de 30 fps.
A continuación, las conexiones de la cámara a la FPGA utilizando el GPIO que existe en la placa de desarrollo:
Pin en la cámara | pin en el FPGA | Descripción | Pin en la cámara | pin en el FPGA | Descripción |
---|
3.3V | 3.3V | Fuente de alimentación (+) | GND | GND | Nivel de suministro de tierra (-) |
Sdioc | GND | Reloj SCCB | SDIOD | GND | Datos SCCB |
VSYNC | P31 | Sincronización vertical | Href | P55 | Sincronización horizontal |
PCLK | P23 | Reloj de píxeles | Xclk | P54 | Sistema de entrada de reloj (25 MHz) |
D7 | P46 | Octavo bit de datos | D6 | P44 | Séptimo bit de datos |
D5 | P43 | 6to bit de datos | D4 | P42 | 5to bit de datos |
D3 | P39 | 4to bit de datos | D2 | P38 | 3er bit de datos |
D1 | P34 | 2do bit de datos | D0 | P33 | 1er bit de datos |
RESET (activo bajo) | 3.3V | Restablecer pin | PWDN | GND | Pin de apagado |
Tenga en cuenta que no utilizamos la interfaz SCCB para la configuración. Entonces, colocamos sus cables correspondientes en el suelo para evitar cualquier señal flotante que pueda afectar los datos.
Para proporcionar el reloj de 25MHz para la cámara, utilizamos el bucle de fase bloqueada (PLL), que es un sistema de control de frecuencia de bucle cerrado para proporcionar el reloj necesario desde los 50MHz proporcionados desde la placa. Para implementar el PLL, utilizamos la herramienta interna del catálogo de IP dentro del software Quartus.
Esta cámara utiliza la señal de sincronización vertical (VSYNC) para controlar el proceso de envío del cuadro y la señal de sincronización horizontal (HREF) para controlar el envío de cada fila del cuadro. Esta cámara usa solo 8 líneas de datos (D0-D7) para transferir los bits que representan los valores de color del píxel, ya que la cámara divide el valor de píxel RGB de 16 bits en 2 partes (8 bits) y envía cada una por separado.
Las siguientes figuras de la hoja de datos del módulo de cámara OV7670 ilustran las señales de sincronización vertical y horizontal.



Convertidor de escala de grises
Para producir una imagen en escala de grises a partir de su imagen en color original, se deben tener en cuenta muchos factores, ya que la imagen puede perder contraste, nitidez, sombra y estructura. Además, la imagen debe preservar la luminancia relativa del espacio de color. Se utilizan varias técnicas lineales y no lineales para convertir la imagen en color a escala de grises. En consecuencia, para lograr nuestro objetivo, utilizamos la conversión colorimétrica (preservación de la luminancia perceptiva) a escala de grises representada en la siguiente ecuación:

Para mejorar el rendimiento en términos de cálculos, es más rápido usar el operador de turno. Por lo tanto, la ecuación anterior se puede reducir a lo siguiente:

Como resultado, después de capturar un valor de píxel (565 RGB) de la cámara, se puede convertir inmediatamente en un valor de píxel en escala de grises de 8 bits aplicando la fórmula de conversión. La imagen en escala de grises es más fácil de almacenar en la memoria y lo suficientemente rápida como para servir a la funcionalidad de nuestro sistema en tiempo real, ya que su complejidad es aproximadamente logarítmica y FPGA puede hacerlo aún más rápido al acceder a la memoria en paralelo. Después de eso, la imagen almacenada está lista para implementar el algoritmo de detección de bordes.
Tenemos 2 buffers, el primero se usa para almacenar los píxeles después de convertirlos a escala de grises y su tamaño (8 bits x 150 x 150) y el segundo se usa para almacenar los píxeles después de aplicar el operador Sobel y el umbral para el valor de salida y su tamaño (1 bit x 150 x 150). Desafortunadamente, las memorias intermedias de 150 x 150 no almacenan toda la imagen de la cámara, sino que solo almacenan parte de ella.
Hemos elegido el tamaño de nuestras memorias intermedias como 150 x 150 debido a la limitación de la memoria del ciclón IV, ya que solo tiene 276.480 Kbit, mientras que nuestras dos memorias intermedias toman 202.500 Kbit (150 x 150 x 9), lo que equivale al 73.24% de la memoria original de El ciclón IV y el resto de la memoria se utilizan para almacenar el algoritmo y la arquitectura. Además, probamos (170 x 170) como un tamaño para nuestros búferes que toma el 94.07% de la memoria que no deja suficiente espacio para implementar el algoritmo.
Nuestros buffers son verdaderos RAM de doble puerto que pueden leer y escribir en diferentes ciclos de reloj simultáneamente. Aquí, creamos nuestra implementación en lugar de utilizar la herramienta de catálogo de IP dentro del software Quartus para tener más flexibilidad en la implementación. Además, integramos ambas memorias intermedias en un solo módulo en lugar de tener módulos diferentes.
Operador Sobel
Utilizamos un primer operador de detección de bordes derivado que es un operador de gradiente de área de matriz que determina el cambio de luminancia entre diferentes píxeles. Para ser más precisos, ya que es un método sencillo y eficiente en términos de uso de memoria y complejidad de tiempo, utilizamos el operador de gradiente Sobel que usa un núcleo de 3x3 centrado en un píxel elegido para representar la fuerza del borde. El operador Sobel es la magnitud del gradiente calculada por:

Donde Gx y Gy pueden representarse usando máscaras de convolución:

Tenga en cuenta que los píxeles que están más cerca del centro de la máscara tienen más peso. Además, G x y G y se pueden calcular de la siguiente manera:

Donde p i es el píxel correspondiente en la siguiente matriz, y el valor de p i es un valor de escala de grises de 8 bits:

Es una práctica común aproximar la magnitud del gradiente del operador Sobel por valores absolutos:

Esta aproximación es más fácil de implementar y más rápida de calcular, lo que nuevamente sirve a nuestra funcionalidad en términos de tiempo y memoria.
Aquí está el diagrama de bloques del operador Sobel que toma 9 píxeles (8 bits) como entrada y produce un valor de píxel (8 bits):

Y aquí está el diagrama de bloques detallado de la implementación del operador Sobel.

Monitor vga
Nuestra placa de desarrollo tiene una interfaz VGA incorporada que tiene la capacidad de mostrar solo 8 colores en el monitor VGA, ya que solo tiene 3 bits para controlar los colores a través de un bit para Rojo, uno para Verde y otro para Azul. Esto ha dificultado nuestra depuración ya que nos impide mostrar la imagen de la cámara directamente al monitor. Entonces, usamos un umbral para convertir los píxeles en un valor de 1 bit para que sea posible mostrar la imagen.
La interfaz VGA funciona como la cámara, ya que funciona píxel por píxel desde la esquina superior izquierda hasta la esquina inferior derecha. Usando la sincronización vertical y horizontal, podemos sincronizar las señales que controlan el flujo de píxeles.
La señal de sincronización vertical se usa para representar el índice de la fila, mientras que la señal de sincronización horizontal se usa para representar el índice de la columna. Además, ambas señales usan el porche delantero, el pulso de sincronización y el porche trasero como señales de sincronización para separar la fila anterior de la fila nueva en la señal de sincronización horizontal, y el cuadro antiguo del nuevo cuadro en la señal de sincronización vertical.

Utilizamos la interfaz de señal VGA estándar (640 x 480 a 60 MHz). Aquí se describen todas las especificaciones estándar de la señal.
Prueba
Antes de armar todo y probar el sistema en tiempo real. Primero tuvimos que probar cada parte por separado. Al principio, verificamos los valores y las señales que provienen de la cámara al mostrar ciertos valores de píxeles. Luego, con la ayuda de OpenCV usando el lenguaje de programación Python, pudimos aplicar el filtro Sobel en varias imágenes para comparar los resultados con nuestro algoritmo y verificar la corrección de nuestra lógica. Además, probamos nuestros buffers y el controlador VGA al mostrar varias imágenes estáticas en el monitor VGA después de aplicar el operador Sobel y el umbral. Además, al cambiar el valor del umbral, la precisión de la imagen se ve afectada.
El código de Python que usamos:
Resultados
Como resultado de nuestra implementación, obtuvimos un sistema de detección de bordes en tiempo real que produce una imagen de 150x150 después de aplicar el filtro de escala de grises y el operador Sobel. El sistema implementado proporciona 30 fps. La cámara funciona con un reloj de 25MHz y el sistema, en general, cumple con los plazos en tiempo real sin retraso notable. Además, el valor umbral puede afectar la cantidad de detalles y el ruido en la imagen final.
Aquí hay una comparación entre el operador Sobel en FPGA y el operador Sobel OpenCV:

A continuación se muestra un video ilustrativo de los resultados:

Aquí está el enlace del repositorio en Github que tiene todos los códigos fuente.
Mejoras futuras
Como estamos usando FPGA Cyclone IV, estamos limitados a su capacidad de memoria y al número de puertas lógicas. Por lo tanto, como una mejora futura, podemos usar una fuente de memoria externa o podemos implementar nuestro trabajo en otra placa para que podamos mostrar todos los píxeles de la imagen recibida de la cámara.
Además, aunque el operador Sobel es rápido y sencillo de implementar, es notablemente sensible al ruido. Para eliminar el ruido producido, podemos usar un filtro de ruido como el filtro mediano no lineal que funciona perfectamente bien con nuestro sistema si tuviéramos suficiente memoria para implementar un tercer búfer. Esto producirá una imagen más suave con características nítidas eliminadas.
En consecuencia, utilizamos la interfaz VGA integrada de la FPGA que solo puede producir una imagen de 3 bits. Por lo tanto, no pudimos mostrar la imagen en escala de grises ya que necesita 8 bits para mostrarse. Como resultado, la implementación de otra interfaz o el uso de una placa más potente mejorará la flexibilidad de mostrar la imagen.
Conclusión
Pudimos usar nuestro conocimiento y comprensión de conceptos cruciales en sistemas embebidos como máquinas de estado, paralelismo de cómputos e interfaz de hardware y software para crear una aplicación eficiente de detección de bordes que cumpla con nuestros objetivos.
Acuse de recibo
Este proyecto está construido por un equipo compuesto por dos estudiantes: Hussein Youness y Hany Hamed en el primer año de licenciatura en Ciencias de la Computación en la Universidad de Innopolis en Rusia.
Este proyecto es parte del curso de Arquitectura de Computadores Otoño 2018 en la Universidad de Innopolis .