feat(video): base of video refactoring
This commit is contained in:
@@ -1,106 +0,0 @@
|
||||
import { DETECTION_TYPES } from '@/constants/detectionModeConfig';
|
||||
|
||||
/**
|
||||
* Color mapping for detection types
|
||||
*/
|
||||
export const DETECTION_COLORS: Record<string, string> = Object.keys(
|
||||
DETECTION_TYPES,
|
||||
).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = DETECTION_TYPES[key].color;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
// Cache for resolved colors to avoid DOM access in every frame
|
||||
const resolvedColorCache = new Map<string, string>();
|
||||
|
||||
/**
|
||||
* Resolves a color string that might contain CSS variables or modern color formats
|
||||
* into an RGB string that HTML5 Canvas understands.
|
||||
*/
|
||||
function resolveCanvasColor(colorStr: string): string {
|
||||
if (typeof window === 'undefined') return colorStr;
|
||||
|
||||
// Return from cache if already resolved
|
||||
if (resolvedColorCache.has(colorStr)) {
|
||||
return resolvedColorCache.get(colorStr)!;
|
||||
}
|
||||
|
||||
// Use a temporary element to let the browser resolve the color (handles var(), oklch(), etc.)
|
||||
try {
|
||||
const temp = document.createElement('div');
|
||||
temp.style.color = colorStr;
|
||||
temp.style.display = 'none';
|
||||
document.body.appendChild(temp);
|
||||
const resolved = getComputedStyle(temp).color;
|
||||
document.body.removeChild(temp);
|
||||
|
||||
if (resolved && resolved !== 'transparent') {
|
||||
resolvedColorCache.set(colorStr, resolved);
|
||||
return resolved;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to resolve color:', colorStr, e);
|
||||
}
|
||||
|
||||
return colorStr;
|
||||
}
|
||||
|
||||
export const drawBoundingBoxes = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
detections: any[],
|
||||
canvasWidth: number,
|
||||
canvasHeight: number,
|
||||
videoWidth: number,
|
||||
videoHeight: number,
|
||||
) => {
|
||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
if (!detections || detections.length === 0) return;
|
||||
|
||||
const scaleX = canvasWidth / videoWidth;
|
||||
const scaleY = canvasHeight / videoHeight;
|
||||
|
||||
detections.forEach((detection) => {
|
||||
const bbox = detection.bbox;
|
||||
if (!bbox) return;
|
||||
|
||||
const x1 = bbox.x1 * scaleX;
|
||||
const y1 = bbox.y1 * scaleY;
|
||||
const x2 = bbox.x2 * scaleX;
|
||||
const y2 = bbox.y2 * scaleY;
|
||||
|
||||
const type = (detection.type || detection._detType || '').toLowerCase();
|
||||
const rawColor = DETECTION_COLORS[type] || '#3b82f6';
|
||||
const boxColor = resolveCanvasColor(rawColor);
|
||||
|
||||
// 1. Draw Border (Always Solid)
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.strokeStyle = boxColor;
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
// 2. Draw Fill (Transparent/Light - using globalAlpha for maximum compatibility)
|
||||
ctx.globalAlpha = 0.1; // 10% Opacity correctly shows the road
|
||||
ctx.fillStyle = boxColor;
|
||||
ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
// 3. Draw Label
|
||||
const id =
|
||||
detection.pothole_id ?? detection.signboard_id ?? detection.detection_id;
|
||||
const label = `${type.replace(/_/g, ' ')} #${id} ${(detection.confidence * 100).toFixed(0)}%`;
|
||||
|
||||
ctx.font = 'bold 12px sans-serif';
|
||||
const metrics = ctx.measureText(label);
|
||||
|
||||
// Label Background (Solid for readability)
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.fillStyle = boxColor;
|
||||
ctx.fillRect(x1, y1 - 20, metrics.width + 10, 20);
|
||||
|
||||
// Label Text (Solid White)
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillText(label, x1 + 5, y1 - 6);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user