NOTA: Zbar sólo funciona con Python 2, por tanto este tutorial no servirá para las versiones más recientes de Python y OpenCV.
¡Saludos, homo fabers! Hoy voy a enseñaros cómo detectar códigos QR en un vídeo en tiempo real en Python utilizando la librería OpenCV y ZBar. Empezaré con un programa sencillo que sólo leerá el mensaje del código QR y lo escribirá por Terminal, y después lo ampliaremos poco a poco para que también encuentre la posición del centro del código QR sobre la imagen, pinte sus esquinas y calcule su rotación. Si no os interesa la explicación paso a paso, al final también encontraréis el código completo ^-^
Bien, ¡manos a la obra! Hoy vamos a trabajar a ritmo de Saint-Saëns(1835-1921), con una pieza llamada “El Carnaval de los Animales“.
Preparativos
Para este tutorial necesitaremos la librería OpenCV para Python, la librería ZBar y Numpy.
También necesitaréis una webcam y algunos códigos QR impresos en papel que lleven algun tipo de mensaje. Hay muchas webs que permiten generar códigos QR gratis y guardarlos como imagen. Los que he usado para este tutorial los he generado desde aquí, y después los he imprimido y recortado.

Mis códigos QR impresos, con una moneda para comparar el tamaño
Es importante que no hagáis los códigos QR muy pequeños porque a la cámara le costará detectarlos.
1- Detectar un código y leer el mensaje
Para este primer ejemplo, veréis cómo detectar códigos QR en un vídeo y leer su mensaje para mostrarlo por la Consola.
Empezamos por cargar las librerías e inicializar la cámara:
import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0)
Dentro del bucle principal, leemos un frame de la cámara, lo convertimos a escala de grises y guardamos sus dimensiones dentro de un array:
while 1: #Capturar un frame val, frame = capture.read() #Hay que comprobar que el frame sea valido if val: #Capturar un frame con la camara y guardar sus dimensiones frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) dimensiones = frame_gris.shape #'dimensiones' sera un array que contendra el alto, el ancho y los canales de la imagen en este orden.
Convertimos la imagen a un formato que ZBar pueda interpretar. Los dos primeros parámetros son las dimensiones de la imagen. ‘Y800’ es el formato. El último parámetro indica que hay que convertir cada píxel de la imagen a un carácter, para facilitar después la búsqueda de códigos QR.
#Convertir la imagen de OpenCV a una imagen que la libreria ZBAR pueda entender imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], 'Y800', frame_gris.tobytes())
Creamos un objeto de tipo scanner y escaneamos la imagen:
#Construir un objeto de tipo scaner, que permitira escanear la imagen en busca de codigos QR escaner = zbar.ImageScanner() #Escanear la imagen y guardar todos los codigos QR que se encuentren escaner.scan(imagen_zbar)
Extraemos el texto de cada uno de los códigos QR que se han detectado en la imagen y los escribimos por la Consola:
for codigo_qr in imagen_zbar: dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar print(dat)
Acabamos mostrando la imagen de la cámara, añadiendo una condición para salir del bucle y destruyendo la ventana que muestra el vídeo.
#Mostrar la imagen cv2.imshow('Imagen', frame) #Salir con 'ESC' k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows()
El código debería lucir como este:
import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0) while 1: #Capturar un frame val, frame = capture.read() if val: #Capturar un frame con la camara y guardar sus dimensiones frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) dimensiones = frame_gris.shape #'dimensiones' sera un array que contendra el alto, el ancho y los canales de la imagen en este orden. #Convertir la imagen de OpenCV a una imagen que la libreria ZBAR pueda entender imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], 'Y800', frame_gris.tobytes()) #Construir un objeto de tipo scaner, que permitira escanear la imagen en busca de codigos QR escaner = zbar.ImageScanner() #Escanear la imagen y guardar todos los codigos QR que se encuentren escaner.scan(imagen_zbar) for codigo_qr in imagen_zbar: dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar print(dat) #Mostrar la imagen cv2.imshow('Imagen', frame) #Salir con 'ESC' k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows()
2- Contorno y centro
Ahora vamos a ampliar este código para que dibuje el contorno del código QR y el mensaje sobre la imagen. Partiendo del código del apartado anterior, habrá que hacerle algunos cambios.
Primero, justo después de inicializar la cámara, habrá que importar la fuente con la que escribiremos el mensaje del código QR:
import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0) #Cargar la fuente font = cv2.FONT_HERSHEY_SIMPLEX
Además de guardar el mensaje, también guardaremos las coordenadas de las cuatro esquinas del código QR sobre la imagen y las convertiremos a un array de Numpy. Después dibujaremos el contorno con la función cv2.polylines(). También he eliminado el print(dat), puesto que ahora el mensaje se mostrará sobre la imagen.
for codigo_qr in imagen_zbar: loc = codigo_qr.location #Guardar las coordenadas de las esquinas dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar #Convertir las coordenadas de las cuatro esquinas a un array de numpy #Asi, lo podremos pasar como parametro a la funcion cv2.polylines para dibujar el contorno del codigo QR localizacion = np.array(loc, np.int32) #Dibujar el contorno del codigo QR en azul sobre la imagen cv2.polylines(frame, [localizacion], True, (255,0,0), 2)
Recordad que el array ‘loc’ siempre guardará las coordenadas respetando su orden: primero la esquina superior izquierda (0), esquina inferior izquierda (1), esquina inferior derecha (2) y esquina superior derecha (3).
Esto será de mucha utilidad más adelante, cuando haya que calcular la rotación del código QR.
Encontramos el centro calculando el punto medio de las esquinas, y escribimos el mensaje sobre este centro:
#Buscar el centro del rectangulo del codigo QR cx = (loc[0][0]+loc[2][0])/2 cy = (loc[0][1]+loc[2][1])/2 #Escribir el mensaje del codigo QR. cv2.putText(frame,dat,(cx,cy), font, 0.7,(255,255,255),2)
El resto es igual. Al final, éste código mejorado debería quedar así:
import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0) #Cargar la fuente font = cv2.FONT_HERSHEY_SIMPLEX while 1: #Capturar un frame val, frame = capture.read() if val: #Capturar un frame con la camara y guardar sus dimensiones frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) dimensiones = frame_gris.shape #'dimensiones' sera un array que contendra el alto, el ancho y los canales de la imagen en este orden. #Convertir la imagen de OpenCV a una imagen que la libreria ZBAR pueda entender imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], 'Y800', frame_gris.tobytes()) #Construir un objeto de tipo scaner, que permitira escanear la imagen en busca de codigos QR escaner = zbar.ImageScanner() #Escanear la imagen y guardar todos los codigos QR que se encuentren escaner.scan(imagen_zbar) for codigo_qr in imagen_zbar: loc = codigo_qr.location #Guardar las coordenadas de las esquinas dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar #Convertir las coordenadas de las cuatro esquinas a un array de numpy #Asi, lo podremos pasar como parametro a la funcion cv2.polylines para dibujar el contorno del codigo QR localizacion = np.array(loc, np.int32) #Dibujar el contorno del codigo QR en azul sobre la imagen cv2.polylines(frame, [localizacion], True, (255,0,0), 2) #Buscar el centro del rectangulo del codigo QR cx = (loc[0][0]+loc[2][0])/2 cy = (loc[0][1]+loc[2][1])/2 #Escribir el mensaje del codigo QR. cv2.putText(frame,dat,(cx,cy), font, 0.7,(255,255,255),2) #Mostrar la imagen cv2.imshow('Imagen', frame) #Salir con 'ESC' k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows()
3- Pintar las cuatro esquinas
Además de dibujar el contorno, también podemos dibujar las cuatro esquinas del código QR, cada una de un color diferente para distingirlas:
Para hacerlo sólo tenéis que añadir este fragmento de código justo después de llamar a la función cv2.polylines():
#Dibujar las cuatro esquinas del codigo QR cv2.circle(frame, loc[0], 3, (0,0,255), -1) #Rojo - esquina superior izquierda cv2.circle(frame, loc[1], 3, (0,255,255), -1) #Amarillo - esquina inferior izquierda cv2.circle(frame, loc[2], 3, (255,100,255), -1) #Rosa -esquina inferior derecha cv2.circle(frame, loc[3], 3, (0,255,0), -1) #Verde - esquina superior derecha
El código completo quedará así:
import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0) #Cargar la fuente font = cv2.FONT_HERSHEY_SIMPLEX while 1: #Capturar un frame val, frame = capture.read() if val: #Capturar un frame con la camara y guardar sus dimensiones frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) dimensiones = frame_gris.shape #'dimensiones' sera un array que contendra el alto, el ancho y los canales de la imagen en este orden. #Convertir la imagen de OpenCV a una imagen que la libreria ZBAR pueda entender imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], 'Y800', frame_gris.tobytes()) #Construir un objeto de tipo scaner, que permitira escanear la imagen en busca de codigos QR escaner = zbar.ImageScanner() #Escanear la imagen y guardar todos los codigos QR que se encuentren escaner.scan(imagen_zbar) for codigo_qr in imagen_zbar: loc = codigo_qr.location #Guardar las coordenadas de las esquinas dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar #Convertir las coordenadas de las cuatro esquinas a un array de numpy #Asi, lo podremos pasar como parametro a la funcion cv2.polylines para dibujar el contorno del codigo QR localizacion = np.array(loc, np.int32) #Dibujar el contorno del codigo QR en azul sobre la imagen cv2.polylines(frame, [localizacion], True, (255,0,0), 2) #Dibujar las cuatro esquinas del codigo QR cv2.circle(frame, loc[0], 3, (0,0,255), -1) #Rojo - esquina superior izquierda cv2.circle(frame, loc[1], 3, (0,255,255), -1) #Amarillo - esquina inferior izquierda cv2.circle(frame, loc[2], 3, (255,100,255), -1) #Rosa -esquina inferior derecha cv2.circle(frame, loc[3], 3, (0,255,0), -1) #Verde - esquina superior derecha #Buscar el centro del rectangulo del codigo QR cx = (loc[0][0]+loc[2][0])/2 cy = (loc[0][1]+loc[2][1])/2 #Escribir el mensaje del codigo QR. cv2.putText(frame,dat,(cx,cy), font, 0.7,(255,255,255),2) #Mostrar la imagen cv2.imshow('Imagen', frame) #Salir con 'ESC' k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows()
4- Rotación
Para algunos proyectos puede ser interesante conocer la orientación del código QR. Supondremos que el ángulo de rotación del código QR viene dado por la pendiente de la recta que une la esquina superior izquierda con la esquina superior derecha.
Si sabemos el vector pendiente de esta recta, tenemos dos coordenadas que se corresponden con los dos catetos de un triángulo. Por tanto, con la fórmula de la tangente puede encontrarse el ángulo de inclinación de la recta. ¿Fácil, verdad?
Pero tenemos un problema. Los píxeles de la pantalla no están ordenados de la misma forma que los puntos en el plano. El eje Y está invertido: cuanto más arriba esté un píxel, menor es el valor de su coordenada Y, y cuánto más abajo, mayor.
Por tanto habrá que aplicar una pequeña conversión a la rotación: restar 360 al ángulo y multiplicarlo por -1. Esto hará que el ángulo quede de la forma correcta.
Justo después de escribir el mensaje del código QR con cv2.putText() calcularemos el vector director de la recta y su inclinación, aplicando las correcciones pertinentes:
#Calcular el angulo de rotacion del codigo QR. Supondremos que el angulo es la pendiente de la recta que une el vertice loc[0] (rojo) con loc[3] (verde) vector_director = [loc[3][0]-loc[0][0], loc[3][1]-loc[0][1]] angulo = (np.arctan2(float(vector_director[1]),vector_director[0])*57.29)%360 #Calculo de la tangente y conversion de radianes a grados #Correccion debida al orden de las coordenadas en la pantalla angulo += -360 angulo *= -1
Por último escrivimos el ángulo debajo del texto del mensaje:
#Escribir el angulo sobre la imagen con dos decimales cv2.putText(frame,str("%.2f" % angulo),(cx,cy+30), font, 0.7,(255,255,255),2)
CÓDIGO FINAL
Por tanto, habiendo calculado la rotación, el código definitivo quedará así:
""" Programa en Python para leer codigos QR. Muestra sus dimensiones en la pantalla, su mensaje y su rotacion en grados. Escrito por Glare, www.robologs.net """ import zbar import numpy as np import cv2 #Inicializar la camara capture = cv2.VideoCapture(0) #Cargar la fuente font = cv2.FONT_HERSHEY_SIMPLEX while 1: #Capturar un frame val, frame = capture.read() #Hay que comprobar que el frame sea valido if val: #Capturar un frame con la camara y guardar sus dimensiones frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) dimensiones = frame_gris.shape #'dimensiones' sera un array que contendra el alto, el ancho y los canales de la imagen en este orden. #Convertir la imagen de OpenCV a una imagen que la libreria ZBAR pueda entender imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], 'Y800', frame_gris.tobytes()) #Construir un objeto de tipo scaner, que permitira escanear la imagen en busca de codigos QR escaner = zbar.ImageScanner() #Escanear la imagen y guardar todos los codigos QR que se encuentren escaner.scan(imagen_zbar) for codigo_qr in imagen_zbar: loc = codigo_qr.location #Guardar las coordenadas de las esquinas dat = codigo_qr.data[:-2] #Guardar el mensaje del codigo QR. Los ultimos dos caracteres son saltos de linea que hay que eliminar #Convertir las coordenadas de las cuatro esquinas a un array de numpy #Asi, lo podremos pasar como parametro a la funcion cv2.polylines para dibujar el contorno del codigo QR localizacion = np.array(loc, np.int32) #Dibujar el contorno del codigo QR en azul sobre la imagen cv2.polylines(frame, [localizacion], True, (255,0,0), 2) #Dibujar las cuatro esquinas del codigo QR cv2.circle(frame, loc[0], 3, (0,0,255), -1) #Rojo - esquina superior izquierda cv2.circle(frame, loc[1], 3, (0,255,255), -1) #Amarillo - esquina inferior izquierda cv2.circle(frame, loc[2], 3, (255,100,255), -1) #Rosa -esquina inferior derecha cv2.circle(frame, loc[3], 3, (0,255,0), -1) #Verde - esquina superior derecha #Buscar el centro del rectangulo del codigo QR cx = (loc[0][0]+loc[2][0])/2 cy = (loc[0][1]+loc[2][1])/2 #Escribir el mensaje del codigo QR. cv2.putText(frame,dat,(cx,cy), font, 0.7,(255,255,255),2) #Calcular el angulo de rotacion del codigo QR. Supondremos que el angulo es la pendiente de la recta que une el vertice loc[0] (rojo) con loc[3] (verde) vector_director = [loc[3][0]-loc[0][0], loc[3][1]-loc[0][1]] angulo = (np.arctan2(float(vector_director[1]),vector_director[0])*57.29)%360 #Calculo de la tangente y conversion de radianes a grados #Correccion debida al orden de las coordenadas en la pantalla angulo += -360 angulo *= -1 #Escribir el angulo sobre la imagen con dos decimales cv2.putText(frame,str("%.2f" % angulo),(cx,cy+30), font, 0.7,(255,255,255),2) #Mostrar la imagen cv2.imshow('Imagen', frame) #Salir con 'ESC' k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows()
¡Espero que este tutorial os haya sido de ayuda! Como siempre, si tenéis dudas, os ha surgido algún problema o queréis que clarifique cualquier cosa del tutorial podéis dejarme un comentario o escribirme un correo electrónico a contacto@robologs.net. ¡Hasta la próxima! 😉
Buenas tengo este problema al ejecutar el primer codigo:
imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], ‘Y800’, frame_gris.tostring())
AttributeError: ‘module’ object has no attribute ‘Image’
Que puede ser?
Ejecutalo como root con el prefijo sudo
Buenas! Soy totalmente novato en esto. Tengo descargado python 3.7 a través de Anaconda. Siguiendo los pasos tal cual en este vídeo: https://www.youtube.com/watch?v=vePJ19ZesZk Tengo el mismo problema que muchos compañeros aquí, no puedo importar zbar. Como bien decís lo he descargado de la página, lo he instalado asegurándome de clickar en lo de Libraries and Heaters. Me he descargado zbar-0.10.tar.bz2 (aunque no puedo abrirlo ni nada) Puse en Anacondapromt lo que ya dice la página de zbar de: python setup.py install y me dice esto: python: can’t open file ‘setup.py’: [Errno 2] No such file or directory Tras ver que… Leer más »
Ya te hemos contestado por DM a través de Facebook ^-^
hola, como puedo hacer que se pare cuando haya detectado el primer qr?
Una forma fácil de hacerlo sin modificar demasiado el código consiste en añadir un break al final del bucle ‘for’. De esta forma sólo te procesará el primer código QR.
Final de línea.
Hola yo uso python 2.7 en windows y no puedo instalar el paquete zbar, no se si alguien ya pudo hacerlo ?
amigazo intenta ejecutando todo esto, a mi me funciono
# pip3 install opencv-python
# sudo apt-get install libcblas-dev
# sudo apt-get install libhdf5-dev
# sudo apt-get install libhdf5-serial-dev
# sudo apt-get install libatlas-base-dev
# sudo apt-get install libjasper-dev
# sudo apt-get install libqtgui4
# sudo apt-get install libqt4-test
Me aparece el mismo error que a Erik, ¿qué puedo hacer para corregirlo? Es con todos los códigos que implementan el método zbar.Image.
Saludos
en imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], ‘Y800’, frame_gris.tobytes()) me dice AttributeError : ‘module’ object has no attribute ‘Image’ que puede ser?
¿Cuál de todos los códigos estás intentando correr?
Hola, no he sido capaz de instalar la libreria de zbar. Donde la puedo instalar facilmente. Gracias
¡Hola! En el tutorial aparece el link para descargar la librería: https://pypi.python.org/pypi/zbar.
Si tienes Ubuntu u otra versión de Linux que utilice un sistema de paquetes .deb también puedes bajarlo desde synaptic o con el comando de Terminal ‘sudo pip install zbar’.
¡Un saludo!
Estos codigos de ejemplos no sirven en windows ? por que no permite instalar el paquete zbar, ayuuda
¡Hola! Todos los códigos que he puesto deberían servir también en Windows. Necesito que me des más detalles si quieres que te ayude. ¿Te aparece algún mensaje de error cuándo intentas instalar la librería?
Hola, me es imposible ejecutar el codigo ya que me da error al importar zbar, he intentado con la version 2.5, 2.6 y 2.7, alguien tiene idea de porque no consigo hacerlo funcionar? Un saludo
¡Hola P323! Vigila que no tengas varias versiones de Python en tu ordenador, y estés utilizando una en la que no hayas instalado zbar (ej. que tengas python2 y python3, y sólo hayas instalado zbar para python3).
hola buenas, tengo est error.
Traceback (most recent call last):
File “/home/pi/Desktop/Prollectos 2018/Brazo robótico/Python/15-6-17 QR.py”, line 21, in
imagen_zbar = zbar.Image(dimensiones[1], dimensiones[0], ‘Y800’, frame_gris.tobytes())
AttributeError: ‘numpy.ndarray’ object has no attribute ‘tobytes’
>>>
¡Hola Daniel! Prueba de cambiar ‘frame_gris.tobytes()’ por ‘frame_gris.tostring()’
buen día, quisiera preguntar si es posible implementar esta aplicación pero con código de barras 2D pdf 417 en vez de código QR
Hola! Mírate este código: http://zbar.sourcearchive.com/documentation/0.10plus-pdoc-2/scan__image_8py-source.html
Felicitaciones, un post increible!!