popular_features_as_the_remote_device.py
# Esta obra está licenciada bajo la licencia MIT.
# Copyright (c) 2013-2023 OpenMV LLC. Todos los derechos reservados.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Control remoto: como dispositivo remoto
#
# Este script configura su OpenMV Cam como un coprocesador que puede ser controlado remotamente por
# otro microcontrolador o computadora, como un Arduino, ESP8266/ESP32, RaspberryPi, e
# incluso otra OpenMV Cam.
#
# Este script está diseñado para emparejarse con "popular_features_as_the_controller_device.py".
import image
import math
import rpc
import csi
import struct
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
csi0.snapshot(time=2000)
# La biblioteca RPC mencionada arriba está instalada en su OpenMV Cam y proporciona varias clases para
# permitir que su OpenMV Cam sea controlada mediante CAN, I2C, SPI o UART.
################################################################
# Elija la interfaz mediante la cual desea controlar su OpenMV Cam.
################################################################
# Descomente la línea de abajo para configurar su OpenMV Cam para controlarla por CAN.
#
# * message_id: mensaje CAN que se usará para el transporte de datos en el bus CAN (11 bits).
# * bit_rate: velocidad de bits del CAN.
# * sample_point: relación Tseg1/Tseg2. Normalmente 75%. (50.0, 62.5, 75, 87.5, etc.)
#
# NOTA: los IDs de mensaje y las velocidades de bits CAN del maestro y del esclavo deben coincidir. Conecte el can high del maestro al
# can high del esclavo, y el can low del maestro al can low del esclavo. El bus CAN debe terminarse con 120 ohmios.
#
# interface = rpc.rpc_can_slave(message_id=0x7FF, bit_rate=250000, sample_point=75)
# Descomente la línea de abajo para configurar su OpenMV Cam para controlarla por I2C.
#
# * slave_addr: dirección I2C.
#
# NOTA: las direcciones del maestro y del esclavo deben coincidir. Conecte el scl del maestro al scl del esclavo y el sda del maestro
# al sda del esclavo. Debe usar resistencias pull-up externas. Finalmente, ambos dispositivos deben compartir una tierra común.
#
# interface = rpc.rpc_i2c_slave(slave_addr=0x12)
# Descomente la línea de abajo para configurar su OpenMV Cam para controlarla por SPI.
#
# * cs_pin: pin de selección de esclavo (Slave Select).
# * clk_polarity: nivel del reloj en reposo (0 o 1).
# * clk_phase: muestrear los datos en el primer (0) o segundo flanco (1) del reloj.
#
# NOTA: la configuración del maestro y del esclavo debe coincidir. Conecte CS, SCLK, MOSI, MISO a CS, SCLK, MOSI, MISO.
# Finalmente, ambos dispositivos deben compartir una tierra común.
#
# interface = rpc.rpc_spi_slave(cs_pin="P3", clk_polarity=1, clk_phase=0)
# Descomente la línea de abajo para configurar su OpenMV Cam para controlarla por UART.
#
# * baudrate: velocidad en baudios del puerto serie.
#
# NOTA: las velocidades en baudios del maestro y del esclavo deben coincidir. Conecte el tx del maestro al rx del esclavo y el rx del maestro
# al tx del esclavo. Finalmente, ambos dispositivos deben compartir una tierra común.
#
interface = rpc.rpc_uart_slave(baudrate=115200)
################################################################
# Funciones de callback
################################################################
# Métodos auxiliares utilizados por los callbacks a continuación.
def draw_detections(img, dects):
for d in dects:
c = d.corners
c_len = len(c)
for i in range(c_len):
img.draw_line(c[(i + 0) % c_len] + c[(i + 1) % c_len], color=(0, 255, 0))
img.draw_rectangle(d.rect, color=(255, 0, 0))
# El control remoto funciona mediante métodos de callback que el controlador
# llamadas de dispositivo a través del módulo rpc en este dispositivo. Las funciones callback
# son funciones que reciben un objeto bytes() como argumento
# y devuelven un objeto bytes() como resultado. El módulo rpc
# se encarga de mover los objetos bytes() a través del enlace.
# bytes() puede tener como tamaño máximo el valor máximo de un int de micropython.
# Cuando se llama, devuelve x, y, w y h de la cara más grande dentro del campo de visión.
#
# data no se utiliza
def face_detection(data):
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
faces = (
img.gamma_corr(contrast=1.5)
.find_features(image.HaarCascade("/rom/haarcascade_frontalface.cascade"))
)
if not faces:
return bytes() # Sin detecciones.
for f in faces:
img.draw_rectangle(f, color=(255, 255, 255))
out_face = max(faces, key=lambda f: f[2] * f[3])
return struct.pack("<HHHH", out_face[0], out_face[1], out_face[2], out_face[3])
# Cuando se llama, devuelve la cadena de datos (payload) del código QR más grande
# dentro del campo de visión de la OpenMV Cam.
#
# data no se utiliza
def qrcode_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((320, 240))
img = csi0.snapshot()
codes = img.find_qrcodes()
if not codes:
return bytes() # Sin detecciones.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Cuando se llama, devuelve una lista json de objetos json de códigos QR para todos los códigos QR en vista.
#
# data no se utiliza
def all_qrcode_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((320, 240))
img = csi0.snapshot()
codes = img.find_qrcodes()
if not codes:
return bytes() # Sin detecciones.
draw_detections(img, codes)
return str(codes).encode()
# Cuando se llama, devuelve el centroide x/y, el número de id y la rotación del
# AprilTag más grande dentro del campo de visión de la OpenMV Cam.
#
# data no se utiliza
def apriltag_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QQVGA)
img = csi0.snapshot()
tags = img.find_apriltags()
if not tags:
return bytes() # Sin detecciones.
draw_detections(img, tags)
output_tag = max(tags, key=lambda t: t.w * t.h)
return struct.pack(
"<HHHH",
output_tag.cx,
output_tag.cy,
output_tag.id,
int(math.degrees(output_tag.rotation)),
)
# Cuando se llama, devuelve una lista json de objetos json de apriltags para todos los apriltags en vista.
#
# data no se utiliza
def all_apriltag_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QQVGA)
img = csi0.snapshot()
tags = img.find_apriltags()
if not tags:
return bytes() # Sin detecciones.
draw_detections(img, tags)
return str(tags).encode()
# Cuando se llama, devuelve la cadena de datos (payload) del datamatrix más grande
# dentro del campo de visión de la OpenMV Cam.
#
# data no se utiliza
def datamatrix_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((320, 240))
img = csi0.snapshot()
codes = img.find_datamatrices()
if not codes:
return bytes() # Sin detecciones.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Cuando se llama, devuelve una lista json de objetos json de datamatrix para todos los datamatrix en vista.
#
# data no se utiliza
def all_datamatrix_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((320, 240))
img = csi0.snapshot()
codes = img.find_datamatrices()
if not codes:
return bytes() # Sin detecciones.
draw_detections(img, codes)
return str(codes).encode()
# Cuando se llama, devuelve la cadena de datos (payload) del código de barras más grande
# dentro del campo de visión de la OpenMV Cam.
#
# data no se utiliza
def barcode_detection(data):
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.VGA)
csi0.window((csi0.width(), csi0.height() // 8))
img = csi0.snapshot()
codes = img.find_barcodes()
if not codes:
return bytes() # Sin detecciones.
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Cuando se llama, devuelve una lista json de objetos json de códigos de barras para todos los códigos de barras en vista.
#
# data no se utiliza
def all_barcode_detection(data):
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.VGA)
csi0.window((csi0.width(), csi0.height() // 8))
img = csi0.snapshot()
codes = img.find_barcodes()
if not codes:
return bytes() # Sin detecciones.
return str(codes).encode()
# Cuando se llama, devuelve el centroide x/y del blob más grande
# dentro del campo de visión de la OpenMV Cam.
#
# data es la tupla de 6 bytes del umbral de seguimiento de color: L_MIN, L_MAX, A_MIN, A_MAX, B_MIN, B_MAX.
def color_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
thresholds = struct.unpack("<bbbbbb", data)
img = csi0.snapshot()
blobs = img.find_blobs(
[thresholds], pixels_threshold=500, area_threshold=500, merge=True, margin=20
)
if not blobs:
return bytes() # Sin detecciones.
for b in blobs:
img.draw_detection(b, color1=(255, 0, 0), color2=(0, 255, 0))
out_blob = max(blobs, key=lambda b: b.density)
return struct.pack("<HH", out_blob.cx, out_blob.cy)
# Cuando se llama, devuelve una imagen comprimida en jpeg de la OpenMV
# Cam en una sola llamada RPC.
#
# data no se utiliza
def jpeg_snapshot(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
img.to_jpeg(quality=90)
return img.bytearray()
# Registrar funciones callback.
interface.register_callback(face_detection)
interface.register_callback(qrcode_detection)
interface.register_callback(all_qrcode_detection)
interface.register_callback(apriltag_detection)
interface.register_callback(all_apriltag_detection)
interface.register_callback(datamatrix_detection)
interface.register_callback(all_datamatrix_detection)
interface.register_callback(barcode_detection)
interface.register_callback(all_barcode_detection)
interface.register_callback(color_detection)
interface.register_callback(jpeg_snapshot)
# Una vez registradas todas las funciones callback, podemos comenzar
# a procesar eventos remotos. interface.loop() no retorna.
interface.loop()