fbpixel
Reconocimiento de textos con Python

Reconocimiento de textos con Python

En este tutorial veremos cómo reconocer texto de una imagen utilizando Python y Tesseract. Tesseract es una herramienta para reconocer caracteres, y por tanto texto, contenidos en una imagen (OCR, Optical Character Recognition).

Instalación de Tesseract

  • En Linux

Para instalar tesseract, introduzca los siguientes comandos en un terminal

sudo apt install tesseract-ocr
sudo apt install libtesseract-dev
  • Para Windows

puede descargar y ejecutar el instalador para su sistema operativo

Una vez finalizada la instalación, añada C:\Archivos de programa\Tesseract-OCR a su variable de entorno Path.

Ahora puede ejecutar tesseract y comprobar el resultado con el siguiente comando

tesseract <path_to_image> <path_to_result_file> -l <language>

ex:

tesseract prueba.png resultado -l fra

Tesseract reconocerá el texto contenido en la imagen test.png y escribirá el texto sin procesar en el archivo result.txt.

Nota: Tesseract puede tener dificultades con la puntuación y la alineación del texto.

Reconocimiento de textos con Pytesseract

A continuación, puede instalar el paquete pytesseract

pip install pytesseract

La ventaja de utilizar Python, y OpenCV en particular, es que puedes procesar imágenes e implementar la herramienta en paquetes de software más grandes. He aquí una lista de algunas de las ventajas:

  • detección de texto en un vídeo
  • Tratamiento y filtrado de imágenes para caracteres obstruidos, por ejemplo
  • Detectar texto de un archivo PDF
  • Escribir los resultados en un archivo Word o Excel

En el siguiente script, cargamos la imagen con OpenCV y dibujamos rectángulos alrededor del texto detectado. Los datos de posición se obtienen utilizando la función image_to_data. El texto en bruto se obtiene utilizando la función image_to_string

from PIL import Image
import pytesseract
from pytesseract import Output
import cv2
 
source = 'test.png'
img = cv2.imread(source)
text=pytesseract.image_to_string(img)
print(text)

d = pytesseract.image_to_data(img, output_type=Output.DICT)
 
NbBox = len(d['level'])
print ("Number of boxes: {}".format(NbBox))

for i in range(NbBox):
	(x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
	# display rectangle
	cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
 
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

El script también funciona con fotos de documentos

Bonus: Reconocimiento de textos con Python à partir d’un fichier PDF

Instalación de la biblioteca pdf2image

pip install pdf2image

pdf2image requiere la instalación de poppler

Bastante sencillo en Linux

sudo apt-get install poppler-utils

Para Windows

  • Descargar el archivo zip
  • Extraiga los archivos donde desee (C:
  • Añade la carpeta bin a la variable de entorno Path (C:³³Users³ADMIN³Documents³poppler³Library³bin)
  • prueba con el comando pdftoppm -h

Script para recuperar texto de un PDF

from pdf2image import convert_from_path, convert_from_bytes
from PIL import Image
import pytesseract
from pytesseract import Output

images = convert_from_path('invoice.pdf')

# get text
print("Number of pages: {}".format(len(images)))
for i,img in enumerate(images):
    print ("Page N°{}\n".format(i+1))
    print(pytesseract.image_to_string(img))

Script para mostrar rectángulos en un PDF

from pdf2image import convert_from_path, convert_from_bytes
from PIL import Image
import pytesseract
from pytesseract import Output
import cv2
import numpy

images = convert_from_path('invoice.pdf')
for i,source in enumerate(images):
	print ("Page N°{}\n".format(i+1))
	
	#convert PIL to opencv
	pil_image = source.convert('RGB') 
	open_cv_image = numpy.array(pil_image) 
	# Convert RGB to BGR 
	img = open_cv_image[:, :, ::-1].copy() 
	#img = cv2.imread(source)

	d = pytesseract.image_to_data(img, output_type=Output.DICT)
	 
	NbBox = len(d['level'])
	print ("Number of boxes: {}".format(NbBox))

	for j in range(NbBox):
		(x, y, w, h) = (d['left'][j], d['top'][j], d['width'][j], d['height'][j])
		# display rectangle
		cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
	 
	cv2.imshow('img', img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

Aplicaciones

  • Lectura de documentos escaneados
  • Reconocimiento de texto en tiempo real a partir de vídeo

Fuentes

Transmisión de vídeo entre dos máquinas con FFMPEG

Transmisión de vídeo entre dos máquinas con FFMPEG

, ,

En este tutorial, veremos cómo enviar un flujo de vídeo de una máquina a otra utilizando FFMPEG. El envío de datos entre dos dispositivos, sea cual sea, es uno de los principales problemas de los objetos conectados (IoT). Si quieres crear una cámara conectada que envíe vídeo a una máquina remota, este artículo debería interesarte.

Instalación de ffmpeg

En una máquina Linux

sudo apt-get install ffmpeg

En un equipo Windows, siga este procedimiento.

N.B.: si es necesario en una máquina Linux sudo apt-get install v4l-utils

Obtenga la información que necesita para la transmisión de vídeo

Para crear un flujo de vídeo de una máquina a otra, las máquinas deben estar conectadas a la misma red (Wifi o Ethernet).

En primer lugar, es necesario obtener la dirección IP de la máquina cliente (que recibe el flujo)

Introduzca un terminal de comandos

ifconfig #por une machine linux
ip addr # si ifconfig n'est pas disponible 

o

ipconfig #por une machine windows

Deberá obtener una dirección de la forma 192.168.X.X (en nuestro caso 192.168.1.67)

A continuación, recupere la salida de vídeo. Para listar todas las entradas de vídeo en una máquina Linux

ls -l /dev/video*
lsusb #list usb devices
v4l2-ctl --list-devices #list video devices

Estos comandos deberían ayudarte a identificar la fuente de vídeo. En nuestro caso, /dev/video5

Para listar las salidas de vídeo en una máquina Windows, puede utilizar ffmpeg

ffmpeg -list_devices true -f dshow -i dummy

En nuestro caso, video=”USB2.0 HD UVC WebCam “.

Recuperar las opciones y comprobar los formatos y resoluciones aceptados por la cámara

Linux

v4l2-ctl -d /dev/video0 --list-formats

Windows

ffmpeg -f dshow -list_options true -i video="USB2.0 HD UVC WebCam"

Una vez registrados estos datos, podemos configurar el streaming.

Crear un flujo de vídeo con ffmpeg

Para crear un flujo de vídeo, necesitas un emisor (servidor) y un receptor (cliente) que estarán conectados por dos terminales que se ejecutan en dos ordenadores diferentes.

  • servidor linux
ffmpeg -re -f v4l2 -i /dev/video5 -r 10 -f mpegts udp://192.168.1.67:8554?pkt_size=1316
  • Servidor Windows
ffmpeg -f dshow -i video="USB2.0 HD UVC WebCam" -r 10 -f mpegts udp://192.168.1.67:8554?pkt_size=1316
  • del lado del cliente
ffplay udp://127.0.0.1:8554

N.B.: Puedes probar el streaming en una sola máquina utilizando la dirección IP de la máquina en la que estás trabajando y utilizando dos terminales, uno para el servidor y otro para el cliente.

Para recuperar las estadísticas de vídeo puede utilizar el comando ffprobe

ffprobe udp://127.0.0.1:8554

o, para más detalles,

ffprobe -show_format -show_streams udp://127.0.0.1:8554

Reducir el tiempo de recepción de secuencias de vídeo entre dos máquinas

Se observa un retardo de unos 15 segundos entre la transmisión y la recepción de vídeo. Puede ser útil reducir este retardo en función de la aplicación.

En el lado del servidor, los parámetros que afectan a la velocidad de transmisión son

  • el tamaño de la imagen (por ejemplo, -s 800×600)
  • conversión del formato del códec (por ejemplo, -c:v libx264)
  • número de fotogramas por segundo o framerate (por ejemplo, -r 10)

Intenta trabajar con estos parámetros para reducir el retardo manteniendo una calidad suficiente.

ffmpeg -re -thread_queue_size 64 -s800x600 -i

En el lado del cliente, hay varias opciones que puede probar a partir de la documentación incluida en esta discusión.

ffplay -fflags nobuffer -probesize 32 -sync ext -vf setpts=0 udp:

Estos cambios en las opciones de los comandos ffmpeg y ffplay reducen el retardo de 15 a unos 2 segundos para el flujo de vídeo.

Creación de un flujo HTTP con ffmpeg

Puede crear un servidor de vídeo HTTP utilizando VLC. La ventaja de este método es que el flujo de vídeo se puede transmitir a cualquier dispositivo.

  • servidor linux
ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -f mpegts -|vlc -I dummy - --sot='#std{access=http,mux=ts,dst=:8554}'
  • del lado del cliente, debe especificar la dirección IP del servidor
ffplay http://192.168.1.9:8554

Creación de un flujo RTSP con ffmpeg

Puede crear un servidor de vídeo HTTP utilizando VLC. La ventaja de este método es que el flujo de vídeo se puede transmitir a cualquier dispositivo.

  • servidor linux
ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -f mpegts -|vlc -I dummy - --sot='#rtp{sdp=rtsp://:8554/} --sot-all --sot-keep'
  • del lado del cliente, vos devez spécifier l’adresse IP du serveur
ffplay rtsp://192.168.1.9:8554/

Script Python para iniciar el streaming

Aquí tienes un script en Python que te permitirá probar los diferentes métodos de streaming y los parámetros de ffmpeg

import subprocess

#UDP
#input client ip address here 192.168.1.9
stream_video="ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -f mpegts udp://192.168.1.9:8554?pkt_size=1316"
#on client side ffplay udp://127.0.0.1:8554

#HTTP
stream_video="ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -f mpegts -|vlc -I dummy - --sot='#std{access=http,mux=ts,dst=:8554}'"
#on client side ffplay http://192.168.1.73:8554

#RSTP
stream_video="ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -f mpegts -|vlc -I dummy - --sot='#rtp{sdp=rtsp://:8554/} --sot-all --sot-keep'"
#on client side ffplay rtsp://192.168.1.73:8554/ # don't forget / at the end

try:
	subprocess.call(stream_video,shell=True)
except:
	pass
print("Done")

Bonus: Visualizar el flujo de vídeo FFMPEG con OpenCV

Una vez establecido el flujo de vídeo entre las dos máquinas, se puede utilizar OpenCV para leer y mostrar las imágenes del flujo UDP.

import cv2

def main(args):

	#cap = cv2.VideoCapture(0) #default camera
	cap = cv2.VideoCapture('udp://127.0.0.1:8554')
	#cap = cv2.VideoCapture('http://192.168.1.9:8554')
	#cap = cv2.VideoCapture('rtsp://192.168.1.9:8554/')

	while(True):
		ret, frame = cap.read()
		if ret: #necessary if packet is lost
			frame=cv2.resize(frame, (800, 600)) 
			cv2.imshow('Capturing',frame)
			
		if cv2.waitKey(1) &amp; 0xFF == ord('q'): #click q to stop capturing
			break

	cap.release()
	cv2.destroyAllWindows()
	return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))

Bonus2 : Mostrar la hora en el vídeo

Para mostrar la hora en el vídeo, vamos a utilizar el filtro drawtext de ffmpeg, que utiliza un determinado parámetro de texto

  • fontfile: fuente utilizada
  • fontsize: tamaño del texto
  • fontcolor: coleur du texte
"drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf: text='%{localtime\:%T}': fontsize=24: fontcolor=white@0.8: x=7: y=460"

El filtro se aplica con la etiqueta -vf. Este es el comando completo

ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vf "drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf: text='%{localtime\:%T}': fontsize=24: fontcolor=white@0.8: x=7: y=460" -vcodec copy -f mpegts udp://192.168.1.9:8554?pkt_size=1316

En python

stream_cmd="""ffmpeg -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vf "drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf: text='%{localtime\:%T}': fontsize=24: fontcolor=white@0.8: x=7: y=460" -vcodec copy -f mpegts udp://192.168.1.9:8554?pkt_size=1316"""

Práctico para imitar una cámara de CCTV o comparar tiempos de diferentes cámaras

Aplicaciones

Fuentes

Visualización de una imagen OpenCV en una interfaz PyQt

Visualización de una imagen OpenCV en una interfaz PyQt

Para ciertas aplicaciones, puede resultarte útil incrustar OpenCV en una interfaz PyQt. En este tutorial, veremos cómo integrar y gestionar correctamente un vídeo capturado por OpenCV en una aplicación PyQt.

N.B.: Utilizamos Pyside, pero la conversión a PyQt es bastante sencilla.

Requisitos previos:

  • Instalación de Python
  • Instalación de OpenCV (pip install opencv-python)
  • PySide o PyQt (pip install pyside6 o pip install PyQt5)

Código para capturar un vídeo con OpenCV

Este es el código básico para visualizar un vídeo de webcam con openCV

import sys
import cv2

def main(args):

	cap = cv2.VideoCapture(0) #default camera

	while(True):
		ret, frame = cap.read()
		if ret:
			frame=cv2.resize(frame, (800, 600)) 
			cv2.imshow("Video",frame)
			
		if cv2.waitKey(1) &amp; 0xFF == ord('q'): #click q to stop capturing
			break

	cap.release()
	cv2.destroyAllWindows()
	return 0

if __name__ == '__main__':
    
    sys.exit(main(sys.argv))

Para integrarlo en una aplicación PyQt, crearemos un objeto QThread que se encargará de reproducir el vídeo sin bloquear la aplicación.

  • La función run es la función que contiene el código openCV que se ejecutará en un bucle cuando se llame a la función QThread.start.
  •  La función stop se utiliza para detener limpiamente el hilo
  • La señal changePixmap se utiliza para indicar a la aplicación que hay una nueva imagen disponible
class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

Creación de la aplicación PyQt

Para la aplicación, crearemos una QLabel en un simple QWidget que contendrá la imagen de video e instanciaremos el QThread. El video se actualizará automáticamente usando la función setImage, que es llamada cuando se recibe la señal changePixmap.

  • Función setImage
	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image))
  • señal changePixmap
		self.th.changePixmap.connect(self.setImage)
class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'PySide Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
	
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

Código completo para mostrar un vídeo en una ventana PyQt

import cv2
import sys
#from PyQt5.QtWidgets import  QWidget, QLabel, QApplication
#from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
#from PyQt5.QtGui import QImage, QPixmap

from PySide6.QtWidgets import  QWidget, QLabel, QApplication
from PySide6.QtCore import QThread, Qt, Signal, Slot
from PySide6.QtGui import QImage, QPixmap
pyqtSignal = Signal
pyqtSlot = Slot

class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				# https://stackoverflow.com/a/55468544/6622587
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
	
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

if __name__ == '__main__':
	
		app = QApplication(sys.argv)
		ex = VideoContainer()
		sys.exit(app.exec())

Aparece una ventana “Vídeo” con la imagen de la webcam.

Puedes adaptar este sencillo código para integrar un vídeo en una interfaz gráfica que te permita modificar las opciones del vídeo o trabajar con filtros, por ejemplo.

Bonificación: interfaz de cierre mejorada

El código funciona bien y puede ser suficiente, pero hay algunos problemas con esta implementación:

  • La aplicación no se puede cerrar con Ctrl+C (KeyboardInterrupt)
  • Al cerrar la ventana, el Qthread no se detiene
  • Si cambia el tamaño de la ventana, el tamaño del vídeo no cambia

Para cerrar la aplicación con Ctrl+C, puedes utilizar la señal de interrupción añadiendo el siguiente código antes de llamar a la aplicación (existen métodos más limpios)

import signal #close signal with Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)

Para finalizar el QThread cuando se cierra la ventana, puede utilizar la señal aboutToQuit de la aplicación para llamar a la función stop del QThread

app.aboutToQuit.connect(ex.th.stop) #stop qthread when closing window

Por último, para redimensionar el vídeo con la ventana cada vez que se actualiza, utilizamos el tamaño de la ventana para calcular el tamaño de la imagen y la posición de la etiqueta, de modo que quede centrada y el vídeo conserve sus proporciones.

	@pyqtSlot(QImage)
	def setImage(self, image):
		#resize image with window and center
		imWidth=self.width()-2*self.padding
		imHeight=self.height()-2*self.padding
		image = image.scaled(imWidth, imHeight, Qt.KeepAspectRatio) # remove Qt.KeepAspectRatio if not needed
		self.label.resize(image.width(), image.height()) #(640, 480)
		self.label.move((self.width()-image.width())/2, (self.height()-image.height())/2)
			
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 

Aquí está el código completo con las mejoras

import cv2
import sys
#from PyQt5.QtWidgets import  QWidget, QLabel, QApplication
#from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
#from PyQt5.QtGui import QImage, QPixmap

from PySide6.QtWidgets import  QWidget, QLabel, QApplication
from PySide6.QtCore import QThread, Qt, Signal, Slot
from PySide6.QtGui import QImage, QPixmap
pyqtSignal = Signal
pyqtSlot = Slot

class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				# https://stackoverflow.com/a/55468544/6622587
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'PySide Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.padding = 10
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#resize image with window and center
		imWidth=self.width()-2*self.padding
		imHeight=self.height()-2*self.padding
		image = image.scaled(imWidth, imHeight, Qt.KeepAspectRatio) # remove Qt.KeepAspectRatio if not needed
		self.label.resize(image.width(), image.height()) #(640, 480)
		self.label.move((self.width()-image.width())/2, (self.height()-image.height())/2)
			
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
		
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(self.width()-2*self.padding,self.height()-2*self.padding) #(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

import signal #close signal with Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)

if __name__ == '__main__':
	
		app = QApplication(sys.argv)
		ex = VideoContainer()
		app.aboutToQuit.connect(ex.th.stop) #stop qthread when closing window
		
		sys.exit(app.exec())

Fuentes

Reconocimiento de formas y colores con Python

Reconocimiento de formas y colores con Python

,

La biblioteca OpenCV se utiliza para el procesamiento de imágenes, en particular el reconocimiento de formas y colores. La biblioteca tiene funciones de adquisición y algoritmos de procesamiento de imágenes que hacen que el reconocimiento de imágenes sea bastante sencillo sin necesidad de inteligencia artificial. Eso es lo que veremos en este tutorial.

Este tutorial se puede aplicar a cualquier ordenador con una instalación de Python con OpenCV y una Cámara. En particular la Raspberry Pi.

Hardware

  • Ordenador con una instalación de python3

Preparar el entorno de trabajo

Para crear el script de reconocimiento de formas, instalamos los módulos OpenCV, numpy e imutils para manipular y procesar las imágenes.

pip3 install opencv-python numpy imutils

La detección de colores se realiza mediante los módulos webcolors y scipy (KDTree)

pip3 install webcolors scipy

Para probar y validar el algoritmo, creamos una imagen que contiene objetos de diferentes formas y colores. Puedes crear tu propia imagen con Paint o utilizar ésta:

Principio de funcionamiento

En el siguiente código, vamos a crear una clase de detección de formas que nos permitirá seleccionar una forma basada en el número de contornos encontrados. A continuación, vamos a definir una función para encontrar el nombre del color basado en su código RGB. Por último, vamos a utilizar OpenCV para cargar, filtrar y enmascarar la imagen con el fin de detectar las formas y los colores contenidos en la imagen.

Código completo para el reconocimiento sencillo de formas y colores

Es necesario crear el archivo python ObjectDetection.py en la misma carpeta que la imagen que se desea analizar.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# import the necessary packages
import cv2
import numpy as np
import imutils

#colors
from webcolors import rgb_to_name,CSS3_HEX_TO_NAMES,hex_to_rgb #python3 -m pip install webcolors
from scipy.spatial import KDTree

def convert_rgb_to_names(rgb_tuple):
    # a dictionary of all the hex and their respective names in css3
    css3_db = CSS3_HEX_TO_NAMES#css3_hex_to_names
    names = []
    rgb_values = []    
    for color_hex, color_name in css3_db.items():
        names.append(color_name)
        rgb_values.append(hex_to_rgb(color_hex))
    
    kdt_db = KDTree(rgb_values)    
    distance, index = kdt_db.query(rgb_tuple)
    return names[index]
    
class ShapeDetector:
	def __init__(self):
		pass
	def detect(self, c):
		# initialize the shape name and approximate the contour
		shape = "unidentified"
		peri = cv2.arcLength(c, True)
		approx = cv2.approxPolyDP(c, 0.04 * peri, True)

		# if the shape is a triangle, it will have 3 vertices
		if len(approx) == 3:
			shape = "triangle"
		# if the shape has 4 vertices, it is either a square or
		# a rectangle
		elif len(approx) == 4:
			# compute the bounding box of the contour and use the
			# bounding box to compute the aspect ratio
			(x, y, w, h) = cv2.boundingRect(approx)
			ar = w / float(h)
			# a square will have an aspect ratio that is approximately
			# equal to one, otherwise, the shape is a rectangle
			shape = "square" if ar &gt;= 0.95 and ar &lt;= 1.05 else "rectangle"
		# if the shape is a pentagon, it will have 5 vertices
		elif len(approx) == 5:
			shape = "pentagon"
		elif len(approx) == 6:
			shape = "hexagon"
		elif len(approx) == 10 or len(approx) == 12:
			shape = "star"
		# otherwise, we assume the shape is a circle
		else:
			shape = "circle"
		# return the name of the shape
		return shape	

if __name__ == '__main__':
	# load the image and resize it to a smaller factor so that
	# the shapes can be approximated better
	image = cv2.imread('python_shapes_detection_base.PNG')
	resized = imutils.resize(image, width=300)
	ratio = image.shape[0] / float(resized.shape[0])
	
	# convert the resized image to grayscale, blur it slightly,
	# and threshold it
	gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
	blurred = cv2.GaussianBlur(gray, (5, 5), 0)
	thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
	
	# find contours in the thresholded image and initialize the
	# shape detector
	cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
	cnts = imutils.grab_contours(cnts)
	sd = ShapeDetector()

	# loop over the contours
	for c in cnts:
		# compute the center of the contour
		M = cv2.moments(c)
		cX = int((M["m10"] / M["m00"]) * ratio)
		cY = int((M["m01"] / M["m00"]) * ratio)  
		
		#detect shape from contour
		shape = sd.detect(c)
		
		# resize the contour
		c = c.astype("float")
		c *= ratio
		c = c.astype("int")
		cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
		
		#draw contour with mask
		mask = np.zeros(image.shape[:2], np.uint8)
		cv2.drawContours(mask, [c], -1, 255, -1)
		img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
		
		#Convert to RGB and get color name
		imgRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
		mean=cv2.mean(imgRGB, mask=mask)[:3]
		named_color = convert_rgb_to_names(mean)
		
		#get complementary color for text
		mean2 = (255-mean[0],255-mean[1],255-mean[2])
		
		#display shape name and color
		objLbl=shape+" {}".format(named_color)
		textSize = cv2.getTextSize(objLbl,cv2.FONT_HERSHEY_SIMPLEX,0.5,2)[0]
		cv2.putText(image, objLbl, (int(cX-textSize[0]/2),int(cY+textSize[1]/2)), cv2.FONT_HERSHEY_SIMPLEX,0.5,mean2, 2)
		
		#show image
		cv2.imshow("Image", image)
		#cv2.waitKey(0)
	cv2.waitKey(0)		

Resultados

Para ejecutar el script, puede ejecutarlo desde su editor de código (como Geany) o ejecutar el siguiente comando en un terminal de comandos abierto en su carpeta de trabajo.python3 ObjectDetection.py

Una vez ejecutado el código, se mostrará la imagen con cada una de las formas rodeadas de verde y un texto en el centro de la forma que contiene el nombre y el color de la forma.

N.B.: Este algoritmo no funcionará para todas las formas. Para detectar otras formas debe adaptar la función detect de la clase ShapeDetector para identificar todos los casos posibles o utilizar inteligencia artificial.

Aplicaciones

Fuentes

Transmisión de vídeo desde una Raspberry Pi a un navegador web

Transmisión de vídeo desde una Raspberry Pi a un navegador web

, , ,

En este tutorial, veremos cómo transmitir vídeo desde una PiCam en una Raspberry Pi a un navegador web. Cuando se conecta una cámara a una Raspberry Pi, la idea es poder acceder a ella desde cualquier lugar. Para ello, es necesario configurar un servidor y enviar una captura del vídeo a su interfaz. Esto permite transmitir vídeo desde la Raspberry Pi a un navegador web u otra aplicación.

Hardware

  •  Raspberry Pi
  • PiCamV2

Nota: este tutorial puede aplicarse a cualquier ordenador que ejecute Python y esté equipado con una cámara web.

Configuración de la Raspberry Pi

Configure PiCamV2 en su Raspberry Pi

Primero vamos a instalar Flask, que nos permitirá configurar el servidor y servir el vídeo.

sudo apt-get install python3-flask

A continuación, utilizamos OpenCV para capturar las imágenes y procesarlas.

pip3 install opencv-python

Código para emitir un vídeo en un servidor

Para transmitir el vídeo desde la Raspberry Pi, vamos a crear un directorio que contenga

  • una carpeta estática que contiene archivos css y javascript
  • una carpeta de plantillas con archivos html
  • el script python

Crear una página html .

<!DOCTYPE html>

<head>
    <title>PiCam Server</title>
    <link rel="stylesheet" href='../static/style.css'/>
</head>

<body>
    
        <h1>PiCam Live Streaming</h1>
        
        <div class="img-container">
            <img src="{{ url_for('video_feed') }}">
        </div>
        <footer>AranaCorp All right reserved &#169;</footer>
        
</body>

</html>

Creación de una página de estilo ./static/style.css

body{
background-color:#70706F;
}

h1{
	color:#3AAA35;
	text-align:center;
}


.img-container{
  	width:50%;
	border: 3px solid #3AAA35;
	border-radius: 6px;
	overflow: hidden; 
	text-align:center;
	margin-left: auto;
	margin-right: auto;
	padding:20px;
}
img{
  width:100%;
}
footer{
  background-color: #777;
  padding: 10px;
  text-align: center;
  color: white;
}

Código para la transmisión de vídeo PiCamServer.py

#!/usr/bin/env python
# -*- coding: utf-8 -*

#sudo apt-get install python3-flask
#pip3 install opencv-python

from flask import Flask, render_template, Response
import cv2

app = Flask(__name__)
#app.config["CACHE_TYPE"] = "null"

@app.route('/')
def index():
	"""Video streaming home page."""
	return render_template('index.html')

def gen():
	"""Video streaming generator function."""
	vs = cv2.VideoCapture(0)
	while True:
		ret,frame=vs.read()
		ret, jpeg = cv2.imencode('.jpg', frame)
		frame=jpeg.tobytes()
		yield (b'--frame\r\n'
		b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
		
	vs.release()
	cv2.destroyAllWindows() 

@app.route('/video_feed')
def video_feed():
	"""Video streaming route. Put this in the src attribute of an img tag."""
	return Response(gen(),mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__': 
	app.run(host='0.0.0.0', port =5000, debug=True, threaded=True)

En este código, abrimos una petaca de servidor en el puerto 5000 y en la dirección local

app.run(host='0.0.0.0', port =5000, debug=True, threaded=True)

Adquirimos el vídeo en la función gen(), que llamamos en la función video_feed().

Una vez cargada la página, la etiqueta llama a la url /video_feed, que lanza la función del mismo nombre.

Recuperar la dirección IP de su Raspberry Pi

Antes de ejecutar el código, debe obtener la dirección IP de su Raspberry Pi utilizando el comando ifconfig en el terminal

ifconfig

Resultados

A continuación, puede ejecutar el código a través de Geany o la línea de comandos en el directorio de trabajo

python3 PiCamServer.py

En su navegador, introduzca la dirección IP de su Raspberry Pi y el puerto vinculado al servidor para visualizar el vídeo (aquí: 192.162.1.46:5000).

Fuentes