popular_features_as_the_remote_device.py
# Эта работа лицензирована по лицензии MIT.
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Удалённое управление — в роли удалённого устройства
#
# Этот скрипт настраивает вашу камеру OpenMV Cam как сопроцессор, которым можно удалённо управлять с помощью
# другого микроконтроллера или компьютера, например Arduino, ESP8266/ESP32, RaspberryPi, а также
# даже другой камеры OpenMV Cam.
#
# Этот скрипт предназначен для работы в паре со скриптом "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)
# Указанная выше библиотека RPC установлена на вашей камере OpenMV Cam и предоставляет несколько классов,
# позволяющих управлять вашей камерой OpenMV Cam по CAN, I2C, SPI или UART.
################################################################
# Выберите интерфейс, через который вы хотите управлять камерой OpenMV Cam.
################################################################
# Раскомментируйте следующую строку, чтобы настроить камеру OpenMV Cam для управления по CAN.
#
# * message_id — идентификатор CAN-сообщения для передачи данных по шине CAN (11 бит).
# * bit_rate — скорость передачи по CAN.
# * sample_point — соотношение Tseg1/Tseg2. Обычно 75%. (50.0, 62.5, 75, 87.5 и т. д.)
#
# ПРИМЕЧАНИЕ: идентификаторы сообщений и скорости CAN у ведущего и ведомого устройств должны совпадать. Соедините CAN high ведущего с
# CAN high ведомого, а CAN low ведущего — с CAN low ведомого. Шина CAN должна быть терминирована резисторами 120 Ом.
#
# interface = rpc.rpc_can_slave(message_id=0x7FF, bit_rate=250000, sample_point=75)
# Раскомментируйте следующую строку, чтобы настроить камеру OpenMV Cam для управления по I2C.
#
# * slave_addr — адрес I2C.
#
# ПРИМЕЧАНИЕ: адреса ведущего и ведомого устройств должны совпадать. Соедините scl ведущего со scl ведомого, а sda ведущего
# со sda ведомого. Необходимо использовать внешние подтягивающие резисторы. Наконец, оба устройства должны иметь общую землю.
#
# interface = rpc.rpc_i2c_slave(slave_addr=0x12)
# Раскомментируйте следующую строку, чтобы настроить камеру OpenMV Cam для управления по SPI.
#
# * cs_pin — вывод выбора ведомого устройства (Slave Select).
# * clk_polarity — уровень тактового сигнала в состоянии простоя (0 или 1).
# * clk_phase — выборка данных по первому (0) или второму (1) фронту тактового сигнала.
#
# ПРИМЕЧАНИЕ: настройки ведущего и ведомого устройств должны совпадать. Соедините CS, SCLK, MOSI, MISO с CS, SCLK, MOSI, MISO соответственно.
# Наконец, оба устройства должны иметь общую землю.
#
# interface = rpc.rpc_spi_slave(cs_pin="P3", clk_polarity=1, clk_phase=0)
# Раскомментируйте следующую строку, чтобы настроить камеру OpenMV Cam для управления по UART.
#
# * baudrate — скорость передачи последовательного порта.
#
# ПРИМЕЧАНИЕ: скорости передачи ведущего и ведомого устройств должны совпадать. Соедините tx ведущего с rx ведомого, а rx ведущего —
# с tx ведомого. Наконец, оба устройства должны иметь общую землю.
#
interface = rpc.rpc_uart_slave(baudrate=115200)
################################################################
# Обратные вызовы
################################################################
# Вспомогательные методы, используемые обратными вызовами ниже.
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))
# Удалённое управление работает через методы обратного вызова, которые
# устройство-контроллер вызывает через модуль rpc на этом устройстве. Обратные вызовы —
# это функции, принимающие объект bytes() в качестве аргумента
# и возвращающие объект bytes() в качестве результата. Модуль rpc
# берёт на себя передачу объектов bytes() по каналу связи.
# размер bytes() может достигать максимального размера int в MicroPython.
# При вызове возвращает x, y, w и h самого крупного лица в кадре.
#
# данные не используются
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() # Обнаружений нет.
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])
# При вызове возвращает строку данных самого крупного QR-кода
# в поле зрения камеры OpenMV Cam.
#
# данные не используются
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() # Обнаружений нет.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# При вызове возвращает json-список json-объектов для всех QR-кодов в кадре.
#
# данные не используются
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() # Обнаружений нет.
draw_detections(img, codes)
return str(codes).encode()
# При вызове возвращает координаты x/y центра, номер id и поворот самой крупной
# метки AprilTag в поле зрения камеры OpenMV Cam.
#
# данные не используются
def apriltag_detection(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QQVGA)
img = csi0.snapshot()
tags = img.find_apriltags()
if not tags:
return bytes() # Обнаружений нет.
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)),
)
# При вызове возвращает json-список json-объектов для всех меток AprilTag в кадре.
#
# данные не используются
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() # Обнаружений нет.
draw_detections(img, tags)
return str(tags).encode()
# При вызове возвращает строку данных самого крупного Data Matrix
# в поле зрения камеры OpenMV Cam.
#
# данные не используются
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() # Обнаружений нет.
draw_detections(img, codes)
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# При вызове возвращает json-список json-объектов для всех кодов Data Matrix в кадре.
#
# данные не используются
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() # Обнаружений нет.
draw_detections(img, codes)
return str(codes).encode()
# При вызове возвращает строку данных самого крупного штрихкода
# в поле зрения камеры OpenMV Cam.
#
# данные не используются
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() # Обнаружений нет.
return max(codes, key=lambda c: c.w * c.h).payload.encode()
# При вызове возвращает json-список json-объектов для всех штрихкодов в кадре.
#
# данные не используются
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() # Обнаружений нет.
return str(codes).encode()
# При вызове возвращает координаты x/y центра самого крупного блоба
# в поле зрения камеры OpenMV Cam.
#
# данные — это 6-байтовый кортеж порогов цветового отслеживания 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() # Обнаружений нет.
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)
# При вызове возвращает jpeg-сжатое изображение с камеры OpenMV
# Cam за один вызов RPC.
#
# данные не используются
def jpeg_snapshot(data):
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
img.to_jpeg(quality=90)
return img.bytearray()
# Зарегистрировать обратные вызовы.
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)
# После регистрации всех обратных вызовов можно начинать
# обработку удалённых событий. interface.loop() не возвращает управление.
interface.loop()