13.1.10. Ventanas de terminal independientes¶
Tools → Open Terminal abre ventanas de terminal independientes, cada una de ellas una sesión en miniatura de OpenMV IDE en su propia ventana, con un visor del búfer de fotogramas (frame buffer), un histograma y un terminal interactivo, conectados a través del transporte que elijas. La conexión de la ventana principal no se ve afectada, de modo que un terminal independiente te permite observar una segunda cámara mientras la primera permanece conectada, o depurar una cámara al otro lado de una red.
Una ventana de terminal independiente sobre un puerto serie: el terminal interactivo a la izquierda con su barra de herramientas de ejecutar / detener / reiniciar, y el búfer de fotogramas (frame buffer) y el histograma a la derecha; se activan cuando la cámara transmite fotogramas dentro de la propia conexión.¶
New Terminal solicita uno de tres transportes:
Serial port – cualquier puerto serie a cualquier velocidad en baudios (115.200 por defecto). Esto abarca el puerto USB de una segunda cámara, una cámara conectada mediante un puente USB-a-UART, un enlace serie por Bluetooth (que aparece como un puerto serie normal) o cualquier dispositivo serie ajeno a OpenMV que necesite un terminal. En un puerto USB la velocidad en baudios no limita la velocidad real – los datos siempre se mueven a la velocidad del enlace USB – pero en el puerto USB de una cámara evita 921.600 y 12.000.000, que conmutan la cámara del REPL al protocolo de depuración del IDE.
TCP – conéctate a un servidor en un host y puerto elegidos, o escucha como tal en un puerto elegido.
UDP – el mismo par de funciones, pero sobre UDP.
El IDE recuerda las últimas diez configuraciones y las muestra en el submenú Open Terminal para reabrirlas con un solo clic; Clear Menu las olvida.
A diferencia del panel de solo salida de la ventana principal, un terminal independiente es totalmente interactivo: es un REPL. Escribe en el prompt y Python se ejecuta en la cámara conectada línea a línea, con historial y autocompletado por tabulador proporcionados por el propio MicroPython. La barra de herramientas añade equivalentes de un clic a las secuencias de control habituales – ejecutar el script actual del editor, detener el script en ejecución y soft reset – y los mismos controles de limpiar, guardar y ajuste de línea que el panel de terminal principal.
El búfer de fotogramas (frame buffer) situado sobre el terminal también está en vivo. Cuando la cámara conectada transmite fotogramas comprimidos dentro de la propia conexión – incrustados en el mismo flujo que su salida de impresión – el terminal los decodifica y los muestra, y los botones Record y Zoom funcionan exactamente igual que en la ventana principal. Esa combinación es la propuesta de depuración por red del IDE: una cámara que expone su REPL por Wi-Fi obtiene el ciclo completo de editar-ejecutar-previsualizar sin ningún cable USB de por medio.
13.1.10.1. Transmisión de fotogramas dentro de la conexión¶
La codificación que entiende el terminal es sencilla: un byte 0xFE abre un fotograma, un segundo 0xFE lo cierra, y cada byte de carga útil entre ambos tiene su bit más alto activado y transporta seis bits de la imagen comprimida – tres bytes de imagen se convierten en cuatro bytes de carga útil. El texto plano nunca utiliza esos valores de byte, así que los fotogramas y la salida de print() comparten el flujo sin colisionar: el terminal muestra el texto y muestra los fotogramas.
El script siguiente captura, convierte cada fotograma a JPEG y lo imprime en ese formato. El empaquetado de bits se realiza mediante ulab (que no tiene operadores de desplazamiento, por lo que los desplazamientos se escriben como multiplicaciones y divisiones), y es lo bastante rápido como para seguir el ritmo de la cámara:
import csi
import sys
import time
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
def encode_for_ide(data):
n = len(data)
n3 = n - (n % 3)
m = (n3 // 3) * 4
out = bytearray(((n * 8) + 5) // 6 + 2)
out[0] = 0xFE
out[-1] = 0xFE
if n3:
src = np.frombuffer(data, dtype=np.uint8)
dst = np.frombuffer(out, dtype=np.uint8)
b0 = src[0:n3:3]
b1 = src[1:n3:3]
b2 = src[2:n3:3]
dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
dst[4:m + 4:4] = (b2 // 4) | 0x80
if n % 3 == 2:
x = data[n - 2] | (data[n - 1] << 8)
out[m + 1] = 0x80 | (x & 0x3F)
out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
elif n % 3 == 1:
out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
out[m + 2] = 0x80 | (data[n - 1] >> 6)
return out
while True:
clock.tick()
img = csi0.snapshot().to_jpeg(quality=80)
sys.stdout.write(encode_for_ide(img.bytearray()))
print(clock.fps())
Esto funciona en un terminal independiente porque la impresión del REPL espera: la cámara se bloquea hasta que el terminal ha tomado los datos, de modo que llega cada byte del fotograma, y un JPEG se mueve con rapidez por un enlace USB full-speed o high-speed. El mismo script funciona sin cambios sobre un terminal TCP, que es la vía de depuración por red descrita arriba. El único lugar donde no funciona es la conexión de depuración principal del IDE, cuyo canal de salida se reinicia a sí mismo ante un desbordamiento en lugar de esperar – ahí el visor del búfer de fotogramas (frame buffer) ya muestra los fotogramas de la cámara de forma nativa, así que no se pierde nada.