fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
161 lines
5.3 KiB
Markdown
161 lines
5.3 KiB
Markdown
---
|
||
id: "0036"
|
||
title: "C++ image_canvas + webcam_texture"
|
||
status: pendiente
|
||
type: feature
|
||
domain:
|
||
- cpp-stack
|
||
scope: multi-app
|
||
priority: baja
|
||
depends: []
|
||
blocks: []
|
||
related: []
|
||
created: 2026-05-17
|
||
updated: 2026-05-17
|
||
tags: []
|
||
---
|
||
# 0036 — C++ image_canvas + webcam_texture
|
||
|
||
## APP Metadata
|
||
|
||
| Campo | Valor |
|
||
|-------|-------|
|
||
| **ID** | 0036 |
|
||
| **Estado** | pendiente |
|
||
| **Prioridad** | baja |
|
||
| **Tipo** | feature — C++ core/gfx (cpp/functions/core, cpp/functions/gfx) |
|
||
|
||
## Dependencias
|
||
|
||
`gl_texture_load_cpp_gfx` (issue 0026) — recomendado. `tokens_cpp_core`. Independiente de los demas.
|
||
|
||
**Desbloquea:** UIs para etiquetado/anotacion de imagenes (CV, OCR debugging) y entrada de webcam para `shaders_lab` reactivo a video.
|
||
|
||
---
|
||
|
||
## Objetivo
|
||
|
||
Dos primitivos:
|
||
|
||
1. **`image_canvas_cpp_core`** — viewer de imagen con pan/zoom + capa de anotaciones (rectangulos, puntos, polilineas, texto). Drag para crear nuevas anotaciones; click para seleccionar; tecla Del para borrar.
|
||
2. **`webcam_texture_cpp_gfx`** — captura de webcam (V4L2 en Linux, Media Foundation en Windows) → GL texture actualizada cada frame. API parejo a `audio_capture`.
|
||
|
||
## Contexto
|
||
|
||
Casos de uso identificados:
|
||
- Inspeccionar resultados de un detector (rectangulos overlay).
|
||
- Etiquetar manualmente datasets pequeños in-app.
|
||
- Pasar webcam a shaders en `shaders_lab` (filtros video en tiempo real).
|
||
|
||
Ningun primitivo del registry actual cubre estos casos.
|
||
|
||
## Arquitectura
|
||
|
||
```
|
||
cpp/functions/core/
|
||
├── image_canvas.h # NEW
|
||
├── image_canvas.cpp # NEW
|
||
└── image_canvas.md # NEW (impure component)
|
||
cpp/functions/gfx/
|
||
├── webcam_texture.h # NEW
|
||
├── webcam_texture.cpp # NEW
|
||
└── webcam_texture.md # NEW (impure)
|
||
cpp/apps/primitives_gallery/
|
||
├── demos_image_webcam.cpp # NEW
|
||
├── demos.h # MOD
|
||
├── main.cpp # MOD
|
||
└── CMakeLists.txt # MOD
|
||
```
|
||
|
||
### API propuesta
|
||
|
||
```cpp
|
||
namespace fn {
|
||
|
||
// --- image_canvas ---
|
||
struct Annotation {
|
||
enum Kind { Rect, Point, Polyline, Text } kind;
|
||
std::vector<ImVec2> points; // image-space coords (no screen)
|
||
ImU32 color = IM_COL32(255, 80, 80, 255);
|
||
std::string label;
|
||
};
|
||
struct ImageCanvasState {
|
||
GLuint texture = 0; // caller-owned
|
||
int img_w = 0, img_h = 0;
|
||
float zoom = 1.f;
|
||
ImVec2 pan = {0, 0};
|
||
std::vector<Annotation> annotations;
|
||
int selected = -1;
|
||
enum Tool { ToolNone, ToolRect, ToolPoint, ToolPolyline } tool = ToolNone;
|
||
};
|
||
void image_canvas(const char* id, ImageCanvasState&, ImVec2 size = {-1, -1});
|
||
|
||
// --- webcam_texture ---
|
||
struct WebcamTextureConfig { int width = 640; int height = 480; int fps = 30; int device = 0; };
|
||
struct WebcamTexture { GLuint id = 0; int w = 0; int h = 0; bool ok() const { return id != 0; } };
|
||
WebcamTexture webcam_texture_start(const WebcamTextureConfig&);
|
||
void webcam_texture_stop(WebcamTexture&);
|
||
bool webcam_texture_update(WebcamTexture&); // copia ultimo frame a GPU; true si hubo nuevo
|
||
const char* webcam_texture_last_error();
|
||
}
|
||
```
|
||
|
||
## Tareas
|
||
|
||
### Fase 1 — image_canvas
|
||
|
||
- 1.1 Render: `ImGui::Image(texture, ...)` con transform por pan/zoom. Drag con MMB para pan, wheel para zoom.
|
||
- 1.2 Tool == Rect: drag con LMB en image-space crea anotacion `Rect` con 2 puntos.
|
||
- 1.3 Tool == Point: click LMB anade un Point.
|
||
- 1.4 Tool == Polyline: click anade puntos; doble-click cierra.
|
||
- 1.5 Hit-test para selection; Del key elimina la seleccionada.
|
||
- 1.6 `.md`.
|
||
|
||
### Fase 2 — webcam_texture (Linux V4L2)
|
||
|
||
- 2.1 Abrir `/dev/video<device>`, MMAP buffers, VIDIOC_STREAMON.
|
||
- 2.2 En `update`, dequeue buffer, convert YUYV → RGBA (CPU), upload a GL texture, requeue.
|
||
- 2.3 `.md` (`kind: function`, `purity: impure`, `error_type`).
|
||
|
||
### Fase 3 — webcam_texture (Windows Media Foundation, opcional)
|
||
|
||
- 3.1 Si `_WIN32`, usar `IMFSourceReader`. Convert NV12 → RGBA.
|
||
- 3.2 Documentar que macOS no se soporta en MVP.
|
||
|
||
### Fase 4 — Gallery demo
|
||
|
||
- 4.1 `demos_image_webcam.cpp`:
|
||
- `demo_image_canvas()`: carga `assets/sample.png` (si existe — fallback a rendered placeholder), permite pintar rectangulos.
|
||
- `demo_webcam_texture()`: start cam, mostrar preview + sliders RGB sobre un shader fullscreen que samplea la cam.
|
||
- 4.2 Registrar.
|
||
|
||
### Fase 5 — Tests + docs
|
||
|
||
- 5.1 Tests puros de transform image-space ↔ screen-space.
|
||
- 5.2 Smoke test webcam (skip si no hay /dev/video0).
|
||
- 5.3 `./fn index`.
|
||
|
||
## Ejemplo de uso
|
||
|
||
```cpp
|
||
auto cam = fn::webcam_texture_start({});
|
||
if (!cam.ok()) std::fprintf(stderr, "%s\n", fn::webcam_texture_last_error());
|
||
|
||
fn::run_app("cam", [&]{
|
||
fn::webcam_texture_update(cam);
|
||
ImGui::Image((ImTextureID)(intptr_t)cam.id, {cam.w*1.f, cam.h*1.f});
|
||
});
|
||
```
|
||
|
||
## Decisiones de diseño
|
||
|
||
- **Anotaciones en image-space**: pan/zoom no afectan coordenadas guardadas. Permite serializar a JSON (issue futuro).
|
||
- **YUYV → RGBA en CPU**: simple, ~5ms/frame a 640×480. Si hace falta, hacer un compute shader (issue 0027).
|
||
- **macOS sin webcam por ahora**: AVFoundation requiere ObjC++; no compensa en MVP.
|
||
|
||
## Riesgos
|
||
|
||
- **Permisos webcam**: documentar troubleshooting (`/dev/video0` permisos en Linux).
|
||
- **Driver V4L2 raro**: algunos formatos de pixel no son YUYV. Documentar fallback a MJPEG (con stb_image decode) o error claro.
|
||
- **`ImTextureID` semantics** entre backends: documentar.
|