diff options
Diffstat (limited to 'crates/asist-tools/src/camera.rs')
| -rw-r--r-- | crates/asist-tools/src/camera.rs | 56 |
1 files changed, 28 insertions, 28 deletions
diff --git a/crates/asist-tools/src/camera.rs b/crates/asist-tools/src/camera.rs index bdc2aa0..9f17c88 100644 --- a/crates/asist-tools/src/camera.rs +++ b/crates/asist-tools/src/camera.rs @@ -1,4 +1,4 @@ -//! La cámara como fuente de imágenes. +//! The camera as an image source. use std::path::PathBuf; use std::time::Duration; @@ -13,19 +13,19 @@ const TOOL: &str = "mirar_por_la_camara"; pub struct CameraConfig { /// Dispositivo V4L2. pub device: PathBuf, - /// Resolución de captura. Medido con este modelo: 1,3 s a 320x240, 2,9 s a - /// 640x480 y 7,8 s a 1280x720, con la misma descripción útil a partir de - /// 640. Para una escena basta; para leer texto no (eso es la pantalla). + /// Capture resolution. Measured with this model: 1.3 s at 320x240, 2.9 s + /// at 640x480 and 7.8 s at 1280x720, with the same useful description from + /// 640 up. Enough for a scene; not for reading text (that is the screen). pub width: u32, pub height: u32, - /// Fotogramas que se descartan antes de quedarse con uno. + /// Frames discarded before keeping one. /// - /// La cámara arranca con la exposición automática sin asentar y el primer - /// fotograma suele salir quemado. Descartar unos pocos es prácticamente - /// gratis: medido, 0,45 s frente a 0,53 s. + /// The camera starts with auto-exposure not settled yet and the first frame + /// is usually blown out. Discarding a few is practically free: measured, + /// 0.45 s versus 0.53 s. pub warmup_frames: u32, pub timeout: Duration, - /// Carpeta donde dejar los fotogramas. `None` = no se guarda ninguno. + /// Directory to leave frames in. `None` = none is saved. pub save_dir: Option<PathBuf>, } @@ -62,7 +62,7 @@ impl FrameSource for Camera { return Err(Error::Tool { tool: TOOL.into(), message: format!( - "no existe {}; comprueba con «v4l2-ctl --list-devices»", + "{} does not exist; check with «v4l2-ctl --list-devices»", self.config.device.display() ), }); @@ -73,9 +73,9 @@ impl FrameSource for Camera { fn capture(&self) -> Result<Vec<u8>> { self.available()?; - // ffmpeg y no V4L2 a pelo: una cámara USB entrega MJPEG, YUYV o lo que - // le parezca, y reimplementar esa negociación para ahorrarse un - // proceso no sale a cuenta. + // ffmpeg rather than raw V4L2: a USB camera delivers MJPEG, YUYV or + // whatever it likes, and reimplementing that negotiation to save one + // process is not worth it. let mut args = vec![ "-hide_banner".into(), "-loglevel".into(), @@ -89,8 +89,8 @@ impl FrameSource for Camera { self.config.device.to_string_lossy().into_owned(), ]; if self.config.warmup_frames > 0 { - // Se leen N fotogramas y se conserva el último: así la exposición - // se asienta sin abrir el dispositivo dos veces. + // N frames are read and the last one is kept: that way exposure + // settles without opening the device twice. args.push("-vf".into()); args.push(format!("select=eq(n\\,{})", self.config.warmup_frames)); } @@ -108,9 +108,9 @@ impl FrameSource for Camera { } } -/// Guarda una copia sólo si se ha pedido expresamente. Por defecto no se -/// escribe nada: un asistente que deja fotogramas por ahí es un problema de -/// privacidad, no una comodidad de depuración. +/// Saves a copy only if explicitly requested. By default nothing is +/// written: an assistant that leaves frames lying around is a privacy +/// problem, not a debugging convenience. pub(crate) fn save(dir: &std::path::Path, bytes: &[u8], prefix: &str) { let name = format!( "{prefix}-{}.jpg", @@ -122,7 +122,7 @@ pub(crate) fn save(dir: &std::path::Path, bytes: &[u8], prefix: &str) { if let Err(err) = std::fs::create_dir_all(dir).and_then(|()| std::fs::write(dir.join(name), bytes)) { - tracing::warn!(target: "vision", %err, "no se pudo guardar la captura"); + tracing::warn!(target: "vision", %err, "could not save the capture"); } } @@ -131,38 +131,38 @@ mod tests { use super::*; #[test] - fn un_dispositivo_inexistente_se_detecta_antes_de_registrar_la_herramienta() { + fn missing_device_is_detected_before_registering_the_tool() { let camera = Camera::new(CameraConfig { - device: PathBuf::from("/dev/video-que-no-existe"), + device: PathBuf::from("/dev/video-does-not-exist"), ..Default::default() }); let err = camera.available().unwrap_err().to_string(); - assert!(err.contains("no existe"), "{err}"); + assert!(err.contains("does not exist"), "{err}"); assert!( err.contains("v4l2-ctl"), - "el error debe decir cómo comprobarlo: {err}" + "the error must say how to check it: {err}" ); } #[test] - fn capturar_sin_dispositivo_falla_sin_llegar_a_ffmpeg() { + fn capturing_without_a_device_fails_before_ffmpeg() { let camera = Camera::new(CameraConfig { - device: PathBuf::from("/dev/video-que-no-existe"), + device: PathBuf::from("/dev/video-does-not-exist"), ..Default::default() }); assert!(camera.capture().is_err()); } #[test] - fn por_defecto_no_se_guarda_ningun_fotograma() { + fn no_frame_is_saved_by_default() { assert!( CameraConfig::default().save_dir.is_none(), - "guardar imágenes por defecto sería una fuga de privacidad" + "saving images by default would be a privacy leak" ); } #[test] - fn la_resolucion_por_defecto_es_la_medida_como_equilibrada() { + fn default_resolution_is_the_measured_balance() { let config = CameraConfig::default(); assert_eq!((config.width, config.height), (640, 480)); } |