Unidades Fundamentales: Bits y Bytes
En arquitectura de computadoras y comunicaciones de red, la información se representa internamente mediante señales binarias. El bit es la unidad atómica (0 o 1), pero los sistemas operativos, la memoria RAM y los protocolos de red están diseñados para operar en bloques de 8 bits, conocidos como bytes. Esta alineación hardware-software convierte al byte en la unidad estándar para el almacenamiento, la serialización y el intercambio de datos.
Dado que un byte contiene 8 bits, su rango decimal va de 0 a 255. Para una representación compacta y legible, se utiliza notación hexadecimal, donde cada byte se expresa con dos dígitos hexadecimales (por ejemplo, \xe4). Esta característica hace que las secuencias de bytes sean el formato ideal para epmaquetar información antes de enviarla a través de sockets o APIs.
Cadenas de Texto vs. Datos Binarios
Es crucial distinguir entre texto legible y datos crudos:
- str (cadena de texto): Secuencia de caracteres Unicode abstractos. No tiene representación binaria directa hasta que se aplica un esquema de codificación.
- bytes (secuencia binaria): Contenedor inmutable de valores enteros (0-255). Representa datos ya codificados y listos para I/O o transmisión.
- Juegos de caracteres: Estándares como UTF-8 o UTF-16 definen cómo mapear caracteres Unicode a secuencias de bytes. UTF-8 es varaible (1 a 4 bytes por carácter) y es el estándar predominante en redes y sistemas modernos.
Codificación y Decodificación en Python
Para transmitir texto, primero debe transformarse a su representación binaria mediante un codec. El proceso inverso reconstruye el texto original.
# Texto original en formato Unicode
mensaje_texto = "Comunicación segura"
# Conversión a secuencia de bytes usando UTF-8
paquete_binario = mensaje_texto.encode(encoding="utf-8")
print(paquete_binario)
# Salida ejemplo: b'Comunicaci\xc3\xb3n segura'
# Reconstrucción del texto en el extremo receptor
mensaje_recuperado = paquete_binario.decode(encoding="utf-8")
Serialización de Imágenes y Arrays de NumPy para Redes
El envío de datos multimedia o matrices numéricas a través de la red requiere convertir los objetos en memoria a un flujo continuo de bytes. Existen dos enfoques principales según el origen de los datos.
Lectura directa desde disco y envío
Cuando la imagen ya existe como archivo, se puede leer en modo binario y transmitir sin cargarla en un array intermedio.
import requests
from pathlib import Path
ruta_captura = Path("/home/usuario/imagenes/captura_01.png")
# Lectura cruda del archivo
with ruta_captura.open(mode="rb") as flujo_entrada:
carga_util = flujo_entrada.read()
# Envío mediante petición HTTP
respuesta_servidor = requests.post(
url="http://192.168.10.50:9000/analizar",
params={"sensibilidad": 0.35},
data=carga_util
)
datos_procesados = respuesta_servidor.json()
Conversión en memoria desde OpenCV/NumPy
Si la imagen ya reside en memoria como un array de NumPy (por ejemplo, tras procesamiento con OpenCV), es obligatorio codificarla a un formato de imagen estándar antes de extraer los bytes.
import cv2
import numpy as np
matriz_cv2 = cv2.imread(str(ruta_captura))
# Paso crítico: codificar la matriz a un formato contenedor (JPEG/PNG)
estado_codificacion, buffer_imagen = cv2.imencode(ext=".jpg", img=matriz_cv2)
if estado_codificacion:
# Extracción del flujo binario listo para transmisión
flujo_red = buffer_imagen.tobytes()
# Simulación de recepción y persistencia en disco
with open("/home/usuario/imagenes/recibida_destino.jpg", "wb") as flujo_salida:
flujo_salida.write(flujo_red)
# Inspección de los primeros bytes del encabezado
print(flujo_red[:16])
Reconstrucción de Imágenes desde Flujos de Bytes
Al recibir datos binarios correspondientes a una imagen, es necesario transformarlos de vuelta a una estructura matricial manipulable. Se muestran dos vías según el origen del flujo.
# Vía 1: Decodificación desde un buffer en memoria
with open("recibida_destino.jpg", "rb") as f_mem:
datos_sin_procesar = f_mem.read()
# Conversión segura de bytes a array 1D de enteros sin signo
vector_crudo = np.frombuffer(datos_sin_procesar, dtype=np.uint8)
imagen_final = cv2.imdecode(vector_crudo, flags=cv2.IMREAD_COLOR)
# Vía 2: Carga directa mapeando el archivo en disco
ruta_disco = "muestra_prueba.jpg"
array_mapeado = np.fromfile(ruta_disco, dtype=np.uint8)
imagen_cargada = cv2.imdecode(array_mapeado, flags=cv2.IMREAD_COLOR)