aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-tools/src/camera.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/asist-tools/src/camera.rs')
-rw-r--r--crates/asist-tools/src/camera.rs56
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));
}