phase cv 0

This commit is contained in:
2026-03-26 22:22:35 -03:00
parent beb0416280
commit 65814b5b9e
46 changed files with 2962 additions and 268 deletions

View File

@@ -14,11 +14,22 @@ export interface FrameBBox {
ocr_text?: string | null
}
export interface FrameOverlay {
/** Base64 JPEG image (same dimensions as main image) */
src: string
label: string
visible: boolean
/** Opacity 0-1, default 0.5 */
opacity?: number
}
const props = defineProps<{
/** Base64 JPEG image */
imageSrc: string
/** Bounding boxes to overlay */
boxes: FrameBBox[]
/** Debug overlay layers (edge images, line visualizations, etc.) */
overlays?: FrameOverlay[]
}>()
const canvas = ref<HTMLCanvasElement | null>(null)
@@ -44,6 +55,10 @@ function draw() {
ctx.clearRect(0, 0, cvs.width, cvs.height)
ctx.drawImage(img, dx, dy, img.width * scale, img.height * scale)
// Draw debug overlays (edge images, line visualizations)
drawOverlays(ctx, dx, dy, img.width * scale, img.height * scale)
// Draw bounding boxes on top
for (const box of props.boxes) {
const bx = dx + box.x * scale
const by = dy + box.y * scale
@@ -53,7 +68,6 @@ function draw() {
const color = sourceColor(box)
const resolved = box.resolved_brand || box.ocr_text
// Box outline only — no labels, no percentages
ctx.strokeStyle = color
ctx.lineWidth = 2
if (!resolved) {
@@ -66,6 +80,29 @@ function draw() {
img.src = `data:image/jpeg;base64,${props.imageSrc}`
}
/** Pending overlay images that need async loading */
const overlayCache = new Map<string, HTMLImageElement>()
function drawOverlays(ctx: CanvasRenderingContext2D, dx: number, dy: number, dw: number, dh: number) {
const layers = props.overlays ?? []
for (const layer of layers) {
if (!layer.visible || !layer.src) continue
const cached = overlayCache.get(layer.src)
if (cached && cached.complete) {
ctx.globalAlpha = layer.opacity ?? 0.5
ctx.drawImage(cached, dx, dy, dw, dh)
ctx.globalAlpha = 1.0
} else if (!cached) {
// Load async, redraw when ready
const overlay = new window.Image()
overlay.onload = () => draw()
overlay.src = `data:image/jpeg;base64,${layer.src}`
overlayCache.set(layer.src, overlay)
}
}
}
const SOURCE_COLORS: Record<string, string> = {
yolo: '#f5a623', // yellow — raw detection
ocr: '#ff8c42', // orange — text extracted
@@ -75,9 +112,19 @@ const SOURCE_COLORS: Record<string, string> = {
unresolved: '#e05252', // red — nothing matched
}
// CV region labels — distinct from source-based colors
const REGION_COLORS: Record<string, string> = {
edge_region: '#00bcd4', // cyan
contour_region: '#ffd54f', // yellow
color_region: '#e040fb', // magenta
candidate: '#4caf50', // green — passed readability
rejected: '#e05252', // red — failed readability
}
function sourceColor(box: FrameBBox): string {
if (REGION_COLORS[box.label]) return REGION_COLORS[box.label]
if (box.resolved_brand) return SOURCE_COLORS.ocr_matched
if (box.source && box.source in SOURCE_COLORS) return SOURCE_COLORS[box.source]
if (box.source && SOURCE_COLORS[box.source]) return SOURCE_COLORS[box.source]
return confidenceColor(box.confidence)
}
@@ -87,7 +134,7 @@ function confidenceColor(conf: number): string {
return 'var(--conf-low)'
}
watch(() => [props.imageSrc, props.boxes], () => nextTick(draw), { deep: true })
watch(() => [props.imageSrc, props.boxes, props.overlays], () => nextTick(draw), { deep: true })
onMounted(() => {
nextTick(draw)

View File

@@ -21,7 +21,7 @@ const emit = defineEmits<{
}>()
const regionStageSet = computed(() => new Set(props.regionStages ?? [
'detect_objects', 'run_ocr', 'match_brands', 'escalate_vlm', 'escalate_cloud',
'detect_edges', 'detect_objects', 'run_ocr', 'match_brands', 'escalate_vlm', 'escalate_cloud',
]))
const statusColors: Record<string, string> = {