Identificación mediante inteligencia artificial con Raspberry Pi
Diseñe un dispositivo de identificación de objetos basado en Raspberry Pi con Raspberry Pi 3 Model B, la cámara Raspberry Pi, Intel Movidius NCS, DesignSpark Pmod HAT y Digilent OLED Pmod.
¿Cree que la inteligencia artificial y las redes neuronales profundas (DNN) están reservadas a matemáticos, científicos e ingenieros expertos? Por suerte, aunque la creación de redes neuronales nuevas no es apta para cardíacos, y su entrenamiento puede conllevar una gran cantidad de tiempo y recursos, podemos recurrir a redes ya existentes que se han entrenado con útiles conjuntos de imágenes y, gracias a Movidius NCS y su SDK, es relativamente fácil crear aplicaciones sencillas que las usan e implementan mediante plataformas informáticas compactas de baja potencia, como Raspberry Pi.
En este artículo descubrimos cómo crear rápidamente un sistema autónomo que, gracias a la integración de una cámara y un módulo OLED compacto, captura imágenes y las descarga para procesarlas mediante GoogLeNet en un NCS, antes de mostrar el resultado final en el módulo OLED.
Hardware
Este proyecto emplea una Raspberry Pi 3 Model B con el sistema operativo Raspbian Stretch y la cámara oficial Raspberry Pi. Además de estos elementos y el Intel Movidius NCS, también contamos con DesignSpark Pmod HAT y Digilent PmodOLEDrgb para mostrar los resultados.
-
Raspberry Pi 3 Model B (896-8660)
-
Cámara Raspberry Pi (913-2664)
-
Carcasa de la cámara Raspberry Pi (867-9049)
-
Intel Movidius NCS (139-3655)
-
DesignSpark Pmod HAT (144-8419)
-
Digilent PmodOLEDrgb (134-6481)
Tras instalar Raspbian y arrancar la Pi, siempre es buena idea empezar por actualizar las últimas versiones del paquete de software.
pi@pidentifier:~$ sudo apt-get update
pi@pidentifier:~$ sudo apt-get dist-upgrade
También tendremos que habilitar la interfaz de la cámara y la SPI del módulo OLED.
pi@pidentifier:~$ sudo raspi-config
-
Option 5 - Interfacing
-
P1 - Camera
-
Enable → YES
-
Option 5 - Interfacing
-
P4 - SPI
-
Enable → YES
Dependencias de software
Podemos obtener la mayoría de dependencias de software con el sistema de gestión de paquetes del sistema operativo.
pi@pidentifier:~$ sudo apt-get install -y build-essential git libusb-1.0-0-dev libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler libatlas-base-dev git automake byacc lsb-release cmake libgflags-dev libgoogle-glog-dev liblmdb-dev swig3.0 graphviz libxslt-dev libxml2-dev gfortran python3-dev python3-pip python3-setuptools python3-markdown python3-pillow python3-yaml python3-pygraphviz python3-h5py python3-nose python3-lxml python3-matplotlib python3-numpy python3-protobuf python3-dateutil python3-skimage python3-scipy python3-six python3-networkx libfreetype6-dev libjpeg-dev python3-gst-1.0 python3-picamera
A continuación, utiliza el gestor de paquetes de Python para instalar la compatibilidad con OpenCV.
pi@pidentifier:~$ sudo pip3 install opencv-python
Instala solo el componente API del SDK de Movidius NC. Aunque se puede instalar todo el SDK en una Raspberry Pi, normalmente no se recomienda, pues es mucho más rápido hacer la compilación necesaria en un PC y después copiar el archivo de gráficos binarios.
pi@pidentifier:~$ mkdir workspace
pi@pidentifier:~$ cd workspace
pi@pidentifier:~$ git clone https://github.com/movidius/ncsdk
pi@pidentifier:~$ cd ncsdk/api/src
pi@pidentifier:~$ make
pi@pidentifier:~$ sudo make install
Se puede instalar la biblioteca de soporte de DesignSpark Pmod HAT mediante un solo comando.
pi@pidentifier:~$ sudo pip3 install designspark.pmod
Por último, tendrás que instalar el SDK de Movidius NC por completo en otro ordenador con Ubuntu para poder compilar modelos para su uso en Raspberry Pi.
user@laptop:~$ mkdir workspace
user@laptop:~$ cd workspace
user@laptop:~$ git clone https://github.com/movidius/ncsdk
user@laptop:~$ cd
user@laptop:~$ sudo make install
Comprobación del hardware
Empezamos comprobando que la cámara de Pi funcione correctamente.
pi@pidentifier:~$ raspivid -d
Al hacer esto, debería mostrarse un vídeo en la pantalla.
A continuación, ejecutamos Hello World para que el NCS confirme su funcionamiento.
pi@pidentifier:~$ cd ~/workspace
pi@pidentifier:~$ git clone https://github.com/movidius/ncappzoo
pi@pidentifier:~$ python3 ncappzoo/apps/hello_ncs_py/hello_ncs.py
Podemos probar el PmodOLEDrgb creando un archivo denominado helloOLED.py con el siguiente contenido:
from DesignSpark.Pmod.HAT import createPmod
from luma.core.render import canvas
from luma.oled.device import ssd1331
if __name__ == '__main__':
try:
oled = createPmod('OLEDrgb','JA')
device = oled.getDevice()
with canvas(device) as draw:
draw.rectangle(device.bounding_box, outline="white", fill="black")
draw.text((16,20), "Hello, World!", fill="white")
while True:
pass
except KeyboardInterrupt:
pass
finally:
oled.cleanup()
A continuación, ejecutamos esto:
pi@pidentifier:~$ python3 helloOLED.py
Puedes consultar la documentación del módulo Pmod, además de otros ejemplos, en ReadTheDocs.
Compilación del modelo
Usaremos un modelo de aprendizaje que es una réplica del modelo descrito en la documentación de GoogLeNet. Sin embargo, primero tenemos que compilarlo en el equipo con Ubuntu en el que hayamos realizado la instalación completa del SDK de NC. Compilaremos todos los ejemplos suministrados con el SDK a la vez, sin olvidar que el NC debe estar conectado a este ordenador durante la compilación.
user@laptop:~$ cd workspace/ncsdk
user@laptop:~$ make examples
Aplicación
Ahora pasamos a crear la aplicación.
Tendremos que copiar el gráfico binario que acabamos de compilar para el modelo de GoogLeNet del ordenador con Ubuntu a la Raspberry Pi.
user@laptop:~$ scp workspace/ncsdk/examples/caffe/GoogLeNet/graph
user@laptop:~$ pi@pidentifier.local:workspace/ncappzoo/caffe/GoogLeNet/
Después, utilizamos un editor de texto para crear un archivo nuevo en la Raspberry Pi denominado "pidentify" (sustituye "vi" por tu editor favorito):
pi@pidentifier:~$ vi pidentify
Y podemos empezar por introducir lo siguiente para importar las bibliotecas de Python necesarias.
#!/usr/bin/python3
import os
import sys
import time
import numpy
import picamera
import mvnc.mvncapi as mvnc
import skimage
from skimage import io, transform
from DesignSpark.Pmod.HAT import createPmod
from luma.core.render import canvas
from luma.oled.device import ssd1331
Después, debemos definir algunos parámetros de entrada de NCS.
NCAPPZOO_PATH = os.path.expanduser( '~/workspace/ncappzoo' )
GRAPH_PATH = NCAPPZOO_PATH + '/caffe/GoogLeNet/graph'
IMAGE_PATH = '/tmp/i-spy.jpg'
IMAGE_MEAN = [ 104.00698793, 116.66876762, 122.67891434]
IMAGE_STDDEV = 1
IMAGE_DIM = ( 224, 224 )
NETWORK_STAT_TXT = NCAPPZOO_PATH + '/apps/stream_infer/googlenet_stat.txt'
NETWORK_CATEGORIES_TXT = NCAPPZOO_PATH + '/apps/stream_infer/googlenet_categories.txt'
Aquí es donde introduciríamos los cambios si quisiéramos usar una red neuronal diferente.
Las dos líneas siguientes crearán una instancia de la cámara Raspberry Pi configurada con la resolución correcta para nuestra red neuronal.
camera = picamera.PiCamera()
camera.resolution = IMAGE_DIM
A continuación, abrimos el NCS y obtenemos un identificador de dispositivo.
ncsdevices = mvnc.EnumerateDevices()
if len( ncsdevices ) == 0:
print( 'No NCS devices found' )
quit()
ncs = mvnc.Device( ncsdevices[0] )
ncs.OpenDevice()
También debemos crear una instancia de DesignSpark Pmod OLEDrgb.
pmoddev = createPmod('OLEDrgb','JA')
oled = pmoddev.getDevice()
Una vez terminada la configuración inicial del dispositivo, podemos cargar el archivo de gráficos en el NCS.
with open( GRAPH_PATH, mode='rb' ) as f:
blob = f.read()
graph = ncs.AllocateGraph( blob )
Y podemos cargar las categorías de red.
with open(NETWORK_CATEGORIES_TXT, 'r') as f:
for line in f:
cat = line.split('n')[0]
if cat != 'classes':
gNetworkCategories.append(cat)
f.close()
last = len(gNetworkCategories)-1
Crearemos una función para descargar una imagen en el NCS, ejecutar la inferencia y devolver el resultado principal.
def getTopInference(img):
img = print_img = skimage.io.imread( IMAGE_PATH )
img = skimage.transform.resize( img, IMAGE_DIM, preserve_range=True )
img = img[:, :, ::-1]
img = img.astype( numpy.float32 )
img = ( img - IMAGE_MEAN ) * IMAGE_STDDEV
graph.LoadTensor( img.astype( numpy.float16 ), 'user object' )
output, userobj = graph.GetResult()
order = output.argsort()
top = gNetworkCategories[order[last-0]]
return top
Y una función sencilla para escribir el texto en la pantalla del módulo PmodOLEDrgb.
def display(message):
with canvas(oled) as draw:
draw.text((16,20), message, fill="white")
Por último, el bucle principal.
try:
while True:
camera.capture(IMAGE_PATH)
thing = getTopInference(IMAGE_PATH)
display(thing)
time.sleep(2)
except KeyboardInterrupt:
pass
finally:
graph.DeallocateGraph()
ncs.CloseDevice()
oled.cleanup()
Con esto, se capturará una imagen, se mostrará la inferencia principal, se detendrá durante 2 segundos y luego se repetirá el proceso.
Ahora podemos guardar el archivo y convertirlo en un ejecutable.
pi@pidentifier:~$ chmod +x pidentify
Testeando la aplicación
Por último, la parte más emocionante: comprobar todo lo anterior.
pi@pidentifier:~$ ./pidentify
Tras un breve retardo, debería mostrarse la inferencia principal, que se actualiza cada pocos segundos. En este punto, es importante tener en cuenta que la red se ha entrenado con imágenes de 1 de 1.000 categorías, lo que significa que tiene limitaciones en cuanto a lo que puede clasificar. Además, la precisión de la inferencia principal (solo se muestra el resultado principal) es de 68,7 %, mientras que de las cinco inferencias principales es de hasta el 88,9 %.
Mejoras potenciales
Es posible crear rápidamente una aplicación sencilla que integre una cámara Raspberry Pi, Intel Movidius NCS y un Pmod HAT con una pantalla OLEDrgb gracias a la disponibilidad del soporte de Python para ello, como muestran los ejemplos proporcionados. Por supuesto, hay margen de mejora y puede ser divertido seguir experimentando con ideas como estas:
-
Probar diferentes redes, lo que sería cuestión simplemente de copiar los archivos de gráficos binarios compilados y actualizar los parámetros de entrada de NCS correspondientes.
-
Utilizar una secuencia de vídeo, en lugar de imágenes fijas. NC App Zoo incluye un ejemplo de aplicación stream_infer que podría servir de base para ello.
-
Usar varios NCS. NC App Zoo también incluye códigos de ejemplo.
-
Optimizar los parámetros de captura de la cámara.
-
Mejorar el formato del texto mostrado.
O incluso, para los más aventureros, entrenar un modelo con tus propios datos y conjuntos de imágenes.