popular_features_as_the_remote_device.py
# Ce travail est publié sous licence MIT.
# Copyright (c) 2013-2023 OpenMV LLC. Tous droits réservés.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Contrôle à distance - En tant que périphérique distant
#
# Ce script configure votre caméra OpenMV en tant que coprocesseur pouvant être contrôlé à distance par
# un autre microcontrôleur ou ordinateur tel qu'un Arduino, un ESP8266/ESP32, un RaspberryPi, ou
# même une autre caméra OpenMV.
#
# Ce script est conçu pour fonctionner avec "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 bibliothèque RPC mentionnée ci-dessus est installée sur votre caméra OpenMV et fournit plusieurs classes
# permettant de contrôler votre caméra OpenMV via CAN, I2C, SPI ou UART.
################################################################
# Choisissez l'interface que vous souhaitez utiliser pour contrôler votre caméra OpenMV.
################################################################
# Décommentez la ligne ci-dessous pour configurer votre caméra OpenMV pour un contrôle via CAN.
#
# * message_id - message CAN à utiliser pour le transport de données sur le bus CAN (11 bits).
# * bit_rate - débit binaire du bus CAN.
# * sample_point - ratio Tseg1/Tseg2. Typiquement 75 %. (50,0, 62,5, 75, 87,5, etc.)
#
# REMARQUE : les identifiants de message et débits binaires CAN du maître et de l'esclave doivent correspondre. Reliez le CAN high du maître au CAN high
# de l'esclave et le CAN low du maître au CAN low de l'esclave. Le bus CAN doit être terminé avec une résistance de 120 ohms.
#
# interface = rpc.rpc_can_slave(message_id=0x7FF, bit_rate=250000, sample_point=75)
# Décommentez la ligne ci-dessous pour configurer votre caméra OpenMV pour un contrôle via I2C.
#
# * slave_addr - adresse I2C.
#
# REMARQUE : les adresses du maître et de l'esclave doivent correspondre. Reliez le scl du maître au scl de l'esclave et le sda du maître
# au sda de l'esclave. Vous devez utiliser des résistances de tirage (pull-up) externes. Enfin, les deux appareils doivent partager une masse commune.
#
# interface = rpc.rpc_i2c_slave(slave_addr=0x12)
# Décommentez la ligne ci-dessous pour configurer votre caméra OpenMV pour un contrôle via SPI.
#
# * cs_pin - broche de sélection de l'esclave (Slave Select).
# * clk_polarity - niveau d'horloge au repos (0 ou 1).
# * clk_phase - échantillonner les données sur le premier (0) ou le second front (1) de l'horloge.
#
# REMARQUE : les réglages du maître et de l'esclave doivent correspondre. Reliez CS, SCLK, MOSI, MISO à CS, SCLK, MOSI, MISO.
# Enfin, les deux appareils doivent partager une masse commune.
#
# interface = rpc.rpc_spi_slave(cs_pin="P3", clk_polarity=1, clk_phase=0)
# Décommentez la ligne ci-dessous pour configurer votre caméra OpenMV pour un contrôle via UART.
#
# * baudrate - vitesse de transmission série.
#
# REMARQUE : les vitesses de transmission du maître et de l'esclave doivent correspondre. Reliez le tx du maître au rx de l'esclave et le rx du maître
# au tx de l'esclave. Enfin, les deux appareils doivent partager une masse commune.
#
interface = rpc.rpc_uart_slave(baudrate=115200)
################################################################
# Fonctions de rappel (Call Backs)
################################################################
# Méthodes d'aide utilisées par les fonctions de rappel ci-dessous.
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))
# Le contrôle à distance fonctionne via des méthodes de rappel que le
# périphérique contrôleur appelle via le module rpc sur ce périphérique. Les fonctions de rappel
# sont des fonctions qui prennent un objet bytes() en argument
# et renvoient un objet bytes() en résultat. Le module rpc
# se charge de transférer les objets bytes() à travers la liaison.
# bytes() peut atteindre la taille maximale d'un int en micropython.
# Lorsqu'elle est appelée, retourne x, y, w et h du plus grand visage dans le champ de vision.
#
# data n'est pas utilisé
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() # Aucune détection.
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])
# Lorsqu'elle est appelée, retourne la chaîne de charge utile (payload) du plus grand QR code
# dans le champ de vision de l'OpenMV Cam.
#
# data n'est pas utilisé
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() # Aucune détection.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Lorsqu'elle est appelée, retourne une liste JSON d'objets JSON qrcode pour tous les QR codes visibles.
#
# data n'est pas utilisé
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() # Aucune détection.
draw_detections(img, codes)
return str(codes).encode()
# Lorsqu'elle est appelée, retourne le centroïde x/y, le numéro d'identifiant et la rotation du plus grand
# AprilTag dans le champ de vision de l'OpenMV Cam.
#
# data n'est pas utilisé
def apriltag_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QQVGA)
img = csi0.snapshot()
tags = img.find_apriltags()
if not tags:
return bytes() # Aucune détection.
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)),
)
# Lorsqu'elle est appelée, retourne une liste JSON d'objets JSON apriltag pour tous les AprilTags visibles.
#
# data n'est pas utilisé
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() # Aucune détection.
draw_detections(img, tags)
return str(tags).encode()
# Lorsqu'elle est appelée, retourne la chaîne de charge utile (payload) du plus grand datamatrix
# dans le champ de vision de l'OpenMV Cam.
#
# data n'est pas utilisé
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() # Aucune détection.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Lorsqu'elle est appelée, retourne une liste JSON d'objets JSON datamatrix pour tous les datamatrices visibles.
#
# data n'est pas utilisé
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() # Aucune détection.
draw_detections(img, codes)
return str(codes).encode()
# Lorsqu'elle est appelée, retourne la chaîne de charge utile (payload) du plus grand code-barres
# dans le champ de vision de l'OpenMV Cam.
#
# data n'est pas utilisé
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() # Aucune détection.
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# Lorsqu'elle est appelée, retourne une liste JSON d'objets JSON barcode pour tous les codes-barres visibles.
#
# data n'est pas utilisé
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() # Aucune détection.
return str(codes).encode()
# Lorsqu'elle est appelée, retourne le centroïde x/y du plus grand blob
# dans le champ de vision de l'OpenMV Cam.
#
# data est le tuple de seuils de suivi de couleur de 6 octets 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() # Aucune détection.
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)
# Lorsqu'elle est appelée, retourne une image compressée JPEG depuis l'OpenMV
# Cam en un seul appel RPC.
#
# data n'est pas utilisé
def jpeg_snapshot(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
img.to_jpeg(quality=90)
return img.bytearray()
# Enregistre les callbacks.
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)
# Une fois tous les callbacks enregistrés, nous pouvons commencer
# à traiter les événements distants. interface.loop() ne retourne jamais.
interface.loop()