import type { CameraFrame, HandSample } from './types' function clamp01(v: number) { return Math.max(0, Math.min(1, v)) } /** * 开发者工具 / 不支持 VisionKit 时的粗开合估计: * 取画面中心 ROI 的亮度方差与边缘能量,映射到 openness。 * 仅用于打通链路与 UI 联调,不能作为产品识别。 */ export function estimateOpennessFromRoi(frame: CameraFrame): HandSample { const { width, height, data } = frame const u8 = new Uint8Array(data) const cx0 = Math.floor(width * 0.28) const cy0 = Math.floor(height * 0.22) const cx1 = Math.floor(width * 0.72) const cy1 = Math.floor(height * 0.78) const step = Math.max(4, Math.floor(Math.min(width, height) / 40)) let count = 0 let sum = 0 let sumSq = 0 let edge = 0 let prev = 0 for (let y = cy0; y < cy1; y += step) { for (let x = cx0; x < cx1; x += step) { const i = (y * width + x) * 4 const r = u8[i] const g = u8[i + 1] const b = u8[i + 2] // 粗略肤色门控 + 亮度 const luma = 0.299 * r + 0.587 * g + 0.114 * b const skinish = r > 60 && g > 30 && b > 20 && r >= g && Math.abs(r - g) > 8 if (!skinish && luma < 40) continue sum += luma sumSq += luma * luma edge += Math.abs(luma - prev) prev = luma count += 1 } } if (count < 20) { return { ts: Date.now(), openness: 0.5, score: 0.05, source: 'roi', } } const mean = sum / count const variance = Math.max(0, sumSq / count - mean * mean) const edgeNorm = edge / count // 张开手掌通常带来更大轮廓/边缘;握拳更紧凑。经验映射,仅联调。 const openness = clamp01((Math.sqrt(variance) / 55 + edgeNorm / 40) * 0.85) const score = clamp01(0.25 + count / 800) return { ts: Date.now(), openness, score, source: 'roi', } }