Proyectos IoT

Conteo de personas con OpenCV, Python y Ubidots

José García
· 11 min de lectura
Enviar por correo electrónico

El procesamiento de imágenes digitales (DIP) está creciendo rápidamente, gracias en gran parte al aumento de las técnicas de aprendizaje automático a las que los desarrolladores pueden acceder a través de la nube. Ser capaz de procesar imágenes digitales a través de la nube evita cualquier requisito de hardware dedicado, lo que en última instancia convierte a DIP en la opción preferida. Como método más económico y versátil para procesar imágenes, DIP ha encontrado una amplia gama de aplicaciones. Uno de los más comunes es la detección y el conteo de peatones, una métrica útil para aeropuertos, estaciones de tren, tiendas minoristas, estadios, eventos públicos y museos.

Los contadores de personas tradicionales y disponibles en el mercado no sólo son costosos: los datos que generan a menudo están vinculados a sistemas propietarios que limitan sus opciones de extracción de datos y optimización de KPI. Por el contrario, un DIP integrado que utiliza su propia cámara y SBC no solo le ahorrará tiempo y dinero, sino que también le dará la libertad de adaptar su aplicación a los KPI que le interesen y extraer información de la nube que de otro modo no sería posible. .

El uso de la nube para habilitar su aplicación DIP IoT permite una funcionalidad general mejorada. Con mayores capacidades en forma de visualizaciones , informes, alertas y referencias cruzadas de fuentes de datos externas (como el clima, precios de proveedores en vivo o sistemas de gestión empresarial), DIP brinda a los desarrolladores la libertad que desean.

Imagínese un tendero con un frigorífico para helados: quiere realizar un seguimiento del número de personas que pasan y seleccionan un producto, así como del número de veces que se abrió la puerta y de la temperatura interna del frigorífico. A partir de estos pocos puntos de datos, un minorista puede ejecutar un análisis de correlación para comprender y optimizar mejor el precio de sus productos y el consumo general de energía del refrigerador.

Para comenzar su aplicación de procesamiento de imágenes digitales, Ubidots ha creado el siguiente tutorial del Sistema de conteo de personas utilizando OpenCV y Python para analizar la cantidad de personas en un área determinada . Amplíe sus aplicaciones más allá del recuento de personas con los recursos adicionales de IoT Ubidots . Aquí puede ver un dashboard de conteo de personas creado con Ubidots .

En este artículo, revisaremos cómo implementar una superposición DIP simple para crear un contador de personas usando OpenCV y Ubidots . Este ejemplo funciona mejor con cualquier distribución basada en Linux y también en Raspberry Pi, Orange Pi o sistemas integrados similares.

Para consultas adicionales sobre integración, comuníquese con el Soporte Ubidots y descubra cómo su empresa puede aprovechar esta tecnología de valor agregado.

Tabla de contenido:

  1. Requisitos de solicitud
  2. Codificación – 8 subsecciones
  3. Pruebas
  4. Creando tu Dashboard
  5. Resultados

1) Requisitos de solicitud

  • Cualquier Linux integrado con una versión derivada de Ubuntu
  • Python 3 o posterior instalado en su sistema operativo.
  • OpenCV 3.0 o superior instalado en su sistema operativo. Si usa Ubuntu o sus derivados, siga el tutorial de instalación oficial o ejecute el siguiente comando:
instalación de pip opencv-contrib-python
  • Una vez que haya instalado correctamente Python 3 y OpenCV, verifique su configuración ejecutando este pequeño fragmento de código (simplemente escriba 'python' en su terminal):
importar cv2 cv2.__versión__

Debería obtener una pantalla de impresión con su versión de OpenCV:

  • Instale Numpy siguiendo las oficiales o simplemente ejecutando el siguiente comando:
instalación de pip numpy
  • Instalar imutils
pip instala imutils
  • Solicitudes de instalación
solicitudes de instalación de pip

2) Codificación

Toda la rutina de detección y envío de datos se puede encontrar aquí . Para una mejor explicación de nuestra codificación, dividiremos el código en ocho secciones para explicar mejor cada aspecto del código para su mejor comprensión.

Sección 1:

de imutils.object_detection importar non_max_suppression importar numpy como np importar imutils importar cv2 solicitudes de importación importar tiempo importar argparse URL_EDUCATIONAL = " ubidots " URL_INDUSTRIAL = " ubidots " INDUSTRIAL_USER = Verdadero # Establece esto en Falso si eres un usuario educativo TOKEN = "...." # Pon aquí tu TOKEN DEVICE = "detector" Ubidots # Dispositivo donde se almacenará el resultado VARIABLE = "personas" # Variable donde se almacenará el resultado # SVM preentrenado de Opencv con características de personas HOG HOGCV = cv2.HOGDescriptor() HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

En la Sección 1 importamos las bibliotecas necesarias para implementar nuestro detector, imutils es una herramienta de biblioteca útil para DIP y nos permitirá realizar diferentes transformaciones a partir de nuestros resultados, cv2 es nuestro contenedor OpenCV Python, las solicitudes nos permitirán enviar nuestros datos/resultados. HTTP a Ubidots y argparse nos permitirán leer comandos desde nuestra terminal de comandos dentro de nuestro script.

IMPORTANTE: No olvide actualizar este código con el TOKEN de su cuenta Ubidots y, si es un usuario educativo , asegúrese de configurar INDUSTRIAL_USER en FALSE .

Después de importar la biblioteca, inicializaremos nuestro descriptor de objeto orientado a histograma. HOG, para abreviar, esta es una de las técnicas más populares para la detección de objetos y se ha implementado en varias aplicaciones con resultados exitosos y, para nuestra fortuna, OpenCV ya ha implementado de manera eficiente combinar el algoritmo HOG con una máquina de vectores de soporte. , o SVM, que es una técnica clásica de aprendizaje automático con fines de predicción.

Esta declaración: cv2.HOGDescriptor_getDefaultPeopleDetector() llama al modelo previamente entrenado para la detección de personas de OpenCV y alimentará nuestra función de evaluación de características de la máquina de vectores de soporte.

Sección 2

def detector(imagen): ''' @image es una matriz numpy ''' imagen = imutils.resize(image, ancho=min(400, image.shape[1])) clone = image.copy() (rects, pesos) = HOGCV.detectMultiScale(image, winStride=(8, 8), padding=(32, 32), escala=1.05) # Aplica la supresión no máxima del paquete imutils para iniciar # cajas superpuestas rects = np.array ([[x, y, x + w, y + h] para (x, y, w, h) en rectas]) resultado = non_max_suppression(rects, probs=Ninguno, superposiciónThresh=0.65) devuelve resultado

La detector() es donde ocurre la "magia", recibe una imagen RGB dividida en tres canales de color. Para evitar problemas de rendimiento, cambiamos el tamaño de la imagen usando imutils y luego llamamos al detectMultiScale() desde nuestro objeto HOG. El método de detección de múltiples escalas nos permite analizar la imagen y saber si existe una persona utilizando el resultado de clasificación de nuestro SVM. Los parámetros de este método están más allá del alcance de este tutorial, pero si desea obtener más información, consulte los documentos oficiales de OpenCV o consulte la excelente explicación de Adrian Rosebrock .

El análisis HOG generará algunos cuadros de captura (objetos detectados), pero a veces estos cuadros se superponen y provocan falsos positivos o errores de detección. Para evitar estas confusiones, usaremos la utilidad de supresión no máxima de la imutils para eliminar los cuadros superpuestos, como se muestra a continuación:

Imágenes reproducidas de https://www.pyimagesearch.com

Sección 3:

def localDetect(image_path): resultado = [] imagen = cv2.imread(image_path) if len(image) <= 0: print("[ERROR] no pudo leer su imagen local") return resultado print("[INFO] Detectando personas") resultado = detector(imagen) # muestra el resultado para (xA, yA, xB, yB) en resultado: cv2.rectangle(imagen, (xA, yA), (xB, yB), (0, 255, 0 ), 2) cv2.imshow("resultado", imagen) cv2.waitKey(0) cv2.destroyAllWindows() retorno (resultado, imagen)

Ahora, en esta parte de nuestro código, debemos definir una función para leer una imagen de un archivo local y detectar personas en él. Para lograr esto, verá que simplemente llamé a la función detector() y agregué un bucle simple para pintar las cajas redondas del detector. Devuelve el número de cuadros detectados y la imagen con la detección pintada. Luego, simplemente recrea el resultado en una nueva ventana del sistema operativo.

Sección 4:

def cameraDetect(token, dispositivo, variable, sample_time=5): cap = cv2.VideoCapture(0) init = time.time() # El tiempo de muestreo permitido para Ubidots es 1 punto/segundo si sample_time < 1: sample_time = 1 while( Verdadero): # Captura ret cuadro por cuadro, frame = cap.read() frame = imutils.resize(frame, width=min(400, frame.shape[1])) result = detector(frame.copy() ) # muestra el resultado de (xA, yA, xB, yB) en resultado: cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2) cv2.imshow( 'frame', frame) # Envía resultados if time.time() - init >= sample_time: print("[INFO] Envío de resultados de fotogramas reales") # Convierte la imagen a base 64 y la agrega al contexto b64 = convert_to_base64( marco) contexto = {"imagen": b64} sendTo Ubidots (token, dispositivo, variable, len(resultado), contexto=contexto) init = time.time() if cv2.waitKey(1) & 0xFF == ord(' q'): break # Cuando todo esté hecho, libera la captura cap.release() cv2.destroyAllWindows() def convert_to_base64(image): image = imutils.resize(image, width=400) img_str = cv2.imencode('.png ', imagen)[1].tostring() b64 = base64.b64encode(img_str) return b64.decode('utf-8')

Similar a la función de la Sección 3 , esta de la Sección 4 llamará al detector() y pintará cuadros y la imagen se recuperará directamente desde la cámara web usando el método VideoCapture() También hemos modificado ligeramente el oficial para obtener imágenes de la cámara y enviar los resultados a una Ubidots cada “n” segundos (la sendTo Ubidots () se revisará más adelante en este tutorial). La función convert_to_base64() convertirá su imagen a una cadena base 64, esta cadena es muy importante para ver nuestros resultados en Ubidots usando código JavaScript dentro de un widget HTML Canvas.

Sección 5:

def detectPeople(args): image_path = args["image"] camera = True if str(args["camera"]) == 'true' else False # Rutina para leer la imagen local if image_path != Ninguno y no cámara: print ("[INFO] Ruta de la imagen proporcionada, intentando leer la imagen") (resultado, imagen) = localDetect(image_path) print("[INFO] enviando resultados") # Convierte la imagen a base 64 y la agrega al contexto b64 = convert_to_base64(image) context = {"image": b64} # Envía el resultado req = sendTo Ubidots (TOKEN, DEVICE, VARIABLE, len(resultado), context=context) if req.status_code >= 400: print("[ERROR ] No se pudieron enviar datos a Ubidots ") return req # Rutina para leer imágenes de la cámara web si la cámara: print("[INFO] leyendo imágenes de la cámara") cameraDetect(TOKEN, DEVICE, VARIABLE)

Este método está destinado a insertar los argumentos a través de su terminal y activar una rutina que busca personas en un archivo de imagen almacenado localmente o a través de su cámara web.

Sección 6:

def buildPayload(variable, valor, contexto): return {variable: {"valor": valor, "contexto": contexto}} def sendTo Ubidots (token, dispositivo, variable, valor, contexto={}, industrial=True): # Crea la URL del punto final = URL_INDUSTRIAL si es industrial, sino URL_EDUCATIONAL url = "{}/api/v1.6/devices/{}".format(url, dispositivo) payload = buildPayload(variable, value, context) headers = {"X -Auth-Token": token, "Content-Type": "application/json"} intentos = 0 estado = 400 mientras que estado >= 400 e intentos <= 5: req = request.post(url=url, headers=headers , json=carga útil) estado = req.status_code intentos += 1 time.sleep(1) solicitud de devolución

Estas dos funciones de la Sección 6 son la vía para enviar sus resultados a Ubidots para comprender y visualizar sus datos. La primera función , def buildPayload, crea la carga útil dentro de la solicitud, mientras que la segunda función, def sendTo Ubidots recibe Ubidots (TOKEN, la variable y las etiquetas del dispositivo) para almacenar los resultados. Que en este caso es la longitud de las cajas redondas detectadas por OpenCV. Opcionalmente, también se puede enviar un contexto para almacenar los resultados como una imagen base64 para poder recuperarlos más tarde.

Sección 7:

def argsParser(): ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default=None, help="ruta al directorio del archivo de prueba de imagen") ap.add_argument("-c" , "--camera", default=False, help="Establezca como verdadero si desea usar la cámara") args = vars(ap.parse_args()) return args

Ahora, en la Sección 7 , llegamos al final de nuestro análisis de código. La función argsParser() simplemente analiza y devuelve como un diccionario los argumentos pasados ​​a través de su terminal a nuestro script. Habrá dos argumentos dentro del analizador:

  • imagen: la ruta al archivo de imagen dentro de su sistema
  • cámara: una variable que, si se establece en "verdadero", llamará al método cameraDetect().

Sección 8:

def main(): args = argsParser() detectPeople(args) if __name__ == '__main__': main()

La sección 8 y la parte final de nuestro código es la main() que simplemente llama los argumentos desde la consola e inicia la rutina especificada.

No olvide que el código completo se puede extraer de Github aquí .

3) Pruebas

Abra su procesador de texto favorito (sublime-text, notepad, nano, etc.) y copie y pegue el código completo disponible aquí . Actualice el código con su TOKEN Ubidots y guarde su archivo como "peopleCounter.py".

Con su código guardado correctamente, probemos las siguientes cuatro imágenes aleatorias seleccionadas del conjunto de datos públicos de Caltech y Pexels:

Para analizar estas imágenes, primero debe almacenar las imágenes en su computadora portátil o PC y rastrear la ruta para analizar las imágenes.

python peopleCounter.py PATH_TO_IMAGE_FILE

En mi caso, almacené las imágenes en una ruta etiquetada como "conjunto de datos". Para ejecutar un comando válido, ejecute el siguiente comando pero con la ruta de su imagen.

python peopleCounter.py -i conjunto de datos/imagen_1.png

Si desea tomar imágenes desde su cámara en lugar de un archivo local, simplemente ejecute el siguiente comando:

python peopleCounter.py -c verdadero

Resultados de la prueba:

Además de estas comprobaciones de prueba, también verás, en tiempo real, los resultados de estas pruebas almacenados en tu cuenta Ubidots :

4) Creando tu Dashboard

Usaremos un lienzo HTML para ver nuestros resultados en tiempo real. Este tutorial no está diseñado para widgets de lienzo HTML, por lo que si no sabe cómo usarlos, consulte los artículos siguientes:

Usaremos el ejemplo básico en tiempo real con modificaciones menores para ver nuestras imágenes. A continuación puedes ver el fragmento de código del widget.

HTML

<img id="img" width="400px" height="auto"/>

js

enchufe var; var srv = " ubidots "; // var srv = "app. ubidots .com:443" // Descomenta esta línea si eres un usuario educativo var VAR_ID = "5ab402dabbddbd3476d85967"; // Pon aquí tu var Id var TOKEN = "" // Pon aquí tu token $ ( document ).ready(function() { function renderImage(imageBase64){ if (!imageBase64) return; $ ('#img'). attr('src', 'data:image/png;base64, ' + imageBase64); // Función para recuperar el último valor, se ejecuta solo una vez function getDataFromVariable(variable, token, callback) { var url = 'https://things. ubidots .com/api/v1.6/variables/' + variable + '/values'; var headers = { 'X-Auth-Token': token, 'Tipo de contenido ': 'aplicación/json' }; $ .ajax({ url: url, método: 'GET', encabezados: encabezados, datos: { page_size: 1 }, éxito: función (res) { if (res.results.length > 0){ renderImage(res.results[0].context.image } callback() } }} // Implementa la conexión al socket del servidor = io; .connect("https://"+ srv, {ruta: '/notificaciones'}); var subscribedVars = [] // Función para publicar el ID de la variable var subscribeVariable = function (variable, callback) { // Publica el ID de la variable que desea escuchar socket.emit('rt/variables/id/last_value', { variable: variable }); // Escucha los cambios socket.on('rt/variables/' + variable + '/last_value', callback); suscritoVars.push(variable); }; // Función para cancelar la suscripción para escuchar var unSubscribeVariable = function (variable) { socket.emit('unsub/rt/variables/id/last_value', { variable: variable }); var pst = subscribedVars.indexOf(variable); if (pst !== -1){ suscritoVars.splice(pst, 1); } }; var connectSocket = function (){ // Implementa la conexión del socket socket.on('connect', function(){ console.log('connect'); socket.emit('authentication', {token: TOKEN}); } ); window.addEventListener('en línea', función () { console.log('en línea'); socket.emit('autenticación', {token: TOKEN}); }); socket.on('autenticado', función () { console.log('autenticado'); suscritoVars.forEach(función (variable_id) { socket.emit('rt/variables/id/last_value', { variable: variable_id }) ; }); } /* Rutina principal */ getDataFromVariable(VAR_ID, TOKEN, function(){ connectSocket(); }); conectarSocket(); //conectarSocket(); // Suscríbete a la variable con tu propio código. subscribeVariable(VAR_ID, function(value){ var parsedValue = JSON.parse(value); console.log(parsedValue); // $ ('#img').attr('src', 'data:image/png;base64 , ' + parsedValue.context.image); renderImage(parsedValue.context.image }) });

No olvides poner el TOKEN y el ID de la variable al principio del fragmento de código.

BIBLIOTECAS DE TERCEROS

Agregue las siguientes bibliotecas de terceros:

Una vez que guarde su widget, debería obtener algo como el siguiente:

5) Resultados

Puedes ver los dashboards con los resultados en este enlace .

En este artículo, hemos explorado cómo crear un IoT utilizando DIP (procesamiento de imágenes), OpenCV y Ubidots . Con estos servicios, su aplicación DIP es mucho más precisa que PIR u otros sensores ópticos al detectar e identificar las diferencias entre personas, lugares o cosas, lo que le brinda un contador de personas eficiente sin toda la estática de la manipulación temprana de datos.

Háganos saber lo que piensa dejando Ubidots en los foros de nuestra comunidad o conéctese con Ubidots simplemente a través de Facebook , Twitter o Hackster .

¡Feliz piratería!

Artículos sugeridos